第 7 章 MCP 标准化工具接入

第 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 接入常见失败

常见问题

  1. "工具发现失败(list_tools 空)":Server 启动失败或未注册工具。对策:先单独跑 Server 看报错;确认装饰器是否生效;检查 stdio 参数。
  2. "鉴权失败(401/403)":远程 Server 需要凭证。对策:检查 API Key / OAuth 配置;确认凭证环境变量是否正确注入。
  3. "调用超时":Server 处理慢或网络问题。对策:设置调用超时;本地 stdio 排查进程是否卡死;远程检查网络。
  4. "版本不兼容(协议握手失败)":SDK 版本与 Server 协议版本不匹配。对策:统一 mcp/fastmcp SDK 版本;升级到支持最新协议的版本。
  5. "模型选不到 MCP 工具":工具描述不清晰或工具过多。对策:写好 docstring 描述(何时用/何时不用);用第 11 章路由分流(MCP 工具 >5 时)。

解决方案速查表

现象 根因 解决方案
发现失败 Server 未启动/未注册 单独跑 Server 排查
鉴权失败 凭证缺失/错误 检查 Key/OAuth 配置
调用超时 处理慢/网络 设超时 + 排查进程
版本不兼容 SDK 版本差 统一 SDK 版本
选不到工具 描述不清/工具多 写好 docstring + 路由分流

实战提示

  1. 工具少而私有 → 不用 MCP:1~3 个自研工具,第 5 章直接写 Schema 更简单。
  2. docstring 即描述:FastMCP 用函数注释生成工具描述,写清楚"何时用"。
  3. 先审查再接入第三方 Server:检查暴露的工具清单,防恶意/多余工具(第 22 章)。
  4. MCP + Function Calling 配合:MCP 解决"接入",Function Calling 解决"调用",别对立。
  5. 协议版本对齐:SDK 升级要回归测试,MCP 协议仍在快速演进。
相关推荐
大模型码小白12 分钟前
Spring AI 框架中集成 MCP 的完整指南:从服务端到客户端的全流程实践
大数据·运维·数据库·人工智能·python·sql·spring
武子康14 分钟前
拆开 Pi Monorepo:改模型、循环、产品和 UI 时,代码应该放在哪一层
人工智能·llm·agent
hh95015 分钟前
Agent Plan × DeepSeek Harness:角色 Prompt 驱动的 Agent 分工优化与协作质量实验
java·前端·人工智能·prompt·adg·agent plan·adg成都社区
YHL15 分钟前
🐉 天龙八部 RAG 知识库实战:从零构建你的武侠 AI 助手
数据库·人工智能
阿基拉de_Akir15 分钟前
② 跨层禁止:机器如何拦截非法语义绑定
人工智能
科技小E18 分钟前
把人从百米高空拉下来:自动化AI算法训练服务器DLTM+无人机巡检让风机光伏缺陷无所遁形
人工智能·自动化·无人机
武子康19 分钟前
一次 Agent 失败后,到底该改模型、Prompt 还是 Router?
人工智能·llm·agent
小K讲AI营销19 分钟前
固态电池战局拆解:机器人为何先于汽车吃到红利
大数据·人工智能·区块链
beiju19 分钟前
别急着埋 SaaS:Agent 时代真正被压缩的是人工胶水层
人工智能