跳转至

第十章 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 通过协议动态发现。

MCP 协议架构

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 是按部署场景选。

MCP 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 列表

  1. GitHub MCP server(github.com/modelcontextprotocol/servers):暴露 GitHub repo / issue / PR / commit 操作工具,让 Agent 能创建 PR、合并 PR、查 issue 历史。开发者最常用。
  2. Slack MCP server:暴露 Slack channel / message / user 操作工具,让 Agent 能发消息、读历史、查用户。
  3. Postgres MCP server:暴露 PostgreSQL 查询 / schema 读取工具,让 Agent 能查表结构、跑 SQL、读结果。只读模式默认,写模式要权限确认。
  4. Filesystem MCP server:暴露文件读写 / 目录列举工具,让 Agent 能读写本地文件、列目录。本地开发常用。
  5. Brave Search MCP server:暴露 Brave Search API 调用工具,让 Agent 能搜索网页,作为联网 RAG 工具。
  6. Puppeteer MCP server:暴露浏览器自动化工具,让 Agent 能开网页、点击、抓内容。Web 操作场景必备。
  7. Google Drive MCP server:暴露 Google Drive 文件读写工具,让 Agent 能读 Docs / Sheets 内容。
  8. 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 行可跑。

练习题

  1. MCP 解决了什么问题?
  2. MCP 的三大原语是什么?各自适用场景?
  3. 三种 Transport 的差异?
  4. MCP 与 Function Calling 的关系?
  5. 为什么说 MCP 是"AI 时代的 USB-C"?
  6. 何时该自定义 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 协作