第十章 MCP:工具协议标准化¶
导读¶
Function Calling 解决了"如何让 LLM 输出结构化工具调用",但留下一个工程痛点:每个工具都要按目标模型(OpenAI / Anthropic / Llama)的 schema 格式重写一遍——同一个是"查 GitHub PR"的工具,给 GPT 用写一份 OpenAI schema,给 Claude 用再写一份 Anthropic schema,给 Llama 用又写一份。MCP(Model Context Protocol,模型上下文协议) 是 Anthropic 2024 年 11 月推出的开放标准,被业界称为"AI 时代的 USB-C"——server 端实现一次工具,client 端(Claude Desktop / Cursor / 任何支持 MCP 的 LLM 应用)都能用,跨模型跨应用复用。本章讲清 MCP 的协议设计、三大原语(Tools / Resources / Prompts)、三种 Transport(stdio / SSE / HTTP)、与 Function Calling 的关系、主流 MCP server 生态、自定义 MCP server 实战。读完本章你能用 Python SDK 在 30 分钟内写一个最小 MCP server,并理解 MCP 让第七章 Function Calling 工具"一次实现多模型用"的工程价值。
10.1 MCP 协议设计¶
MCP(modelcontextprotocol.io)是 Anthropic 2024 年 11 月开源的协议标准,目标是"让工具一次实现多模型可用"。它定义了 server / client / host 三角色,用 JSON-RPC over chosen transport 通信。
三角色定义¶
MCP 的三角色:Host(宿主应用)是终端用户使用的 LLM 应用——如 Claude Desktop、Cursor、Continue、自研 Agent 应用。Host 内部跑多个 Client。Client(客户端)是 Host 内部的协议适配器——一个 Client 对应一个 Server,负责 JSON-RPC 通信、协议握手、能力协商。Server(服务端)是工具提供方——如 GitHub MCP server、Slack MCP server、Postgres MCP server,每个 server 暴露一组工具 / 资源 / 提示词给 Client 调用。一个 Host 可同时连多个 Server(多 Client),每个 Server 暴露的能力由 Host 通过协议动态发现。
JSON-RPC 通信¶
MCP 用 JSON-RPC 2.0 作为消息格式——所有请求 / 响应 / 通知都是 JSON 文档。Client 与 Server 之间用 JSON-RPC 通信:Client 发 tools/list 请求拿 server 暴露的工具列表,发 tools/call 请求执行某个工具,发 resources/read 请求读某个资源。Server 用 JSON-RPC 响应返回结果或错误。JSON-RPC 选型原因是它简单、语言无关、易调试——一个 HTTP 请求体就是一段 JSON,能直接用 curl 测试。
协议握手与能力协商¶
MCP 连接建立时做协议握手——Client 与 Server 互相声明自己支持的能力。Client 声明"我支持 tools / resources / prompts 中的哪些",Server 声明"我暴露 tools / resources / prompts 中的哪些"——两边取交集,只有双方都支持的能力才生效。这种"能力协商"让协议可扩展——新版本 Server 可以加新能力,老版本 Client 不识别就忽略,不会破坏兼容性。
三角色的工程意义¶
三角色分离的工程意义是解耦——Server 不需要知道哪个 LLM 在用它,Client 不需要知道 Server 内部怎么实现。Server 实现者专注业务(如 GitHub server 专注怎么调 GitHub API);Client 实现者专注协议适配(一次适配,所有 Server 都能用);Host 实现者专注 UX(怎么把多 Server 工具整合给用户)。这种分层让生态可以并行发展——Server 库与 Host 应用各自迭代,互不阻塞。
一个 Host 多 Server 的典型部署¶
实际部署中一个 Host 通常同时连多个 Server。例如 Cursor IDE 用户同时连:GitHub MCP server(管代码 PR)、Filesystem MCP server(读写本地文件)、Postgres MCP server(查数据库 schema)、Brave Search MCP server(联网搜索)。Host 启动时分别启动 4 个 Client 与 4 个 Server 通信,每个 Client 调用 tools/list 拿对应 Server 的工具列表,Host 把所有工具汇总去重后传给 LLM——LLM 看到 20+ 个工具可用。LLM 输出 tool_calls 后 Host 按 tool name 找到对应 Client 转发执行。这种"多 Server 聚合"是 MCP 的核心价值——一个 Host 接入生态里所有 Server 后能力指数级扩展,比硬编码 Function Calling 工具集灵活得多。
协议握手的具体步骤¶
MCP 协议握手的具体步骤值得工程师理解。第一,初始化阶段:Client 启动后向 Server 发 initialize 请求,包含 Client 支持的协议版本与能力列表;Server 响应自己支持的协议版本与能力。第二,能力协商:双方取交集——只有双方都支持的能力才生效(如 Client 支持 tools 但不支持 prompts,Server 暴露三者,最终只有 tools 与 resources 生效)。第三,初始化完成通知:Client 发 notifications/initialized 告诉 Server 握手完成。第四,正常通信:Client 可以开始调 tools/list / resources/list / prompts/list 拿 Server 暴露的内容,调 tools/call 等执行操作。这种握手让协议版本演进不破坏兼容——新版本 Server 加新能力,老版本 Client 不识别就忽略。
MCP vs 传统 Function Calling¶
传统 Function Calling 的痛点:工具 schema 必须按目标模型格式写(OpenAI / Anthropic / Llama 三套),工具实现绑定到具体模型 API。MCP 解决这个痛点:Server 用 MCP 协议暴露工具,schema 是统一格式;Client 把 MCP schema 翻译成各模型的 schema 格式喂给 LLM——翻译在 Client 层做,Server 不感知。这意味着同一个 GitHub MCP server 可以被 Claude / GPT / Llama 用,不需要为每个模型重写。MCP 让 Function Calling(第 7 章)的工具真正"跨模型复用"——这是 MCP 的核心工程价值。
10.2 三大原语¶
MCP 定义三大原语(primitives):Tools / Resources / Prompts,分别对应"函数调用 / 静态数据 / 模板"三种使用场景。
Tools:函数调用¶
Tools 是最常用的原语——对应 Function Calling 里的"工具"。Server 暴露一组工具,每个工具有 name / description / input_schema(JSON Schema 描述参数)。Client 调 tools/list 拿工具列表,把列表(翻译成模型 schema)传给 LLM;LLM 决定调某工具后,Client 发 tools/call 给 Server 执行,Server 返回结果,Client 把结果作为 Observation 塞回 LLM 上下文。Tools 适合"有副作用、需要参数、动态执行"的场景——如 send_email(to, subject, body)、create_issue(repo, title)、query_db(sql)。Tools 是 Agent 工具调用的主要原语。
Resources:静态数据¶
Resources 是"静态数据"原语——Server 暴露一组资源(用 URI 标识,如 file:///path/to/file / github://repo/issue/123),Client 调 resources/list 拿资源列表,调 resources/read 读某资源内容。Resources 适合"无副作用、可重复读、内容相对静态"的场景——如读文件、读 GitHub issue 内容、读数据库 schema。Resources 与 Tools 的区别:Resources 是"读"(GET),Tools 是"做"(POST)——Resources 不带参数只带 URI,Tools 带参数执行。LLM 可以主动调 resources/read 拉静态数据进上下文,类似 RAG 检索。
Prompts:模板¶
Prompts 是"模板"原语——Server 暴露一组预定义的 prompt 模板(name / description / arguments / template),Client 调 prompts/list 拿模板列表,调 prompts/get 用参数渲染模板得到最终 prompt 文本。Prompts 适合"常用 prompt 模式可以参数化复用"的场景——如 code_review(language, code) 渲染成"请用 X 语言评审这段代码:Y"的 prompt。Prompts 让 Server 可以"打包"自己的最佳实践 prompt,用户用 Server 时不只拿到工具还能拿到推荐的用法。
三原语的使用场景对比¶
| 原语 | 用途 | 类比 HTTP | 典型场景 |
|---|---|---|---|
| Tools | 函数调用、有副作用 | POST | 发邮件、改数据库、调 API |
| Resources | 静态数据、可重复读 | GET | 读文件、读 issue 内容、查 schema |
| Prompts | prompt 模板渲染 | 模板引擎 | code review、generate tests、summarize |
三原语的设计哲学¶
三原语的设计哲学是"职责分离"。Tools 是"做"——有副作用、需要参数、需要权限管理。Resources 是"读"——无副作用、可缓存、可批量。Prompts 是"建议"——是 Server 给用户的"推荐用法",不强制。这种分离让 Server 实现者可以按职责组织代码——读操作放 Resources,写操作放 Tools,推荐 prompt 放 Prompts。也让权限管理更清晰——Resources 一般只读权限,Tools 要细分权限(如"只能查不能改"),Prompts 不需要权限(只是模板)。
实际 MCP server 的三原语分布¶
实际 MCP server 大多用 Tools 为主、Resources 辅助、Prompts 可选。如 GitHub MCP server:Tools 包括 create_issue / create_pr / merge_pr 等写操作;Resources 包括 repo_info / issue_content 等读操作;Prompts 可选包括 review_pr / summarize_issue 等模板。Slack MCP server:Tools 包括 send_message / create_channel;Resources 包括 channel_history / user_info;Prompts 可选。Filesystem MCP server:Tools 包括 write_file / delete_file;Resources 包括 read_file / list_directory;Prompts 一般没有。
三原语的工程实现差异¶
三原语在 Server 端的实现差异要工程师理解。Tools 实现要点:每个 tool 函数要处理参数校验、错误处理、副作用管理——执行结果可能是 success / failure,失败信息要返回给 LLM 让它知道下一步怎么改。Resources 实现要点:resource 是只读操作——同一 URI 多次读结果一致(除非底层数据变了);要支持 ETag / 缓存让 Client 减少重复读;URI 设计要稳定(不要轻易改 path scheme)。Prompts 实现要点:prompt 模板渲染要安全——用户输入的参数要 escape 防注入(如代码块用 ``` 包裹防 prompt injection);模板要支持变量默认值;要支持模板组合(多个 prompt 串联)。三原语代码组织建议:Tools 单独一个目录(含权限注解)、Resources 单独一个目录(含 URI 路由)、Prompts 单独一个目录(含模板引擎),不要混在一起便于维护。
10.3 Transport¶
MCP 通信可以走三种 Transport:stdio / SSE / HTTP。Transport 是底层传输协议,上层 JSON-RPC 消息格式不变——选 Transport 是按部署场景选。
stdio:本地子进程¶
stdio Transport 是最简单的形态——Host 把 Server 作为子进程启动,通过 stdin / stdout 通信。Host 启动 Server 子进程后,把 JSON-RPC 消息写到子进程 stdin,从子进程 stdout 读响应。stderr 留给 Server 输出日志。优点:第一,最简单——不需要网络配置,启动子进程就能跑。第二,零依赖——不需要 Server 监听端口、不需要 TLS、不需要认证(本机进程间通信够安全)。第三,适合本地开发——开发者在本地跑 MCP server,Cursor / Claude Desktop 直接 spawn 子进程即可。缺点:第一,仅限本地——子进程必须与 Host 在同一台机器。第二,单实例——一个 Server 子进程只能服务一个 Host。第三,无并发——stdio 是双向管道,多 Client 并发复杂。
SSE:单向流¶
SSE(Server-Sent Events)Transport 是单向流——Server 监听 HTTP 端口,Client 用 HTTP 长连接订阅事件流。Server 推送消息通过 SSE 事件,Client 发消息通过单独的 HTTP POST。优点:第一,跨网络——Server 可以在远端,Client 通过 HTTP 连接。第二,多 Client——一个 Server 可同时服务多个 Client。第三,穿透防火墙友好——SSE 走标准 HTTP 端口,防火墙一般放行。缺点:第一,单向——Client 发消息要单独 POST,不是真正的双向。第二,复杂度高——要管理 SSE 连接保活、断线重连。第三,协议设计要分两路——一路 SSE 收、一路 POST 发。SSE 是 MCP 早期推荐的远程 Transport,但 2025 年逐渐被 Streamable HTTP 取代。
HTTP:双向请求响应¶
Streamable HTTP Transport 是 MCP 2025 年新增的 Transport——单个 HTTP 端点支持双向请求响应,Client 用 HTTP POST 发 JSON-RPC 请求,Server 可以选择用 SSE 流式响应(适合长任务)或普通 HTTP 响应(适合短任务)。优点:第一,真正的双向——一个 HTTP 端点支持双向通信,不用分两路。第二,支持流式——Server 可以流式推送中间结果(如长任务进度)。第三,生产友好——HTTP 是生产标准协议,有成熟的负载均衡 / 网关 / 鉴权基础设施。缺点:第一,复杂度最高——要处理流式响应、断线重连、会话管理。第二,生产化要求高——要 TLS / 鉴权 / 限流 / 监控全套基础设施。HTTP Transport 是生产 MCP server 推荐的远程形态。
三种 Transport 的选择¶
按部署场景选 Transport。第一,本地开发:stdio 最简单——Cursor / Claude Desktop 直接 spawn MCP server 子进程,零配置。第二,远程部署小规模:SSE 或 HTTP——Server 跑在远端机器,Client 通过网络连接。SSE 简单但单向,HTTP 双向但要处理流式。第三,生产大规模:HTTP Transport——支持负载均衡、流式响应、会话管理。生产 MCP server 大多走 HTTP Transport,stdio 留给本地开发与测试。
Transport 切换的工程成本¶
MCP 设计上让 Transport 可切换——同一 Server 代码可以同时支持 stdio / SSE / HTTP。但实际切换有工程成本。第一,状态管理差异:stdio 是单进程单 Client 无并发问题;HTTP 是多 Client 并发要处理会话状态隔离。第二,鉴权差异:stdio 不需要鉴权(本机进程间通信);HTTP 要加鉴权(Bearer token / OAuth)。第三,错误处理差异:stdio 错误直接抛到 stderr Host 能看到;HTTP 错误要按 JSON-RPC error code 返回客户端。第四,日志差异:stdio 用 stderr 写日志 Host 自动捕获;HTTP 要独立日志系统(stdout 写文件 / log service)。生产 Server 开发期用 stdio 测试,上线时切换 HTTP 要把这几条差异都处理好。MCP Python SDK 用 FastMCP 抽象层让 Transport 切换只改一行代码(mcp.run(transport="stdio"|"streamable-http")),降低切换成本。
10.4 MCP vs Function Calling¶
MCP 与 Function Calling 的关系是"上层协议 vs 下层接口"——MCP 是工具协议(跨模型跨应用的标准),Function Calling 是模型接口(具体 LLM 的工具调用 API)。两者不互斥,是层级关系。
协议层级¶
层级关系:上层是 MCP 协议(Server 用 MCP 暴露工具,Client 用 MCP 调用工具);中层是 Client 适配器(把 MCP 工具 schema 翻译成具体模型的 Function Calling schema);下层是 Function Calling(OpenAI / Anthropic / Llama 各自的工具调用 API)。MCP 不替代 Function Calling——MCP 在 Function Calling 之上加了一层"协议标准化",让 Server 实现者不用关心具体模型 schema,Client 适配器做翻译。
一次实现多模型用的核心价值¶
MCP 的核心价值是"Server 一次实现,多模型可用"。传统 Function Calling:写一个"查 GitHub PR"的工具,给 GPT 用写 OpenAI schema,给 Claude 用写 Anthropic schema,给 Llama 用又写一份——三份代码三份维护。MCP:写一个 GitHub MCP server 用 MCP 协议暴露工具——Claude Desktop / Cursor / 任何 MCP Client 都能用,模型换 GPT / Claude / Llama 不重写工具。这是 MCP 被称为"AI 时代 USB-C"的原因——USB-C 标准化了接口,让任何设备都能用任何充电器;MCP 标准化了工具协议,让任何 LLM 都能用任何 Server。
MCP 让 Function Calling 工具可移植¶
MCP 让第 7 章 Function Calling 的工具真正可移植——Server 实现者按 MCP 协议写一次工具,所有支持 MCP 的 LLM 应用(Claude Desktop / Cursor / Continue / 自研 Agent)都能用。Client 适配器把 MCP schema 翻译成 OpenAI / Anthropic / Llama 各自的 schema——这种翻译是一次性投入(每个模型一份适配器),后续所有 Server 都能复用。MCP 不是替代 Function Calling 的训练层(模型还是按 Function Calling 训练),而是在工程层让 Function Calling 工具可跨模型复用。
MCP 不替代 Function Calling¶
要分清:MCP 不替代 Function Calling。LLM 还是要按 Function Calling 接口输出 tool_calls——只是 tool schema 来源从"硬编码在每个应用里"变成"从 MCP Server 动态发现"。Client 把 MCP 工具 schema 翻译成模型 Function Calling schema 喂给 LLM,LLM 输出 tool_calls 后 Client 把调用转成 MCP tools/call 发给 Server——MCP 在中间做了协议适配。Function Calling 仍是底层接口,MCP 是上层工具协议。
MCP 让生态可以并行发展¶
MCP 的更深层价值是"让生态可以并行发展"。Server 实现者可以专注业务(写更多更好的 MCP server)——不用关心哪个模型用它;Client 实现者可以专注协议适配(写更好的 Client SDK)——不用关心 Server 内部;Host 应用可以专注 UX(写更好的 LLM 应用)——多 Server 工具整合给用户。这种解耦让 MCP 生态可以像 npm / PyPI 那样爆发——每个领域(GitHub / Slack / 数据库 / 文件系统)都有专门的 MCP server,Host 应用可以"接入即可用"。
MCP 与 LangChain 工具的关系¶
MCP 与 LangChain 工具生态有重叠但不冲突。LangChain 的 Tools 是 Python 类(继承 BaseTool),实现 run() 方法——是代码层面的工具抽象,绑定到 LangChain 框架。MCP 是协议层面的工具抽象——Server 暴露工具,Client 通过协议调用,跨语言跨框架。两者关系:LangChain 可以作为 MCP Client(调 langchain-mcp-adapters 把 MCP server 包装成 LangChain Tool),也可以作为 MCP Server(把 LangChain Tool 暴露成 MCP server)。生产建议:新项目优先用 MCP 标准化工具协议,让工具不被锁死在单一框架;老项目用 LangChain 的可以渐进迁到 MCP——把高频复用工具先 MCP 化。
MCP 的安全模型¶
MCP 引入新的安全考量——Server 暴露给任意 Client 调用,权限与安全要严格设计。第一,权限分级:Tools 按风险分级——read-only / read-write / admin;高风险工具(写数据库 / 转账)要二次确认或人工审批。第二,Client 鉴权:远程 Transport 要鉴权 Client——Bearer token / OAuth / mTLS,避免任意 Client 调用。第三,审计日志:每次 tool call 都要记 (client_id, tool_name, args, result, timestamp),便于事后追溯。第四,沙箱:执行用户输入的工具(如运行代码)要沙箱隔离——Docker / gVisor / Firecracker 隔离环境。第五,速率限制:单 Client 对单工具调用频率要限流——防滥用与 DDoS。第六,敏感数据脱敏:工具返回内容含敏感数据(如信用卡号)要脱敏——不让敏感数据进 LLM 上下文。第七,权限白名单:Host 应配置可信 Server 列表,不让用户接入未知 Server——防恶意 Server 偷取 Host 上下文。
MCP 的生态演进方向¶
MCP 从 2024 年 11 月发布到 2025 年仍在快速演进。第一,Transport 标准化:早期 stdio + SSE 双轨,2025 年加 Streamable HTTP——Transport 标准在收敛。第二,OAuth 集成:远程 Server 鉴权从简单 token 走向 OAuth 2.1 标准化,让 MCP server 能用企业 IdP 鉴权。第三,Resources 流式:Resources 原来是同步读,2025 年加 streaming 支持让大资源可流式读。第四,Tree 订阅:Resources 加订阅能力,Server 数据变化主动 push 给 Client——避免 Client 轮询。第五,多模态:原来工具调用主要是文本,2025 年加图像 / 音频 / 视频支持——让 Agent 能调多模态模型。第六,生态目录:mcp.directory / Glama 等目录服务让用户能发现可用 Server,类比 npm / PyPI。这些演进方向让 MCP 从"协议草案"走向"生产标准"——是 AI 工具协议层的事实标准。
MCP 与 Agent 框架的整合¶
MCP 在主流 Agent 框架里的整合情况。第一,LangChain / LangGraph:通过 langchain-mcp-adapters 包把 MCP server 包装成 LangChain Tool,Agent 可以像用普通 Tool 一样调 MCP server——LangGraph 的图节点也可以包装 MCP server。第二,LlamaIndex:通过 llama-index-tools-mcp 把 MCP server 暴露给 LlamaIndex Agent。第三,AutoGen:Microsoft AutoGen 通过 MCP 把工具暴露给多 Agent 协作。第四,OpenAI Assistants API:OpenAI 2025 年宣布 Assistants API 支持 MCP——直接接入 MCP server 不需要写 Function Calling schema。第五,Claude Desktop / Cursor / Continue:原生支持 MCP server 配置,用户在配置文件加 server 路径就能用。这种"框架原生支持 MCP"的趋势让 MCP 成为事实标准——新写 Agent 应用应该直接基于 MCP 接入工具,跳过自己写 Function Calling schema 这一步。
MCP 的公司内部业务接入路径¶
公司要把内部业务系统(CRM / ERP / 内部 API)暴露给 Agent 用,MCP 是最佳路径。第一,写自定义 MCP server:用 Python / TypeScript SDK 把内部 API 包装成 MCP server 暴露 Tools / Resources / Prompts 三原语。第二,Transport 选 stdio 本地:内网开发期用 stdio 让 Cursor / Claude Desktop 直接连本地 server 测试。第三,生产部署 HTTP Transport:上 HTTP Transport 加 OAuth / mTLS 鉴权,部署到公司 K8s 集群做负载均衡。第四,权限分级:CRM server 的 read 工具放行给所有 Agent,write 工具要二次确认或人工审批。第五,审计日志:每次 tool call 落公司日志系统(ELK / Loki),便于合规追溯。第六,接入公司 IdP:用 OAuth 2.1 让 MCP server 用公司 Azure AD / Okta 鉴权——Agent 调用时带员工 token,权限按员工角色控制。这套路径让公司业务系统一次实现后所有 Agent 应用(Claude Desktop / Cursor / 自研 Agent)都能用——避免每应用重写工具 schema 的工程浪费。
MCP 的几个常见误解¶
工程师对 MCP 有几个常见误解需要澄清。第一,"MCP 是 Anthropic 私有协议"——错,MCP 2024 年 11 月开源,规范与 SDK 在 github.com/modelcontextprotocol 仓库,社区贡献开放,多家公司(OpenAI / Microsoft / Cursor Labs)都已表态支持。第二,"MCP 替代 Function Calling"——错,MCP 是工具协议层(跨模型标准),Function Calling 是模型接口层(具体 LLM API),两者层级不同不互斥。第三,"用了 MCP 就不需要写 Function Calling 代码"——部分对,Host 应用不需要为每个工具写 schema,但 Client 适配器仍要做 MCP schema 到模型 schema 的翻译。第四,"MCP server 必须用 Python / TypeScript 写"——错,MCP 是协议,任何语言实现都可以——社区已有 Go / Rust / Java 实现。第五,"stdio Transport 不能用于生产"——不完全对,本地开发与单机部署场景 stdio 完全够用,只有远程多 Client 才需要 HTTP。第六,"MCP 比 Function Calling 慢"——错,MCP 的额外开销只是协议层 JSON-RPC 序列化(毫秒级),相比 LLM 推理本身的几百毫秒可忽略。
10.5 主流 MCP server 生态¶
MCP 生态从 2024 年 11 月发布到 2025 年已经发展出几十个主流 server,覆盖开发、办公、数据、搜索等场景。下面列举 8 个主流 MCP server。
主流 MCP server 列表¶
- GitHub MCP server(github.com/modelcontextprotocol/servers):暴露 GitHub repo / issue / PR / commit 操作工具,让 Agent 能创建 PR、合并 PR、查 issue 历史。开发者最常用。
- Slack MCP server:暴露 Slack channel / message / user 操作工具,让 Agent 能发消息、读历史、查用户。
- Postgres MCP server:暴露 PostgreSQL 查询 / schema 读取工具,让 Agent 能查表结构、跑 SQL、读结果。只读模式默认,写模式要权限确认。
- Filesystem MCP server:暴露文件读写 / 目录列举工具,让 Agent 能读写本地文件、列目录。本地开发常用。
- Brave Search MCP server:暴露 Brave Search API 调用工具,让 Agent 能搜索网页,作为联网 RAG 工具。
- Puppeteer MCP server:暴露浏览器自动化工具,让 Agent 能开网页、点击、抓内容。Web 操作场景必备。
- Google Drive MCP server:暴露 Google Drive 文件读写工具,让 Agent 能读 Docs / Sheets 内容。
- Memory MCP server:暴露基于向量库的长期记忆工具(与第 9 章呼应),让 Agent 有跨会话记忆能力。
MCP server 的选型¶
工程师选 MCP server 时几个考量。第一,官方维护优先:github.com/modelcontextprotocol/servers 仓库的 server 是官方维护,质量与安全有保证。第二,社区贡献次之:社区贡献的 server 要看维护活跃度与 star 数——冷门 server 可能没人修 bug。第三,权限范围最小化:每个 server 授权要细——GitHub server 给 read 权限够就别给 write,Postgres server 默认只读模式。第四,Transport 适配:本地开发用 stdio server,生产部署用 HTTP server。第五,生态目录:mcp.directory / Glama 这类 MCP server 目录可以快速发现可用 server。
MCP server 的工程集成¶
工程上把 MCP server 集成进 Agent 应用的流程:1. 选 MCP server(如 GitHub);2. 配置 Client 连接(stdio / SSE / HTTP);3. 启动时 Client 调 tools/list 发现工具;4. 把工具 schema 翻译成目标模型 Function Calling schema 喂给 LLM;5. LLM 输出 tool_calls 后 Client 转 tools/call 调 Server;6. Server 返回结果后 Client 塞回 LLM。整个流程对 Agent 应用是透明的——LLM 不感知用的是 MCP 还是原生 Function Calling,只感知"我有这些工具可用"。
10.6 自定义 MCP server¶
自定义 MCP server 是工程上的常见需求——公司内部业务系统(CRM / ERP / 内部 API)想给 Agent 用,要自己写 MCP server 暴露工具。下面用 Python SDK 写一个最小 MCP server。
Python SDK 实战¶
Python SDK 是 MCP 官方维护的实现(github.com/modelcontextprotocol/python-sdk),用 pip install mcp 安装。下面是最小 MCP server 代码(约 30 行),暴露一个"查天气"工具:
# pip install mcp
from mcp.server.fastmcp import FastMCP
import httpx
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str, date: str = "today") -> dict:
"""查询指定城市指定日期的天气。
Args:
city: 城市中文全称,如 "北京" / "上海"
date: 日期 YYYY-MM-DD,默认今天
Returns:
dict: {"temp_celsius": int, "rain_mm": float, "condition": str}
"""
# 调真实天气 API(这里 mock)
resp = httpx.get(f"https://api.weather.example.com/{city}/{date}")
return resp.json()
@mcp.tool()
def get_weather_forecast(city: str, days: int = 7) -> list:
"""查指定城市未来 N 天预报"""
return [{"date": f"day+{i}", "temp_celsius": 20 + i} for i in range(days)]
@mcp.resource("weather://cities")
def list_supported_cities() -> list:
"""列出支持查询的城市"""
return ["北京", "上海", "广州", "深圳", "成都"]
if __name__ == "__main__":
# 默认 stdio transport,被 Host 作为子进程启动
mcp.run()
几个关键工程要点¶
最小 MCP server 代码几个关键要点。第一,FastMCP 装饰器:@mcp.tool() 装饰函数自动暴露为 MCP 工具,函数签名自动转 JSON Schema——name 从函数名推导,description 从 docstring 抽取,parameters 从类型注解推导。这比手写 JSON Schema 省事得多。第二,Resources 与 Prompts:@mcp.resource("uri://...") 暴露资源,@mcp.prompt() 暴露 prompt 模板——同样的装饰器风格。第三,Transport 选择:mcp.run() 默认 stdio transport,适合本地开发;要 HTTP 传 transport="streamable-http"。第四,docstring 重要:工具 description 自动从 docstring 抽取——docstring 写清楚"做什么 / 何时调 / 参数语义 / 返回格式"让 LLM 调用更准(与第 7 章 Function Calling description 写法一致)。
测试 MCP server¶
测试 MCP server 有几种方式。第一,MCP Inspector:官方提供的 GUI 调试工具(npx @modelcontextprotocol/inspector),启动后能看到所有 tools / resources / prompts,手动调任意工具看返回。本地开发首选。第二,集成到 Host 测试:把 server 配置进 Claude Desktop / Cursor,在真实 Host 里测——能验证 LLM 是否会正确调用。第三,自动化测试:用 MCP Python client SDK(from mcp import Client)写 unit test 自动测。生产 server 应该有完整自动化测试覆盖。
部署 MCP server 到生产¶
部署生产 MCP server 几个要点。第一,Transport 用 HTTP:生产用 streamable HTTP transport,能负载均衡与流式响应。第二,鉴权:HTTP transport 要加鉴权(如 Bearer token / OAuth),避免任意 Client 调用。第三,限流:单个 Client 调用频率要限流,防止滥用。第四,监控:每个 tool call 要落日志,便于审计与排查。第五,权限分级:工具按风险分级——read-only 工具放行,write 工具要二次确认(如发邮件、改数据库)。第六,沙箱:执行用户输入的工具(如运行代码)要沙箱隔离,避免 RCE。
代码片段:最小 MCP server¶
下面代码(约 30 行)实现最小可跑的 MCP server,演示 Tools / Resources / Prompts 三原语:
# pip install mcp
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("demo-server")
# === Tool:函数调用 ===
@mcp.tool()
def search_knowledge(query: str, top_k: int = 3) -> list:
"""搜索知识库,返回 top_k 相关文档。
适用:用户问"X 是什么"、"X 怎么用"等知识查询。
不适用:实时业务数据查询(请用 query_db)。
"""
# 这里 mock,真实实现调 RAG 流水线(第 5 章)
return [{"title": f"doc-{i}", "content": f"关于 {query} 的第 {i} 段"} for i in range(top_k)]
@mcp.tool()
def query_db(sql: str) -> list:
"""执行只读 SQL 查询业务数据库"""
if not sql.strip().lower().startswith("select"):
raise ValueError("仅支持 SELECT 查询")
return [{"col1": "value1", "col2": "value2"}]
# === Resource:静态数据 ===
@mcp.resource("schema://db/tables")
def get_db_schema() -> str:
"""返回业务数据库的表结构"""
return "users(id, name, email) | orders(id, user_id, amount, status)"
# === Prompt:模板 ===
@mcp.prompt()
def code_review(language: str, code: str) -> str:
"""生成代码评审 prompt"""
return f"请用 {language} 规范评审以下代码,指出问题并给改进建议:\n\n```\n{code}\n```"
if __name__ == "__main__":
mcp.run() # stdio transport,默认被 Host 作为子进程启动
把这段代码保存为 server.py,在 Claude Desktop / Cursor 配置 mcpServers: {"demo": {"command": "python", "args": ["server.py"]}},重启 Host 就能用 Agent 调用 search_knowledge / query_db 工具与 schema://db/tables 资源、code_review prompt 模板。
工程实战要点¶
- MCP 让工具一次实现多模型用:Server 按 MCP 协议写一次,Claude / GPT / Llama 通过 Client 适配器都能用——避免 Function Calling 时代"每模型一份 schema"的工程痛点。
- stdio 最简单——本地开发首选:本地开发用 stdio Transport,Host 直接 spawn server 子进程,零网络配置;Cursor / Claude Desktop 默认支持 stdio 接入。
- 生产用 HTTP transport:远程部署用 Streamable HTTP Transport,支持负载均衡、流式响应、会话管理;要加鉴权 / 限流 / 监控 / 沙箱全套生产基础设施。
- MCP server 要做权限分级:read-only 工具放行,write 工具(发邮件 / 改数据库 / 转账)要二次确认;高风险工具要人工 in the loop(与第 6 章 ReAct 工程要点呼应)。
- MCP 不是替代 Function Calling——是上层抽象:LLM 仍按 Function Calling 输出 tool_calls;MCP 在中间做协议适配,让 Server 不感知具体模型,让 Client 适配器做 schema 翻译——MCP 是工具协议层,Function Calling 是模型接口层。
小结¶
- MCP(modelcontextprotocol.io,Anthropic 2024.11)是 AI 时代的 USB-C——Server 一次实现,多模型多应用可用;用 JSON-RPC over chosen transport 通信。
- 三角色:Host(终端 LLM 应用)/ Client(协议适配器,一 Client 对一 Server)/ Server(工具提供方);Host 多 Server 多 Client 并行。
- 三大原语:Tools(函数调用,有副作用)/ Resources(静态数据,只读可缓存)/ Prompts(模板渲染,推荐用法);职责分离让权限管理清晰。
- 三种 Transport:stdio(本地子进程,最简单)/ SSE(单向流,远程)/ HTTP(双向请求响应,生产推荐);按部署场景选。
- vs Function Calling:MCP 是工具协议层(跨模型标准),Function Calling 是模型接口层(具体 LLM API);MCP 不替代 Function Calling,是上层抽象——Client 把 MCP schema 翻译成模型 schema 喂给 LLM。
- 主流 server 8 个:GitHub / Slack / Postgres / Filesystem / Brave Search / Puppeteer / Google Drive / Memory;自定义 server 用 Python SDK FastMCP 装饰器 30 行可跑。
练习题¶
- MCP 解决了什么问题?
- MCP 的三大原语是什么?各自适用场景?
- 三种 Transport 的差异?
- MCP 与 Function Calling 的关系?
- 为什么说 MCP 是"AI 时代的 USB-C"?
- 何时该自定义 MCP server 而非直接用 Function Calling?
参考答案¶
第 1 题答案
MCP 解决了"工具实现绑定到具体模型"的工程痛点。传统 Function Calling(第 7 章)每个工具要按目标模型写 schema——同一个"查 GitHub PR"工具给 GPT 用写 OpenAI schema(`tools=[{type:function, function:{name, parameters}}]`),给 Claude 用写 Anthropic schema(`tools=[{name, input_schema}]`),给 Llama 用又写一份——三份代码三份维护,且工具实现耦合具体模型 API。MCP 让 Server 用统一协议暴露工具,schema 是统一格式;Client 适配器把 MCP schema 翻译成各模型 schema 喂给 LLM——翻译在 Client 层做,Server 不感知具体模型。结果是同一个 GitHub MCP server 可以被 Claude / GPT / Llama 通过各自 Client 用,不需要为每个模型重写。MCP 让 Function Calling 工具真正"跨模型复用"。第 2 题答案
MCP 三大原语是 **Tools / Resources / Prompts**。**Tools**(函数调用):有副作用、需要参数、动态执行——类比 HTTP POST。适用场景:发邮件 `send_email(to, subject, body)` / 改数据库 `execute_sql` / 调外部 API。是 Agent 工具调用的主要原语。**Resources**(静态数据):无副作用、可重复读、内容相对静态——类比 HTTP GET,用 URI 标识(如 `file:///path` / `github://repo/issue/123`)。适用场景:读文件 / 读 GitHub issue 内容 / 查数据库 schema——LLM 主动调 `resources/read` 拉静态数据进上下文,类似 RAG 检索。**Prompts**(模板):预定义的 prompt 模板可参数化渲染——Server 暴露 `code_review(language, code)` 模板,Client 用参数渲染得到最终 prompt 文本。适用场景:常用 prompt 模式复用——code review / generate tests / summarize。设计哲学是职责分离:Tools 做(写,有权限)、Resources 读(无权限)、Prompts 建议(模板,无权限)。第 3 题答案
三种 Transport 差异。**stdio**:Host 把 Server 作为子进程启动,通过 stdin/stdout 双向管道通信,stderr 留日志。优点是最简单(零网络配置、零依赖、零鉴权)、本地开发首选;缺点是仅限本地(子进程必须与 Host 同机器)、单实例(一 Server 子进程只能服务一 Host)、无并发。**SSE**(Server-Sent Events):Server 监听 HTTP 端口,Client 用 HTTP 长连接订阅事件流,Server 推送消息通过 SSE 事件,Client 发消息通过单独 HTTP POST。优点是跨网络、多 Client、防火墙友好;缺点是单向(Client 发消息要单独 POST,不是真双向)、复杂度高(要管理 SSE 保活、断线重连)、协议分两路(一路 SSE 收一路 POST 发)。**HTTP**(Streamable HTTP):单个 HTTP 端点支持双向请求响应,Client 用 POST 发 JSON-RPC 请求,Server 可以用 SSE 流式响应(长任务)或普通 HTTP 响应(短任务)。优点是真正双向、支持流式、生产友好(成熟负载均衡 / 网关 / 鉴权基础设施);缺点是复杂度最高(要处理流式响应、断线重连、会话管理)、生产化要求高(TLS / 鉴权 / 限流 / 监控全套)。生产推荐 HTTP,本地开发用 stdio。第 4 题答案
MCP 与 Function Calling 的关系是"上层协议 vs 下层接口"——不互斥,是层级关系。**层级关系**:上层 MCP 协议(Server 用 MCP 暴露工具,Client 用 MCP 调用);中层 Client 适配器(把 MCP 工具 schema 翻译成具体模型 Function Calling schema);下层 Function Calling(OpenAI / Anthropic / Llama 各自的工具调用 API)。MCP 不替代 Function Calling——MCP 在 Function Calling 之上加一层"协议标准化",让 Server 实现者不用关心具体模型 schema,Client 适配器做翻译。LLM 还是要按 Function Calling 接口输出 tool_calls——只是 tool schema 来源从"硬编码在每个应用里"变成"从 MCP Server 动态发现"。Client 把 MCP 工具 schema 翻译成模型 Function Calling schema 喂给 LLM,LLM 输出 tool_calls 后 Client 把调用转成 MCP `tools/call` 发给 Server——MCP 在中间做协议适配。Function Calling 仍是底层接口,MCP 是上层工具协议。第 5 题答案
MCP 被称为"AI 时代的 USB-C"是因为它标准化了工具协议,让任何 LLM 都能用任何 Server——类比 USB-C 标准化了充电接口,让任何设备都能用任何充电器。传统 Function Calling 的痛点:工具实现绑定到具体模型——同一个工具给 GPT / Claude / Llama 用要写三份 schema,三份代码三份维护。MCP 的解决:Server 按 MCP 协议写一次,schema 是统一格式;Client 适配器把 MCP schema 翻译成各模型 schema 喂给 LLM——翻译是一次性投入(每模型一份适配器),后续所有 Server 都能复用。结果是同一个 GitHub MCP server 可以被 Claude Desktop / Cursor / 任何 MCP Host 用,不需要为每个模型重写工具。USB-C 让设备厂商与充电器厂商解耦——设备厂商专注设备、充电器厂商专注充电器,两者通过 USB-C 标准对接。MCP 让 Server 实现者与 Host 应用解耦——Server 专注业务、Host 专注 UX、两者通过 MCP 协议对接。这种"协议标准化带来生态爆发"是 MCP 与 USB-C 的本质相似。第 6 题答案
何时该自定义 MCP server 而非直接用 Function Calling?**该自定义 MCP server 的场景**:第一,**公司内部业务系统**:CRM / ERP / 内部 API 想给多个 Agent 应用用——写一个 MCP server 暴露工具,所有支持 MCP 的 Host(Claude Desktop / Cursor / 自研 Agent)都能接入,避免每个应用重写一遍 Function Calling schema。第二,**工具要跨模型复用**:同一个工具要给 GPT / Claude / Llama 都能用——MCP server 一次实现多模型用,比每模型写一份 schema 省事。第三,**工具有完整生态(多工具 / 资源 / 模板)**:一组相关工具(如 GitHub 的 PR / issue / commit 操作)打包成一个 server,配上 Resources(issue 内容读取)与 Prompts(PR review 模板),比散落的 Function Calling 工具更易管理。第四,**要给社区用**:写好 MCP server 发布到 MCP 目录,让其他开发者也能用——成为生态的一部分。**直接用 Function Calling 的场景**:工具只给单个 Agent 应用用、不跨模型、工具数量少(1-3 个)——简单场景直接写 Function Calling schema 更快,自定义 MCP server 是过度设计。下一章:第十一章 多 Agent 协作