Skip to content

MCP(模型上下文协议)

MCP(Model Context Protocol,模型上下文协议)回答的问题是:当模型需要调用你的函数、读取你的数据、操作你的系统时,用什么统一的方式接入? 在它出现之前,每个 Agent 框架、每家模型厂商都定义一套「工具调用」私有格式,每个系统都要为每个模型重复写一遍集成(N×M 问题)。MCP 把「模型 ↔ 工具/数据」这一层标准化,让工具供应商写一次接入,任何支持 MCP 的客户端都能用。

生态位置:由 Anthropic 于 2024 年 11 月开源,2025 年起被 OpenAI / Google / Microsoft 等采用,2025 年 12 月捐赠给 Linux Foundation(Agentic AI Foundation),成为模型连接外部世界的实际标准。规范以日期版本迭代,最新为 2026-07-28(无状态 + OAuth 2.1 的重大修订,2026-08-28 过渡期结束后旧版废弃)。与 Agent 编排 的关系:Agent 负责「规划与循环」,MCP 负责「工具接入」这一层标准接口。

一、为什么需要 MCP:集成问题的解法

在 MCP 之前,「让模型干活」的接入是碎片化的:

问题表现
接口私有每家模型厂商的 function calling / tool use 格式不同,绑死模型供应商
重复集成同一能力(查日历 / 发消息 / 查数据库)要为每个框架各写一遍 connector
会话内临时搭Agent 的「内置动作」是预置死的,运行时新增能力要靠重写或插件机制

MCP 的解法是把连接标准化为「通用插座」:

                    tools / resources / prompts
模型宿主 (Host)  ───  Client(1) ──────► MCP Server A(日历)
    ↑                  Client(2) ──────► MCP Server B(GitHub)
  用户使用的主应用        Client(n) ──────► MCP Server C(私有数据)
  • Host(宿主):用户直接使用的应用(如 Claude / Claude Code / Cursor 等代码与桌面工具),负责「拿模型回答 + 拿用户授权」;
  • Client(连接器):Host 内部的协议客户端,一个 Host 可挂多个 Client,每个 Client 连一个 Server;
  • Server(服务端):提供能力的一方——暴露工具、数据、模板给模型消费。

类比:MCP 之于 AI 应用,类似 USB-C(Anthropic 官方类比)之于外设——把「接口形态」标准化,接上即用;也常被比作 AI 界的 LSP(语言服务器协议,把编辑器与语言工具解耦)。

二、架构:角色与三大原语

Server 向模型暴露三类能力(即「上下文」的来源):

原语是什么谁来触发典型例子
Tools(工具)模型可执行的函数,带输入 Schema模型自主决定调用发邮件、执行 SQL、创建 Issue
Resources(资源)结构化的数据 / 文档,用 URI 寻址模型或用户在上下文里引用file:/// 项目文件、https:// 文档、数据库记录
Prompts(提示模板)可复用的多轮消息模板用户手动选用「写周报」「按模板生成复盘」

客户端(Host)侧可提供的能力(旧版为协议内置):Sampling——Server 反向往模型发起「帮我生成一段内容」的请求。2026-07-28 新版将 Sampling 与 Prompts 移入扩展机制,Resources 入口也并入工具形态(见第四节),但「工具为主、资源/模板为辅」的心智不变。

通用能力贯穿所有消息:进度上报、请求取消、错误码、日志(按级别过滤)。

三、跑通最小 Server:传输、消息与示例

传输:本地与远程两条线

传输适用形态2026-07-28 版状态
stdio本地子进程(开发工具场景)客户端拉起服务端进程,stdin/stdout 走 JSON-RPC保留(本地主力)
Streamable HTTP远程服务无状态 HTTP + SSE 流式返回,可水平扩展标准化为远程唯一(取代旧版 HTTP+SSE)

消息层:JSON-RPC 2.0

所有通信都是 JSON-RPC 2.0 消息。旧版需要先 initialize 握手协商版本与能力;2026-07-28 版移除该握手,改为 HTTP 头协商(本地 stdio 由客户端在启动参数中带协议版本与能力声明):

MCP-Protocol-Version: 2026-07-28
MCP-Capabilities: tools,listChanged

一次工具调用的两端消息(示意,字段以规范为准):

json
// 模型方(Host/Client)发起调用
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "add", "arguments": { "a": 1, "b": 2 } } }

// Server 返回(内容可带结构化文本,供模型读取)
{ "jsonrpc": "2.0", "id": 1, "result": {
    "content": [{ "type": "text", "text": "3" }],
    "isError": false } }

理解要点:Server 不感知「具体是哪个模型」在调它——协议里只有参数与结果,没有厂商绑定。这正是 MCP 存在的意义。

示例:一个最小 Server

社区最常见的写法是 Python 的 FastMCP(也有官方 TS/Python SDK 与社区多语言实现)。以下示例按 fastmcp 常见形态编写,2026-09-04 已在本地实测(fastmcp 4.0.2 + MCP Python SDK 2.1.1,Python 3.14 venv;fastmcp 迭代快,以你实际安装版本为准):

python
# 示例依赖:fastmcp(建议 Python>=3.10 的 venv: python -m venv .venv && .venv/bin/pip install fastmcp)
# 实测通过(fastmcp 4.0.2);文件保存为 server.py
from fastmcp import FastMCP

mcp = FastMCP("calculator")  # stdio 传输,默认被宿主拉起

@mcp.tool
def add(a: int, b: int) -> int:
    """两数相加(description 会被模型看到,用于决定何时调用)"""
    return a + b

@mcp.tool
def list_tables(sql: str) -> list[str]:
    """列出给定 SQL 中的表名(示意:真实实现应执行解析而非字符串处理)"""
    import re
    return re.findall(r"(?:from|join)\s+([a-zA-Z_][\w]*)", sql, re.I)

if __name__ == "__main__":
    mcp.run()

配套的 stdio 客户端(与 server.py 放同一目录,命名为 client.py;以子进程拉起 server,走 JSON-RPC 完成发现与调用):

python
# 实测通过(MCP Python SDK 2.1.1)
import asyncio, sys
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    params = StdioServerParameters(command=sys.executable, args=["server.py"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            res = await session.list_tools()   # 注意: 2.x 返回 ListToolsResult 对象(.tools), 不是裸列表
            print("已发现工具:", [t.name for t in res.tools])
            for name, args in [
                ("add", {"a": 2, "b": 3}),
                ("list_tables", {"sql": "SELECT * FROM orders JOIN users ON orders.uid = users.id"}),
            ]:
                r = await session.call_tool(name, args)
                print(f"call_tool({name}, {args}) ->",
                      "".join(c.text for c in r.content if hasattr(c, "text")))

if __name__ == "__main__":
    asyncio.run(main())

# 实测输出(2026-09-04):
#   已发现工具: ['add', 'list_tables']
#   call_tool(add, {'a': 2, 'b': 3}) -> 5
#   call_tool(list_tables, {'sql': 'SELECT * FROM orders JOIN users ...'}) -> ["orders","users"]

要点:

  • 函数的 name / docstring(描述)/ 参数类型 会被自动整理为工具声明与 JSON Schema,模型据此决定「何时、传什么参数」调用——所以描述要写清边界与副作用(见安全节);
  • 工具内部是普通代码,可访问数据库 / 网络 / 文件系统,因此工具=任意代码执行入口
  • 本地调试可用官方 MCP Inspector 连接观察消息往返。

四、2026-07-28 新版规范:无状态与 OAuth 2.1

这是自发布以来最大的一次修订(Release Candidate 于 2026-05-21 发布,正式版 2026-07-28,30 天过渡期后旧版废弃)。如果从零搭远程 MCP 服务,直接用新版;旧服务需在过渡期内迁移。

维度旧版(如 2025-03-26)2026-07-28 新版
会话有状态,维护 session_id无状态:协议层无会话;确需跨请求状态由 Server 自行下发 state_token,客户端透传
握手initialize RPC 协商移除,改为 HTTP 头协商(协议版本 + capabilities)
认证授权OAuth 可选实现OAuth 2.1 强制:Server=资源服务器、Client=OAuth 客户端;PKCE 必选;提供 /.well-known/oauth-authorization-server 元数据端点;支持 OpenID Connect
远程传输HTTP + SSEStreamable HTTP(无状态、水平扩展);SSE 弃用;stdio 保留
原语位置resources/list、prompts/list、sampling 为内置 RPCResources 并入工具形态、Prompts 与 Sampling 移入扩展机制
错误码通用 JSON-RPC 错误标准化并新增认证错误:AUTHENTICATION_REQUIRED=-32000、AUTHORIZATION_FAILED=-32001、TOKEN_EXPIRED=-32002
扩展无正式机制正式扩展机制,如 mcp-apps/v1(服务端渲染 UI)、tasks/v1(长时间运行任务)

迁移优先级的实操建议:

  1. :移除 initialize 调用与 session_id 依赖;OAuth 2.1 + PKCE;替换 resources/listprompts/listsampling 用法;
  2. :远程传输 SSE → Streamable HTTP;错误处理适配新错误码;
  3. :评估 mcp-apps / tasks 扩展是否对你的场景有价值。

五、客户端接入与调试

  • 接入形态:主流 AI 客户端工具(Claude Code / Cursor 等)都支持「添加 MCP Server」;本地开发最常用 stdio 配置(指定命令与参数拉起你的 Server 进程),远程 Server 走 HTTP URL + OAuth 授权;
  • 开发调试流程:先用 MCP Inspector 单测 Server(无界面直接看请求/响应)→ 再挂到真实客户端验证「模型是否会按描述调用」;
  • 工具声明可观测:给工具描述与 schema 后,先在客户端里试问模型能否在正确时机带上正确参数调用——模型用不对工具,八成是描述写得不够好,而不是模型笨
  • 回归影响:Host 工具升级后,模型行为会变(新增工具、改 schema 都是行为变更),建议对关键 Agent 场景留评测回归(见 AI 工程化 的评测门禁)。

六、生产化与安全红线

MCP 的核心安全信条(规范层面明确要求,但需实现者落地):用户同意与控制、数据隐私最小暴露、工具安全、采样批准。落到工程上:

  • 工具最小权限:每个工具只开它需要的那点权限;删除、写库、支付等破坏性操作要么不做成工具,要么做成「先干查后干写」的两段式并二次确认;
  • 工具=任意代码执行入口:供应链风险最高的一环。只接入来源可信的 Server;Server 的依赖要锁版本 + 审计,就像对待生产服务本身;
  • 提示注入的回灌通道:模型读到的任何内容(网页 / 邮件 / 工具返回)都可能携带指令。工具返回不可信内容后,若模型据此再调用敏感工具,就形成「注入→外带」链路——敏感动作前要求用户确认,或在工具间隔离不可信内容(防注入原则见 Prompt 工程);
  • 认证与凭据:远程 Server 一律走 OAuth 2.1 + PKCE;密钥放在 Server 侧,绝不下发到 Host 上下文;长期凭据要支持吊销(对应新错误码 TOKEN_EXPIRED 的刷新闭环);
  • 部署与可观测:无状态服务天然好扩容;给每个工具调用留结构化日志(谁、何时、带了什么参数、返回什么),这是审计与排障的第一现场;
  • 速率与配额:Server 要为「模型并发/循环调用」设限(Agent 步数爆炸时尤其重要,参见 RAG 与 Agent 的可控性忠告)。

七、与 Function Calling / A2A 的边界

概念管哪一层与 MCP 的关系
Function calling / tool use模型厂商 API 里的「让模型输出结构化调用意图」私有实现;MCP 是开放通用替代/补充,两者在 host 内部常配合
MCP「模型 ↔ 工具 / 数据」的连接本页主题
A2A(Agent-to-Agent)「智能体 ↔ 智能体」的协作(Google 等推动)与 MCP 互补:MCP 向下接工具,A2A 横向接 agent
插件 / 内置动作单个宿主应用内的扩展早期形态;MCP 把扩展做成跨宿主的标准

一句话定位:MCP 解决「模型的手」(接工具),Agent 解决「模型的脑」(规划循环),A2A 解决「多个脑的协作」。 选型时先想清楚你要打通的是哪一层。

八、踩坑清单

  1. 把 MCP 当 RPC 框架用:它首先是给模型设计声明式接口——工具描述、Schema 质量决定模型会不会正确用,而非代码能跑就行;
  2. 忽视无状态化迁移:2026-07-28 后还依赖 session_id / initialize 的远程服务在过渡期结束后不可用;本地 stdio 影响小但也要跟 SDK 版本;
  3. OAuth 当摆设:远程服务不做 OAuth 2.1 + PKCE,Host 侧授权流程会直接失败(或你被迫绕过规范,留下凭据泄漏口);
  4. 工具描述与实现脱节:模型按 description 决策调用,描述说「返回最近订单」实现却漏了排序,错误不可见、难排查;
  5. 工具过宽:一个「执行任意 SQL」的工具 = 把数据库钥匙交给所有能触发它的 prompt;按操作拆细、只读与写分离;
  6. 忽略注入回灌:工具返回网页/文档内容后直接喂给模型再让模型自由操作,等于给不可信内容开执行通道;
  7. 没有工具调用日志:Agent 出错时看不到「调了谁、传了什么、返回什么」,只能盲调;
  8. 不设速率/步数上限:模型自循环高频调用远程 Server,账单与限流双爆炸;
  9. SDK 示例照抄:fastmcp 与官方 SDK 迭代快、API 易变,示例代码不验证就上生产;
  10. 把「能连上」当「接得好」:接入只是开始,工具被模型正确调用、副作用受控、行为有回归测试才是生产标准(用 AI 工程化 的评测与可观测闭环)。

检查清单

  • [ ] 说清接入层:本地 stdio 还是远程 Streamable HTTP?对应配好 Client 端配置;
  • [ ] 远程服务:OAuth 2.1 + PKCE、/.well-known/oauth-authorization-server 元数据、无状态部署;
  • [ ] 基于 2026-07-28 版规范:无 initialize 握手、无 session 依赖、错误码按新版处理;
  • [ ] 工具最小权限、读写分离,破坏性操作用户确认;
  • [ ] 每个工具的 name / description / 参数 Schema 经过「模型能否正确调用」验证;
  • [ ] 不可信内容(网页/上传/工具返回)与敏感操作之间做了隔离或确认;
  • [ ] 工具调用有结构化日志(含参数脱敏),可审计可回放;
  • [ ] 设了速率/步数/超时上限,防自循环拖垮服务;
  • [ ] Server 依赖锁定与审计,来源不明的 MCP 目录不直接接入生产;
  • [ ] 关键 Agent 场景挂了评测回归,工具升级视同行为变更。

相关延伸:RAG 与 Agent(Agent 循环与框架选型)、Prompt 工程(提示注入防线)、AI 工程化(评测门禁与可观测、安全合规)。

基于 VitePress 构建 · 内容以知识共享方式沉淀