今天,我和我的专属 AI 助手(Silvermoon)联手完成了一项极具挑战性的工程:为 Memos 打造一个功能完备、安全到近乎“偏执”、且兼容所有主流大模型客户端的 MCP (Model Context Protocol) 服务器。
起因:打破信息孤岛
Memos 是一款极其优秀的轻量级开源笔记应用,它是我的第二大脑。随着 Claude Desktop、Cursor、Windsurf 乃至 Grok 等支持 MCP 的现代 AI 客户端崛起,我迫切需要一个桥梁,让 AI 能够直接阅读、搜索甚至自动记录我的想法。市面上虽然有一些简单的脚手架,但在安全性、多端兼容性和高级检索能力上,远远达不到我心中“企业级”的标准。于是,我们决定自己动手。
第一阶段:构建与赋能
基础的 API 桥接并不难,但我们想要的是“满血版”。
- 真正的语义搜索:我们没有依赖任何外部(比如 OpenAI)的 Embedding API,而是直接在 Node.js 中集成了
@xenova/transformers向量引擎。它在后台自动下载 ONNX 模型(all-MiniLM-L6-v2),完全在本地离线计算笔记的向量特征。当 AI 想找“关于未来架构的想法”时,不再是死板的关键字匹配,而是懂语义的精准召回。 - 告别黑框框的 Web 面板:修改 JSON 配置文件太反直觉。我们在服务器内嵌了一个极具现代感的 Web 控制面板(Admin Dashboard)。可视化管理工具权限,一键生成连接密钥,不仅优雅而且高效。
第二阶段:踩坑与“防御战”
有了功能,安全问题接踵而至。把一个能随意读写个人笔记的端点直接暴露在公网,无异于裸奔。我们为其加入了 128位自动生成的安全令牌(Token) 进行鉴权。
然而,当我们试图把这个带 Token 的链接喂给 Grok 时,噩梦开始了:
- Grok 的“强迫症”裁剪:Grok 的客户端在解析 URL 时,粗暴地去掉了
?token=...查询参数,导致服务器收到的是无 Token 请求,直接被我们刚建好的安全网拦截,抛出401 Unauthorized。 - “企业级”的误会:Grok 一看到 401 报错,立刻触发了它的企业级防御机制,不由分说地弹出了一个极其复杂的 OAuth 2.0 验证表单(要求输入 Client ID、Secret、Token Endpoint 等),强迫我们走完整的 OAuth 授权流。
第三阶段:奇技淫巧与降维打击
为了绕过 Grok 的裁剪,我们一开始尝试了 路径融合密钥(Path-Based Secrets)。我们将 Token 融合进 URL 实体(如 /mcp/sse/acks_xxxxxxxx),而不是作为查询参数。这一招成功骗过了 Grok 的 URL 解析器,成功建立了初始的 SSE 连接!
但万万没想到,Grok 很快又主动断开了连接!原来,由于我们下发的 messages 接收端点是相对路径或跨层级路径,它触碰了 Grok 极其严格的跨域/子路径安全策略。为了兼容这种“死脑筋”且高度严格的客户端,我们不断修补路由路径的生成规则,但隐患与维护成本依然巨大。
终局:打不过,就加入(工业标准)
在反复对抗中,我突然意识到:与其用黑客手段跟客户端的死板规则较劲,不如用最优雅的架构去兼容工业标准。
既然 Grok 那么想要一个标准的 OAuth 2.0 认证流,那我们就在这个轻量级的 MCP 服务器里,直接实现一套 Mock OAuth2 PKCE 认证引擎!
我们迅速增加了 /authorize 和 /token 端点:
- 将用户的专属
acks_Token 巧妙映射为Client ID。 - 通过标准的 302 跳转逻辑下发临时授权
code。 - 最终客户端通过
/token接口换取并签发Bearer Token。
一切发生得行云流水。当我们在 Grok 那张吓人的表单里填入我们自建的端点后,点击“连接”——绿灯亮起,测试完美通过!
总结
从一开始的简单连接构想,到最后实现内置语义检索、可视化管理面板,甚至为了兼容极致的安全策略手搓了一套内置的 OAuth2 认证引擎,这个 Memos MCP 项目彻底脱胎换骨。
它不仅保护了个人知识库数据的绝对隐私,更在一次次试错中证明了一个道理:优秀的架构设计永远应该向“标准”对齐,而不是在奇技淫巧中打转。 经过这次洗礼,这套系统可以说是目前市面上最健壮、安全性拉满的个人 MCP 服务器之一了。
了解 ~/jintaoblog/朝夕见闻志⚡️ 的更多信息
订阅后即可通过电子邮件收到最新文章。
