---
name: memory
description: "Graph memory plugin. Provides memory-search, memory-rank, memory-write, and memory-update tools for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses."
tools:
  - memory-search
  - memory-rank
  - memory-write
  - memory-update
  - memory-reindex
  - memory-ingest
  - memory-ingest-extract
  - memory-ingest-web
  - memory-delete
  - memory-list-attachments
  - memory-read-attachment
  - memory-edit-attachment
  - memory-rename-attachment
  - conversation-list
  - conversation-search
  - profile-read
  - profile-update
  - profile-delete
hidden:
  - session-compact
  - session-compact-status
skills:
  - skills/conversational-memory.md
always: true
embed: false
---

# Memory

Provides read and write access to the Neo4j knowledge graph — search, structured writes, document ingestion, attachment management, conversation history, and user profiles. Tool routing is each agent's IDENTITY.md responsibility; this plugin describes what tools are available, not when to use them. The admin agent and specialist subagents call these tools directly via MCP. The public agent has no MCP tools — its knowledge is assembled server-side from the graph into the system prompt.

Tools are available via the `memory` MCP server.

## LLM-Reasoned Ranking

`memory-rank` is a ranking tool built on top of `memory-search`. It retrieves a candidate pool from the graph using the same hybrid vector+BM25 pipeline, then passes the candidates to Claude Haiku along with a natural-language ranking criterion. The response is an ordered list with a short reasoning string per item explaining why each candidate landed where it did.

Ranking is ephemeral and contextual — nothing is written back to the graph. When the Anthropic API key is unavailable or the model call fails, the tool returns the unranked memory-search results with a visible `[Ranking unavailable: <reason>]` notice so the agent can tell the user that ranking did not run. The ranking criterion is sandboxed as a directive inside the user message; imperative instructions embedded in it are treated as data, not control flow.

## Search Output Size

`memory-search` enforces an output character budget. When the formatted output exceeds the budget, it degrades gracefully: related nodes are stripped from the lowest-scored results first, then entire results are dropped from the tail. A truncation notice at the end states what was trimmed.

Use `expandHops: 0` for listing and inventory queries (returns node properties only — compact output). Use `expandHops: 1` (default) for deep context queries where related nodes add value. Queries that combine a high `limit` with `expandHops: 1` on node types that have many relationships (e.g. KnowledgeDocument with HAS_SECTION) are most likely to trigger trimming.

## Conversational Memory

The owner's profile and preferences accumulate organically from conversation — never from questionnaires. Load the conversational memory skill via `plugin-read` for full guidance on when to observe preferences, how to handle remember/forget requests, and how to answer "what do you know about me?" transparently with confidence scores and evidence trail.

## UserProfile Field Management

`profile-update` serves two purposes: managing Preference nodes (category/key/value) and setting top-level UserProfile node properties via the `profileFields` parameter.

Settable UserProfile fields: `timezone` (IANA format, e.g. `Europe/London`), `locale`, `givenName`, `role`, `expertise`. These are account metadata stored directly on the UserProfile node — distinct from Preference nodes, which represent learned behavioural patterns.

Example — setting a user's timezone:
```json
{ "category": "scheduling", "key": "timezone_set", "value": "User requested timezone configuration", "profileFields": { "timezone": "Europe/London" } }
```

Restricted fields (`accountId`, `embedding`, `profileVersion`) cannot be set via `profileFields`.

## Schema References

Before any structured write, load `references/schema-base.md` via `plugin-read`. This defines universal node types, property naming rules, and relationship patterns. If the `LocalBusiness` node has a `businessType` property, also load the matching vertical schema (`references/schema-{businessType}.md`) — it extends the base with vertical-specific types. Confirm which schemas were consulted before writing.

## Document Ingestion

### Scope

Before calling `memory-ingest`, ask the user what visibility scope the document should have. Do not assume a default. Present the options:

- **public** — visible to public agents and the admin. Use for business knowledge that customers or visitors should be able to ask about (product info, services, FAQs, policies).
- **shared** — visible to all agents on the account but not surfaced to unauthenticated public visitors. Use for internal operational knowledge that multiple agents need.
- **admin** — visible only to the admin agent. Use for sensitive or internal-only content (contracts, credentials, internal processes).

If the user's intent is unambiguous from context — e.g., "save this for the sales agent" implies public, "this is just for my reference" implies admin — confirm the inferred scope rather than asking. When delegating to the content-producer, include the confirmed scope in the task description.

### Keywords

After a successful `memory-ingest` call, present the ingestion results to the user before moving on:

1. **Display the document summary** from the `documentSummary` field in the tool response.
2. **Display the extracted keywords** from the `keywords` array in the tool response (omit if absent).
3. **Prompt for user keywords** — e.g., "Would you like to add any of your own keywords or tags to file this under?"

If the user provides keywords, normalize them (lowercase, trim), merge with the existing `keywords` array (deduplicate), and call `memory-update` on the `documentNodeId` with the merged array as the `keywords` property. User-supplied keywords appear first in the merged array.

If the user declines or says nothing further, the flow is complete.

Always generate LLM-extracted keywords as `keywords` on the `memory-ingest` call. The two sources are complementary — LLM keywords capture topic signals; user keywords define the user's intended classification.

Keywords support user-defined collections via naming convention (e.g., `["reports", "reports/quarterly", "reports/quarterly/q1-2026"]`). When the user describes a hierarchical filing intent, build the full keyword path.
