关于 Roam Research MCP Server
Roam Research MCP 与 CLI 是一个 MCP 服务兼命令行工具,用于通过 Roam 后端 REST API 管理 Roam 图谱。它无需 Roam 桌面应用即可运行,适合服务器、容器或脚本环境,也可作为独立 CLI 使用。
主要功能
- 01读取、搜索和保存页面及块内容
- 02通过 roam_update_page_markdown 差异更新并保留 UID
- 03支持 stdin 管道输入的 roam 命令行工具
- 04可选的共享 HTTP 守护进程与 Bearer 认证
- 05通过 ROAM_GRAPHS 管理多个图谱并设置写入密钥
适用场景
- 01在 Claude Code 等客户端中读写 Roam 图谱笔记
- 02在定时任务或 CI 中将内容自动保存到 Roam
- 03通过脚本批量创建、移动或更新块结构
MCP 相关
该项目作为 MCP 服务运行,向 AI 助手暴露页面读取、搜索、写入、批量操作和记忆等工具,并提供结构化的写入结果。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍

Roam Research MCP + CLI
简介
我创建这个项目是为了解决一个个人问题:我想直接从 Claude Code(以及其他大模型)管理我的 Roam Research 图谱。在构建 Model Context Protocol (MCP) 服务以便 AI Agent 访问我的笔记时,我意识到这些底层工具本身就足够强大,可以独立使用。
这个项目最初是 AI Agent 的后端,后来发展成了功能齐全的独立命令行工具。现在,你可以直接在终端中使用同样强大的 API 功能——将内容写入 Roam、搜索图谱和管理任务——完全不需要大模型。
无论你是想让 Claude 更强大地使用你的知识库,还是只想为自己的脚本配备可靠的命令行工具,这个项目都能满足你的需求。

v5.0 新增内容
一句话概括:roam save 现在为每种指定父级块的方式提供了一个标志,而且两者都不会猜测。--parent-uid 始终表示 UID;--parent 始终表示文本。
在 4.1.0 之前,--parent 接受块 UID 或标题文本,并通过检查值中是否包含数字来判断类型。大约五分之一的 Roam UID 不含数字,因此这些保存操作会被悄悄地路由到错误位置。4.1.0 新增了 --parent-uid,并弃用了向 --parent 传入 UID 的用法。5.0 完成了这项拆分。
--parent-uid <uid>将内容嵌套到具有该 UID 的块下。它接受uid或((uid));如果不存在这样的块,命令会以非零状态码退出,且不会写入任何内容。--parent "<text>"将内容嵌套到目标页面上具有该文本的块下;如果该块不存在,则会创建它。它绝不会被视为 UID。--parent "((uid))"会查找或创建一个内容为该引用的块,而此前无法可靠地实现这一点。- 创建父级块时会在 stderr 上提示,并附上其 UID。如果你传给
--parent的文本看起来像 UID,第二行会说明实际发生的情况,并给出应使用的--parent-uid命令。Stdout 保持不变。 roam save --help会优先说明如何选择。“选择父级块”一节位于示例上方,因此你首先会看到这两个标志的区别。
为何升为主版本。roam save --parent "((uid))" 和 roam save --parent <uid> 以前会嵌套到该块下,现在则会嵌套到包含该文本的块下。**如果脚本向 --parent 传入 UID,请将标志改为 --parent-uid。**这只影响命令行工具:MCP 工具将 parent-uid 作为类型化字段接收,行为与之前完全相同,因此如果你通过 AI 助手使用该服务,无需做任何更改。
详情请参阅更新日志。
v4.0 新增内容
一句话概括: 包含软换行(Shift+Enter)的块现在可以在页面重写后保持原样。读取页面,再写回去,内容不会发生变动。
此前,多行块会渲染为两行物理行,第二行从第 0 列开始。这会重置解析器的缩进基准,因此后续所有块都会向根节点收缩,roam_update_page_markdown 也会尽职尽责地生成移动操作,让实际页面与之匹配。仅仅读取页面并写回一个修订版,就足以触发这种情况,而这正是该工具文档中所述的用途。Callout 正文和围栏代码块恰好是包含软换行的块。
- 软换行渲染为
⏎。 包含软换行的页面会多出一行开头的 `` 标记;如果要把 Markdown 写回去,请保留它。没有多行块的页面渲染结果与 3.x 字节完全一致,不会出现标记或编码。 - 反斜杠不再具有特殊含义。 早期设计会将换行符转义为
\n,但这也是作者文本中常见的前缀,例如\nabla、\neq、C:\newdir。哨兵符不需要这样的规则,因此这些内容在任何地方都会按输入原样写入。 - 原样往返不会产生操作。 将渲染器的输出不作改动地提交回去(包括标题行),不会产生任何操作。CLI 也共享此修复:将
roam get的输出传入roam save --update后,页面会保持原样。 - 页面重写增加了两项防护。 现在,解析后不包含任何块的非空 Markdown 会被拒绝,而不是删除页面上的所有块(真正为空的 Markdown 仍会清空页面,如文档所述)。此外,手写的首个块即使恰好是与页面标题相同的 H1,在普通更新时也不会再被剥除。
- 链接引用也会编码。
roam_fetch_page_full_view会转义引用块和面包屑中的软换行,而不仅仅是页面自身的内容。 - 浏览器客户端可以通过 CORS 预检。 HTTP 传输现在允许
MCP-Protocol-Version和Last-Event-ID,客户端在初始化后必须发送这两个标头。非浏览器客户端此前不受影响。 - MCP SDK 已固定版本,与测试套件使用的版本一致,因此全新安装会获得经过测试的协议接口,而不是 npm 当天提供的任意版本。
为什么是主版本更新。 对于任何包含多行块的页面,四种读取方式都会返回不同的字节:roam_fetch_page_by_title(format: "markdown")、roam_fetch_page_full_view、roam_get_subpages 和 roam get。如果你通过 AI 助手使用该服务,无需做任何操作。解析多行页面 Markdown 输出的脚本会看到新的编码。如果你固定使用 roam-research-mcp@3,则会继续使用 3.2.0 的修复,并保留其文档中说明的多行限制,直到你重新固定版本。
完整详情(包括边缘情况,以及如何针对先前状态验证每项修复)请参阅变更日志。
与 Roam 官方 MCP 服务的区别
Roam Research 提供了自己的 MCP 服务和 CLI(@roam-research/roam-mcp)。它是一个很好的工具,本项目并不打算取代它。两者连接的是不同的 Roam API,其他所有区别都由此而来。
| 本项目 | 官方 @roam-research/roam-mcp | |
|---|---|---|
| 连接目标 | Roam 的后端 REST API(图谱令牌 + 图谱名称) | Roam Desktop 的本地 HTTP API |
| 是否需要运行 Roam | 不需要 — 可无界面运行 | 需要,桌面应用必须处于打开状态(会通过深层链接启动它) |
| 可运行的位置 | 任何地方:笔记本电脑、服务端、容器、CI | 运行 Roam Desktop 的机器 |
| 共享守护进程 | 是 — roam server 为所有客户端运行一个 HTTP 守护进程 | 每个客户端使用 stdio |
| 多图谱 | ROAM_GRAPHS 环境变量,并通过 write_key 保护指定图谱 | ~/.roam-tools.json,每个图谱一个令牌 |
| 仅 Web 图谱 | 支持 | 仅限 Desktop |
**适合使用官方服务的情况:**你希望采用 Roam 自己支持的方式,或者需要只有正在运行的应用才能完成的操作:控制 Desktop UI(打开页面、读取当前选区、操作侧边栏)、语义/嵌入搜索、链接建议、文件上传、评论,或调用 Roam 扩展注册的工具。
**适合使用本服务的情况:**Roam 未运行或未安装 — 例如在服务端、容器、cron 任务或 CI 步骤中使用。或者你想使用本项目新增的功能:支持从 stdin 管道输入的完整独立 CLI、带可选 bearer 身份验证的共享 HTTP 守护进程、保留块 UID 的智能页面差异处理(从而保留块引用)、使用 UID 占位符在一次调用中构建嵌套结构的批量操作,以及 Agent 记忆工具。
有一项刻意省略的功能:**这里没有删除页面的工具。**Roam 没有撤销功能,无法还原通过 API 批量删除的内容。官方服务提供 delete_page;本项目选择采取更谨慎的做法。
两者可协同工作
这两个服务有意采用相同的约定,因此同时运行不会带来任何问题:
[[roam/agent guidelines]]— 两者都会读取同一页面中的约定。只需编写一次,两者都会遵循。参见Agent 指南。#.rm-hide/#.rm-private— 两者都会从面向 AI 的内容中排除带有这些标签的块。添加一次标签,即可同时隐藏。参见对 AI 隐藏内容。
独立 CLI:roam
roam CLI 可让你直接在终端中与图谱交互。所有内容创建和检索命令都支持通过 标准输入(stdin)管道传入内容,非常适合自动化工作流。
快速示例
# Save a quick thought to your daily page
roam save "Idea: A CLI for Roam would be cool"
# Pipe content from a file to a new page
cat meeting_notes.md | roam save --title "Meeting: Project Alpha"
# Create a TODO item on today's daily page
echo "Buy milk" | roam save --todo
# Prepend to top of page (newest-first ordering)
roam save -p "Changelog" --order first "v2.18.0 release"
# Search your graph and pipe results to another tool
roam search "important" --json | jq .
# Search for pages by namespace prefix
roam search --namespace "Convention" # Finds all Convention/* pages
# Fetch a page by title
roam get "Roam Research"
# Fetch daily pages using any date format (auto-normalized)
roam get today # Today's daily page
roam get 2026-03-21 # ISO date → "March 21st, 2026"
roam get "03/21/2026" # US date → "March 21st, 2026"
roam get "March 21" # Named (assumes current year)
# Fetch a block with ancestors (parent chain to page root)
roam get abc123def -a # Block + children + ancestors
roam get abc123def -a -d 0 # Ancestors only, no children
# Fetch page by UID or Roam URL
roam get page abc123def
roam get page "https://roamresearch.com/#/app/my-graph/page/abc123def"
# Sort and group results
roam get --tag Project --sort created --group-by tag
# Find references (backlinks) to a page
roam refs "Project Alpha"
# Update a block (e.g., toggle TODO status)
roam update ((block-uid)) --todo
# Multi-graph: read from a specific graph
roam get "Page Title" -g work
# Multi-graph: write to a protected graph
roam save "Note" -g work --write-key "$ROAM_SYSTEM_WRITE_KEY"
可用命令: get、search、save、refs、update、batch、rename、status、server。
运行 roam <command> --help 可查看任意命令的详细信息。
安装
npm install -g roam-research-mcp
# The 'roam' command is now available globally
MCP 服务工具
MCP 服务向 AI 助手(如 Claude)提供这些工具,使其能够智能地读取、写入和整理你的 Roam 图谱。
多图谱支持: 所有工具都接受可选的
graph和write_key参数。使用graph可指定ROAM_GRAPHS配置中的某个图谱;对受保护的图谱执行写入操作时,使用write_key。
| 工具名称 | 描述 |
|---|---|
roam_fetch_page_by_title | 根据标题获取页面内容。 |
roam_fetch_page_full_view | 获取页面内容,以及所有链接引用(包含面包屑上下文和子项)。 |
roam_fetch_block | 根据 UID 获取块,可选获取子项(深度)和/或祖先项(直至页面根节点)。 |
roam_create_page | 创建新页面,可选包含混合文本和表格内容。 |
roam_update_page_markdown | 使用智能差异更新页面(保留块 UID)。 |
roam_get_subpages | 列出命名空间前缀(例如 "Project/")下的子页面,可选按标签筛选。 |
roam_search_by_text | 在整个图谱或指定页面中进行全文搜索。支持按命名空间前缀搜索页面标题。 |
roam_search_block_refs | 查找引用某个页面、标签或块 UID 的块。 |
roam_search_by_status | 查找 TODO 或 DONE 项。 |
roam_search_for_tag | 查找包含特定标签的块(支持排除标签)。 |
roam_search_by_date | 按创建或修改日期查找块/页面。 |
roam_find_pages_modified_today | 列出自午夜以来修改过的页面。 |
roam_add_todo | 将 TODO 项添加到今天的每日页面。 |
roam_create_table | 创建格式规范的 Roam 表格。 |
roam_create_outline | 创建分层大纲。 |
roam_process_batch_actions | 在一个批次中执行多个底层操作(创建、移动、更新、删除)。 |
roam_move_block | 将块移动到新的父级或位置。 |
roam_remember / roam_recall | 用于在 Roam 中管理 AI 记忆的专用工具。 |
roam_datomic_query | 执行原始 Datalog 查询,以进行高级筛选。 |
roam_markdown_cheatsheet | 获取 Roam 风格的 Markdown 参考指南。 |
roam_get_guidelines | 获取此图谱中用户定义的 Agent 约定。 |
写入工具的结构化结果(v3.0.0+)
这十个写入工具声明了 outputSchema,并在通常的文本之外返回 structuredContent——一个经过验证的对象。客户端可以直接读取 page_uid、uid_map 或 success,无需在字符串中查找 JSON,从而让调用链更加可靠:
// roam_process_batch_actions
{ "success": true, "uid_map": { "parent1": "Xk7mN2pQ9" },
"validation_passed": true, "actions_attempted": 4 }
有三点值得了解:
- 没有任何内容被移除。 文本通道保持不变,因此忽略
structuredContent的客户端,其行为与以往完全相同。 - 读取工具有意不包含这两项。 它们已经将完整结果序列化到文本通道中,因此加入模式只会让载荷翻倍。
- 这些字段只会新增。 有些客户端会根据缓存的工具列表验证实时响应,因此字段只会新增或弃用——绝不会在主版本更新之外重命名或移除。
升级到 5.0.0: 仅限 CLI。
roam save --parent现在始终按文本处理,因此向其传递块 UID 的脚本(--parent "((uid))"或--parent <uid>)必须改用--parent-uid。如果不修改,这类调用会以状态码 0 退出,并将内容嵌套在一个包含该引用的新块下;除非指定-p,否则该块会位于每日页面中。MCP 工具不受影响。请参阅更新日志。
升级到 4.0.0: 读取包含软换行(Shift+Enter)的页面时,现在会将换行渲染为
⏎,并附带一个前导 `` 标记,以确保页面写回后保持完整。不含多行块的页面与 3.x 版本逐字节相同。AI 助手用户无需进行任何操作;解析多行页面 Markdown 输出的脚本会遇到新的编码方式。请参阅更新日志。
从 2.x 升级: 三个写入结果字段已重命名——
uid→page_uid(roam_create_page)、created_uids→created_blocks(roam_create_outline、roam_import_markdown),以及preservedUids→preserved_uids(roam_update_page_markdown)。这只会影响读取这些名称的代码;如果你通过 AI 助手使用该服务,则不会有任何变化。请参阅更新日志,了解原因。
Agent 指南(按图谱区分)
roam_get_guidelines 会读取图谱内的一个页面——默认为 [[roam/agent guidelines]]——其中包含你自己的约定:如何添加标签、如何为页面命名空间,以及 Agent 绝不能做什么。Roam 官方 MCP 服务也会读取同名页面,因此一个页面即可同时供双方使用。
这与 CUSTOM_INSTRUCTIONS_PATH 不同,两者可以组合使用:
CUSTOM_INSTRUCTIONS_PATH | [[roam/agent guidelines]] | |
|---|---|---|
| 所在位置 | 磁盘上的文件 | 图谱中的页面 |
| 作用范围 | 全服务,适用于所有图谱 | 按图谱区分 |
| 修改方式 | 编辑文件,重启服务 | 编辑页面 |
| 用途 | 如何编写 Roam Markdown | 当前用户希望如何处理此图谱 |
只需创建该页面。 无需任何配置,roam_get_guidelines 就会读取 [[roam/agent guidelines]]——这与 Roam 自有服务读取的标题相同,因此创建一次,双方都会遵循。创建具有该精确命名空间标题的页面即表示选择启用;除非 Agent 显式调用该工具,否则不会从图谱中读取任何内容。
如果页面不存在,该工具会返回 exists: false,而不会报错,因此始终可以安全调用。
它还会返回那些不由你设定的规则
除了你的约定之外,每个 roam_get_guidelines 响应都会携带一个 roamSyntax 字段:一份简短的注意事项清单,列出会破坏内容的操作——roam_update_page_markdown 会删除 Markdown 中省略的所有块,将截断的 structure 预览写回并当作内容,以及把块引用重新输入为纯文本——还会提醒,读取时会静默排除 #.rm-hide 子树,并列出 Roam Markdown 与标准 Markdown 规则相反的少数情况。
之所以把它放在这里,而不是放进速查表,有两个原因。它会触达每个客户端,包括从未调用 roam_markdown_cheatsheet 的客户端;即使图谱中没有指南页面,它也会返回——而这正是 Agent 最缺乏上下文的情况。这样的分层是有意为之:你的约定决定风格,roamSyntax 保障数据安全。 任何约定都无法让截断的预览变得完整。
完整的语法参考——组件、查询、嵌入内容、工具选择——仍在 roam_markdown_cheatsheet 中。roamSyntax 约 800 个 token,并刻意限制了长度。
每个图谱都可以指向不同的页面,也可以将其关闭:
ROAM_GRAPHS='{
"personal": {"token": "...", "graph": "..."},
"work": {"token": "...", "graph": "...", "guidelinesPage": "work/agent rules"},
"private": {"token": "...", "graph": "...", "guidelinesPage": false}
}'
ROAM_GUIDELINES_PAGE='team/agent guidelines' # change the default for every graph
解析顺序为每个图谱的 guidelinesPage → ROAM_GUIDELINES_PAGE → roam/agent guidelines。上例中,personal 使用环境变量覆盖值,work 使用自己的页面,而 private 则完全关闭了指南。只有显式设置为 false 才会禁用指南——未设置值不会禁用。
结果会缓存 30 秒——编辑页面后无需重启即可生效。入门模板位于 .roam/agent-guidelines.template.md。
请注意,指南通过常规页面路径读取,因此标记为 #.rm-hide / #.rm-private 的块也会被隐藏——见下文。
读取包含软换行符的页面时,会将其渲染为 ⏎,让每个块
保持在一行中——未转义的换行符会落在第 0 列,并在写回时将
其后的所有内容重新设为子级。此类负载会带有前导的
`` 标记;如果要将 Markdown 写回,请保留它。你编写的 Markdown 永远不会被解码:反斜杠没有特殊含义,且只有带标记的负载中的 ⏎ 才会被解释。roam_get_guidelines 的输出是纯文本——不含哨兵,也不含标记。
对 AI 隐藏内容
带有 #.rm-hide 或 #.rm-private 标签的区块及其所有嵌套内容,都不会包含在这些工具返回的内容中。井号标签形式(#.rm-hide、#[[.rm-hide]])和链接形式([[.rm-hide]])均可使用。.rm-private 是 Roam 现有的“对其他用户隐藏”标签;.rm-hide 则专门用于对 AI 隐藏内容。
这遵循与 Roam 官方 MCP 服务相同的约定,因此为其中一个服务添加标签的区块,也会对另一个服务隐藏。
适用工具:roam_fetch_page_by_title、roam_fetch_block、roam_fetch_page_full_view、roam_get_subpages、roam_search_by_text、roam_search_for_tag、roam_search_by_status、roam_search_block_refs、roam_search_hierarchy、roam_search_by_date。
隐藏的区块也会从页面重写差异中排除,这样就不会因为 Agent 无法写出的 Markdown 中缺少这些区块而将其删除。roam_update_page_markdown(以及 roam save --update)会用你提供的内容替换整个页面,并删除 Markdown 中省略的所有内容。因此,它的基线也会使用相同的过滤规则进行裁剪,遵循的原则是:差异操作会删除内容的基线,必须与调用方获准读取的页面一致。 如果有内容受到保护,它会报告 preserved_hidden。内容会得到保留;但相对于可见同级区块的确切顺序可能会改变。这是修复前一个真实的数据丢失漏洞,详情请参阅更新日志。
这是一个便利过滤器,不是安全保障。 roam_datomic_query 会直接读取数据库,并有意不应用此过滤器,因此能力足够强的 Agent 仍可通过原始 Datalog 显示隐藏区块。请将这些标签视为“避免让 AI 接触这些内容”,而非“对内容保密”。
标签匹配不区分大小写,且只匹配完全一致的标签——#.rm-hidden 和 #.rm-highlight 不受影响。隐藏 UID 集会缓存 30 秒,因此刚添加标签的区块可能会在这段时间内继续显示。
配置
环境变量
单图谱模式
对于单个 Roam 图谱,请在环境变量或 .env 文件中设置以下内容:
ROAM_API_TOKEN=your-api-token
ROAM_GRAPH_NAME=your-graph-name
多图谱模式(v2.0+)
通过单个服务实例连接多个 Roam 图谱:
ROAM_GRAPHS='{
"personal": {"token": "token-1", "graph": "personal-db", "memoriesTag": "#[[Personal Memories]]"},
"work": {"token": "token-2", "graph": "work-db", "protected": true, "memoriesTag": "#[[Work Memories]]"},
"research": {"token": "token-3", "graph": "research-db"}
}'
ROAM_DEFAULT_GRAPH=personal
ROAM_SYSTEM_WRITE_KEY=your-secret-key
图谱配置选项:
| 属性 | 必填 | 说明 |
|---|---|---|
token | 是 | 此图谱的 Roam API 令牌 |
graph | 是 | 图谱名称/数据库标识符 |
protected | 否 | 如果为 true,写入操作需要通过 ROAM_SYSTEM_WRITE_KEY 确认——默认图谱除外,见下文 |
memoriesTag | 否 | roam_remember/roam_recall 使用的标签(覆盖全局默认值) |
服务有两道相互独立的锁。它们很容易混淆,因为两者都是“密钥”——简单来说如下(两者均为可选,默认关闭):
Bearer 令牌——HTTP_AUTH_TOKEN | 写入密钥——ROAM_SYSTEM_WRITE_KEY | |
|---|---|---|
| 简单来说 | 前门的钥匙 | 保险箱内部的门闩 |
| 控制内容 | 谁可以访问服务 | 是否允许写入标记为 protected 的图谱 |
| 适用范围 | 所有内容——所有图谱的读取和写入 | 仅限写入,且只适用于标记为 protected 的图谱 |
| 是否保护读取? | 是 | 否 |
| 何时需要 | 仅当服务可从你自己的机器之外访问时(例如,使用 -H 0.0.0.0) | 只要你希望防止意外修改重要图谱,就需要设置 |
| 发送方式 | HTTP 标头:Authorization: Bearer <token> | 写入工具/CLI 命令中的 write_key 参数 |
可以把它想象成一栋房子:持有者令牌锁住前门(彻底把陌生人挡在门外),而写入密钥锁住里面的保险箱(即使已经进入房子,也需要它才能更改保险箱里的东西)。在你自己的机器上绑定到 127.0.0.1 时,前门面对的是一堵墙;没有令牌的服务也会拒绝试图通过 DNS 重绑定访问它的网页(见下文的 HTTP_ALLOWED_HOSTS),因此在那里不需要持有者令牌。写入密钥在本地仍然很有用,可以作为一道“确定吗?”的保护,因为Roam 没有撤销功能。 |
所以:若要将某个图谱设为需要写入密钥,请在该图谱上设置 protected: true,并配置 ROAM_SYSTEM_WRITE_KEY;之后,调用方对该图谱进行任何写入时,都必须传入匹配的 write_key。
⚠️
protected对默认图谱不起作用。 无论protected的设置如何,在检查该标记之前,都会始终允许写入ROAM_DEFAULT_GRAPH指定的图谱;此标记保护的是那些必须按名称主动指定的图谱,因为访问非默认图谱本身就是值得确认的有意操作。如果你希望对某个图谱的写入进行保护,它就不能是你的默认图谱。
可选:
ROAM_MEMORIES_TAG:roam_remember/roam_recall的默认标签(当未设置每个图谱的memoriesTag时作为后备值)。HTTP_STREAM_PORT:HTTP Stream 传输的端口(默认为 8088)。仅限--server模式——stdio 模式不会打开套接字,因此此设置在该模式下会被忽略。HTTP_STREAM_HOST:HTTP 传输要绑定的主机(默认为127.0.0.1,仅限环回地址)。仅限--server模式。 设置为0.0.0.0可在局域网中开放服务;这样做时,请设置HTTP_AUTH_TOKEN。HTTP_AUTH_TOKEN:用于锁定整个 HTTP 端点的可选持有者令牌。未设置时,不使用令牌:服务只会响应发往localhost、IP 地址或HTTP_ALLOWED_HOSTS中某个名称的请求,并且只会响应来自CORS_ORIGIN中某个源的浏览器请求。设置后,每个 MCP 请求都必须发送Authorization: Bearer <token>(GET /health仍然开放)。绑定到127.0.0.1以外的地址时,请务必使用。它与ROAM_SYSTEM_WRITE_KEY不同——请参见两种访问控制及其区别。HTTP_ALLOWED_HOSTS:未设置HTTP_AUTH_TOKEN时,服务会响应的主机名,除localhost和 IP 地址外还可包含其他主机名(以逗号分隔;端口会被忽略),例如 Docker 服务名称roam-mcp或mybox.local。发往任何其他名称的请求都会收到 403。这可以阻止网页将自己的主机名指向127.0.0.1后访问服务(DNS 重绑定)。设置了HTTP_AUTH_TOKEN时会忽略此项。CORS_ORIGIN:允许调用 HTTP 服务的浏览器源(以逗号分隔;默认为http://localhost:5678,https://roamresearch.com;*表示允许任何源)。未设置HTTP_AUTH_TOKEN时,来自其他任何源的请求都会收到 403。
运行服务
1. 默认模式(stdio) 最适合本地集成(例如 Claude Desktop、IDE 扩展)。MCP 客户端会在每个会话启动一个进程,并通过 stdin/stdout 与其通信。不会打开端口——stdio 模式下的 MCP 无需端口。
在 3.1.0 之前,此模式也会打开 HTTP 监听器,并将其绑定到所有网络接口。如果你正在使用该端点,请改为运行
--server守护进程;详见下文。
npx roam-research-mcp
2. 共享服务模式(--server)
最适合运行单个长期驻留、仅使用 HTTP 的守护进程,供多个 MCP 客户端共享,而不是让每个会话都启动自己的子进程。这样可以节省内存,并为客户端提供稳定的 URL。
HTTP_STREAM_PORT=8088 npx roam-research-mcp --server
或者通过 roam CLI 管理,它提供启动、停止、状态和日志功能:
roam server start # start the shared daemon in the background
roam server start -H 0.0.0.0 # expose on the LAN (no transport auth!)
roam server status # is it up? version, graphs, active sessions
roam server logs -f # follow the log
roam server stop # stop a CLI-started daemon
无论守护进程通过何种方式启动,roam server status 都可以正常工作(它会探测 /health),因此也能报告由 LaunchAgent/systemd 单元启动的守护进程。状态文件(pidfile + 日志)位于 ~/.roam/(可通过 ROAM_HOME 覆盖)。
这两种模式互斥,且各自只启用一种传输方式:stdio 模式使用 stdio,不绑定任何端口;--server 使用 HTTP,不读取 stdin。在 --server 模式下,服务:
- 仅运行 HTTP(不使用 stdio 传输),
- 将 准确的
HTTP_STREAM_PORT绑定到HTTP_STREAM_HOST,如果端口已被占用则以非零状态退出(不会静默切换端口——共享守护进程必须保持 URL 稳定), - 提供
GET /health→{"status":"ok", ...},用于存活检查。
通过 HTTP 传输配置将 MCP 客户端指向该服务:
{
"mcpServers": {
"roam-research-mcp": {
"type": "http",
"url": "http://127.0.0.1:8088/mcp"
}
}
}
环境变量(令牌、图谱)配置在服务进程中,而不是客户端配置中。
保护对外开放的服务(两层):
如果绑定范围超出回环地址(-H 0.0.0.0),请添加边界防护:
HTTP_AUTH_TOKEN=$(openssl rand -hex 32) roam server start -H 0.0.0.0
然后,客户端通过请求头发送令牌:
{
"mcpServers": {
"roam-research-mcp": {
"type": "http",
"url": "http://<host>:8088/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
两者都要保留——它们的作用不同(参见上文两种访问控制及其区别):Bearer 令牌控制谁可以连接,写入密钥只保护对受保护图谱的写入操作。
⚠️ 写入密钥不能替代 Bearer 令牌。在未设置
HTTP_AUTH_TOKEN的对外开放服务上,网络中的任何人仍可读取所有图谱(并写入未受保护的图谱)。只要服务绑定范围超出回环地址,就应设置HTTP_AUTH_TOKEN。
保持服务运行(macOS LaunchAgent):
创建 ~/Library/LaunchAgents/com.example.roam-mcp.plist,设置 RunAtLoad 和 KeepAlive,在 EnvironmentVariables 中配置环境变量,并将 --server 作为最后一项 ProgramArguments。将 StandardOutPath/StandardErrorPath 设置为本地路径(例如 ~/Library/Logs/),然后:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.roam-mcp.plist
curl -s http://127.0.0.1:8088/health # verify
3. Docker
docker build -t roam-research-mcp .
docker run --init -p 127.0.0.1:8088:8088 \
-e HTTP_STREAM_HOST=0.0.0.0 -e HTTP_STREAM_PORT=8088 \
--env-file .env roam-research-mcp --server
运行前,在 .env 中将 HTTP_AUTH_TOKEN 设置为随机密钥;MCP 请求必须包含 Authorization: Bearer <token>。默认情况下,发布的端口仅绑定到回环地址。如需远程访问,请遵循保护对外开放的服务,并明确将端口发布到所需的主机地址。
-e 的值优先于 --env-file,因此无论 .env 中设置了什么,服务都会监听发布的端口。--init 可让 docker stop 立即结束服务,而不是等待 10 秒后再将其强制终止。
在 LLM 中配置
Claude Desktop / Cline:
添加到 MCP 设置文件中(例如,~/Library/Application Support/Claude/claude_desktop_config.json):
固定版本。
npx -y roam-research-mcp会在客户端每次启动服务时获取最新版本,因此新主版本发布时不会有任何提示。固定主版本号,何时升级由你决定:
args获取的版本 ["-y", "roam-research-mcp"]始终获取最新版本——包括下一个主版本 ["-y", "roam-research-mcp@3"]仅限 3.x;升级主版本时需要在此处修改 ["-y", "[email protected]"]精确指定此版本 固定主版本是明智的默认选择:你仍然能获得修复和新工具,但是否接受破坏性变更由你决定。以下示例不固定版本,以符合大多数人首次粘贴配置时的习惯。
单个图谱:
{
"mcpServers": {
"roam-research": {
"command": "npx",
"args": ["-y", "roam-research-mcp"],
"env": {
"ROAM_API_TOKEN": "your-token",
"ROAM_GRAPH_NAME": "your-graph"
}
}
}
}
多个图谱:
{
"mcpServers": {
"roam-research": {
"command": "npx",
"args": ["-y", "roam-research-mcp"],
"env": {
"ROAM_GRAPHS": "{\"personal\":{\"token\":\"token-1\",\"graph\":\"personal-db\",\"memoriesTag\":\"#[[Memories]]\"},\"work\":{\"token\":\"token-2\",\"graph\":\"work-db\",\"protected\":true}}",
"ROAM_DEFAULT_GRAPH": "personal",
"ROAM_SYSTEM_WRITE_KEY": "your-secret-key"
}
}
}
}
查询块解析器(v2.11.0+)
用于以编程方式解析和执行 Roam 查询块的实用工具。将 {{[[query]]: ...}} 语法转换为 Datalog 查询。
支持的子句
| 子句 | 语法 | 说明 |
|---|---|---|
| 页面引用 | [[page]] | 引用了某个页面的块 |
| 块引用 | ((uid)) | 引用了某个块的块 |
and | {and: [[a]] [[b]]} | 必须匹配所有条件 |
or | {or: [[a]] [[b]]} | 匹配任意条件 |
not | {not: [[tag]]} | 排除匹配项 |
between | {between: [[date1]] [[date2]]} | 日期范围筛选 |
search | {search: text} | 全文搜索 |
daily notes | {daily notes: } | 仅限每日笔记页面 |
by | {by: [[User]]} | 由用户创建或编辑 |
created by | {created by: [[User]]} | 由用户创建 |
edited by | {edited by: User} | 由用户编辑 |
相对日期
between 子句支持相对日期:today、yesterday、last week、last month、this year、7 days ago、2 months ago 等。
用法
import { QueryExecutor } from 'roam-research-mcp/query';
const executor = new QueryExecutor(graph);
// Execute a query
const results = await executor.execute(
'{{[[query]]: "My Query" {and: [[Project]] {between: [[last month]] [[today]]}}}}'
);
// Parse without executing (for debugging)
const { name, query } = QueryParser.parseWithName(queryBlock);
实用函数
import { isQueryBlock, extractQueryBlocks } from 'roam-research-mcp/query';
// Detect if text is a query block
isQueryBlock('{{[[query]]: [[tag]]}}'); // true
// Extract all query blocks from a string
extractQueryBlocks(pageContent); // ['{{[[query]]: ...}}', ...]
支持
如果这个项目帮助你管理知识库或构建有趣的 Agent,不妨请我喝杯咖啡!这有助于持续推出更新。
许可证
MIT 许可证 - 由 Ian Shen 创建。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
