第 7 章 MCP:标准化工具接入
本章要解决的问题
每接一个工具就写一套适配代码?MCP 如何让工具接入从「手工作坊」变成「即插即用」?
章节大纲
- 7.1 MCP 是什么:解决工具碎片化
- 7.2 Server / Client 架构与协议
- 7.3 工具发现、调用与鉴权
- 7.4 动手:自建一个 MCP Server(Python)
- 🛠 解决方案:MCP 接入常见失败(发现失败/鉴权/超时/版本兼容)
7.1 MCP 是什么:解决工具碎片化
7.1.1 工具接入的"手工作坊"困境
在第 5 章,我们给每个工具写一套 Function Schema。当工具多了,问题来了:
css
系统 A:GitHub 工具 → 写一套 OpenAI 格式 Schema
系统 B:数据库工具 → 再写一套(换个 Agent 框架,又写一套)
系统 C:内部 API → 又写一套......
每个 Agent 框架:LangChain 一套、AutoGen 一套、自研一套
同一个工具,N 个 Agent 框架 = N 套适配代码------这就是工具碎片化:连接成本高、重复劳动、难以复用。MCP 就是为了终结这种"手工作坊"。
7.1.2 MCP 是什么:工具的"USB 接口"
MCP(Model Context Protocol,模型上下文协议)是 Anthropic 发起的开放协议,为"AI 应用连接外部工具/数据"定义了统一标准。

图 1:MCP = 工具的 USB 接口
类比理解:
| 概念 | 类比 |
|---|---|
| MCP | USB 接口标准 |
| MCP Server | USB 设备(一个工具/服务一个 Server) |
| MCP Client | USB 接口(Agent 应用内置) |
| 工具发现 | 插上即识别(无需为每台电脑定制) |
一次接入,到处使用:写一个 MCP Server,任何支持 MCP 的 Agent(Claude、自研 Agent、LangChain 生态......)都能即插即用------这就是 MCP 的价值。
7.1.3 什么时候用 MCP
| 场景 | 用不用 MCP |
|---|---|
| 单个 Agent、1~3 个自研工具 | 不必,直接 Function Schema(第 5 章) |
| 多个系统/跨团队共享工具 | 用 MCP(一次封装,多处复用) |
| 接入第三方生态(GitHub/数据库/云服务) | 用 MCP(官方 Server 已存在) |
| 工具要长期演进、多框架兼容 | 用 MCP(协议层解耦) |
经验法则:工具少且私有 → 第 5 章直接调;工具多/共享/生态化 → MCP。
7.2 Server / Client 架构与协议
7.2.1 架构总览
arduino
┌─────────────────┐ ┌──────────────────┐
│ Agent 应用 │ MCP │ MCP Server │
│ (MCP Client) │◄────────►│ 工具/数据提供方 │
│ │ 协议 │ │
│ - 发现工具 │ │ - 暴露工具 │
│ - 调用工具 │ │ - 访问底层系统 │
│ - 读取资源 │ │ (GitHub/DB/内部API)│
└─────────────────┘ └──────────────────┘

图 2:MCP Server/Client 架构
关键点 :MCP Client 在 Agent 应用侧(负责发现和调用),MCP Server 在工具提供方侧(负责暴露能力)。两者通过协议通信,Agent 不再关心工具内部实现。
7.2.2 协议传输与核心能力
| 维度 | 说明 |
|---|---|
| 传输方式 | 本地 stdio(进程间,适合开发调试)、远程 HTTP/SSE/Streamable HTTP(跨机器,适合生产部署)。不同传输方式的认证、延迟、运维复杂度不同------stdio 零配置但仅限本地;远程部署需要考虑鉴权、网络延迟和连接稳定性 |
| 核心能力 | 工具 (tools)、资源 (resources)、提示词(prompts) |
| 工具 | Server 暴露可调用函数(类似第 5 章 Function,但标准化)------生产中最常用、最成熟 |
| 资源 | Server 暴露可读取数据(文档、文件、数据库内容)------生态成熟度较低 |
| 提示词 | Server 暴露可复用的提示词模板------生态成熟度较低 |
和 MCP 生态的关系 :MCP 是第 14 章"工具增强"的标准化实现------MCP Server 就是"工具库"的即插即用形态;MCP 的"资源"能力与 RAG(第 8 章)天然互补(Server 直接喂数据)。
7.2.3 MCP vs Function Calling:不是替代,是封装
| 对比 | Function Calling(第 5 章) | MCP |
|---|---|---|
| 定位 | 模型输出的函数调用协议 | 工具接入的标准协议 |
| 关注点 | "模型怎么填参数" | "工具怎么被找到和调用" |
| 关系 | 在多数 LLM Agent 框架中,MCP 工具最终会被转换为模型可用的工具 schema/function | 在 Function 之上加一层标准化 |

图 3:MCP vs Function Calling
一句话:MCP 是"工具接入层"的标准,Function Calling 是"模型调用层"的机制------两者配合使用,MCP Server 暴露的工具在 Agent 内部仍通过 Function Calling 触发。
7.3 工具发现、调用与鉴权
7.3.1 发现(Discovery):插上即识别
MCP 的 tools/list 让 Client 自动获取 Server 的所有工具及 Schema------Agent 无需预写工具定义:

图 4:MCP 发现→调用流程
python
# 伪代码:MCP Client 发现流程
server = await mcp.connect("github-server") # 连接 Server
tools = await server.list_tools() # 发现工具
# tools = [{name: "create_issue", description: "...", inputSchema: {...}}, ...]
发现后,这些工具以标准 Function Schema 形式注入模型上下文------和手写 Schema 的效果一致,但自动化了。
7.3.2 调用(Call):标准化的执行
python
result = await server.call_tool(
"create_issue",
{"title": "Bug: 登录超时", "body": "复现步骤..."},
)
MCP 调用的价值:Client 不需要知道 GitHub API 长什么样------Server 封装了全部细节(认证、参数转换、错误处理)。这就是"一次接入,到处使用"。
7.3.3 鉴权(Auth):谁有权限调什么
MCP 的鉴权分两层:
| 层 | 说明 |
|---|---|
| 传输层鉴权 | 远程 Server 需要 API Key / OAuth 才能连接 |
| 工具级权限 | Server 内部对敏感工具做权限控制(呼应第 22 章 RBAC) |
安全提醒 :接入第三方 MCP Server 时,先审查它暴露了哪些工具(尤其有无写操作/数据外发)------MCP 让工具接入变简单,也让"接入恶意 Server"变简单 ,供应链安全要重视(呼应第 22 章安全治理)。MCP Server 的权限边界与沙箱:Server 能暴露什么工具、是否能读文件/写数据库/访问外部网络,需要服务端做权限隔离,不能假设 Server 自带安全约束。
7.4 动手:自建一个 MCP Server(Python)
用官方 SDK 写一个"订单查询" MCP Server,演示完整流程。
7.4.1 安装与创建
bash
pip install mcp fastmcp
7.4.2 定义工具(FastMCP 声明式)
python
from fastmcp import FastMCP
# 创建 Server(名称即"工具包"名字)
mcp = FastMCP("order-service")
# 用装饰器暴露工具:一个函数 = 一个工具
@mcp.tool()
def query_order(order_id: str) -> dict:
"""查询订单状态。当用户询问订单/发货/物流进度时使用。
Args:
order_id: 订单号,如 A123456789
"""
# 真实场景:查数据库/调用内部 API
return {"order_id": order_id, "status": "shipped", "eta": "明天送达"}
@mcp.tool()
def calculate_refund(order_id: str, amount: float) -> dict:
"""计算退款金额。仅限已支付订单。
Args:
order_id: 订单号
amount: 支付金额(元)
"""
refund = round(amount * 0.95, 2) # 示例:扣 5% 手续费
return {"order_id": order_id, "refund_amount": refund}
if __name__ == "__main__":
mcp.run(transport="stdio") # 本地 stdio 传输
注意:函数 docstring 就是工具的 description,参数类型注解就是 Schema------FastMCP 自动生成标准 MCP 定义,无需手写 JSON Schema(比第 5 章手写更省事)。
7.4.3 客户端接入(发现 + 调用)
python
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# 启动 Server 进程并连接
params = StdioServerParameters(command="python",
args=["order_server.py"])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
# 1. 发现工具
tools = await session.list_tools()
for t in tools.tools:
print(f"发现工具: {t.name} → {t.description}")
# 2. 调用工具
result = await session.call_tool(
"query_order", {"order_id": "A123456789"})
print(f"调用结果: {result}")
import asyncio
asyncio.run(main())
7.4.4 接入 Agent(第 6 章循环里用 MCP)
python
# 步骤 1:发现 MCP 工具并转换为 OpenAI Function Schema
mcp_tools = (await server.list_tools()).tools
tools = [] # 转换为 OpenAI tools 格式
for t in mcp_tools:
tools.append({
"type": "function",
"function": {
"name": t.name,
"description": t.description,
"parameters": t.inputSchema, # MCP 的 inputSchema → OpenAI parameters
}
})
# 步骤 2:模型决策(第 6 章循环中的 Think 阶段)
resp = client.chat.completions.create(
model="deepseek-chat", messages=messages, tools=tools, tool_choice="auto")
# 步骤 3:模型选择工具后,通过 MCP 执行调用(Act 阶段)
if resp.choices[0].message.tool_calls:
call = resp.choices[0].message.tool_calls[0]
try:
args = json.loads(call.function.arguments)
except json.JSONDecodeError:
args = {} # 参数解析失败时降级为空参数,生产环境应记录告警
result = await server.call_tool(call.function.name, args)
# 结果回填给模型(Observe 阶段)
messages.append({"role": "tool", "tool_call_id": call.id,
"content": str(result)})
完整闭环:MCP Server 暴露工具 → Client 发现并转换为 Function Schema → 模型决策选择工具 → Client 通过 MCP 执行调用 → 结果回填(第 6 章循环)。
🛠 解决方案:MCP 接入常见失败
常见问题
- "工具发现失败(list_tools 空)":Server 启动失败或未注册工具。对策:先单独跑 Server 看报错;确认装饰器是否生效;检查 stdio 参数。
- "鉴权失败(401/403)":远程 Server 需要凭证。对策:检查 API Key / OAuth 配置;确认凭证环境变量是否正确注入。
- "调用超时":Server 处理慢或网络问题。对策:设置调用超时;本地 stdio 排查进程是否卡死;远程检查网络。
- "版本不兼容(协议握手失败)":SDK 版本与 Server 协议版本不匹配。对策:统一 mcp/fastmcp SDK 版本;升级到支持最新协议的版本。
- "模型选不到 MCP 工具":工具描述不清晰或工具过多。对策:写好 docstring 描述(何时用/何时不用);用第 11 章路由分流(MCP 工具 >5 时)。
解决方案速查表
| 现象 | 根因 | 解决方案 |
|---|---|---|
| 发现失败 | Server 未启动/未注册 | 单独跑 Server 排查 |
| 鉴权失败 | 凭证缺失/错误 | 检查 Key/OAuth 配置 |
| 调用超时 | 处理慢/网络 | 设超时 + 排查进程 |
| 版本不兼容 | SDK 版本差 | 统一 SDK 版本 |
| 选不到工具 | 描述不清/工具多 | 写好 docstring + 路由分流 |
实战提示
- 工具少而私有 → 不用 MCP:1~3 个自研工具,第 5 章直接写 Schema 更简单。
- docstring 即描述:FastMCP 用函数注释生成工具描述,写清楚"何时用"。
- 先审查再接入第三方 Server:检查暴露的工具清单,防恶意/多余工具(第 22 章)。
- MCP + Function Calling 配合:MCP 解决"接入",Function Calling 解决"调用",别对立。
- 协议版本对齐:SDK 升级要回归测试,MCP 协议仍在快速演进。