用途
rustpad-mcp 是一个 Model Context Protocol(MCP)服务器,用于连接 Rustpad——高效、极简、自托管的协作文本编辑器。它让 Claude Code、Claude Desktop、Codex 等 MCP 客户端可以读取和写入某个 Rustpad 实例中的 pad:获取文档、创建文档、整体替换文档或在原处编辑。
主要能力
- 读取通过 Rustpad 的 HTTP API 完成。
- 写入使用操作转换(operational-transformation)WebSocket 协议,因此针对性的编辑(append_to_document、replace_in_document)能与人类协作者同时输入的内容干净合并,而不是覆盖它。服务器编辑 pad 期间,会以名为 rustpad-mcp 的协作者身份对 pad 中所有人可见。
- 两个无法撤销的编辑会请求真人确认。在客户端支持 MCP elicitation 时,替换非空 pad 以及跨多个匹配项进行搜索替换会弹出真实对话框,模型无法代为回答;replace_in_document 的提示会说明即将改动多少处。客户端不支持时会回退为两次调用的 token,并明确说明这一点,而不是暗示已有人批准。ELICITATION=false 会主动采用该回退方式,但不会移除这层保护。
- 默认注册八个工具;RUSTPAD_ALLOW_TOOLS=essential 会改为注册精选的五个工具,模型从五个工具中选择比从八个中可靠得多。
- 每个工具都声明 outputSchema,并在文本块之外返回 structuredContent,客户端无需解析文本即可使用结果。
使用方式
环境要求
- 一个可访问的 Rustpad 实例(自托管;服务器无状态且不需要凭据——Rustpad 没有认证)
- Node.js >= 22,或 Docker
同一个 URL 同时提供 HTTP API、WebSocket 端点和工具返回的分享链接(<RUSTPAD_URL>/#<pad-id>)。RUSTPAD_* 布尔值必须严格为 true。服务器无需配置即可启动并列出工具;但每次调用都会失败并给出设置说明。
ELICITATION 是唯一的例外:它不带前缀,因此会影响到同一环境中的所有 MCP 服务器;取值既不是 true 也不是 false 时服务器会停止而不是回退——它是这里唯一默认开启的变量,否则一个笔误会让你以为对话框已关闭而实际仍在运行。以关闭状态启动的服务器会打印一行说明。
工具加载选择
RUSTPAD_ALLOW_TOOLS 和 RUSTPAD_DENY_TOOLS 接受逗号分隔的工具名;结尾的 * 匹配整个工具族。essential 是精选的五个工具预设:get_document、get_document_info、create_document、set_document、append_to_document。
有一个边界比列表看上去更软:Rustpad 没有创建操作,任何 id 只要被写入,pad 就存在,所以拒绝 create_document 并不能阻止新建 pad——对新 id 执行 set_document 或 append_to_document 同样会创建它。要拒绝你真正想拒绝的写入工具,或使用 RUSTPAD_READ_ONLY。
匹配不到任何工具的条目会导致启动中止并指出该条目,因此拼写错误不会悄悄隐藏某个工具——工具缺失不是人们会去追溯环境变量的东西。被过滤的工具永远不会注册,因此在 tools/list 中不存在,对 tools/call 也是未知的,与 RUSTPAD_READ_ONLY 下的写入工具完全一样。
如果同时运行多个这样的服务器,另一个方案是 mcp-hub——它的 /hub 端点用六个元工具替代每个服务器的工具。
通过 mcp-hub 接入
无法启动本地进程的客户端(ChatGPT connectors、Claude 网页版、Cursor、LibreChat)可通过 mcp-hub 访问 rustpad-mcp:一个容器通过 Streamable HTTP 服务多个 stdio MCP 服务器,单个密码后面是 OAuth 2.1 登录,并为不支持 OAuth 的客户端提供长期 token。其 /hub 端点让所有服务器都藏在六个元工具之后,因此一个连接器就能访问全部,而不会在模型上下文中塞入 N×工具 schema。
其 /config/mcp.json 使用 Claude Code 的格式,因此条目就是你已有的那个。
结构化输出
每个工具都声明 outputSchema,并在文本块之外以 structuredContent 作答,因此客户端无需解析文本即可使用结果。五个写入工具过去用一句话作答(例如 “Appended 12 characters to pad …”),这句话仍然保留在文本块中。
get_document 以 {text} 作答,而不是把 pad 作为整个结果——理由和 get_document_info 一直是对象一样:根是字符串的 schema 会被面向 2025 年代客户端的实现改写为 {result: …},这样工具就会根据提问者返回两种形状。这里也是 empty 和 truncated 可以存在的地方——过去空答案是靠一句话表达的。
两个报告 pad 内容的读取工具带有 untrusted: true 和 source: "rustpad" 字段。pad 对任何知道其 id 的人都是可写可读的,包括此服务器早先写入的文本,如果客户端只读取结构化部分,将得不到任何框架性提示。
适用场景
- 让 MCP 客户端(Claude Code、Claude Desktop、Codex)在 Rustpad 实例中读取、创建、替换或就地编辑 pad。
- 在有人在同时输入时进行协作编辑,针对性编辑能干净合并。
安全与限制
- 固有设计:不暴露 pad 列表——Rustpad 没有这样的 API。pad 隐式存在于每个 id 之下,因此你必须知道自己关心的 id。get_stats 报告服务器当前持有多少文档,但不报告其名称。
- 无账户、无权限。Rustpad 两者皆无,因此每个 pad 都被当作不可信输入,而不是登录担保过的内容。
- pad 内容对任何知道 id 的人可写可读,因此不可信:每个读取结果前都加有标记,告诉模型将其视为数据,绝不视为指令。
- 两个不可逆编辑会请求真人确认:模型无法代答的真实对话框,绑定到该 pad 和确切替换内容。客户端无法显示对话框时,使用一次性 token,只出现在上一次工具结果中——这只能证明同一参数调用了两次,别无其他。回退文本会说明是两者中的哪一种。
- 工具结果有大小上限;实例写入的一切都会剥除控制字符,上游错误体在到达模型前会被净化。
- 实例本身也不可信:WebSocket 帧在缓冲之前就在头部层面受到限制,消息队列按字节有界,每个帧在折叠进 pad 前都会检查形状。
- RUSTPAD_INSECURE_TLS 仅针对所配置的连接放宽证书验证,绝不进程级放宽。
- pad 是临时的:服务器重启以及 24 小时不活动后会丢失,除非实例使用 SQLITE_URI 运行。不要把秘密放进 pad。
完整指南、工具参考和安全说明位于 rustpad-mcp.ni-c.de(源码在 docs/ 中)。
测试套件通过真实的 MCP 协议与一个内存中的 rustpad-server 伪造实现(包含并发编辑的 OT 转换)通信;无需在线实例。架构图和社会化卡片是生成的——编辑 docs/assets/architecture.source.svg 并运行 npm run assets,绝不要改渲染后的副本。
发布由 tag 驱动。先提升 package.json、把 CHANGELOG.md 中的 [Unreleased] 笔记移到新版本下、提交。发布工作流通过 Trusted Publishing(OIDC,带 provenance)发布到 npm,将多架构容器镜像推送到 GHCR,根据 CHANGELOG 章节创建 GitHub release,并更新官方 MCP registry 中的条目。
问题、讨论和拉取请求都欢迎——参见 CONTRIBUTING.md。漏洞请使用私密报告而非公开 issue;政策见 SECURITY.md。
MIT © Willi Thiel