首页
/ Model Context Protocol Memory Server 深度解析:基于本地知识图谱的跨会话持久化记忆

Model Context Protocol Memory Server 深度解析:基于本地知识图谱的跨会话持久化记忆

2026-09-04 09:50:09作者:宣海椒Queenly

Memory Server 是 MCP(Model Context Protocol)官方服务器集合中的一个参考实现,它用一个纯本地的 JSONL 知识图谱文件为 LLM(如 Claude)提供跨会话的长期记忆能力。本文以仓库内 src/memory/README.md 为主体,完整梳理其核心数据模型(实体/关系/观察)、9 个 MCP 工具的输入输出语义、memory://knowledge-graph 资源订阅机制,并结合 服务端入口源码 与配套测试用例,给出可直接复制运行的安装配置方案,帮助你在 Claude Desktop 或 VS Code 中落地一个真正"记得住用户"的 MCP 记忆服务。

一、Memory Server 的定位

根据 README,Memory Server 是一个"使用本地知识图谱实现持久化记忆的基础实现,让 Claude 能够跨聊天记住关于用户的信息"。它通过 stdio 传输接入 MCP 客户端,以 npm 包 @modelcontextprotocol/server-memory 发布(当前版本 0.6.3,包元数据中 mcpNameio.github.modelcontextprotocol/server-memory),也提供 Docker 镜像 mcp/memory

从源码结构看,整个服务器逻辑集中在单一入口文件 index.ts 中,由三部分组成:

  1. 存储与图管理KnowledgeGraphManager 类封装了知识图谱的加载、保存和全部增删改查操作(见 index.ts#L69-L239);
  2. 工具注册:通过 server.registerTool(...) 向客户端暴露 9 个 MCP 工具;
  3. 资源与订阅:注册 memory://knowledge-graph 资源,并支持客户端订阅资源更新通知(见 index.ts#L547-L586)。

服务端在 main() 函数 中完成内存文件路径解析、KnowledgeGraphManager 初始化、资源/订阅注册,最后连接 StdioServerTransport

二、核心数据模型:实体、关系与观察

README 将知识图谱的核心概念划分为三类,这也是理解整个工具 API 的基础。

2.1 实体(Entities)

实体是知识图谱中的主要节点。每个实体包含:

  • 唯一名称(标识符)
  • 实体类型(如 "person"、"organization"、"event")
  • 观察列表
{
  "name": "John_Smith",
  "entityType": "person",
  "observations": ["Speaks fluent Spanish"]
}

2.2 关系(Relations)

关系定义实体之间的有向连接,始终以主动语态存储,描述实体之间如何交互或关联:

{
  "from": "John_Smith",
  "to": "Anthropic",
  "relationType": "works_at"
}

2.3 观察(Observations)

观察是关于实体的离散信息片段,具有以下特征:

  • 以字符串形式存储
  • 挂在特定实体上
  • 可以独立增删
  • 应当是原子的(一条观察只描述一个事实)
{
  "entityName": "John_Smith",
  "observations": [
    "Speaks fluent Spanish",
    "Graduated in 2019",
    "Prefers morning meetings"
  ]
}

这三类结构在源码中有精确对应的 TypeScript 接口(index.ts#L51-L66):

export interface Entity {
  name: string;
  entityType: string;
  observations: string[];
}

export interface Relation {
  from: string;
  to: string;
  relationType: string;
}

export interface KnowledgeGraph {
  entities: Entity[];
  relations: Relation[];
}

2.4 磁盘存储格式:JSONL

从源码实现看,图谱并不以单个 JSON 对象落盘,而是采用 JSONL(每行一个 JSON 记录) 格式,每条记录通过 type 字段区分是实体还是关系(saveGraph 实现,index.ts#L102-L118)。例如一次写入 Alice 实体和 Alice → Bob 关系后,memory.jsonl 文件内容为:

{"type":"entity","name":"Alice","entityType":"person","observations":["likes coffee"]}
{"type":"relation","from":"Alice","to":"Bob","relationType":"knows"}

loadGraph() 读取时按行解析,仅按 type"entity""relation" 分发到对应数组,并在内存对象中剥离 type 字段(loadGraph 实现,index.ts#L72-L100)。持久化测试 专门验证了这些行为:

  • 数据能在两个 KnowledgeGraphManager 实例(同一文件路径)之间持久化;
  • 文件中每行记录的 type 字段确实存在(entity / relation);
  • 从文件重新加载后的实体/关系对象上 type 字段被剥离,只保留业务字段。

三、存储路径解析与旧格式迁移

README 中提到的环境变量 MEMORY_FILE_PATH("记忆存储 JSONL 文件的路径,默认是服务器目录下的 memory.jsonl")在源码中的完整解析逻辑见 ensureMemoryFilePath()

  1. 设置了 MEMORY_FILE_PATH:绝对路径原样使用;相对路径则解析为"相对于服务器入口文件所在目录"的绝对路径;
  2. 未设置:默认使用 path.dirname(入口文件) / memory.jsonl(由 defaultMemoryPath 定义);
  3. 向后兼容迁移:若目录下存在旧版单文件 memory.json 且不存在 memory.jsonl,服务器会用 fs.rename 将旧文件重命名为 memory.jsonl,并通过 console.error 输出 DETECTED: ...COMPLETED: ... 两条迁移日志;若两个文件都存在,则直接使用新文件、不执行迁移。

路径解析测试 对上述每条分支都做了断言,包括:绝对路径/相对路径/Windows 路径的处理、迁移时旧文件被移除且内容逐字节保留(should preserve file content during migration)、以及两文件并存时不触发任何迁移日志。

这意味着升级旧版 Memory Server 的用户无需手工搬移记忆文件;而使用 Docker 卷挂载时(见第六节),记忆文件的实际位置就是 /app/dist/memory.jsonl

四、工具 API 详解:9 个 MCP 工具

以下 9 个工具在 index.ts 中通过 server.registerTool 注册。README 中每个工具的输入参数与行为语义,均可在源码中找到一一对应。

4.1 总览

工具 作用 输入 关键语义
create_entities 批量创建实体 entities: [{name, entityType, observations}] 忽略同名已存在实体,只返回实际新增的实体
create_relations 批量创建关系 relations: [{from, to, relationType}] 跳过三元组(from/to/relationType)完全重复的关系
add_observations 为已有实体追加观察 observations: [{entityName, contents}] 实体不存在则抛错;返回每个实体实际新增的观察
delete_entities 删除实体及其关系 entityNames: string[] 级联删除关联关系;实体不存在时静默成功
delete_observations 删除指定观察 deletions: [{entityName, observations}] 观察/实体不存在时静默成功
delete_relations 删除指定关系 relations: [{from, to, relationType}] 关系不存在时静默成功
read_graph 读取整个图谱 返回全部实体与关系
search_nodes 按查询词检索节点 query: string 匹配实体名/类型/观察内容,返回匹配实体及相关关系
open_nodes 按名称精确取节点 names: string[] 返回指定实体及与其相连的关系,不存在的名称被跳过

4.2 写入工具的去重语义

三个写入工具的共同特点是"幂等式写入":重复调用不会造成数据重复。

  • createEntities 过滤掉名字已存在的实体(index.ts#L120-L126),并只返回真正新增的实体——测试用例 should not create duplicate entities 验证了第二次调用返回长度为 0 的数组,而图中实体总数保持为 1(测试)。
  • createRelations(from, to, relationType) 三元组判重(index.ts#L128-L138)。
  • addObservations 对同一实体的重复字符串自动去重(index.ts#L140-L153)。与另两个创建工具不同的是,目标实体不存在时它直接抛错 Entity with name xxx not found,而不会自动创建实体。这一点由测试 should throw error for non-existent entity 固化(测试)。

工具注册时还附带了 MCP 工具注解(annotations):写工具的 readOnlyHint: false,三个删除工具额外标记 destructiveHint: true,所有工具均标记 idempotentHint: false(删除类工具除外,它们标记为 true,与"静默操作"语义一致)。每个工具同时返回人类可读的 content(JSON 文本)和符合 outputSchemastructuredContent 结构化结果,方便支持结构化输出的客户端直接消费。

4.3 删除工具的级联与静默语义

deleteEntities 会同时过滤掉端点命中被删实体的所有关系index.ts#L155-L160),即"删除实体"必然清空其出入边。测试 should cascade delete relations when deleting entities 构造了 Alice → Bob → Charlie 两条关系,删除 Bob 后断言关系数量为 0(测试)。deleteObservationsdeleteRelations 则对"目标不存在"的情况不做任何报错——删除操作整体是幂等的。

4.4 读取工具:搜索与精确打开的差异

search_nodes 是一个基础的子串匹配检索(源码注释即标明 "Very basic search function",index.ts#L188-L213):

  • 在三个维度上做大小写不敏感的子串匹配:实体名 name、实体类型 entityType、以及任一观察内容;
  • 实体过滤后,再保留"至少一个端点在匹配集合中"的全部关系——源码注释解释了这样做的目的:"让调用方能够发现指向结果集之外节点的连接"(index.ts#L201-L205)。

searchNodes 测试Alice(person,观察含 programming)Bob(person)Acme Corp(company) 三个实体和两条关系验证了全部语义:按名字搜 Alice、按类型搜 company、按观察内容搜 programming、大小写不敏感(ALICE 命中 Alice)、以及搜索 Acme 时返回 2 个实体和 2 条关系(其中 Bob → Acme Corp 这条关系被带入,即使 Bob 本身不匹配)。

open_nodes 按名字精确取节点,不存在的名词被静默跳过。这里有一个值得注意的演进:源码注释(index.ts#L224-L227)指出旧版实现要求关系的两个端点都在请求集合中才会被返回,导致"从一个已打开节点到未请求节点"的关系被静默丢弃,无法在不读取全图的前提下发现某节点的连接;现实现改为"任一端点在请求集合中即返回"。openNodes 测试 专门针对这一修复设计了用例(如 openNodes(['Alice']) 必须返回 Alice → Bob 关系,openNodes(['Charlie']) 必须返回入边 Bob → Charlie)。

read_graph 无输入,直接 readGraph() 返回全图,等价于读取整个 JSONL 文件后的解析结果;文件不存在时返回空图 {entities: [], relations: []} 而非报错(见 loadGraph 的 ENOENT 分支 及对应测试)。

五、资源与订阅:memory://knowledge-graph

除了工具,README 还定义了 MCP Resource 层能力:

  • URImemory://knowledge-graph
  • MIME 类型application/json
  • 内容:与 read_graph 相同形状的完整图谱(实体 + 关系)
  • 更新通知:所有变更类工具(create_entitiescreate_relationsadd_observationsdelete_entitiesdelete_observationsdelete_relations)在执行后都会对该 URI 发出 notifications/resources/updated,使已订阅的客户端能实时感知图谱变化。

源码实现印证了这一机制的每个环节(index.ts#L262-L274#L547-L586):

  1. RESOURCE_URI = "memory://knowledge-graph",资源经 registerKnowledgeGraphResource 注册,标题为 "Knowledge Graph";
  2. 服务端用 registerCapabilities({ resources: { subscribe: true } }) 声明订阅能力,并注册 SubscribeRequestSchema / UnsubscribeRequestSchema 两个请求处理器,维护一个 resourceSubscribers 集合;
  3. 每次写工具执行完 saveGraph 后调用 notifyGraphUpdated()——只有当有客户端订阅了该 URI 时才真正调用 sendResourceUpdated,未订阅时为无操作(no-op),避免向不关心该资源的客户端推送噪音。

资源与订阅测试 用 mock 服务器验证:资源以 kebab-case 名称 knowledge-graph 注册且 MIME 为 application/json、handler 返回的 contents[0].textreadGraph() 结果完全一致、订阅/退订 handler 均返回空结果 {} 确认成功。

六、安装与部署

6.1 Claude Desktop 配置

README 给出三种接入方式。

方式一:NPX(最简单),写入 claude_desktop_config.json

{
  "mcpServers": {
    "memory": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-memory"]
    }
  }
}

Windows 下用 cmd /c 启动 npx

{
  "mcpServers": {
    "memory": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-memory"]
    }
  }
}

方式二:Docker

{
  "mcpServers": {
    "memory": {
      "command": "docker",
      "args": ["run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory"]
    }
  }
}

这里 -v claude-memory:/app/dist 将 Docker 命名卷挂载到 /app/dist,持久化容器内的工作目录(记忆文件 memory.jsonl 即生成于此)。

方式三:NPX + 自定义存储路径,通过 env 指定 MEMORY_FILE_PATH

{
  "mcpServers": {
    "memory": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-memory"],
      "env": {
        "MEMORY_FILE_PATH": "/path/to/custom/memory.jsonl"
      }
    }
  }
}

Windows 下同样使用 cmd /c 形式,env 配置不变。README 对该变量的说明是:记忆存储 JSONL 文件的路径,默认为服务器目录下的 memory.jsonl——与第三节源码中的路径解析逻辑一致。

6.2 VS Code 配置

README 提供了 VS Code(Stable 与 Insiders)的 NPX / Docker 一键安装按钮,也支持手工配置,两种方式:

  • 用户级配置(推荐):打开命令面板(Ctrl + Shift + P)执行 MCP: Open User Configuration,在打开的用户级 mcp.json 中添加服务器配置;
  • 工作区级配置:写入工作区下的 .vscode/mcp.json,便于在团队间共享配置。

VS Code 的配置文件结构与 Claude Desktop 略有不同——顶层键是 "servers" 而非 "mcpServers"

{
  "servers": {
    "memory": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-memory"]
    }
  }
}
{
  "servers": {
    "memory": {
      "command": "docker",
      "args": ["run", "-i", "-v", "claude-memory:/app/dist", "--rm", "mcp/memory"]
    }
  }
}

6.3 构建 Docker 镜像

在仓库根目录执行:

docker build -t mcp/memory -f src/memory/Dockerfile .

Dockerfile 采用两阶段构建:node:22.12-alpine 构建阶段编译 TypeScript 并裁剪 dev 依赖,node:22-alpine 发布阶段仅拷贝 dist/ 产物,ENTRYPOINTnode dist/index.js

重要注意事项(README 原文明确提示):如果使用 Docker 卷做存储,旧版 mcp/memory 卷中可能残留旧版的 index.js 文件,新容器启动时可能覆盖它。因此在新容器启动前,应删除旧卷中的 index.js 文件。

七、系统提示词指导:控制记忆的"写入颗粒度"

README 指出,"利用记忆的方式取决于用例;改变提示词可以帮助模型决定创建记忆的频率和类型",并给出一份面向聊天个性化场景的示例提示词(可放入 Claude.ai Project 的 "Custom Instructions" 字段):

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 observations

这份提示词与工具 API 的设计形成了对应关系:先"检索再回答"对应 search_nodes/open_nodes,"身份识别"对应以 default_user 为枢纽实体的建图习惯,"更新记忆"则对应 create_entities + create_relations + add_observations 的三步组合。也就是说,工具层提供的是原子的图操作,而何时记忆、记什么由系统提示词驱动——这是把该服务器用在生产会话中时需要自行调优的关键点。

八、实现要点小结与使用边界

结合源码与测试,可以总结该实现的设计取向与适用边界:

  • 存储模型简单透明:单个 JSONL 文件即全部状态,人类可直接查看、手工编辑和版本化;每次写操作都是"读全文件 → 内存修改 → 整文件重写"(loadGraph + saveGraph),无索引、无并发写保护,适合单客户端、低频写入的记忆场景,而非高并发数据源;
  • 语义全部可测试:去重、级联删除、静默失败、搜索/打开的关系端点规则、JSONL 格式、type 字段剥离、路径迁移等每一条行为都有 vitest 测试 固化,src/memory 目录下的 npm test 即运行这些用例;
  • 检索是朴素子串匹配search_nodes 无分词、无权重、无排序,适合中小规模记忆图的"找名字/找关键词"场景;
  • 实时性靠订阅:支持 MCP 资源订阅的客户端可以在每次写工具执行后收到 notifications/resources/updated,而不必轮询 read_graph;不支持订阅的客户端则退化为显式调用读取工具;
  • 部署差异点:NPX 模式下 MEMORY_FILE_PATH 决定记忆落在本地哪;Docker 模式下记忆落在挂载卷的 /app/dist/memory.jsonl,且升级镜像前注意清理旧卷残留的 index.js

整体来看,Memory Server 用最小依赖(一个文件、一个类、九个工具)演示了 MCP 服务器如何把"长期记忆"抽象为可被模型直接操作的图数据结构:数据模型在 README 中定义、实现收敛于 index.ts、行为由 tests/ 目录 逐条验证,是理解 MCP 工具/资源/订阅三种能力如何组合的一个典型参考样本。

登录后查看全文
热门项目推荐
相关项目推荐