展示通过 MCP 协议将 AI 知识图谱集成到 Notion 界面,实现知识管理工具化的实践案例。
当我开始为妻子开发 AI 工具时,原因是她已经用腻了 Notion。
她使用 LLM 作为人生教练、心理治疗师和思想伙伴。为了给 AI 提供背景信息,她维护了一个 35,000 token 的巨大"主提示词"页面,其中记录了她的生活、医疗史和目标。她必须手动把这堵文字墙复制粘贴到每一个新的聊天对话中。
为了自动化这个流程,我构建了 Synapse,一个用时序知识图谱(Neo4j + Graphiti)替代手动提示词的系统。当她聊天时,AI 会在后台悄悄提取实体和关系,逐步构建起持续的记忆。
效果完美。但随后我遇到了 UX 的瓶颈。
我构建了一个知识图谱的可视化器,让她能够探索 AI 的记忆。我认为它很漂亮。对我来说,看着图谱增长、新的连接不断形成是一件很有意思的事。但对她来说,这太让人不知所措了。节点的数量和漂浮的边太多了,无法处理,最终她完全忽视了应用的这一部分。
事实证明,虽然图的概念对于理解关系很有用,但导航一个庞大的原始图视图是为机器准备的,不是为人类准备的。她想念 Notion。她想念结构化表格、清晰的属性,以及简单地点击输入来修正错误的能力。
所以,我让这个项目画上了圆满的句号。我使用新的 Notion MCP,把 Notion 变成了她 AI 大脑的终极人机交互界面。
我构建了一个双向的、human-in-the-loop 的 Neo4j 知识图谱和 Notion 之间的同步系统。
这不仅仅是一个简单的"AI 向页面追加文本"脚本。它是一个动态的双向管道:
Export(AI 设计 UI): 而不是使用硬编码的 Notion 模板,Synapse 编译用户的图谱并要求 Gemini 设计一个自定义数据库结构。如果用户经常谈论他们的健康,AI 会创建一个带有"活跃/暂停"选择标签的"药物"数据库。如果他们谈论代码,它会创建一个带有技术栈的"项目"数据库。没有两个导出看起来相同。
Import(Human-in-the-Loop): AI 记忆系统会出现幻觉。为了修正这一点,每个 AI 生成的 Notion 数据库都会获得一个"需要审查"复选框和一个"纠正备注"列。如果 AI 理解错了什么,我妻子只需要勾选框,在 Notion 中输入纠正内容,然后点击同步。系统会更新知识图谱(使旧的事实失效)并自动修补 Notion 行。
整个架构是开源的:
后端(Synapse Cortex): https://github.com/juandastic/synapse-cortex
前端(Synapse Chat): https://github.com/juandastic/synapse-chat-ai
Synapse Cortex 是一个知识图谱驱动的后端,旨在为 AI 聊天应用程序提供长期记忆能力。Synapse Cortex 不是孤立地对待每一个对话,而是:
吸取聊天会话中的对话数据
使用 LLM 提取实体、关系和事实
将其存储在时序知识图谱中(Neo4j)
为未来对话检索相关上下文
可视化知识图谱供用户探索和调试
该系统建立在 Graphiti 之上,Graphiti 是一个时序知识图谱框架,处理实体解析、关系提取和时序事实失效…
然而,要查看实现 Notion 集成的实际后端代码,你可以查看 Export 功能提交和纠正提交。
这里的 UI 工作很少,因为 Notion 将是实际的 UI,但我决定有一个简单的界面来设置 Notion 配置(为了简单起见,我没有实现完整的 OAuth 流程)并触发导出和同步纠正。
将 AI 与严格的 API 集成通常是映射模式、格式化 JSON 和处理边界情况的噩梦。MCP 从根本上改变了这一点。我不再编写僵化的 ETL 管道;我只是给推理引擎提供工具。
我将架构分为两个阶段。
首先,我使用标准 Notion SDK 创建空数据库。这是一个严格的结构操作。
其次,我使用 @notionhq/notion-mcp-server 与 LangGraph(一个 ReAct agent)实际填充数据并处理纠正。
当一行被标记为需要纠正时,我不会编写复杂的 if/else 逻辑来判断如何更新 Notion。我只是将用户的纠正和更新的图数据传递给装备了 Notion MCP 工具的 LangGraph agent。agent 自主决定是使用 API-patch-page(更新特定属性)还是 API-delete-block(如果事实完全失效且行应该被归档)。
我的后端是用 Python(FastAPI)编写的。官方的 Notion MCP 服务器是用 Node.js 编写的。
由于 Synapse 是一个多租户系统(每个用户都有自己独立的 Notion OAuth 令牌),我无法只让一个全局 MCP 服务器一直运行。我需要一种方法来安全地隔离连接,并确保我的 Python agent 和 MCP 工具之间的低延迟。
我决定将官方的 Node.js MCP 服务器作为子进程(stdio)直接运行在我的 FastAPI 后端内部。
这带来了一些有趣的生命周期管理挑战:
Docker 调整: 我必须修改 Python Dockerfile 来安装 Node.js,以便环境可以执行 npx @notionhq/notion-mcp-server。
上下文管理: 我在 Python 中构建了一个异步上下文管理器(_NotionAgentContext)。当导出或纠正作业启动时,它会启动 Node 子进程,通过环境变量安全地传递特定用户的 NOTION_TOKEN,初始化 LangGraph agent,处理数据批次,并在作业完成时优雅地关闭子进程。
class _NotionAgentContext:
async def __aenter__(self):
# 1. Start the Node.js MCP subprocess via stdio
self._stdio_cm = stdio_client(server_params)
read, write = await self._stdio_cm.__aenter__()
# 2. Initialize session and load Notion tools
self._session_cm = ClientSession(read, write)
session = await self._session_cm.__aenter__()
await session.initialize()
tools = await load_mcp_tools(session)
# 3. Return a LangGraph autonomous agent equipped with Notion MCP
return create_react_agent(llm, tools)
async def __aexit__(self, exc_type, exc_val, exc_tb):
# Gracefully shut down the subprocess to prevent zombie Node processes
await self._session_cm.__aexit__(exc_type, exc_val, exc_tb)
await self._stdio_cm.__aexit__(exc_type, exc_val, exc_tb)
通过 stdio 而不是 SSE 运行它,LangGraph 推理循环和 Notion MCP 服务器之间的通信速度闪电般快速,本地化,并安全地限制在当前用户的作业范围内。
Notion MCP 让我停止编写脆弱的 API 包装器,专注于真正重要的事:构建一个让人类与他们的 AI 记忆无缝协作的系统。
这个项目极其有回报感。我妻子绝对喜欢这个结果;她最终有了她的 AI 大脑,格式是她能够真正读取、组织和纠正的,而不会感到不知所措。
我还必须承认,这个 Notion MCP Challenge 的时机真是完美。我已经知道我的图形可视化器对她不起作用,但这个比赛提供了确切的动力和正确的技术(MCP)来实现这个双向集成。当一个新工具与你正在试图解决的真实世界问题完美对齐时,这是一个很棒的感受。
如果你对 Synapse 架构的其余部分感到好奇——比如我为什么选择知识图谱而不是标准的向量 RAG,或者我如何处理处理大规模上下文窗口的后端扩展挑战——你可以在我的 DEV 档案中查看我以前的文章。
Synapse 已上线,网址为 https://synapse-chat.juandago.dev/,如果你想检查一下。
构建软件很有趣,但看着它活起来并为你关心的人解决实际问题是神奇的。
我很想听听你对这种方法的想法,或者你如何在自己的项目中使用 MCP。让我们在 X 上继续对话或在 LinkedIn 上连接。