作者通过三个项目实践MCP(Model Context Protocol),详解服务器构建、权限 scoping 和常见安全错误扫描器开发。
Model Context Protocol 是连接 AI Agent 与实际能力的插头。每当客户端需要调用你的工具时,无需各自发明一套方式,MCP server 通过标准的 JSON-RPC 2.0 接口暴露一组工具、资源和 prompt,任何 MCP 客户端(Claude Code、Cursor,或基于 Claude Agent SDK 构建的 Agent)都可以发现并调用它们。这种标准化是核心价值所在,但也正是危险所在:一旦你暴露了一个工具,就等于递给大模型一根它可以在你的系统上操作的杠杆。我通过三个项目构建了一个 server,将其中一个按客户端权限进行了细粒度划分,并写了一个扫描器来捕捉错误。以下是真正重要的经验。
我的第一个 server 是 casebook-mcp,它把 AgentPostmortem(一个公开的 AI Agent 故障记录注册表)变成了 Agent 在调查过程中可以查询的东西。思路很简单:每个团队在调试 Agent 事故时,都在重新发现别人已经总结过的失败模式。因此这个 server 暴露了四个工具:search_cases 用于排序全文搜索,get_case 获取完整案例详情,similar_failures 将事故描述与语料库进行匹配,list_tags 列出标签。
这里的经验教训是:你不需要重型框架。我直接按照 2025-03-26 的 streamable HTTP 规范在无状态模式下实现了传输层:一个单独的 POST /mcp 端点,处理 initialize、tools/list 和 tools/call。没有会话,没有 Durable Objects,没有认证,因为数据是公开的且只读。它跑在一个 Cloudflare Worker 上,协议路由在一个文件里,纯排序逻辑在另一个文件里(独立单元测试),数据层请求实时的 agentpostmortem.com API,带五分钟内存缓存,离线时回退到绑定的数据集。每个 IP 轻度限速每分钟 60 个请求,保持礼貌。你可以用一条 curl tools/list 来冒烟测试,用一条 claude mcp add --transport http 命令将它添加到 Claude Code。
真正值得的分离是:将搜索和相似度排序保持为纯函数,意味着我可以完全不需要启动传输层就能测试核心逻辑。MCP 协议处理是样板代码,你真正的价值在工具实现里,所以要把它们隔离出来。
公开只读数据是简单的场景。难的是为一家公司的真实系统服务的 server。Bridgekit 正是这样的例子:它向 AI 技术栈暴露 Shopify、Triple Whale 和 Postgres,四个工具中有三个是读操作(shopify_orders、triplewhale_metrics、db_query 针对允许列表中的表),一个写操作(shopify_tag_order)。
我最在乎的设计决策是:作用域在发现阶段强制执行,而不仅仅是调用时。客户端在 secret 中配置为 JSON,每个客户端有名称、允许的工具列表和 allowWrite 标志。当客户端调用 tools/list 时,server 只广告该客户端有权限的工具。只读客户端甚至看不到写工具的存在。调用者用 bearer key(或 x-bridgekit-key 头)认证,每次调用尝试都被写入仅追加的审计日志。当只读 key 试图调用写工具时,调用被拒绝且拒绝行为被记录。
对于构建这种 server,我想告诉任何人的两件事:第一,按客户端过滤工具的重要性超出你的预期,因为看不到某个工具的 Agent 无法被 prompt 注入去调用它。减少暴露面是安全控制,不只是整洁。第二,要能安全地演示:Bridgekit 的读工具在上游凭证未配置时返回明确标记的示例数据,这样你可以在不连接真实店铺的情况下展示完整流程。
构建了两个 server 之后,我确信自己迟早会交付一个糟糕的工具,而且大多数人在交付时根本没有安全审查。所以我写了 mcp-audit,一个 MCP server 的扫描器和 linter。它通过 stdio 或 HTTP 连接到 server(或者对静态 JSON 清单进行 lint,不执行任何操作——这是代码审查中处理不可信 server 时需要的),枚举每个工具、资源和 prompt,然后对该表面运行 18 条规则。
这些规则覆盖了我一直担心的几类失败:任意命令或 shell 执行工具(MCP002,严重)、无确认参数的危险操作工具(MCP001)、可能植入在工具描述中的 prompt 注入文本(MCP020)、作为资源暴露的密钥或系统路径如 .env 文件(MCP030,严重)、调用者可控制的 URL 参数引发 SSRF(MCP041)、无认证的 HTTP 传输(MCP040)、以及让模型可以传递任意内容的无约束输入 schema。每条发现都有一个稳定的 MCPxxx ID、严重级别和具体修复方案。
它为 CI 而构建。离线运行,完全确定性,输出 JSON 和 SARIF 2.1.0 格式,这样发现结果可以作为注释出现在 GitHub 代码扫描中。当任何发现达到 --fail-on 阈值(默认 high)时进程退出非零,所以一次糟糕的审计会打断构建。你可以通过 .mcpauditrc 文件禁用噪音规则、重映射严重级别或忽略特定位置。用它扫描我自己的 server 之后,实现了从"我觉得这没问题"到"扫描器认为这没问题"的转变。
mcp-audit 是一个静态和结构化分析器。它推理你工具的形态:名称、描述和输入 schema。它标记名为 run_shell 的工具和指向 .env 的资源,但无法知道你这个无辜命名的 update_record 工具在幕后悄悄运行原始 SQL,因为它看不到实现。对描述的模式匹配也意味着它可能漏掉措辞巧妙的注入接收端,或误标记一个良性的。它缩小了攻击者可触及的表面,捕捉了明显的危险默认值,但它是第一道防线,不是阅读每个工具背后代码的替代品。
构建传输层是最不重要的部分。真正重要的工作是决定存在哪些工具、谁被允许看到它们,以及在 Agent 替你发现之前证明它们都不是傻瓜。这三个项目都在 github.com/royalpinto007 下开源:casebook-mcp、Bridgekit 和 mcp-audit。如果你正在交付一个 MCP server,至少先用扫描器跑一遍。