模型上下文协议 MCP

面向后端工程师讨论 MCP(Model Context Protocol),重点不在于引入一个新名词,而在于回答 3 个核心问题:

  1. 为什么在 Function Calling 已经存在的情况下,还需要 MCP?
  2. MCP 到底标准化了什么?
  3. 在真实工程里,MCP 的调用链是怎么跑起来的?

目录

    1. 背景与定位:为什么需要 MCP
    1. MCP vs Function Calling
    1. MCP 的核心原理
    • 3.1 核心架构(Host / Client / Server)
    • 3.2 核心交互流程
    • 3.3 关键技术规范
    1. MCP 应用实例(智能数据分析助手)
    • 4.1 先看完整调用链
    • 4.2 Step 1:MCP Server 定义工具
    • 4.3 Step 2:实现 MCP Server
    • 4.4 Step 3:大模型如何“使用” MCP
    1. 工程实践与注意要点

1. 背景与定位:为什么需要 MCP

MCP(Model Context Protocol,模型上下文协议)是一套面向大模型与外部系统交互的开放协议。它的核心目标,不是替代模型 API,也不是替代 Prompt 工程,而是把“模型如何安全、结构化、可维护地调用外部能力”这件事标准化。

这里的外部能力包括:

  • 工具
  • 数据源
  • 文件系统
  • 业务服务
  • 应用端上下文

1.1 没有 MCP 时,问题出在哪里

在 MCP 出现之前,模型和外部系统的集成通常会遇到 5 类典型问题:

1. 交互格式碎片化

不同模型、不同 SDK、不同工具往往有各自的字段命名和请求格式,例如:

  • 有的系统用 history 表示历史消息
  • 有的系统用 context
  • 有的系统把上下文直接拼进 Prompt

工具调用的参数结构、返回结构也经常各不相同,导致每接一个新模型或新工具,都要重新写一层适配逻辑。

2. 上下文管理混乱

复杂任务不是一次请求就能完成的,通常会涉及:

  • 历史对话
  • 用户身份
  • 当前任务状态
  • 上一步工具返回结果

如果这些上下文没有统一的生命周期管理机制,多轮交互时就容易出现“失忆”“串上下文”“工具结果接不上”的问题。

3. 工具接入缺少统一抽象

传统做法中,工具调用往往散落在业务代码里:

  • 这里手写一个 HTTP 请求
  • 那里直接查一次数据库
  • 另一处再拼一个本地脚本调用

这种方式短期能跑,长期很难治理。因为模型侧看不到统一的工具描述,服务侧也没有统一的校验、鉴权和错误处理。

4. 错误处理与观测割裂

调用失败时,常见问题包括:

  • 参数不合法
  • 权限不足
  • 工具不存在
  • 目标服务超时
  • 工具本身执行异常

如果没有统一的消息结构和错误码约定,排查问题时就会非常痛苦,尤其是在多组件协作的链路里。

5. 工程可维护性差

对于后端工程师来说,真正麻烦的不是“第一次接上”,而是后续的:

  • 新增工具
  • 升级模型
  • 替换客户端
  • 做权限控制
  • 加审计日志

没有统一协议时,这些改动都会演变成到处修改、重复适配。

1.2 MCP 的定位

MCP 的定位可以概括为一句话:

它是模型与外部系统之间的“统一交互层”。

它主要标准化 4 件事:

  • 上下文如何传
  • 工具如何描述
  • 请求和响应如何封装
  • 错误和生命周期如何管理

因此,MCP 不是某个厂商的私有能力,而更接近一层“连接规范”。它解决的重点,不是让模型“更聪明”,而是让模型更稳定地接入外部世界。

1.3 MCP 不是什么

为了避免混淆,需要先明确边界:

  • MCP 不是模型 API 本身。API 解决“怎么访问模型”,MCP 解决“模型怎么访问外部能力”。
  • MCP 不是 Prompt 工程。Prompt 工程优化的是“怎么提问”,MCP 优化的是“怎么接系统”。
  • MCP 不是向量数据库。向量数据库负责“存和检索信息”,MCP 负责“把这些信息和工具能力规范地送进调用链”。

2. MCP vs Function Calling

很多工程师第一次看到 MCP 时都会问:Function Calling 已经能让模型调用工具了,为什么还要 MCP?

短答案是:

Function Calling 解决的是“单个模型如何调用某个工具”;MCP 解决的是“不同模型 / 客户端 / 工具服务如何通过统一协议协作”。

2.1 对比表

对比维度 Function Calling MCP
关注点 模型输出一个工具调用意图 用统一协议组织模型、客户端、工具服务之间的协作
抽象层级 更偏模型能力层 更偏系统集成层
工具描述 往往绑定在某个模型 API 或 SDK 上 通过协议统一描述,可被不同 Host / Client / Server 理解
上下文管理 通常由业务方自己处理 协议显式关注上下文的传递、更新与生命周期
错误处理 取决于具体 SDK / 业务实现 可以统一消息结构、错误码与回传方式
跨厂商互操作 较弱,常常跟具体模型供应商绑定 更强,目标就是解耦模型、客户端和工具服务
工程治理 适合快速接一个工具 更适合长期维护、扩展和治理整套工具体系

2.2 一句话理解增量价值

二者可以概括为:

  • Function Calling:让模型“知道该调用哪个工具”
  • MCP:让整个系统“知道这次工具调用该怎么被组织、传输、校验、执行、回传和追踪”

所以,MCP 的增量价值主要在 3 点:

  1. 标准化:把工具调用从“各写各的”变成统一协议。
  2. 互操作性:降低模型、客户端、工具服务之间的耦合。
  3. 生命周期管理:不仅关注“调一下工具”,还关注前后的上下文、错误、状态和治理。

可以归纳为如下判断:

如果你只是想让某个模型快速调一个工具,Function Calling 可能已经够了。
如果你在做的是一个长期维护的 Agent 系统,需要跨组件、跨工具、跨客户端协作,那么 MCP 更值得引入。

2.3 什么情况下不必急着上 MCP

并不是所有项目都值得一开始就引入 MCP。

如果你的场景同时满足下面几个条件,那么直接使用 Function Calling 或一层轻量工具封装,通常已经够用:

  • 只有 1 到 2 个工具
  • 工具调用链很短,没有复杂的上下文生命周期
  • 不需要跨多个 Host / Client / Server 组件协作
  • 不需要统一治理权限、审计、错误码和可观测性
  • 项目本身规模小,更关注“先跑通”而不是“长期平台化”

换句话说:

MCP 更适合“系统化建设”,不一定适合“所有小项目的第一天”。
如果问题规模还没到协议层,先用简单方案往往更划算。

3. MCP 的核心原理

MCP 的核心可以概括为一句话:

用统一的消息结构和组件分工,把“模型 ↔ 工具”的临时集成,变成“Host ↔ Client ↔ Server”的可维护系统。

3.1 核心架构(Host / Client / Server)

MCP 通常可以拆成 3 个核心组件。

MCP Host(宿主应用)

Host 是整个 MCP 调用链的入口和中枢。它通常就是你真正的 AI 应用,例如:

  • Claude Desktop
  • 编辑器插件
  • 企业级 AI 助手后端
  • 自己实现的 Agent 服务

它的核心职责包括:

  • 接收用户请求
  • 管理上下文
  • 决定要不要调用工具
  • 把请求分发给对应的 MCP Client
  • 汇总结果后返回给用户

从职责划分看,Host 对应业务调度层。

MCP Client(客户端)

Client 是协议适配层,负责把 Host 的意图转成 MCP 标准请求,再把 Server 的结果转回 Host 能理解的内容。

它的核心职责包括:

  • 与 MCP Server 建立连接
  • 封装 MCP 请求
  • 接收 MCP 响应
  • 处理超时、重试、连接状态

从职责划分看,Client 对应协议适配层。

MCP Server(工具服务端)

Server 部署在真正提供能力的一侧,也就是:

  • 数据库代理
  • 文件系统工具
  • 搜索服务
  • 邮件服务
  • 内部业务接口

它的核心职责包括:

  • 暴露工具能力
  • 描述这些工具能做什么
  • 校验参数是否合法
  • 执行真实操作
  • 返回标准化结果或错误

从职责划分看,Server 对应受控能力层。

3.2 核心交互流程

以一次完整的工具调用为例,MCP 的核心交互流程可以概括为 6 步:

  1. Host 收集上下文
    Host 收集用户请求、历史消息、当前任务状态等信息。

  2. Client 封装标准请求
    Client 把 Host 给出的请求和上下文,封装成 MCP 标准消息。

  3. Server 解析请求
    Server 解析消息,识别调用的是哪个工具、参数是否合法、当前调用是否有权限。

  4. Server 执行工具
    如果校验通过,Server 调用本地能力,例如数据库查询、文件读取、HTTP 接口请求。

  5. Client 接收并解析响应
    Client 把 Server 的结果或错误转换为 Host 更容易消费的形式。

  6. Host 更新上下文并继续推理
    Host 把工具结果放回当前上下文,让 LLM 继续总结、推理或发起下一次调用。

这个流程的重点在于:工具调用不是孤立的一跳,而是嵌在上下文生命周期里的。

3.3 关键技术规范

MCP 的工程价值,最终落在 3 类规范上:

  • 消息结构
  • 错误处理
  • 传输方式

标准消息结构

MCP 消息通常采用 JSON 格式,核心字段类似这样:

{
  "message_id": "uuid-123",
  "sender": "agent-billing",
  "receiver": "tool-stripe",
  "timestamp": "2025-07-30T12:00:00Z",
  "performative": "request",
  "content": "{\"action\":\"charge\",\"amount\":99.99}",
  "metadata": {
    "trace_id": "span-456",
    "priority": "high"
  }
}

这些字段的意义不是“多写几个 JSON 字段”,而是把一次调用变成可追踪、可审计、可治理的标准事件。

标准化错误处理

MCP 很适合采用类似 JSON-RPC 2.0 的错误码体系,把常见错误统一起来:

错误码 错误含义 后端处理建议
-32700 解析错误 重点检查请求 JSON 是否损坏、字段是否缺失
-32600 无效请求 检查请求结构是否符合 MCP 约定
-32601 方法不存在 确认工具名拼写正确,且 Server 已注册该工具
-32602 参数错误 对照工具 schema 校验参数完整性与类型
-32603 服务器错误 查看 Server 日志,排查工具执行异常或依赖故障

灵活的传输方式

MCP 可以根据部署方式选择不同传输层:

应用场景 传输方式 核心特点
本地工具调用 Stdio 简单直接,适合本地进程通信
远程服务集成 HTTP+SSE 更容易跨网络部署,兼容性较好
实时双向交互 WebSocket 适合持续推送和低延迟场景

这里最重要的不是背协议名字,而是理解:MCP 把“传什么”和“怎么传”分开了。消息结构由协议约束,底层传输方式按部署场景选择。

4. MCP 应用实例(智能数据分析助手)

下面用一个“智能数据分析助手”的例子,说明 MCP 在工程里到底怎么跑。

4.1 先看完整调用链

在进入代码之前,先看完整调用链:

User
  ↓
Host(接收请求、维护上下文)
  ↓
LLM(判断需要调用哪个工具)
  ↓
MCP Client(封装协议请求)
  ↓
MCP Server(校验并执行工具)
  ↓
DB / API / File
  ↑
MCP Server 返回结果
  ↑
MCP Client 解析结果
  ↑
Host 更新上下文
  ↑
LLM(总结 & 推理)
  ↑
User

这张图里最关键的点是:

  • LLM 不直接访问数据库
  • LLM 不直接执行代码
  • LLM 只是表达“我要什么能力”
  • 真正执行能力的是 MCP Server

有了这个全局视角,再看下面的步骤会清晰很多。

4.2 Step 1:MCP Server 定义工具

先定义你允许模型使用什么能力。

这个例子里,我们只开放两类能力:

  • 查看有哪些表
  • 对指定表做只读查询
// tools.ts
export const tools = [
  {
    name: "list_tables",
    description: "列出当前数据库的表",
    inputSchema: {
      type: "object",
      properties: {}
    }
  },
  {
    name: "query_table",
    description: "对指定表执行只读 SQL 查询",
    inputSchema: {
      type: "object",
      properties: {
        table: { type: "string" },
        columns: {
          type: "array",
          items: { type: "string" }
        },
        limit: { type: "number", default: 100 }
      },
      required: ["table", "columns"]
    }
  }
]

这里的关键不是代码量,而是设计思想:

  • 不是让模型自由生成 SQL
  • 而是由你先把“允许的能力”定义成结构化接口
  • 模型只能在你开放的边界内调用

这构成了“受控能力”的核心。

4.3 Step 2:实现 MCP Server

有了工具描述之后,就要把这些工具真正挂到 Server 上。

// server.ts
import { Server } from "@modelcontextprotocol/sdk/server"
import { tools } from "./tools"
import { db } from "./db"

const server = new Server({ name: "db-mcp-server" })

server.setTools(tools)

server.onToolCall(async (tool, args) => {
  if (tool === "list_tables") {
    return await db.listTables()
  }

  if (tool === "query_table") {
    return await db.select(
      args.table,
      args.columns,
      args.limit
    )
  }
})

server.start()

这段代码中,最需要解释的是两个 API:

server.setTools(tools) 做了什么

它的作用是:把你定义好的工具 schema 注册给 MCP Server。

注册之后,Server 才知道:

  • 当前有哪些工具
  • 每个工具叫什么
  • 每个工具接收什么参数
  • 参数是否合法

它可以视为“能力清单注册”。

更精确地说,这一步完成的是 capability discovery 的基础准备
它使 Client / Host 一侧能够获知:这个 Server 暴露了哪些能力、这些能力各自的 schema 是什么。这个“能力发现”机制,也是 MCP 与普通 RPC 风格接口的重要区别之一。普通 RPC 往往假设双方已经预先强绑定接口,而 MCP 更强调通过协议显式暴露能力,再由调用方按协议理解和消费这些能力。

server.onToolCall(...) 做了什么

它的作用是:定义当某个工具真的被调用时,服务端该怎么执行。

也就是说:

  • setTools 负责“声明能力”
  • onToolCall 负责“执行能力”

前者更接近接口定义,后者更接近路由分发与真实业务执行。

到这里为止,已经形成了一个“AI 可调用的数据库服务”,但它仍然处在受控边界内:

  • 模型不能随便执行 DROP
  • 模型不能绕过你定义好的 schema
  • 模型也不能跳过服务端校验直接碰数据库

4.4 Step 3:大模型如何“使用” MCP

假设用户输入:

分析一下最近 30 天用户增长趋势

此时实际发生的并不是“模型直接去查数据库”,而是如下链路:

  1. Host 收到用户请求,并把当前对话上下文交给 LLM。
  2. LLM 判断:这个问题需要先获取结构化数据。
  3. LLM 生成一个“工具调用意图”。
  4. Host / Client 把这个意图封装成 MCP 请求,发送给 MCP Server。
  5. MCP Server 根据工具名和参数执行真实查询。
  6. 返回结果后,Host 再把结果交给 LLM 做总结。

从“模型意图”的角度看,LLM 内部更接近于表达如下结构:

{
  "tool": "query_table",
  "arguments": {
    "table": "user_daily_stats",
    "columns": ["date", "new_users"],
    "limit": 30
  }
}

这段 JSON 本身不是重点,重点是它在链路里的位置:

  • 它不是数据库查询结果
  • 它不是 Server 代码
  • 它是模型表达出来的“我要调用哪个工具、带什么参数”

而真正把这段意图变成一次受控调用的,是 Host → Client → Server 这一整套机制。

MCP Server 可能返回这样的数据:

[
  { "date": "2026-01-01", "new_users": 120 },
  { "date": "2026-01-02", "new_users": 135 }
]

然后,LLM 再基于这个结果生成自然语言结论,例如:

近 30 天用户数整体呈现上升趋势,周末增长略有放缓,但工作日增长更明显。

这个例子要说明的核心点是:

MCP 不是让模型“自己直接操作数据库”,而是让模型通过标准化、受控的协议链路去调用数据库能力。

5. 工程实践与注意要点

这一节对后端工程师最重要,因为系统真正上线后,大多数问题都会集中暴露在这里。

5.1 安全规范

1. 传输安全不能后补

MCP 链路里传输的不只是普通请求,还可能包含:

  • 用户上下文
  • 工具参数
  • 工具返回结果
  • 凭证或资源定位信息

所以应默认使用加密传输,例如 HTTPS / TLS
如果在开发阶段为了缩短调试路径而直接使用明文 HTTP,本地可运行并不意味着线上可接受。

开发阶段临时使用明文链路并不少见,但一旦系统被复用到更长的调用链中,这类“临时方案”通常会最先演化为风险点。

2. 身份认证必须落到工具层

很多人以为“用户已经登录了”就够了,但 MCP 链路里还要继续问一个问题:

当前这个工具调用,是否真的有权限执行?

例如:

  • 查询报表可以开放给分析角色
  • 删除文件不应该默认开放
  • 修改生产配置必须更严格

如果没有把权限校验放到 Server 或工具层,模型就可能在“意图正确”的情况下,执行“权限错误”的操作。

3. 凭证管理不能偷懒

不要把:

  • API Key
  • 数据库密码
  • OAuth Token

直接硬编码在工具实现里,也不要明文落盘。

正确做法是:

  • 使用密钥管理设施
  • 做最小权限分配
  • 支持轮换与吊销
4. 高风险操作要有二次确认和审计

像下面这类能力,不应该和普通查询放在同一级别对待:

  • 删除
  • 修改
  • 批量导出
  • 调用高敏感内部接口

如果你的 MCP Server 对这些操作没有审计日志、调用记录和额外确认机制,那么后续追责和排查会非常被动。

5.2 性能优化技巧

1. 不要把所有上下文一次性塞满

上下文越长,传输和推理成本越高。
在多轮工具调用场景中,更合理的做法是:

  • 保留当前任务真正需要的信息
  • 把历史结果做摘要
  • 对超长内容做分片传输

如果每轮请求都把完整历史、完整工具返回和完整附件重新塞给模型,延迟会非常容易失控。

2. 连接复用比频繁重建更重要

如果每次调用工具都重新建连,再立刻断开,那么在高频调用场景下,性能损耗会非常明显。

更稳妥的做法是:

  • 复用 Client 与 Server 的连接
  • 明确超时与重试策略
  • 在工具执行层做并发控制
3. 只在必要时调用工具

工具调用不是越多越好。

这会带来:

  • 更多延迟
  • 更多错误面
  • 更高资源开销

所以,Host 需要有基本的调用判断逻辑,避免无意义的“碰碰运气式”调用。

4. 批处理要看场景,不要机械套用

批处理可以减少网络往返,但也会带来新的复杂度:

  • 错误定位更难
  • 超时更难拆分
  • 单个请求失败可能影响整批处理

所以不要把“能批量”误认为“应该批量”。
对于相互独立且轻量的查询,批处理很合适;对于高风险写操作,则要更谨慎。

5.3 可观测性与排障建议

如果要把 MCP 真正用到生产环境,至少要把这几类信息打出来:

  • 请求 ID / trace ID
  • 工具名
  • 调用参数摘要
  • 响应状态
  • 错误码
  • 耗时

这些字段不能只停留在“应该记录”,而应明确落到链路中的具体位置:

  • Host 层 打用户请求、路由决策、上下文摘要和最终响应耗时。
  • Client 层 打协议请求与响应、重试、超时、连接状态。
  • Server 层 打工具名、参数校验结果、真实执行耗时、下游依赖错误。

如果团队已经有现成的 tracing 体系,例如 OpenTelemetry、Jaeger、Zipkin 或云厂商 APM,更合理的做法是把 MCP 调用链直接接入,而不是单独维护一套新的日志体系。一个常见做法是:

  • Host 生成顶层 trace_id
  • Client / Server 沿用同一个 trace_id
  • 每次工具调用生成自己的 span

这样在一次调用失败时,就可以沿着同一条链路直接定位:

  • 是模型没有正确表达意图
  • 还是 Host 路由错了
  • 还是 Client 发请求超时
  • 还是 Server 校验失败
  • 或者真正的问题出在下游 DB / API

否则,一旦链路出问题,你就会分不清问题到底出在:

  • 模型意图识别
  • Host 路由
  • Client 传输
  • Server 参数校验
  • 工具执行本身

对于后端工程,这比“模型会不会调用工具”更关键。

5.4 一句话结论

如果要用一句话概括 MCP:

Function Calling 让模型学会“调用工具”,而 MCP 让工程系统学会“把工具调用这件事做成标准化、可治理、可维护的基础设施”。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐