如果你同时用 Claude Desktop 和 Cursor 干活一定撞过这堵墙上午在 Claude Desktop 里跟模型反复对完的项目背景、技术选型、踩坑清单下午打开 Cursor 写代码时它一概不知。模型没有跨会话记忆更不会自己把 Claude Desktop 里聊出来的结论同步给 Cursor。所以我花了一个周末搭了一条让两边共享记忆的通道在 Claude Desktop 里存一条记忆在 Cursor 里读出来。这篇文章就把这条通道的完整搭建过程、代码、配置和踩坑记录全部摊开讲适合同时用这两个工具、又不想每次开工都重复一遍上下文的开发者参考。1. 思路拆解为什么“记忆外置”比复制粘贴靠谱1.1 两个工具的本质差异Claude Desktop 是一个会话型桌面助手适合长对话、方案推演、文档分析这类场景。它的对话上下文天然是“一次性”的——你关掉会话模型就什么都不记得了。Cursor 是一个 AI 原生编辑器Agent 和 Composer 会读你的仓库上下文但它同样不会知道你在 Claude Desktop 里聊过什么。很多人解决这个问题的方法很原始手动复制 Claude Desktop 里的总结粘贴到 Cursor 的规则文件里。这个方法能顶一阵子但有两个硬伤。一是信息会过时今天粘贴的结论明天项目一变就失效而你不会记得去更新它。二是 Cursor 的 rules 是“静态上下文”每次请求都要塞给模型项目一多、规则一长Token 消耗和响应延迟都上去了。我们要的不是“复制一次”而是“写一次、多处读、随时更新”。也就是把记忆从模型上下文里剥离出来放到一个两个工具都能随时读写的共享仓库里。1.2 MCP 是打通两边的关键MCPModel Context Protocol模型上下文协议现在已经是 Claude Desktop 和 Cursor 共同支持的标准协议。简单理解MCP 的作用就是给 AI 模型外接“工具”和“数据源”。Claude Desktop 原生支持 MCP 服务器Cursor 从 0.46 版本开始也在设置里加入了 MCP 配置入口。我们的方案就是写一个本地 MCP Server它不连接任何云服务只是一个读写本地 JSON 文件的 Python 脚本。Claude Desktop 和 Cursor 各自通过 stdio 拉起这个脚本进程两个进程读写同一个文件记忆就打通了。全程不需要额外装数据库不需要跑一个常驻服务不依赖任何第三方云平台。这里要澄清一个概念MCP Server 不是“服务器”它更像是一个“翻译层”。它把模型发出的工具调用请求翻译成对本地文件、数据库、API 的操作。同一个 MCP Server 可以被多个客户端拉起只要它们读写同一个存储目标数据就是共享的。这正是我们要的效果。1.3 方案选型对比动手之前我对比过几种方案各有取舍。方案优点缺点适合场景自建 JSON 文件 MCP Server轻量、可控、可 diff、零部署并发写入要加锁、搜索是线性匹配个人开发者、小型团队SQLite 版 MCP Server查询强、并发安全需要额外装依赖、配置更重记忆量大、多人协作手动复制总结到 rules简单直接动态性差、上下文膨胀一次性小任务只用一个工具管理文档心智负担小无法让模型主动读写非技术用户我自己选了 JSON 文件方案核心原因是可以直接把存储文件纳入 Git 管理改了什么一目了然出问题回滚也方便。如果你的记忆量到了几千条、并发写入频繁再迁移到 SQLite 也不迟MCP 的工具接口不用变。2. 动手搭建一个不到 100 行的本地记忆服务2.1 环境准备与依赖我们只用 Python 和一个轻量的 MCP 库。推荐用fastmcp它把 MCP 协议的细节封装得很好只需要用装饰器就能注册工具。pip install fastmcp写脚本之前先确认 Python 路径后面配置客户端要用。which python # macOS/Linux 输出示例: /usr/local/bin/python3 # Windows 用: where python建议用绝对路径的 Python因为 Claude Desktop 和 Cursor 拉起子进程时不一定继承你的 shell 环境变量容易出现“python 找不到”的问题。2.2 记忆数据结构设计存储文件路径我放在了~/.cross_ai_memory/memory_store.json这样不污染项目仓库。每条记忆是一个对象包含这些字段id: 唯一标识用时间戳加随机数生成content: 记忆正文支持一段话或几条关键结论tags: 标签数组用于分类和筛选source: 来源标记写入时传claude_desktop或cursor方便追溯created_at/updated_at: 时间戳project: 可选的项目名用来做项目级隔离我把并发安全放在了一个很重要的位置上因为 Claude Desktop 和 Cursor 可能同时启动两个进程去读写同一个文件。直接写原文件有损坏风险我用“临时文件写入 原子替换”的方式再配合一个简单的文件锁保证数据不会写花。2.3 核心代码实现下面是完整脚本存为memory_server.py。#!/usr/bin/env python3 import json import random import string import time from pathlib import Path from fastmcp import FastMCP mcp FastMCP(memory-server) BASE_DIR Path.home() / .cross_ai_memory STORE_PATH BASE_DIR / memory_store.json BASE_DIR.mkdir(parentsTrue, exist_okTrue) def _read_store(): if not STORE_PATH.exists(): return [] with open(STORE_PATH, r, encodingutf-8) as f: return json.load(f) def _write_store(memories): tmp_path STORE_PATH.with_suffix(.tmp) with open(tmp_path, w, encodingutf-8) as f: json.dump(memories, f, ensure_asciiFalse, indent2) tmp_path.replace(STORE_PATH) mcp.tool def remember(content: str, tags: list[str] None, project: str default) - str: 写入一条记忆content是记忆正文tags是标签列表project是项目名。 memory { id: .join(random.choices(string.ascii_lowercase string.digits, k10)), content: content, tags: tags or [], project: project, created_at: int(time.time()), updated_at: int(time.time()), } memories _read_store() memories.append(memory) _write_store(memories) return f记忆已保存ID: {memory[id]} mcp.tool def recall(query: str , limit: int 10) - str: 按关键词搜索记忆query为空时返回最近limit条。 memories _read_store() if not memories: return 当前没有存储任何记忆。 keywords [kw.lower() for kw in query.split() if kw.strip()] def score(mem): sc 0 text (mem.get(content, ) .join(mem.get(tags, []))).lower() for kw in keywords: if kw in text: sc 1 return sc matched sorted(memories, keylambda m: (score(m), m.get(created_at, 0)), reverseTrue) if query: tmp [m for m in matched if score(m) 0][:limit] else: tmp matched[:limit] if not tmp: return 没有匹配的记忆。 lines [] for m in tmp: lines.append(f[{m.get(created_at)}] ({m.get(project, default)}) {m.get(content)}) if m.get(tags): lines.append(f 标签: {, .join(m[tags])}) return \n.join(lines) mcp.tool def forget(memory_id: str) - str: 根据ID删除一条记忆。 memories _read_store() new_memories [m for m in memories if m.get(id) ! memory_id] if len(new_memories) len(memories): return f未找到ID为 {memory_id} 的记忆。 _write_store(new_memories) return f记忆 {memory_id} 已删除。 mcp.tool def list_memories(project: str , tag: str , limit: int 20) - str: 按项目和标签筛选记忆列表。 memories _read_store() if project: memories [m for m in memories if m.get(project) project] if tag: memories [m for m in memories if tag in m.get(tags, [])] memories sorted(memories, keylambda m: m.get(created_at, 0), reverseTrue)[:limit] if not memories: return 没有符合条件的记忆。 lines [f[{m.get(id)}] ({m.get(project)}) {m.get(content)} for m in memories] return \n.join(lines) if __name__ __main__: mcp.run(transportstdio)代码逻辑不复杂我解释几个关键设计。remember是写入入口Claude Desktop 里让它调用Cursor 里也可以调用。recall是读取入口做了简单的关键词打分排序没有用向量检索因为个人记忆量一般不到千条线性搜索足够快省去一个向量数据库的复杂度。forget和list_memories用来做维护和清理。2.4 并发安全与原子写入两个客户端同时启动进程时如果碰巧同时写入直接写原文件可能产生损坏。我这里用了一个简单技巧先写一个.tmp临时文件写入成功后用replace替换原文件。replace在操作系统层面是原子操作要么替换成功、要么保持原样不会出现写一半的情况。文件锁我没有放进代码里是因为个人场景下同时写入的概率很低而且万一两个进程同时读旧数据再写最多丢一条数据不会损坏整个文件。如果你和队友多人共用建议加一个fcntl.flock或者msvcrt.locking这块属于进阶优化后面可以再做。写完之后用命令行自测一下python memory_server.py # 如果没有立即退出说明 MCP Server 正常进入监听状态出现“进程一直在跑、不打印东西”就对了MCP 的 stdio 传输就是这样的工作方式。3. Claude Desktop 端接入从聊天里自动沉淀记忆3.1 修改配置文件Claude Desktop 的 MCP 配置在claude_desktop_config.json里。macOS 路径是~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 路径是%APPDATA%\Claude\claude_desktop_config.json往mcpServers字段里加一段{ mcpServers: { memory-server: { command: /usr/local/bin/python3, args: [/Users/yourname/scripts/memory_server.py] } } }command一定要用which python查出来的绝对路径args里也必须是脚本的绝对路径。我最初贪省事写了相对路径Claude Desktop 启动 MCP 时找不到脚本那个调试过程很浪费生命。改完配置重启 Claude Desktop然后在对话里输入“搜索记忆里的 xxx”正常情况下 Claude 会提示你允许使用 MCP 工具点同意后就能读到了。也可以在设置里找到 MCP 服务器列表确认 memory-server 显示为已连接。3.2 让 Claude 主动写记忆MCP 工具接好之后Claude 默认不会主动调用工具写记忆。你需要给它一个明确的“行为守则”否则它只会在你明确要求时才动。我建议在 Claude Desktop 里新建一个固定的会话把这段 System Prompt 粘进去作为“记忆管家”专用会话你有一个外部记忆工具叫作 memory-server。 当出现以下情况时请主动调用 remember 写入记忆 1. 用户明确说“记住”“记录一下”“存档” 2. 对话中出现了需要长期维护的项目背景、技术选型、账号配置、踩坑结论 3. 用户给出了跨会话复用的关键信息比如 API 结构、目录约定、部署命令 写入时建议带 2-3 个标签标签用简短的名词项目名统一放 project 参数。 当用户问起“之前聊过什么”“之前的结论是什么”时先用 recall 搜索再结合搜索结果回答。 如果搜索结果不充分不要编造记忆直接告诉用户没找到。这段提示词的关键是第二条和第三条它让 Claude 主动判断哪些信息值得沉淀而不是机械地等你发指令。实测下来跟它聊半小时项目方案它能提炼出三五条有用的记忆比我自己手动整理还细致。3.3 cc switch 切换模型后配置被覆盖的坑很多人用 cc switch 这类工具在 Claude Desktop 里切换不同模型供应商比如切换到大模型厂商的 API这个工具本身很好用但它重写配置时可能会把mcpServers字段覆盖掉。我踩过的坑是这样的用 cc switch 从 A 供应商切到 B 供应商后Claude Desktop 里的 MCP 服务器列表空了记忆功能全部失效而 cc switch 界面里没有任何提示。处理方法有两个。第一个是切换后马上检查一遍claude_desktop_config.json如果mcpServers没了手动补回去再重启。第二个更稳妥看看你用的 cc switch 版本是否支持自定义 JSON 模板把mcpServers段直接写进模板里这样每次切换都会自动带上。如果你在 cc switch 里配置了自定义模型还要注意模型名称要和供应商提供的一致。比如有些厂商的模型对外叫deepseek-chat你写成deepseek就会在连接时报错Claude Desktop 会提示类似 “couldnt sign in to gateway, the provider rejected” 的信息。这类问题本质是 Endpoint、Key、模型名三者中有一项不对齐去供应商文档里核对一遍基本能解决。3.4 验证闭环存一条真的记忆配置完成后我建议做一个最简单的闭环测试。在 Claude Desktop 里输入记住博客项目使用 VitePress 搭建部署命令是 npm run docs:build域名解析到服务器 A。它应该调用remember写入。接着输入刚才让你记的东西是什么它应该调用recall读出来。这个闭环跑通Claude Desktop 端就完全正常了。如果读不出来多数情况是 System Prompt 没写清楚先检查这一层。4. Cursor 端接入把记忆带进编码环境4.1 两种配置方式Cursor 接入 MCP 有两种方式。第一种是全局方式打开 Cursor 的设置界面搜索 “MCP”点Add MCP Server类型选择stdio然后填写Command:/usr/local/bin/python3Arguments:[/Users/yourname/scripts/memory_server.py]保存后重启 Cursor在 MCP 面板里能看到 memory-server 的状态。第二种是项目级方式在项目根目录创建.cursor/mcp.json{ mcpServers: { memory-server: { command: /usr/local/bin/python3, args: [/Users/yourname/scripts/memory_server.py] } } }项目级配置的优点是跟着仓库走队友拉下来代码也能用适合团队统一一套记忆服务。缺点是如果记忆文件本身没有纳入 Git别人拉下来还是一个空库。4.2 在 Agent/Composer 里读取记忆Cursor 添加 MCP 之后不是所有模型都会自动调用它。你在 Agent 模式下输入应该能在工具列表里看到 memory-server 相关的工具选中recall之类再提问它就会去读。更进一步的用法是给 Cursor 写一条规则让它遇到特定场景时自己去查记忆。在项目里创建.cursor/rules/memory.mdc当用户提到“记忆”或“之前讨论过”或“按上次的约定”时先调用 recall 查询。 当用户说“记住”时调用 remember 写入一条记忆标签从上下文推断。 查询结果为空时明确告诉用户没有找到相关记忆不要猜测。这样你写代码时输入 “继续做博客项目记得上次的技术选型”Cursor 就会自动去记忆库里搜 VitePress 相关的背景然后把结论带到上下文里。我在实际使用时通常会让 Cursor 在开工前执行一次 “recall 项目名”把之前定的关键约定全部拉出来再开始写代码。效果相当于每次开工前给自己一份“项目交接文档”。4.3 项目级记忆和全局记忆怎么组织我在设计里给每条记忆加了project字段这很有用。Claude Desktop 里聊天会自动把项目名带上Cursor 里也可以按项目名过滤。实际使用中我是这样组织的project命名为仓库名或业务名比如blog-site、ai-tools-backend。全局通用知识比如“服务器的 Python 路径是什么”“部署流程是哪些步骤”放default项目下。这样recall时按项目过滤不会从全局里捞出一堆无关的记忆。一个容易踩的坑是Claude Desktop 对话里聊了 A 项目但你没有传 project 参数记忆全跑到default下等到 Cursor 里按项目名搜就什么都搜不到。所以我在 System Prompt 里特意加了一条要求每一条记忆都要判断项目归属不确定就问用户。4.4 顺便聊聊 Cursor 中文界面与基础设置热词里很多人问 Cursor 中文怎么设置。Cursor 的界面语言默认跟随系统但部分版本在设置里找不到语言选项。比较稳的办法是在 Settings 里搜Language如果有直接切换成中文如果没有官方扩展市场里有个 Chinese (Simplified) Language Pack 的语言包插件装上重启即可。要注意很多第三方汉化脚本会修改 Cursor 的安装文件版本一更新就失效还可能被安全软件拦。我建议首选官方渠道不要为了汉化去网上随便下载来源不明的脚本那比英文界面的成本高多了。工具能接中文模型 API 之后界面是中文还是英文不影响核心能力我现在的习惯是保留英文界面避免汉化带来的兼容问题规则文件还是保证中文语义准确更重要。5. 从“单条记忆”到“记忆体系”5.1 好的记忆模板长什么样如果只是把零散对话往记忆里塞塞多了就是一座垃圾山。我自己总结了一套记忆格式每次引导模型按这个结构整理。格式是项目名 结论/背景 为什么 相关标签。一个正面的例子是项目 blog-site 使用 VitePress部署走 npm run docs:build 后把 dist 目录同步到服务器 A原因服务器 A 已有 Nginx 配置可直接指向静态目录不需要额外反代。标签: #部署 #VitePress #服务器反面例子是部署命令是 npm run docs:build前者包含了全上下文哪怕三个月后翻出来也能看懂后者只是零散的命令到时候你还得重新回忆“这个命令是哪个项目的、部署到哪台机器”。5.2 标签与项目分区设计标签设计要克制。我现在的规则是技术栈标签#Python#VitePress、场景标签#部署#重构#bug、业务标签#支付#会员每个标签不超过 10 个。不会出现#某个项目的某个模块的某个接口的这个 bug这种标签太长了实际没用。项目分区和标签是两条正交的维度project告诉你“这条记忆属于哪个项目”tags告诉你“这条记忆属于什么主题”。查询的时候可以组合过滤。比如想在 blog 项目里找所有部署相关的内容就是projectblog-site tag部署。5.3 定期整理与过期清理记忆存了不需要清理吗需要。我每周做一次小整理用list_memories把本周的记忆拉出来看一遍把已经完成的任务打上#done标签或者直接forget掉过期的临时信息比如一次性的排错步骤、临时账号密码。每两周做一次大整理把同一项目分散的记忆合并成一条“项目纪要”用remember写入同时删掉旧条目。这相当于给记忆库做碎片整理让 recall 的结果更精准。5.4 多设备同步方案现在很多人有两台设备一台台式机一台笔记本记忆库最好是两边同步。最简单的方案是把~/.cross_ai_memory/memory_store.json放到同步盘目录里让 Claude Desktop 和 Cursor 都指向这个文件。需要注意的是两个设备同时写入同一个文件仍然有并发风险。我建议只在主力设备上频繁写入另一台设备以读取为主。如果哪天真的需要多人同时读写还是老实迁移到 SQLite 版本文件锁在同步盘上的表现不可靠我有过数据冲突的经历不推荐在高频写入场景下用同步盘。6. 常见问题排查与避坑手册6.1 MCP Server 启动失败的排查顺序MCP Server 启动失败是最常见的一类问题现象是 Claude Desktop 或 Cursor 里 MCP 状态显示未连接。我的排查顺序是命令行手动跑一下脚本看有没有语法错误。确认command用的是绝对路径不能是python因为 GUI 应用不一定有你的 shell 环境变量。确认配置 JSON 没有格式错误尤其注意 Windows 路径里的反斜杠要写成双反斜杠或正斜杠。改完配置必须重启客户端只重开会话是不加载 MCP 的。还有一个小技巧在 Cursor 的 MCP 面板里可以直接看到日志报错信息通常会精确到脚本路径或参数问题先看日志再猜原因。6.2 cc switch 报错 couldnt sign in to gateway这个报错常见于用 cc switch 切换了自定义模型供应商之后。它的本质是供应商的鉴权失败了跟你的本地 MCP 配置没有关系。排查思路是反过来核对三件事Endpoint 地址是否正确、API Key 是否有可用额度、模型名称是否和供应商文档里完全一致。比如有些平台的模型名带版本后缀你少写一个-v1就会拒绝有些平台的 Endpoint 需要带/v1路径不带也会报错。如果三样都核对无误用命令行直接 curl 一下供应商的接口看看返回是否正常。这个操作能快速区分是“供应商服务端问题”还是“本地配置问题”不用反复重启客户端瞎猜。6.3 Cursor 连接异常问题合集Cursor 有时会报 “taking longer than expected” 或一直 “reconnecting”这类问题大多是模型请求超时或认证令牌临时失效。我的经验是先退出登录再重新登录一次清掉本地的认证缓存如果还不行检查当前网络环境能否正常访问模型服务商 API最后再看是不是同一个时间点请求量太大错峰再试。注册或登录时遇到 “cant verify the user is human” 这类验证提示大概率是浏览器触发了风控。换一个常用浏览器、关闭广告拦截插件、等十几分钟再试一般能过。不要反复点击验证频率越快越容易触发限制。6.4 免费额度不够用怎么办Coder 免费版不是“只能用多少天”而是每月有请求额度限制。用完后会提示等待或降速。想省额度有几个方向一是把常用提示词和项目约定写进 rules 文件减少 Agent 的探索性提问二是尽量用 Tab 补全而不是每次都开 Agent三是把长对话拆短减少上下文 Token 消耗。团队协作的话可以考虑 Teams 版按席位购买管理员可以在后台看到每个成员的用量方便排查“额度被谁消耗了”。6.5 不要把提示词和密钥泄露出去接触了 Cursor 的人应该都知道提示词和规则文件有时候会被人想办法套走。这个问题在接入了模型 API 之后更要注意。我的建议是.cursor/rules目录下不要写任何 API Key、密码、数据库连接串。敏感配置一律用环境变量规则文件里只写“去 .env 读取 DATABASE_URL”这种说明。准备把仓库推到公开平台之前检查一遍.cursor目录和项目里的.env文件确保没有把密钥晒出去。另外如果你接入了第三方模型平台模型请求会把上下文发送到对应服务商的服务器。对数据敏感的项目要么选服务商会签保密协议的供应商要么开启 Cursor 的 Privacy Mode团队版可用再使用别把什么内容都往 Agent 里塞。最后分享一个我目前每天都在用的小技巧每天收工前我会在 Claude Desktop 里说一句“把今天聊的所有关键结论整理成 3 到 5 条记忆存起来”。第二天打开 Cursor第一件事就是让它recall一下对应项目把所有结论重新拉进上下文。这个流程已经替代了我之前手工写日报和交接文档的习惯而且信息不会丢项目的连续性好了很多。如果你也受够了两个 AI 工具之间“互不记得”的割裂感这套方案可以直接抄。记住一点记忆文件是你的外置大脑记得也给它做个 Git 备份。