Skip to content

24|MCP 集成:共享工具的标准协议

上一篇讲了 agent 间协作。还有一个外部集成问题:agent 要用的工具,如果不想每个 agent 都自己实现一遍,怎么共享?

ops-assistant 写了个 search_runbook(),函数在本地代码里。如果只有这一个项目用,直连函数最简单。但 IDE 插件也想查 Runbook,桌面小工具也想复用同一个检索工具。复制一份函数过去能跑,后面变成三份代码一起维护。MCP 解决的就是"多个客户端共享同一套工具"。

MCP 不是远程调函数

很多人把 MCP 当成"远程函数调用"。这是第一个要纠正的认知。

AIOps 概念图:MCP 三类能力

MCP(Model Context Protocol)定义的能力不止工具调用一种。它有三类能力:tools、resources、prompts。

tools 是模型主动调用的动作(查日志、查指标),这个最常用。resources 是模型可以按地址读取的数据——一份配置文件、一张服务拓扑表,平时不塞进上下文,模型需要时自己去读。prompts 是服务端预先写好的提示模板,客户端按名字取来用。

把 MCP 等同于远程函数调用,会漏掉 resources 和 prompts 这两条线。适配 MCP 时不能只接 tools,三类都要考虑。

四层架构

MCP 集成不是简单调远程函数,是四层架构:配置、连接、适配、调用。每层管一件事。

AIOps 概念图:MCP 四层架构

text
┌─────────────────────────────────────────┐
│  配置层:多来源 MCP server 配置合并        │
├─────────────────────────────────────────┤
│  连接层:维护连接、认证、传输安全           │
├─────────────────────────────────────────┤
│  适配层:MCP 能力转内部格式               │
├─────────────────────────────────────────┤
│  调用层:复用内部权限、错误处理、执行流程    │
└─────────────────────────────────────────┘

配置层收集多来源配置(用户本地、项目级、企业策略)并按优先级合并。企业策略支持 allowlist/denylist,denylist 中的服务器即使在用户配置里存在也被强制移除。

连接层建立和维持连接,处理认证和传输安全。

适配层把 MCP 协议的数据结构转成内部格式——tools 转成内部 Tool(第 18 篇的接口),resources 映射成可检索数据源,prompts 映射成可加载模板。注意 MCP 工具的 schema 字段叫 inputSchema(和 Anthropic Messages API 同源),不是 OpenAI 的 parameters

调用层让 MCP 工具走和内部工具完全相同的执行路径——同样的权限检查、参数校验、错误处理。MCP 工具不享特殊待遇,安全边界一致。

传输:stdio 和 Streamable HTTP

MCP 当前规范定义两种传输:stdio 和 Streamable HTTP。

AIOps 概念图:传输和安全边界

stdio 把 server 作为子进程拉起,通过标准输入输出传 JSON-RPC 消息。适合 server 和 client 同机,简单。

Streamable HTTP 是远程传输,单一端点支持 POST/GET,可选 SSE 流式。它取代了旧版的 HTTP+SSE(协议版本 2024-11-05 里的传输,现已废弃,仅作向后兼容保留)。两者都基于 HTTP,但 Streamable HTTP 用单一 MCP 端点处理 POST 和 GET,不是旧版那种分离的 SSE 长连接加 POST 端点。

DNS rebinding 防护

远程/本地 HTTP 传输有个容易被忽视的攻击面:DNS rebinding。

恶意网页把一个公网域名解析到 127.0.0.1,浏览器里运行的 JS 就能对本地 MCP server 发起"看似跨域、实际打本地"的请求,读到本地数据甚至触发本地工具。stdio 传输不受影响(不走 HTTP),Streamable HTTP 要防。

规范要求 Streamable HTTP server 做三件事:校验 Origin/Host 头(浏览器无法伪造 Host,这是阻断 DNS rebinding 的关键)、本地绑定 127.0.0.1 而非 0.0.0.0、所有连接认证。这是真实 CVE 背后的漏洞类型,不是理论风险。

python
ALLOWED_HOSTS = {"localhost", "127.0.0.1", "::1"}

def validate_request_headers(origin, host):
    if host and host not in ALLOWED_HOSTS:
        raise PermissionError(f"reject host: {host}")
    if origin and origin not in ALLOWED_ORIGINS:
        raise PermissionError(f"reject origin: {origin}")

连接层还要处理会话(Mcp-Session-Id 头维持会话)、协议版本头(客户端和服务端协商版本)、认证过期(OAuth token 过期引导用户重新授权)。

Anthropic MCP connector

Anthropic 的 Messages API 内置 MCP connector,直接在请求里声明 MCP server,服务端替你连:

python
client.beta.messages.create(
    model="claude-...",
    messages=[{"role": "user", "content": "查 Runbook"}],
    mcp_servers=[{"type": "url", "url": "https://...", "name": "runbook"}],
    tools=[{"type": "mcp_toolset", "mcp_server_name": "runbook"}],
)

服务端处理连接和调用,模型返回 mcp_tool_use,结果以 mcp_tool_result 回填。OpenAI 侧没有这种内置 connector,MCP 连接由客户端自己维护,工具转成 function 类型后照常走工具调用流程。一个是连接管理外包给服务端,一个全在客户端。

动态刷新

MCP 服务器的工具列表可能在运行时变化(服务器升级、新工具上线)。工具发现结果可以在 agent loop 轮次间动态刷新。

但运行时刷新有风险:agent 正在用的工具可能突然消失或改名。新发现的工具延迟一个轮次再暴露,给当前轮次完成的机会;被移除的工具标记弃用,不立即删除。工具列表顺序影响 prompt cache 缓存断点,动态增删要保证不破坏缓存前缀。

和本地工具的取舍

MCP 不是必须。工具只在 ops-assistant 内部使用时,本地函数更简单、更直接。

场景推荐方案
只有 ops-assistant 用本地函数
IDE、小工具也要用MCP Server
工具需要独立部署、水平扩展MCP over Streamable HTTP
工具敏感,不能暴露给外部本地函数 + 严格权限

MCP 工具相比本地函数多一层序列化和协议转换开销,性能上有代价。但 MCP 的价值在复用和标准化,不在性能——多个客户端共享同一套工具、远程服务按协议暴露后任何 agent 都能自动发现,这是本地函数给不了的。没有多客户端复用需求时,为用 MCP 而拆分只会增加进程管理复杂度。

MCP 也不是通用 RPC 框架的替代品。它解决的是模型上下文的标准化暴露,不是通用远程调用。只想从远程服务拉数据,直接用 HTTP API 更简单。

第四阶段收尾

到这里工程化与协作设计七篇完成:工具接口、权限体系、BashTool 安全、只读写分级、多 Agent 模式、协作机制、MCP 集成。agent 现在能安全行动、能协作、能接外部工具。

但前面所有概念都还是分散的设计决策。最后一阶段把它们串进一个能跑的 ops-assistant 项目:目录怎么组织、工具怎么接、会话怎么存、报告怎么出、上线要注意什么。