Knowledge Graph Memory Server
Use it as a persistent local knowledge-graph memory for Claude, letting you manage and query entities, relations, and observations.
Create entities with names, types, and observations; existing or duplicate names are skipped.
Create directed active-voice relations between existing entities; duplicate relations are skipped.
Add observations to existing entities.
Delete entities with cascading relation deletion, delete specific observations, or delete specific relations.
Read the full graph, search nodes by name/type/observation content, or open specific nodes by name.
Use the
memory://knowledge-graphresource to read the graph as JSON, with update notifications after mutations.Persist memory in a local JSONL file configurable via
MEMORY_FILE_PATH, with atomic saves and inter-process locking.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Knowledge Graph Memory Serverremember that I'm allergic to peanuts and prefer video calls over meetings"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Knowledge Graph Memory Server
A basic implementation of persistent memory using a local knowledge graph. This lets Claude remember information about the user across chats.
Published on npm as @modelcontextprotocol/server-memory.
Core Concepts
Entities
Entities are the primary nodes in the knowledge graph. Each entity has:
A unique name (identifier)
An entity type (e.g., "person", "organization", "event")
A list of observations
Example:
{
"name": "John_Smith",
"entityType": "person",
"observations": ["Speaks fluent Spanish"]
}Relations
Relations define directed connections between entities. They are always stored in active voice and describe how entities interact or relate to each other.
Example:
{
"from": "John_Smith",
"to": "Anthropic",
"relationType": "works_at"
}Observations
Observations are discrete pieces of information about an entity. They are:
Stored as strings
Attached to specific entities
Can be added or removed independently
Should be atomic (one fact per observation)
Example:
{
"entityName": "John_Smith",
"observations": [
"Speaks fluent Spanish",
"Graduated in 2019",
"Prefers morning meetings"
]
}Related MCP server: Loc Knowledge Graph Memory Server
API
Tools
create_entities
Create multiple new entities in the knowledge graph
Input:
entities(array of objects)Each object contains:
name(string): Entity identifierentityType(string): Type classificationobservations(string[]): Associated observations
Ignores entities with existing names (and repeats of a name earlier in the same call); their observations are not added
Returns the created entities. When any were skipped, the structured result also lists their names in
skipped, and a second text item says which were skipped (useadd_observationsto extend an existing entity)
create_relations
Create multiple new relations between entities
Input:
relations(array of objects)Each object contains:
from(string): Source entity nameto(string): Target entity namerelationType(string): Relationship type in active voice
Skips duplicate relations
Fails if either the source or target entity doesn't exist
add_observations
Add new observations to existing entities
Input:
observations(array of objects)Each object contains:
entityName(string): Target entitycontents(string[]): New observations to add
Returns added observations per entity
Fails if entity doesn't exist
delete_entities
Remove entities and their relations
Input:
entityNames(string[])Cascading deletion of associated relations
No error if an entity doesn't exist; the response reports which names were not found
delete_observations
Remove specific observations from entities
Input:
deletions(array of objects)Each object contains:
entityName(string): Target entityobservations(string[]): Observations to remove
No error if an observation doesn't exist; the response reports how many were deleted
delete_relations
Remove specific relations from the graph
Input:
relations(array of objects)Each object contains:
from(string): Source entity nameto(string): Target entity namerelationType(string): Relationship type
No error if a relation doesn't exist; the response reports how many were deleted
read_graph
Read the entire knowledge graph
Optional input:
entityTypes(string[], at least one): return only entities of these types, and the relations that touch one of themobservationLimit(integer, 0 or more): see below
Returns complete graph structure with all entities and relations
With
observationLimit, each entity carries at most that many observations (the first ones, in the order they were added) and anobservationCountwith its total.observationLimit: 0returns names, types and counts only: a cheap overview of a large graph, from whichopen_nodesreads the entities you need in full
search_nodes
Search for nodes based on query
Input:
query(string, at most 2048 characters; a longer query is rejected)Searches across:
Entity names
Entity types
Observation content
Returns matching entities and their relations
Optional input:
limit(integer, 1 or more): return at most this many matches (the first, in stored order), with the relations that touch themobservationLimit(integer, 0 or more): as forread_graph
open_nodes
Retrieve specific nodes by name
Input:
names(string[])Returns:
Requested entities
Relations between requested entities
Silently skips non-existent nodes
Optional input:
observationLimit(integer, 0 or more), as forread_graph
Resources
knowledge-graph (
memory://knowledge-graph)The full knowledge graph as a readable MCP Resource
MIME type:
application/jsonReturns the same shape as
read_graph(entities and relations)Mutation tools (
create_entities,create_relations,add_observations,delete_entities,delete_observations,delete_relations) emitnotifications/resources/updatedfor this URI, so subscribed clients see live changesHow a client subscribes depends on its protocol version: a 2026-07-28 client names the URI in the
resourceSubscriptionsof itssubscriptions/listenrequest and gets the updates on that stream; a 2025-era client callsresources/subscribe
Protocol versions
The server serves both protocol eras on stdio from the same process. The client's first message picks the era of the connection: a 2026-07-28 client opens with server/discover and sends its protocol version, identity and capabilities with each request, and a 2025-era client opens with the initialize handshake. The tools and the resource are the same on both.
Requirements
Node.js 24 or later, when the server runs through npx or from source.
Usage with Claude Desktop
Setup
Add this to your claude_desktop_config.json:
Docker
{
"mcpServers": {
"memory": {
"command": "docker",
"args": ["run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory"]
}
}
}NPX
{
"mcpServers": {
"memory": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
]
}
}
}On Windows, use cmd /c to launch npx:
{
"mcpServers": {
"memory": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@modelcontextprotocol/server-memory"
]
}
}
}NPX with custom setting
The server can be configured using the following environment variables:
{
"mcpServers": {
"memory": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
],
"env": {
"MEMORY_FILE_PATH": "/path/to/custom/memory.jsonl"
}
}
}
}On Windows, use:
{
"mcpServers": {
"memory": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@modelcontextprotocol/server-memory"
],
"env": {
"MEMORY_FILE_PATH": "/path/to/custom/memory.jsonl"
}
}
}
}MEMORY_FILE_PATH: Path to the memory storage JSONL file (default:memory.jsonlin the server directory). A leading~expands to your home directory; a relative path resolves against the server directory. Set it to an absolute path you own; see Choosing where the graph is stored.
When MEMORY_FILE_PATH is not set and the server directory still holds a legacy memory.json but no memory.jsonl, the server renames memory.json to memory.jsonl at startup. If that rename fails, the server exits with an error naming both paths and the cause, rather than starting on an empty graph; fix the cause (a permission problem, say) and restart, or set MEMORY_FILE_PATH to choose a file explicitly.
Choosing where the graph is stored
Set MEMORY_FILE_PATH to an absolute path you own, such as ~/memory.jsonl, as in the configuration above. The default memory.jsonl sits in the server's own directory, next to its code. Under npx or an npm install, that directory is inside a node_modules tree, which npm manages as its own: reinstalling or updating the server, or clearing the npx cache, can delete the file and the knowledge graph in it. A relative MEMORY_FILE_PATH resolves against that same directory, so it has the same problem.
When the memory file it resolves is inside a
node_modulesdirectory, the server prints a warning on stderr at startup naming the file; most clients show a server's stderr in its log. Move the file to the new path when you set the variable, or the server starts on an empty graph.The directory must already exist: the server creates the memory file, not the directories above it.
The default stays where it is so that existing graphs keep loading. The Docker setup above keeps it in a named volume mounted over
/app/dist, so the graph there survives restarts and draws no warning.
Saves are atomic. Every write tool writes the whole graph to a temporary file in the same directory and renames it over the memory file, so a server stopped or killed mid-save leaves the previous graph intact rather than a half-written file. The file's permission bits are kept across the rename. Writes from several processes are serialized by a lock, described next.
Sharing one memory file between clients
Each MCP client starts its own server process, so two clients using the same MEMORY_FILE_PATH (or both using the default) are two processes writing one file. Every write tool takes an exclusive lock file, <memory file>.lock, next to the memory file while it reads, changes and saves the graph, so writes from different processes take turns and none is lost. The lock file exists only while a write is in progress, so the directory holding the memory file must be writable (it already must be, for the atomic save).
A lock left behind by a server that crashed mid-write is removed automatically: at once if its process is known to have exited on the same machine, otherwise once it has gone 30 seconds without being refreshed (a server holding the lock refreshes it every 15 seconds while it works). Only one waiting server breaks a stale lock at a time, guarded by a momentary
<memory file>.lock.breakfile.A write that cannot get the lock within 60 seconds fails with a tool error rather than overwriting the file.
The lock relies on exclusive file creation, which local filesystems provide; a network filesystem that does not honour it cannot be shared safely this way.
VS Code Installation Instructions
For quick installation, use one of the one-click installation buttons below:
For manual installation, you can configure the MCP server using one of these methods:
Method 1: User Configuration (Recommended)
Add the configuration to your user-level MCP configuration file. Open the Command Palette (Ctrl + Shift + P) and run MCP: Open User Configuration. This will open your user mcp.json file where you can add the server configuration.
Method 2: Workspace Configuration
Alternatively, you can add the configuration to a file called .vscode/mcp.json in your workspace. This will allow you to share the configuration with others.
For more details about MCP configuration in VS Code, see the official VS Code MCP documentation.
NPX
{
"servers": {
"memory": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-memory"
]
}
}
}On Windows, use:
{
"servers": {
"memory": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@modelcontextprotocol/server-memory"
]
}
}
}Docker
{
"servers": {
"memory": {
"command": "docker",
"args": [
"run",
"-i",
"-v",
"claude-memory:/app/dist",
"--rm",
"mcp/memory"
]
}
}
}System Prompt
The prompt for utilizing memory depends on the use case. Changing the prompt will help the model determine the frequency and types of memories created.
Here is an example prompt for chat personalization. You could use this prompt in the "Custom Instructions" field of a Claude.ai Project.
Follow these steps for each interaction:
1. User Identification:
- You should assume that you are interacting with default_user
- If you have not identified default_user, proactively try to do so.
2. Memory Retrieval:
- Always begin your chat by saying only "Remembering..." and retrieve all relevant information from your knowledge graph
- Always refer to your knowledge graph as your "memory"
3. Memory
- While conversing with the user, be attentive to any new information that falls into these categories:
a) Basic Identity (age, gender, location, job title, education level, etc.)
b) Behaviors (interests, habits, etc.)
c) Preferences (communication style, preferred language, etc.)
d) Goals (goals, targets, aspirations, etc.)
e) Relationships (personal and professional relationships up to 3 degrees of separation)
4. Memory Update:
- If any new information was gathered during the interaction, update your memory as follows:
a) Create entities for recurring organizations, people, and significant events
b) Connect them to the current entities using relations
c) Store facts about them as observationsBuilding
Docker:
docker build -t mcp/memory -f src/memory/Dockerfile . For Awareness: a prior mcp/memory volume contains an index.js file that could be overwritten by the new container. If you are using a docker volume for storage, delete the old docker volume's index.js file before starting the new container.
License
This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
Available Tools
9 toolsadd_observationsAdd ObservationsB
Add new observations to existing entities in the knowledge graph
| Name | Required | Description | Default |
|---|---|---|---|
| observations | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-read-only, non-destructive, non-idempotent write, so the safety profile is covered. The description adds the useful constraint that target entities must already exist, but says nothing about duplicate accumulation (relevant given idempotentHint=false) or error behavior for unknown entities.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single efficient sentence with the resource and scope front-loaded. Nothing is wasted, but at ten words it is close to under-specified rather than optimally concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained. However, for a mutation tool with an unannotated parameter structure and a non-idempotent flag, the description should at least cover append semantics and the failure mode when the named entity does not exist.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Reported schema description coverage is 0%, and the description contains no parameter guidance at all — nothing about the observations array, its entityName/contents fields, or the fact that observations are appended rather than replaced. The nested schema properties carry some of this, but the description does not compensate for the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb + resource: 'Add new observations to existing entities in the knowledge graph.' The qualifier 'existing' implicitly separates it from create_entities, though the sibling is never named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: the tool is for appending observations to entities that already exist. There is no explicit when/when-not guidance or routing to siblings like create_entities (for new entities) or delete_observations (for removal).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_entitiesCreate EntitiesA
Create multiple new entities in the knowledge graph. An entity whose name already exists, or repeats an earlier entity in the same call, is skipped and its observations are not added; the result lists the skipped names in skipped. Use add_observations to add observations to an existing entity.
| Name | Required | Description | Default |
|---|---|---|---|
| entities | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| skipped | No | |
| entities | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the generic write profile (readOnlyHint=false, idempotentHint=false, destructiveHint=false). The description adds genuinely non-obvious behavior: duplicate names within the call or against the graph are skipped rather than erroring, their observations are dropped, and skipped names surface in a `skipped` result field. It does not mention failure modes or auth, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, zero padding, front-loaded with the action before the conflict semantics and the sibling pointer. Every clause adds information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and the description still usefully flags the `skipped` field. Combined with annotations covering the write profile, the definition is close to complete; only the entity object structure is left to the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is reported at 0% (the top-level `entities` array is undocumented), so the description has to carry some of the load. It conveys that multiple entities with observations are passed and hints at the duplicate-name semantics, but never describes the entity object's name/entityType/observations shape. Partial compensation only.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create multiple new entities in the knowledge graph') and explicitly distinguishes itself from add_observations, the sibling used for existing entities. An agent can route between create_entities and add_observations without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly states the alternative for the adjacent case: 'Use add_observations to add observations to an existing entity.' It does not spell out a when-not-to-use clause or contrast with create_relations, but the primary routing decision is covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_relationsCreate RelationsB
Create multiple new relations between entities in the knowledge graph. Relations should be in active voice
| Name | Required | Description | Default |
|---|---|---|---|
| relations | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| relations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the mutation/safety profile is covered. The description adds one genuinely useful domain convention (relations in active voice) that the annotations do not carry, but it is silent on non-idempotency — whether re-submitting the same relation creates duplicates or errors — which is the key behavioral risk here.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the action, with no filler. The active-voice rule is stated tersely, though it arrives as an orphan second sentence rather than being attached to the relationType field it governs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and annotations cover the safety profile. What remains missing for a batch, non-idempotent graph mutation — duplicate handling, partial-failure semantics, and whether `from`/`to` entities must already exist — is not addressed anywhere.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Top-level schema description coverage is 0% and the description adds no meaning for the single `relations` parameter: it never explains that the argument is an array of {from, to, relationType} objects, nor whether entity names must match existing entities. Only the nested item-level field descriptions in the schema carry any semantics, and the description does not leverage or extend them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Create multiple new relations") plus the domain scope ("between entities in the knowledge graph"). It is clearly distinguishable in kind from siblings like delete_relations and create_entities, though it never names them explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied by the verb: call this when adding new relations. There is no statement of when-not to use it, no mention of the create_entities prerequisite, and no routing guidance versus siblings. The "active voice" sentence is a formatting convention rather than invocation guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_entitiesDelete EntitiesADestructiveIdempotent
Delete multiple entities and their associated relations from the knowledge graph
| Name | Required | Description | Default |
|---|---|---|---|
| entityNames | Yes | An array of entity names to delete |
Output Schema
| Name | Required | Description |
|---|---|---|
| message | Yes | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true and readOnlyHint=false, so the safety profile is covered structurally. The description adds genuinely new behavioral context: deletion is both batched and cascading, taking associated relations with it. It does not describe partial-failure behavior, which keeps it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence that front-loads the action and packs the two salient facts (batch scope, relation cascade) with no filler. Nothing to trim and nothing buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and annotations carry the destructive/idempotent profile. The cascade is called out, leaving only edge behavior (nonexistent names, partial results) unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With one parameter at 100% schema description coverage, the schema already fully documents entityNames. The description only echoes the batch nature ("multiple entities") and adds no format, naming, or matching semantics beyond what the schema states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb (Delete) and resource (entities), plus batch scope ("multiple") and the cascade effect ("their associated relations"), which meaningfully separates it from delete_relations and delete_observations. It stops short of explicitly naming a sibling to route against, so it does not reach a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The scope term "multiple" implies this is the bulk deletion path, but the description never states when to prefer it over delete_relations or how to remove individual entities. Usage is inferred rather than instructed, and no prerequisites or warnings are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_observationsDelete ObservationsCDestructiveIdempotent
Delete specific observations from entities in the knowledge graph
| Name | Required | Description | Default |
|---|---|---|---|
| deletions | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| message | Yes | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the safety profile is fully covered by structured data. The description adds nothing beyond restating the operation: it does not say the deletion is permanent, whether missing observations are silently skipped, or what happens on partial failure across multiple entities.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with no filler or repetition. It is efficiently sized for the operation, though the brevity reflects under-specification rather than disciplined concision.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is low-complexity (one parameter) and has an output schema plus destructive/idempotent annotations, so the missing return-value and safety detail is largely covered elsewhere. The remaining real gap is routing guidance among the many sibling mutation tools, which the description leaves unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% at the parameter level, and the description does not compensate by explaining the 'deletions' array shape, batching semantics, or match requirements. Only the nested entityName/observations fields carry schema descriptions, so the description contributes almost no additional parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (delete) and resource (observations belonging to entities), which distinguishes it from delete_entities and delete_relations by the level of granularity. However it does not explicitly name those siblings or contrast scope, so an agent must infer the distinction from the resource noun alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance, no prerequisites, and no mention of the sibling tools (add_observations to reverse, delete_entities for whole entities). The agent is left to infer timing entirely from the verb and resource.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_relationsDelete RelationsCDestructiveIdempotent
Delete multiple relations from the knowledge graph
| Name | Required | Description | Default |
|---|---|---|---|
| relations | Yes | An array of relations to delete |
Output Schema
| Name | Required | Description |
|---|---|---|
| message | Yes | |
| success | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the safety profile is covered. The description adds nothing beyond that: no note on partial failure behavior, whether missing relations are silently ignored, or any auth requirement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with zero filler; the verb and resource lead. It is efficient, though its brevity borders on under-specification rather than true economy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need no explanation, and the single parameter is fully documented in the schema. However, for a destructive batch operation the description leaves behavioral and routing questions entirely to annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the nested relation object's from/to/relationType fields are each documented in the schema, so the description need not compensate. It adds no syntax or format detail beyond the schema, which is the expected baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb (Delete), resource (relations), batch scope (multiple), and domain (knowledge graph). This distinguishes it from delete_entities and delete_observations, though the description never explicitly contrasts with those siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides no when-to-use guidance, no prerequisites, and no mention of alternatives such as delete_entities or delete_observations for removing other graph elements. The agent must infer usage purely from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_nodesOpen NodesBRead-onlyIdempotent
Open specific nodes in the knowledge graph by their names
| Name | Required | Description | Default |
|---|---|---|---|
| names | Yes | An array of entity names to retrieve |
Output Schema
| Name | Required | Description |
|---|---|---|
| entities | Yes | |
| relations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is fully covered. The description adds only the name-based scope, saying nothing about behavior with missing/nonexistent names; with annotations carrying the burden, a baseline 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler; nothing is repeated or wasted. It is not padded, though it is arguably too terse to be maximally informative.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return format need not be explained, and the single-param schema is well documented. However, for a tool with close siblings search_nodes and read_graph, the description gives the agent no basis for choosing among them, leaving a real contextual gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Single parameter with 100% schema description coverage ('An array of entity names to retrieve'), so the schema already carries the semantics. The description's 'by their names' merely echoes this without adding format, batching, or matching-behavior detail, matching the baseline 3 for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a verb ('Open') and resource ('specific nodes in the knowledge graph') scoped by name, which distinguishes it from the broad read_graph and the query-based search_nodes. The verb 'Open' is slightly unconventional for a retrieval operation, but the schema's 'retrieve' wording and the name-scoped phrasing make the intent discernible.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use guidance or named alternatives, even though the sibling set (search_nodes, read_graph) makes the selection question real. The agent must infer that you call this only when you already know the entity names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_graphRead GraphARead-onlyIdempotent
Read the entire knowledge graph
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| entities | Yes | |
| relations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint=false, so the safety profile is fully covered by structured data. The description's contribution is the 'entire' scope, which warns that the whole graph is returned (potentially large), but it says nothing about size, performance, or pagination.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single five-word sentence that front-loads the verb and scope with zero filler. Nothing could be removed without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-parameter, read-only tool with an output schema (so return values need not be described) and full annotation coverage, the description is nearly sufficient. The only missing piece is a note on expected payload size or when to prefer a scoped sibling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4. There is nothing for the description to disambiguate, and it correctly avoids inventing parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Read) and resource (knowledge graph) with an explicit scope qualifier (entire), so the agent knows this is a full-graph fetch rather than a filtered one. It does not name search_nodes or open_nodes, but 'entire' implicitly distinguishes it from those subset readers.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: 'entire' hints that this is for whole-graph retrieval versus the targeted sibling tools (search_nodes, open_nodes), but there is no explicit when-to-use or when-not-to-use statement. The agent must infer the tradeoff itself.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_nodesSearch NodesCRead-onlyIdempotent
Search for nodes in the knowledge graph based on a query
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | The search query to match against entity names, types, and observation content |
Output Schema
| Name | Required | Description |
|---|---|---|
| entities | Yes | |
| relations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint=false, so safety and repeatability are covered. The description adds nothing beyond that — no matching behavior (fuzzy vs exact), ranking, or result limit context — so it neither contradicts nor enriches the structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short, front-loaded sentence with no filler. It is efficient, though its brevity is also the source of the missing guidance rather than a virtue of tight editing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return structure need not be explained, and with one fully documented parameter the surface is small. Still, the description omits how search results are matched or ordered, which matters for a search tool with several retrieval siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the single parameter is fully documented in the schema (matching names, types, and observation content). The description only restates that a query drives the search, adding no syntax or format detail. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Search for nodes in the knowledge graph') with the query basis made explicit. However, it offers no differentiation from siblings like open_nodes or read_graph, so an agent cannot tell from the description alone which retrieval tool fits which situation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use search over alternatives such as open_nodes (direct lookup) or read_graph (full dump). No exclusions or preconditions are stated; the agent must infer the use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
9 tool updates
v2.0.0- Changed
add_observations2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
create_entities2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
create_relations2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
delete_entities2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
delete_observations2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
delete_relations2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
open_nodes2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
read_graph2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
search_nodes2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
9 tool updates
- Changed
add_observations2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "results": { + "items": { + "additionalProperties": false, + "properties": { + "addedObservations": { + "items": { + "type": "string" + }, + "type": "array" + }, + "entityName": { + "type": "string" + } + }, + "required": [ + "entityName", + "addedObservations" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "results" + ], + "type": "object" +}
- Changed
create_entities2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "entities": { + "items": { + "additionalProperties": false, + "properties": { + "entityType": { + "description": "The type of the entity", + "type": "string" + }, + "name": { + "description": "The name of the entity", + "type": "string" + }, + "observations": { + "description": "An array of observation contents associated with the entity", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "entityType", + "observations" + ], + "type": "object" + }, + "type": "array" + }, + "skipped": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entities" + ], + "type": "object" +}
- Changed
create_relations2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "relations": { + "items": { + "additionalProperties": false, + "properties": { + "from": { + "description": "The name of the entity where the relation starts", + "type": "string" + }, + "relationType": { + "description": "The type of the relation", + "type": "string" + }, + "to": { + "description": "The name of the entity where the relation ends", + "type": "string" + } + }, + "required": [ + "from", + "to", + "relationType" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "relations" + ], + "type": "object" +}
- Changed
delete_entities2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "success", + "message" + ], + "type": "object" +}
- Changed
delete_observations2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "success", + "message" + ], + "type": "object" +}
- Changed
delete_relations2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "success", + "message" + ], + "type": "object" +}
- Changed
open_nodes2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "entities": { + "items": { + "additionalProperties": false, + "properties": { + "entityType": { + "description": "The type of the entity", + "type": "string" + }, + "name": { + "description": "The name of the entity", + "type": "string" + }, + "observations": { + "description": "An array of observation contents associated with the entity", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "entityType", + "observations" + ], + "type": "object" + }, + "type": "array" + }, + "relations": { + "items": { + "additionalProperties": false, + "properties": { + "from": { + "description": "The name of the entity where the relation starts", + "type": "string" + }, + "relationType": { + "description": "The type of the relation", + "type": "string" + }, + "to": { + "description": "The name of the entity where the relation ends", + "type": "string" + } + }, + "required": [ + "from", + "to", + "relationType" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "entities", + "relations" + ], + "type": "object" +}
- Changed
read_graph2 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "entities": { + "items": { + "additionalProperties": false, + "properties": { + "entityType": { + "description": "The type of the entity", + "type": "string" + }, + "name": { + "description": "The name of the entity", + "type": "string" + }, + "observations": { + "description": "An array of observation contents associated with the entity", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "entityType", + "observations" + ], + "type": "object" + }, + "type": "array" + }, + "relations": { + "items": { + "additionalProperties": false, + "properties": { + "from": { + "description": "The name of the entity where the relation starts", + "type": "string" + }, + "relationType": { + "description": "The type of the relation", + "type": "string" + }, + "to": { + "description": "The name of the entity where the relation ends", + "type": "string" + } + }, + "required": [ + "from", + "to", + "relationType" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "entities", + "relations" + ], + "type": "object" +}
- Changed
search_nodes3 fields changed- added
Input schema / $schemaAdded value: +"http://json-schema.org/draft-07/schema#" - added
Input schema / properties / query / maxLengthAdded value: +2048 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "entities": { + "items": { + "additionalProperties": false, + "properties": { + "entityType": { + "description": "The type of the entity", + "type": "string" + }, + "name": { + "description": "The name of the entity", + "type": "string" + }, + "observations": { + "description": "An array of observation contents associated with the entity", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "entityType", + "observations" + ], + "type": "object" + }, + "type": "array" + }, + "relations": { + "items": { + "additionalProperties": false, + "properties": { + "from": { + "description": "The name of the entity where the relation starts", + "type": "string" + }, + "relationType": { + "description": "The type of the relation", + "type": "string" + }, + "to": { + "description": "The name of the entity where the relation ends", + "type": "string" + } + }, + "required": [ + "from", + "to", + "relationType" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "entities", + "relations" + ], + "type": "object" +}
9 tool updates
v1.0.0- First observed
add_observations - First observed
create_entities - First observed
create_relations - First observed
delete_entities - First observed
delete_observations - First observed
delete_relations - First observed
open_nodes - First observed
read_graph - First observed
search_nodes
TDQS
Scored across 9 tools
Each tool targets a distinct operation on a distinct object type (entities, relations, observations), and descriptions actively disambiguate (create_entities points to add_observations for existing entities). The only mild overlap is among the retrieval trio read_graph/search_nodes/open_nodes, though their query-vs-names-vs-full-graph semantics differ.
Names consistently use snake_case verb_noun form (create_entities, delete_relations, search_nodes, open_nodes). The only minor deviation is mixing 'create_' for entities/relations with 'add_' for observations, which is a small vocabulary inconsistency rather than a structural one.
Nine tools is well-scoped for a knowledge graph memory server, covering create/read/delete/search across the three graph primitives without redundancy. Each tool earns its place.
The surface covers creation, retrieval, deletion, and search for entities, relations, and observations. The main gap is the absence of true update operations (e.g. renaming an entity or editing an observation), which is only partially mitigated by delete-and-recreate or add_observations.
Maintenance
Related MCP Connectors
Persistent knowledge graph for AI agents. Remember, recall, and forget facts.
Persistent memory for AI agents across Claude, ChatGPT and any MCP client.
Persistent memory layer for AI tools. Save and recall notes across Claude and other MCP clients.
Personal wiki and memory layer for AI assistants. Persistent, structured memory across sessions.
Related MCP Servers
- AlicenseBqualityFmaintenanceA persistent memory implementation using a local knowledge graph that lets Claude remember information about users across conversations.96MIT
- AlicenseNot gradedqualityDmaintenanceEnables Claude to remember information about users across conversations by storing and retrieving data as entities, relations, and observations in a persistent local knowledge graph.299,999 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables Claude to remember information about users across chats using a persistent local knowledge graph that stores entities, relationships, and observations.299,999 npmMIT
- AlicenseAqualityDmaintenanceEnables Claude to maintain persistent memory across conversations using a local knowledge graph with fuzzy search capabilities, allowing it to remember and recall information about users, relationships, and context.9MIT