一次讲清 MCP:OpenAI 和 Anthropic 的格式差异,以及 Agent 的统一接入层

在这里插入图片描述

问题不在“能不能接”,而在“以后还接不接得动”

你做了一个 Agent 项目,第一版跑在 OpenAI 上。

你翻到 OpenAI 文档,发现有 type: "mcp",感觉这事很简单:远程 MCP Server 直接挂进去就行。后来团队又说要支持 Claude,于是你去看 Anthropic 文档,发现它也有 MCP connector。

这时问题就来了。

如果你把 MCP 直接绑在某一家模型厂商的原生字段上,系统很快会长成这样:

OpenAI Adapter 里写一套 MCP 接法
Anthropic Adapter 里再写一套

表面上看,两家都“支持 MCP”。实际上,你会得到两套边界不同、回填方式不同、执行位置也不同的工具接入代码。后面再叠加兼容 OpenAI / Anthropic 的模型,复杂度只会继续长。

真正的问题不是“哪家文档写得更详细”,而是:

在多模型 Agent 项目里,MCP 这层到底应该放在模型 API 上,还是放在你自己的 Agent 运行时里?

这篇文章就讲清楚这件事。

一句话定义

MCP 统一接入的关键,不是挑哪家厂商“原生支持 MCP”,而是让你的 Agent 自己做 Host,自己管理 MCP Client,再把 MCP 工具统一包装成模型无关的 Tool。

先纠正三个常见误区

误区一:OpenAI 和 Anthropic 都在讲 MCP,所以接入方式应该差不多

不对。它们讲的经常不是同一层。

MCP 规范讲的是协议层:Host、Client、Server 怎么通过 JSON-RPC 和 transport 交互。模型厂商文档讲的更多是产品接入层:在我的 API、SDK、编码工具或平台里,开发者怎么把 MCP 能力接进去。

误区二:只要 API 支持 MCP,我就没必要自己管 MCP Client

不对。API 原生支持的通常是远程 HTTP MCP,或者平台托管的 Connector。只要你要接本地 stdio server、私有网络系统、内网数据库,MCP Client 还是得在你自己的 Agent 进程里。

误区三:只要后续模型兼容 OpenAI / Anthropic 格式,就不需要统一接入层

也不对。兼容格式只能降低 adapter 层的重复工作,不能替你解决工具命名、schema normalize、审批、审计、tool search 和本地 stdio 这类 Host 侧问题。

MCP 本身到底解决什么

MCP 要解决的问题其实很朴素:让 LLM 应用和外部工具之间有一套标准协议,而不是每个 Agent 都为每个系统单独写一层私有集成。

MCP 规范里最核心的三个运行时角色是:

  • Host:真正的 AI 应用本体,比如 Claude Code、Codex、IDE 助手,或者你自己写的 Agent。
  • Client:Host 内部负责和某个 MCP Server 建立连接的组件。
  • Server:暴露 Tools、Resources、Prompts 的程序。

Transport 层历史上经历过变化。旧版规范里常见的是 stdioHTTP+SSE;2025-03-26 之后,标准化方向变成了 stdioStreamable HTTP,后者用来替代旧的 HTTP+SSE 表述。

这意味着一件很重要的事:

MCP 只规定 Host、Client、Server 怎么说话,但它并不替你决定 MCP Client 到底是放在你的应用里,还是放在模型厂商的平台里。

OpenAI 和 Anthropic 的真实差异,就从这里开始。

OpenAI:一条是平台托管 MCP,一条是 SDK 自管 MCP

OpenAI 现在给了两条不同的路。

第一条路是 Responses API 里的 hosted MCP / Connectors。请求里可以直接把远程 MCP Server 写进 tools

{
  "model": "gpt-5.5",
  "tools": [
    {
      "type": "mcp",
      "server_label": "stripe",
      "server_url": "https://mcp.stripe.com",
      "authorization": "$STRIPE_OAUTH_ACCESS_TOKEN",
      "require_approval": "never"
    }
  ],
  "input": "Create a payment link for $20"
}

如果不是自己维护的 MCP Server,也可以直接用 OpenAI 维护的 Connector,写法会变成 connector_id

{
  "model": "gpt-5.5",
  "tools": [
    {
      "type": "mcp",
      "server_label": "Dropbox",
      "connector_id": "connector_dropbox",
      "authorization": "<oauth access token>",
      "require_approval": "never"
    }
  ],
  "input": "Summarize the Q2 earnings report."
}

这条路的特点是:MCP Client 由 OpenAI 平台代管。OpenAI 会先去远程 MCP Server 拉工具列表,响应里会出现 mcp_list_tools;模型真的调用工具时,输出里会出现 mcp_call;如果需要审批,还会出现 mcp_approval_request,你的应用再回一个 mcp_approval_response

如果后端 MCP Server 在内网或防火墙后面,OpenAI 还给了 Secure MCP Tunnel,意思是你不用把私有 MCP Server 直接暴露到公网。

第二条路是 OpenAI Agents SDK。这个 SDK 既支持 HostedMCPTool,也支持本地自管的 MCPServerStdioMCPServerStreamableHttp,还支持 tool filtering。

这条路的工程含义很明确:

  • 只接远程公开 MCP 或官方 Connector,可以直接吃平台托管能力。
  • 要接本地 stdio、私有工具链、企业内网系统,还是应该把 MCP Client 放在你自己的 Agent Host 里。

OpenAI 这套设计,本质上是“双轨制”:既支持平台代管,也允许你自己掌控运行时。

Anthropic:MCP connector 和 Tool Use 是两层东西,不要混看

Anthropic 的文档更容易让人看混,因为它同时有“普通工具调用”与“MCP connector”两种形态。

1. 普通 Tool Use

Anthropic 自定义工具的格式是:

{
  "name": "search_docs",
  "description": "Search documentation for a keyword and return matching snippets.",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": { "type": "string" }
    },
    "required": ["query"]
  }
}

这和 OpenAI 常见的函数工具格式已经不一样了。Anthropic 的重点字段是:

  • name
  • description
  • input_schema

Claude 决定使用这种客户端工具时,响应会给你:

  • stop_reason: "tool_use"
  • 一个或多个 tool_use block

然后由你的应用执行真实工具,再把 tool_result 发回去。

也就是说,在普通 Tool Use 这条路里,Anthropic 的边界非常清楚:

模型负责提出工具调用意图,执行发生在你的应用侧。

2. MCP connector

Anthropic 还提供了 MCP connector,让 Messages API 直接连远程 MCP Server。这个接口不是把 MCP 塞进 tools 数组,而是单独用一个顶层字段 mcp_servers

{
  "mcp_servers": [
    {
      "type": "url",
      "url": "https://example-server.modelcontextprotocol.io/sse",
      "name": "example-mcp",
      "authorization_token": "YOUR_TOKEN",
      "tool_configuration": {
        "enabled": true,
        "allowed_tools": ["example_tool_1", "example_tool_2"]
      }
    }
  ]
}

这就是 Anthropic 和 OpenAI 在“托管远程 MCP”这件事上的第一个显著格式差异:

  • OpenAI:把远程 MCP 当成 tools 数组里的一个 type: "mcp" 工具。
  • Anthropic:把远程 MCP 当成独立的 mcp_servers 配置块。

第二个更重要的差异在响应格式上。Anthropic 的 MCP connector 会返回两种新 block:

{
  "type": "mcp_tool_use",
  "id": "mcptoolu_014Q35RayjACSWkSj4X2yov1",
  "name": "echo",
  "server_name": "example-mcp",
  "input": { "param1": "value1", "param2": "value2" }
}

和:

{
  "type": "mcp_tool_result",
  "tool_use_id": "mcptoolu_014Q35RayjACSWkSj4X2yov1",
  "is_error": false,
  "content": [
    {
      "type": "text",
      "text": "Hello"
    }
  ]
}

Anthropic 这条路同样是平台级托管远程 MCP Client,但它的请求和响应 shape 与普通 Tool Use 并不一样。当前版本还要求 beta header:anthropic-beta: "mcp-client-2025-11-20"

所以你看 Anthropic 文档时,一定要把这两件事拆开:

  • tools + tool_use + tool_result:普通客户端工具机制。
  • mcp_servers + mcp_tool_use + mcp_tool_result:远程 MCP connector 机制。

两者相关,但不是同一个抽象层。

在这里插入图片描述

OpenAI 和 Anthropic 到底差在哪

如果只抓工程上最关键的差异,我会把它们压缩成下面这张表。

维度 OpenAI Anthropic
普通自定义工具定义 常见是 tools: [{ type: "function", function: { name, description, parameters, strict } }];Responses API 回填 function_call_output tools: [{ name, description, input_schema, strict }];回填 tool_result
托管远程 MCP 的声明位置 放在 tools 里,type: "mcp" 顶层 mcp_servers
托管远程 MCP 的服务标识 server_label + server_urlconnector_id name + url + authorization_token
工具白名单 allowed_tools tool_configuration.allowed_tools
审批机制 require_approval,响应里可能出现 mcp_approval_request 由 MCP connector 流程和结果 block 承接
平台托管 MCP 的响应痕迹 mcp_list_toolsmcp_callmcp_approval_request mcp_tool_usemcp_tool_result
本地 stdio MCP Responses API 不直接管;但 Agents SDK 支持 MCPServerStdio MCP connector 走远程 server;普通 Tool Use 则由你的应用自己执行

如果只用一句话总结:

OpenAI 更像“把 MCP 做成了一种内置工具类型”,Anthropic 更像“把 MCP 做成了一个并列的远程 server 配置机制”。

为什么把 OpenAI 和 Anthropic 讲清楚就够了

如果你后面还要接 GLM、DeepSeek 这类模型,反而更应该先把 OpenAI 和 Anthropic 两套主格式理顺。

原因很简单:

  • 一类兼容 OpenAI 风格函数工具。
  • 一类兼容 Anthropic 风格工具块。
  • 你真正需要稳定的,还是 Host 自己的统一抽象。

换句话说,后续模型是落到 OpenAI-compatible adapter,还是落到 Anthropic-compatible adapter,不会改变 MCP 统一层应该由 Host 持有这件事。

一套更稳的统一接入层,应该长什么样

到这里基本就能得出结论了:

如果你真要做多模型 Agent,最不应该做的事,就是把 MCP 直接写死在某一个 provider 的原生字段上。

在这里插入图片描述

User
  │
  ▼
Agent Loop
  │
  ├── Provider Adapter
  │     ├── OpenAI formatter / parser
  │     ├── Anthropic formatter / parser
  │     └── Compatible formatter branches
  │
  ├── Tool Registry
  │     ├── Builtin Tools
  │     └── MCP Tool Wrappers
  │
  ├── Tool Search / Defer Loading
  │
  └── MCP Manager
        ├── stdio clients
        ├── Streamable HTTP clients
        └── Remote server auth / approvals / retries

这套设计里,真正需要稳定的不是“某个 provider 的 MCP 格式”,而是你自己的内部抽象。

1. 先定义一层统一内部工具协议

无论工具来自内置工具、MCP server,还是别的 registry,先统一成一个内部结构:

type UnifiedTool = {
  name: string;
  description: string;
  inputSchema: JsonSchema;
  source: {
    kind: "builtin" | "mcp";
    serverName?: string;
    originalToolName?: string;
  };
  risk: {
    readOnly: boolean;
    destructive: boolean;
    requiresApproval: boolean;
  };
  visibility: {
    deferred: boolean;
  };
};

这层抽象一旦立住,后面的所有 provider 适配,都只是在做“格式转换”,而不是改业务逻辑。

2. MCP Manager 统一管理 transport、连接和工具发现

MCPManager 只干一件事:接管所有 MCP server 的生命周期。

它负责:

  • 读取配置;
  • 判断 stdio 还是 Streamable HTTP
  • 执行 initialize
  • 拉取 tools/list
  • 缓存工具定义;
  • 处理重连、超时、认证和错误隔离。

伪代码大概是这样:

async function registerMcpTools(configs, registry) {
  for (const serverConfig of configs) {
    const client = await createMcpClient(serverConfig);
    await client.initialize();

    const tools = await client.listTools();
    for (const tool of tools) {
      registry.register({
        name: `mcp__${serverConfig.name}__${tool.name}`,
        description: tool.description,
        inputSchema: normalizeSchema(tool.inputSchema),
        source: {
          kind: "mcp",
          serverName: serverConfig.name,
          originalToolName: tool.name
        },
        risk: inferRisk(tool),
        visibility: {
          deferred: true
        }
      });
    }
  }
}

这里最重要的是三点:

  • 工具名必须带 server 前缀,不然 searchquerycreate 这种名字迟早撞。
  • schema 要先归一化,别把 provider 不支持的 JSON Schema 特性原样往外传。
  • 某个 MCP server 连接失败,只应该让这个 server 的工具不可用,不应该拖垮整个 Agent。

3. Tool Registry 只暴露模型当前该看到的工具

很多工具系统一开始就错在这里:每轮都把所有工具定义完整塞给模型。

这在 MCP 场景里会非常快地失控。一个 GitHub server、一个 Slack server、一个 Grafana server、一个内网平台 server,加起来就是几十上百个工具。

所以 Tool Registry 必须和 Tool Search / defer loading 配合:

  • 高频内置工具常驻;
  • MCP 工具默认延迟加载;
  • 模型先看到 ToolSearch,再按名字或语义搜索需要的工具;
  • 只有被选中的工具才展开完整 schema。

Anthropic 现在已经把 Tool Search 做成官方能力,OpenAI 也在 function calling 文档里明确建议函数很多时搭配 tool search。即使你最后自己在客户端实现 BM25 或 regex 版 ToolSearch,也比每轮全量暴露稳定得多。

4. Provider Adapter 只做“翻译”,不要做业务

不同模型的差异,应该被压缩在 provider adapter 里。

这一层只负责三件事:

interface ProviderAdapter {
  formatTools(tools: UnifiedTool[]): unknown[];
  parseToolCalls(response: unknown): UnifiedToolCall[];
  appendToolResults(history: unknown[], results: UnifiedToolResult[]): unknown[];
}

比如:

  • OpenAI Adapter:把内部工具转成 tools 数组;普通函数工具回填 function_call_output,托管远程 MCP 则按需构造成 type: "mcp"
  • Anthropic Adapter:普通工具走 tools + tool_use + tool_result;如果你明确选择 Anthropic MCP connector 模式,再额外填充 mcp_servers
  • 兼容模型:只要它落在 OpenAI-compatible 或 Anthropic-compatible 其中一类,就复用这两条 adapter 分支,不再单独发明一套工具协议。

最关键的一点是:

Adapter 只负责“翻译协议”,不要让它掺进工具执行、权限判断和业务逻辑。

5. Policy、Approval、Audit 放在 Host,不放在模型 prompt

一旦你开始接 MCP,权限边界就不能继续靠“提示词自觉”了。

至少要有这几层控制:

  • Policy Engine:路径越权、命令白名单、写操作风险分级。
  • Approval Gate:高风险工具、外部敏感系统调用、数据出域审批。
  • Result Adapter:大结果截断、敏感信息脱敏、错误结构化。
  • Trace / Audit:记录哪个模型在什么时候调用了哪个工具,参数摘要是什么,结果如何。

OpenAI 托管 MCP 里有 require_approvalmcp_approval_request;Anthropic connector 也有自己的 server 配置和结果 block。但如果你要跨模型统一行为,这些控制最终还是应该落回 Host 自己的规则系统里。

什么时候该用厂商原生 MCP,什么时候一定要自己接

这件事其实可以用一个很实用的判断框架来决定。

可以直接用厂商原生 MCP 的情况

  • 你只支持一家模型。
  • 你接的是远程公开 MCP Server 或官方 Connector。
  • 你希望尽量少写基础设施代码。
  • 你接受执行链路的一部分运行在模型平台侧。

更应该自己接 MCP 的情况

  • 你要同时支持 OpenAI、Anthropic,以及后续兼容其中某一套格式的模型。
  • 你要接本地 stdio server。
  • 你要接企业内网数据库、GitLab、日志平台、私有服务。
  • 你要自己统一审批、审计、缓存、工具搜索和降级策略。
  • 你不想让工具系统随着 provider API 变化不断返工。

一句话说就是:

只要你不是“单模型 + 纯远程公有工具”的简单场景,Host 自己掌控 MCP Client 通常都更稳。

最容易做错的四件事

1. 把 MCP 直接绑死在 provider request shape 上

今天写 type: "mcp",明天写 mcp_servers,后天再补一套 OpenAI-compatible tools,最后整个工具层就碎了。

2. 不做 schema 归一化

不同 provider 对 strict mode、required、additionalProperties、JSON Schema 子集的要求并不完全一样。OpenAI 明确强调 strict mode 的 schema 约束,Anthropic 也单独提供 strict tool use。你不先做 schema normalize,后面一定会在边界 case 上爆炸。

3. 不做 Tool Search,默认全量暴露

少量工具时看不出来,一上 MCP server 数量就立刻出问题。token 成本、选择准确率和上下文污染都会一起上升。

4. 以为接入层只要“能调用”就算完成

真正费时间的从来不是“第一次调通”,而是后面的:

  • 权限
  • 超时
  • 重试
  • 审批
  • 日志
  • 结果裁剪
  • 故障降级

这些都不在 provider 的 demo 代码里,但它们才是生产系统的一半。

在这里插入图片描述

最小落地方案

如果你现在就在做一个多模型 Agent,我建议直接按下面这个顺序上:

  1. 先把 MCP server 配置抽出来,只描述 transportcommand/urlenv/headersallowed_tools
  2. 建立 MCPManager,统一完成连接、发现、缓存、重连和错误隔离。
  3. 把 MCP 工具包装成内部统一命名,例如 mcp__github__search_issues
  4. 建立 ToolRegistry,控制哪些工具常驻,哪些工具延迟加载。
  5. 为 OpenAI 和 Anthropic 各写一个薄的 ProviderAdapter;兼容模型直接挂到对应分支。
  6. 在 Host 侧落 Policy、Approval、Trace,不把这些规则寄托在模型 prompt 上。
  7. 先跑通 3 类工具:只读工具、写工具、高风险执行工具。
  8. 最后再决定哪些 provider 场景可以走原生托管 MCP,哪些必须走 Host 自己的 MCP Client。

在这里插入图片描述

总结

这篇文章如果只留一句结论,那就是:

OpenAI 和 Anthropic 的差别,不在于“谁有 MCP”,而在于谁把 MCP 做成了什么形态;而对一个真正要长期演进的 Agent 项目来说,最稳的办法永远是自己掌握这层 Host 能力。

所以,多模型 Agent 里最推荐的 MCP 架构不是“哪个厂商原生支持 MCP 就用哪个字段”,而是:

Agent 自己做 Host,自己管理 MCP Client,把 MCP 工具统一包装成内部 Tool,再由不同 ProviderAdapter 翻译给各家模型。

这才是最不容易被厂商 API 变化绑架的接入方式。

参考资料

  • OpenAI API Docs:MCP and Connectors
    https://developers.openai.com/api/docs/guides/tools-connectors-mcp
  • OpenAI Agents SDK:Model context protocol
    https://openai.github.io/openai-agents-python/mcp/
  • OpenAI API Docs:Function calling
    https://developers.openai.com/api/docs/guides/function-calling
  • Anthropic Claude Docs:MCP connector
    https://platform.claude.com/docs/en/agents-and-tools/mcp-connector
  • Anthropic Claude Docs:Tool use with Claude
    https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview
  • Anthropic Claude Docs:Define tools
    https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools
  • Anthropic Claude Docs:Tool search tool
    https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool
  • Model Context Protocol:Specification
    https://modelcontextprotocol.io/specification/2025-11-25
  • Model Context Protocol:Transports
    https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐