首页 › tutorials › 造记忆库MCP:跨会话存取笔记

概述

本教程手把手带你用 TypeScript 从零造一个带持久化能力的「记忆库 MCP 服务器」。学完你将掌握:MCP 客户端/服务器/传输三件套的运行原理、如何用 McpServer 高层 API 声明带 schema 校验的工具、如何把数据落盘实现跨会话记忆、以及如何把它注册进 Claude Code 并用 MCP Inspector 调试。适合已跑通 Claude Code、想给自己的 AI 装上「长期记忆」的开发者。

前置条件


一、先搞懂 MCP:客户端、传输、三原语

动手前先把概念理清,否则后面排错会两眼一抹黑。MCP(Model Context Protocol)本质是一套基于 JSON-RPC 2.0 的通信协议,让 LLM 客户端(Claude Code 就是其中一个客户端)能调用外部服务器暴露的能力。

三个核心概念:

  1. 客户端(Host/Client):发起请求的一方。Claude Code 启动时会拉起你注册的服务器进程,并作为客户端与之对话。
  2. 服务器(Server):你写的程序,暴露工具给客户端调用。
  3. 传输(Transport):双方用什么通道收发消息。本地工具用 stdio(标准输入输出),远程服务用 Streamable HTTP / SSE。

服务器能暴露三类能力,也就是 MCP 的「三原语」:

⚠️ stdio 服务器的第一铁律:绝不能往 stdout 写日志。stdio 通道上跑的就是 JSON-RPC 报文,你一旦 console.log,就会把协议报文污染掉,客户端立刻解析失败、连接断开。要打日志只能写 console.error(stderr)或落文件。

整体架构如下:

flowchart LR
    U[用户在 Claude Code 里提问] --> CC[Claude Code 客户端]
    CC -- "JSON-RPC over stdio" --> SVR["memory-mcp 服务器\n(Node 进程)"]
    SVR --> T1[remember 工具]
    SVR --> T2[recall 工具]
    SVR --> T3[forget 工具]
    SVR --> T4[list_memory 工具]
    SVR --> T5[search_memory 工具]
    T1 --> DISK[(store.json 磁盘)]
    T2 --> DISK
    T3 --> DISK
    T5 --> DISK
    DISK -. 跨会话持久化 .-> CC

二、初始化 TypeScript 项目

mkdir -p ~/projects/memory-mcp && cd ~/projects/memory-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init

这里用的是 v1 稳定版 的包名 @modelcontextprotocol/sdk——它在 2026 年仍是生产受支持版本。文末会提到 v2(@modelcontextprotocol/server)将于 2026-07-28 随 MCP 新规范稳定发布,到时 API 会从 .tool() 迁移到 .registerTool()。

预期结果:package.json 里出现依赖,目录多出 node_modules 与 tsconfig.json。

接着编辑 package.json,加上 type 与脚本:

{
  "name": "memory-mcp",
  "version": "1.0.0",
  "type": "module",
  "bin": { "memory-mcp": "dist/index.js" },
  "scripts": {
    "build": "tsc",
    "dev": "tsx index.ts",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.x",
    "zod": "^3.x"
  }
}

调整 tsconfig.json 关键项(ESM 项目要 NodeNext):

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "dist",
    "rootDir": ".",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["index.ts"]
}

常见问题: - 报错 ERR_UNKNOWN_FILE_EXTENSION .ts:忘了 "type": "module",或没用 tsx/编译后产物运行。开发期用 npm run dev,正式注册用编译后的 dist/index.js。 - Cannot find module '@modelcontextprotocol/sdk/server/mcp.js':确认 moduleResolution 是 NodeNext,且 SDK 版本在 1.x 以上。

三、搭建服务器骨架

新建 index.ts,先写持久化层与服务器实例:

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";

// ---- 持久化层:记忆读写到本地 JSON ----
const STORE_PATH = process.env.MEMORY_STORE_PATH
  ?? path.join(os.homedir(), ".memory-mcp", "store.json");

type Item = { value: string; tags: string[]; updated: string };
type Store = Record<string, Item>;

function load(): Store {
  try {
    return JSON.parse(fs.readFileSync(STORE_PATH, "utf-8"));
  } catch {
    return {}; // 文件不存在或损坏,从空开始
  }
}

function save(data: Store) {
  fs.mkdirSync(path.dirname(STORE_PATH), { recursive: true });
  fs.writeFileSync(STORE_PATH, JSON.stringify(data, null, 2), "utf-8");
}

// ---- 创建 MCP 服务器 ----
const server = new McpServer({ name: "memory", version: "1.0.0" });

这段代码做了三件事:定义存储路径(可用环境变量覆盖,默认放 ~/.memory-mcp/store.json)、提供 load/save 两个落盘函数、用 McpServer 高层 API 建一个名为 memory 的服务器。

四、设计并实现五个工具

下面用 server.tool(名称, 描述, zod schema, 处理函数) 声明五个工具。schema 同时承担「参数校验」和「给 LLM 看的入参说明」双重职责——描述写得越清楚,Claude 越知道何时该调它。

工具 1:remember(存/更新一条记忆)

server.tool(
  "remember",
  "把一条事实或笔记存到长期记忆库,key 相同则覆盖。可带 tags 归类。",
  {
    key: z.string().describe("键名,如 user_prefers_tabs 或 api_base_url"),
    value: z.string().describe("记忆内容"),
    tags: z.array(z.string()).optional().describe("可选标签数组"),
  },
  async ({ key, value, tags }) => {
    const data = load();
    data[key] = { value, tags: tags ?? [], updated: new Date().toISOString() };
    save(data);
    return {
      content: [{ type: "text", text: `已记住 ${key}(库中共 ${Object.keys(data).length} 条)` }],
    };
  }
);

工具 2:recall(按 key 取记忆)

server.tool(
  "recall",
  "按 key 取出一条记忆,不存在则提示未找到。",
  { key: z.string() },
  async ({ key }) => {
    const item = load()[key];
    if (!item) return { content: [{ type: "text", text: `没有找到 ${key} 这条记忆。` }] };
    return {
      content: [{
        type: "text",
        text: `${key} = ${item.value}\n标签: ${item.tags.join(", ") || "无"}\n更新于: ${item.updated}`,
      }],
    };
  }
);

工具 3:forget(删除一条记忆)

server.tool(
  "forget",
  "按 key 删除一条记忆。",
  { key: z.string() },
  async ({ key }) => {
    const data = load();
    if (!(key in data)) return { content: [{ type: "text", text: `${key} 不存在,无需删除。` }] };
    delete data[key];
    save(data);
    return { content: [{ type: "text", text: `已忘记 ${key}。` }] };
  }
);

工具 4:list_memory(列出全部,可按 tag 过滤)

server.tool(
  "list_memory",
  "列出记忆库中所有 key,可按 tag 过滤。",
  { tag: z.string().optional() },
  async ({ tag }) => {
    const data = load();
    const keys = Object.keys(data).filter(k => !tag || data[k].tags.includes(tag));
    if (keys.length === 0) return { content: [{ type: "text", text: "记忆库为空。" }] };
    return {
      content: [{
        type: "text",
        text: keys.map(k => `- ${k}:${data[k].value.slice(0, 40)}`).join("\n"),
      }],
    };
  }
);

工具 5:search_memory(在 value 里全文检索)

server.tool(
  "search_memory",
  "在记忆内容中做大小写不敏感的全文检索,返回命中的 key 与内容。",
  { query: z.string() },
  async ({ query }) => {
    const q = query.toLowerCase();
    const hits = Object.entries(load()).filter(([, v]) => v.value.toLowerCase().includes(q));
    if (hits.length === 0) return { content: [{ type: "text", text: `没有命中 "${query}" 的记忆。` }] };
    return {
      content: [{ type: "text", text: hits.map(([k, v]) => `- ${k}:${v.value}`).join("\n") }],
    };
  }
);

最后绑定 stdio 传输并启动:

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch((err) => {
  console.error("memory MCP 启动失败:", err); // stderr,安全
  process.exit(1);
});

编译验证:

npm run build   # 产物在 dist/index.js

预期结果:编译无报错,dist/index.js 生成。直接 node dist/index.js 运行时,进程会阻塞等待输入(它在等 JSON-RPC 报文)——这是正常的,说明 stdio 服务器就绪了,按 Ctrl+C 退出即可。

常见问题: - 工具返回值被 Claude 当成乱码:检查返回结构必须是 { content: [{ type: "text", text: "..." }] },type 字段不能漏。 - schema 报 Input not match:zod 对象的 key 必须与解构参数名一致;可选参数用 .optional()。 - 日期/时间相关报错:本服务器跑在 Node 进程里,new Date() 正常可用(别和某些脚本环境混淆)。

五、注册到 Claude Code

在普通终端(不要在 claude 会话里)执行注册命令:

claude mcp add memory -- node /绝对路径/dist/index.js

想自定义存储位置,加环境变量:

claude mcp add memory \
  --env MEMORY_STORE_PATH=/abs/path/store.json \
  -- node /abs/path/dist/index.js

验证连接:

claude mcp list

看到 ✓ Connected 即成功。状态含义速查:✓ Connected 就绪;! Connected · tools fetch failed 服务器起来了但没列出工具;✗ Failed to connect 进程没起来。

进会话使用:

claude
# 然后输入:
用 remember 工具记住:本项目后端基地址是 https://api.example.com,标签打 backend

第一次调用会请求授权,批准即可。输出里工具调用会带 memory 前缀,确认走的是你的服务器。

作用域(scope)很关键,决定配置存哪、谁能用:

scope 存储位置 可见范围
local(默认) ~/.claude.json 当前项目条目下 仅你,仅当前项目
user ~/.claude.json 顶层 mcpServers 仅你,所有项目
project 项目根 .mcp.json 克隆该仓库的所有人

记忆库这种全局能力,建议加 --scope user:

claude mcp add --scope user memory -- node /abs/path/dist/index.js

也可直接手写 .mcp.json(项目根,可提交到 git 作为「配置即代码」):

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/abs/path/dist/index.js"],
      "env": { "MEMORY_STORE_PATH": "/abs/path/store.json" }
    }
  }
}

改完 .mcp.json 要重启会话才生效(Claude Code 只在启动时读它)。

常见问题: - /mcp 显示 No MCP servers configured:多半是在别的目录注册的。local 作用域绑定到注册时的目录;要么回到原目录,要么用 --scope user。 - 首次启动超时:npx 首次下载包很慢,提高超时:MCP_TIMEOUT=60000 claude。 - 同名服务器已存在:先 claude mcp remove memory 再加。

六、用 MCP Inspector 调试

服务器不连 Claude 也能单独测,用官方 Inspector:

npx @modelcontextprotocol/inspector node /abs/path/dist/index.js

它会启动一个本地 Web UI(默认 http://localhost:6274,具体端口看终端输出),浏览器打开后可以:

  1. 看 Tools 列表,确认五个工具与入参 schema 都正确暴露
  2. 手动填参数调用 remember、recall,看返回
  3. 检查 store.json 是否被正确写入

这是排查「Claude 里调不通」最快的办法——先在 Inspector 里证明服务器本身没问题,再回头查注册配置。

实战案例:让 Claude 跨会话记住你的项目偏好

一个完整可复现的链路。第一次会话:

你:记住,我们团队代码风格是用 4 空格缩进、禁止 any,标签打 convention
(Claude 调用 remember,key 自动取类似 convention_style)
你:再记住,部署用 pnpm deploy:prod,标签打 deploy

关掉终端,喝杯咖啡,第二天开新会话:

你:帮我写个新模块,按团队风格来
(Claude 调 list_memory 或 recall 取出 convention_style,照此生成代码)

你:怎么部署来着?
(Claude 调 search_memory "deploy" 命中并告诉你 pnpm deploy:prod)

这就是「长期记忆」的价值——store.json 在磁盘上,跨会话、跨项目(若用 user scope)都活着。你甚至可以让 Claude 在每次会话开头主动 recall 几个固定 key(如 user_profile、project_context)来快速「载入上下文」。

完整工作流:

sequenceDiagram
    participant U as 用户
    participant CC as Claude Code
    participant M as memory-mcp
    participant F as store.json
    Note over U,F: 第一次会话——写入
    U->>CC: 记住后端地址是 X
    CC->>M: tools/call remember(key,value)
    M->>F: 写入
    M-->>CC: 已记住
    Note over U,F: 新会话——读取
    U->>CC: 帮我连后端
    CC->>M: tools/call recall(api_base_url)
    M->>F: 读取
    M-->>CC: 返回 X
    CC-->>U: 用 X 发起请求

常见问题与排错

延伸阅读

训练营适用性评估

参考来源

  1. Connect to MCP servers — Claude Code Docs — 官方注册、作用域、.mcp.json、排错权威说明
  2. Build an MCP server — Model Context Protocol — MCP 三原语、stdio 日志铁律、FastMCP 官方示例
  3. MCP TypeScript SDK — GitHub README — v1/v2 包名、McpServer 高层 API、2026-07-28 规范路线图
  4. MCP Inspector — Debugging — 官方调试工具用法
  5. Tools concept — Model Context Protocol — 工具原语的设计原则与返回结构规范 方 MCP 介绍 — Redis 团队对"MCP 连接 AI 与数据"的产品视角阐述 - 2026 MCP 规范候选版 — blog.modelcontextprotocol.io,2026-07-28 发布的下一代规范(含 Streamable HTTP、无状态会话) - Claude Code claude mcp CLI** — claude mcp add/list/get/remove 管理服务器生命周期的官方命令

参考来源

  1. Introducing Model Context Protocol (MCP) for Redis — Redis 官方博客 — Redis 团队阐述为何用 MCP 连接 AI 与数据层
  2. MCP SDKs — Model Context Protocol 官方文档 — TypeScript/Python SDK 的 McpServer 高层 API 与 Zod schema 集成
  3. Create MCP Server in Node.js: Step-by-Step Guide — ServerAvatar — Node.js 从零搭建 MCP 服务器的分步教程,含项目结构
  4. The 2026-07-28 MCP Specification Release Candidate — MCP 官方博客 — 2026 年下一代 MCP 规范,涵盖 Streamable HTTP 传输与无状态会话模型
  5. Model Context Protocol (MCP) Architecture Guide: From N×M to N+M [2026] — Preporato — MCP 如何消除 N×M 集成税,2026 生产级 agent 栈架构解析

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入