Function Calling 与 MCP 协议|深究 MCP 协议的设计

_

本文从 Function Calling 的问题出发,逐步梳理 MCP 协议为何出现、它试图解决什么问题,以及它在架构和传输层面是如何落地的。为方便阅读,文中的口语化讨论已整理为偏博客正文的结构,原有图片、代码块与核心示例均予以保留。

一、Function Calling

2.1 要解决的问题

传统聊天大模型只会说话,没有工具调用能力。这会带来两个直接限制:

  1. 无法感知环境:无法与外部数据源交互,例如通过 API 查询网页、查看本地文件、访问远程数据库等。
  2. 无法改变环境:无法替用户实际执行任务,例如跑代码、发邮件、上传作业等。

2.2 如何解决

一个常见思路是采用“后端 + LLM”的组合,由后端负责串联模型与工具。

传统方案

工作流程

Hard Code 方案  |  by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

Hard Code 方案 | by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

存在的问题
  1. 是否调用工具、调用什么工具,通常由后端负责判断,逻辑复杂且容易误判。

💡

AI 这么智能,为什么不让它来帮我判断?

  1. 调用工具的参数通常也由后端手工构建,开发成本较高。

💡

AI 这么智能,为什么不让它来帮我生成参数?

Function Calling 方案

Function Calling 是什么

广义的 Function Calling,是指让大模型能够调用外部工具的一类技术实现:先向模型提供可用函数的列表及说明,再由模型在对话中判断是否需要调用某个函数,并生成调用所需参数,最后以约定格式返回函数调用请求

狭义的 Function Calling,则特指模型提供商在模型与 API 层面内建支持的一种能力。它最早由 OpenAI 引入,通常包含两层含义:

  • 模型层面:模型经过专门优化,具备根据上下文选择合适函数、生成有效参数的能力,例如通过监督微调或强化学习。
  • API 层面:模型提供商在接口中直接开放 Function Calling 支持,例如 GPT API 中提供 functions 参数。

基于提示词的 Function Calling

工作流程

基于提示词的方案  |  by @bilibili 堂吉诃德拉曼查的英豪(Not Ai,Carbon Based Version)

基于提示词的方案 | by @bilibili 堂吉诃德拉曼查的英豪(Not Ai,Carbon Based Version)

# 你的角色
你是一个函数调用助手,我将提供多个函数的定义信息,包括函数名称、作用、参数及参数类型。

# 你的任务
- 根据用户的输入,判断是否需要调用某个函数
- 如果需要,请严格按照以下格式输出函数调用指令:

```json
{ "name": "函数名", "arguments": { "参数名": "参数值" } }
```

# 函数定义信息
1. get_weather
   - 作用:查询指定城市的天气情况
   - 参数:
     - `city`(string):城市名称
2. get_time
   - 作用:查询指定城市的当前时间
   - 参数:
     - `city`(string):城市名称

System Prompt 示例

广州的天气怎么样?

用户提问示例

{ "name": "get_weather", "arguments": { "city": "广州" } }

模型回复示例

存在的问题
  1. 输出格式不稳定:调用指令中可能混入多余自然语言。
  2. 容易出现幻觉:模型可能编造并不存在的函数名或参数。

💡

大模型提供商能否对模型进行微调、强化学习,提升大模型在这一方面的能力?

  1. 对开发者依赖度高:函数描述、调用指令格式、提示词逻辑都需要开发者自行设计。

💡

函数描述、调用指令格式能否由大模型提供商来指定?系统提示词中的说明与规则,能否由大模型提供商来兜底?

  1. 上下文冗长、Token 消耗大:为了保证调用逻辑正确,往往需要在 system prompt 中加入大量说明与规则。

基于 API 的 Function Calling

工作流程

基于 API 的 Function Calling  |  by @bilibili 堂吉诃德拉曼查的英豪(Not Ai,Carbon Based Version)

基于 API 的 Function Calling | by @bilibili 堂吉诃德拉曼查的英豪(Not Ai,Carbon Based Version)

  1. 用户发起提问

    用户通过自然语言提出问题,例如:“广州今天天气如何?适合出门吗?”

  2. 后端第一次调用大模型 API,获取函数调用指令

    后端向大模型 API 传入用户输入、函数描述及其他上下文,获取调用指令。函数描述通常包含函数名称、用途说明、参数结构等。

    {
      "messages": [
        {
          "role": "system",
          "content": "你是一个助手,可以根据用户的请求调用工具来获取信息。"
        },
        {
          "role": "user",
          "content": "广州今天天气如何?适合出门吗?"
        }
      ],
      "functions": [
        {
          "name": "getWeather",
          "description": "获取指定城市的天气",
          "parameters": {
            "type": "object",
            "properties": {
              "location": {
                "type": "string",
                "description": "城市名称,比如北京"
              },
              "date": {
                "type": "string",
                "description": "日期,比如 2025-08-07"
              }
            },
            "required": ["location", "date"]
          }
        }
      ]
    }
    

    接口入参

  3. 模型生成调用指令

    模型会判断是否需要调用函数,选择合适的函数,并生成结构化的调用指令(函数名 + 参数),例如:

{
  "function_call": {
    "name": "getWeather",
    "arguments": {
      "location": "Guangzhou",
      "date": "2025-07-17"
    }
  }
}

调用指令示例(OpenAI Function Calling)

  1. 后端解析调用指令并执行实际函数

    后端接收到模型返回的调用指令后,解析函数名称和参数,执行对应方法,例如调用天气查询函数并拿到结果。

  2. 后端第二次调用大模型 API,生成最终回复

    后端再将函数执行结果和其他上下文信息(包括用户原始输入)传给模型。此时模型判断信息已足够,就会直接生成最终答案,例如:“广州今天 35 度,暴雨,建议在室内活动”。

存在的问题
  1. 后端应用在适配不同大模型时,往往存在大量重复开发。
  2. 可选模型通常比较有限。

二、MCP 协议

3.1 要解决的问题

  1. 工具接入存在冗余开发

    AI 应用接入他人开发的新工具时,往往需要完整 copy 工具代码和函数描述。接入几个工具,就要重复做几次。

  2. 工具复用困难

    环境问题会导致 copy 回来的代码不一定能直接运行;很多企业也不会提供可直接 copy 的源码;如果工具与应用不是同一语言,单纯复制代码通常也没有意义。

3.2 如何解决问题

💡

如果是你,会怎么解决以上问题?假设此时还完全没有 MCP 协议。

从问题出发

问题的根源在于:冗余开发和复用困难,很多时候都来自“复制代码”这件事本身。
那么,怎样才能用上别人的方法,却又不用复制别人的代码?

  • 思路一:导包式接入

    AI 应用开发者把工具代码拉到本地后直接调用。

    • 可行性:⚠️ 跨语言调用问题难以解决。
    • 进一步思考:这是否真的是客观无解?能否在本地另起一个进程,运行该语言的执行环境,让工具代码原封不动地运行在自己的语言环境里,再由 AI 应用通过进程间通信(如管道、套接字)获取标准化返回结果?
    • 结论:✅ 可以,这种方式更适合被称为“本地服务式接入”。
  • 思路二:远程服务式接入

    工具开发者把工具独立部署成标准化 API,约定统一请求与响应格式。AI 应用开发者只需按约定传参和解析返回值,不必关心实现语言、运行环境和内部逻辑。

    • 可行性:✅

从目标出发

如果从“理想的接入体验”来思考,那么最理想的状态是:

  • 开发者只需增加一条配置(例如工具唯一标识或访问地址),就可以接入自己或他人的工具。

而当前的非理想状态是:

  • 每新增一个工具,开发者都要在链路中做两处人工适配
    1. 补充工具描述。
    2. 补充工具代码。

要从“人工适配”走向“配置驱动”,至少要满足两点:

  • AI 应用后端能够根据配置自动获取工具描述信息
  • AI 应用后端能够根据配置自动定位工具调用入口并执行调用

从问题推导技术需求

本地服务式接入场景

如何在任意 AI 应用中,通过一条标准化配置完成以下工作:

  • 拉取任意工具包到本地,并启动一个本地进程,让工具以服务形式运行起来(前提是工具开发者提供了对应的包)。
  • 通过本地进程间通信自动获取工具描述信息,并完成调用。
远程服务式接入场景

如何在任意 AI 应用中,通过一条标准化配置完成以下工作:

  • 访问任意工具的远程服务(前提是工具开发者对外提供了服务)。
  • 通过远程服务调用自动获取工具描述信息,并完成调用。

技术方案思路

  1. 工具与 AI 应用必须解耦。

  2. 工具与 AI 应用之间的交互必须标准化。

    需要统一的内容至少包括:

    1. AI 应用与工具服务的通信协议
    2. AI 应用与工具服务的接口定义
    3. AI 应用与工具服务的数据交换格式
    4. AI 应用接入工具时的配置内容
    5. 所有工具服务都要提供标准化的接入方式,支持通过标准化配置加载工具。
    6. 所有 AI 应用内部都要实现标准化的工具加载与调用逻辑,支持通过标准化配置接入工具。

系统架构设计

MCP 协议的雏型架构 |  by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

MCP 协议的雏型架构 | by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

3.3 MCP 协议是什么

  • 诞生

    2024 年 11 月,Anthropic 提出 MCP(Model Context Protocol),官方文档见:https://modelcontextprotocol.io/introduction

  • 定义

    MCP is an open protocol that standardizes how applications provide context to large language models (LLMs). Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools. MCP enables you build agents and complex workflows on top of LLMs and connects your models with the world. [1]

    MCP 是一个开放协议,用于标准化应用程序向大语言模型(LLM)提供上下文的方式。可以把 MCP 理解成 AI 应用的 USB-C 接口:就像 USB-C 用统一方式连接各种外设,MCP 也希望用统一方式把 AI 模型连接到不同的数据源与工具。借助 MCP,可以在 LLM 之上构建智能体和复杂工作流,并让模型与外部世界建立连接。

  • 如何理解

    • 应用程序:指集成了 LLM 的具体应用,例如各家大模型的在线对话网站、集成大模型的 IDE(如 Claude Desktop)、各种 Agent(例如 Cursor)以及其他接入了大模型的普通应用。
    • 上下文:指模型在决策时可访问的所有信息,例如当前用户输入、历史对话、外部工具(tool)信息、外部数据源(resource)信息、提示词(prompt)信息等。本文重点只讨论工具。

💡

和传统 API 的区别是什么?

3.4 MCP 核心架构

MCP follows a client-server architecture where an MCP host — an AI application like Claude Code or Claude Desktop — establishes connections to one or more MCP servers. The MCP host accomplishes this by creating one MCP client for each MCP server. Each MCP client maintains a dedicated one-to-one connection with its corresponding MCP server. The key participants in the MCP architecture are:

  • MCP Host: The AI application that coordinates and manages one or multiple MCP clients
  • MCP Client: A component that maintains a connection to an MCP server and obtains context from an MCP server for the MCP host to use
  • MCP Server: A program that provides context to MCP clients

For example: Visual Studio Code acts as an MCP host. When Visual Studio Code establishes a connection to an MCP server, such as the Sentry MCP server, the Visual Studio Code runtime instantiates an MCP client object that maintains the connection to the Sentry MCP server. When Visual Studio Code subsequently connects to another MCP server, such as the local filesystem server, the Visual Studio Code runtime instantiates an additional MCP client object to maintain this connection, hence maintaining a one-to-one relationship of MCP clients to MCP servers. Note that MCP server refers to the program that serves context data, regardless of where it runs. MCP servers can execute locally or remotely. For example, when Claude Desktop launches the filesystem server, the server runs locally on the same machine because it uses the STDIO transport. This is commonly referred to as a “local” MCP server. The official Sentry MCP server runs on the Sentry platform and uses the Streamable HTTP transport. This is commonly referred to as a “remote” MCP server. [2]

MCP 采用客户端 - 服务端架构。一个 MCP Host(例如 Claude Code、Claude Desktop 等 AI 应用)可以连接一个或多个 MCP Server。为了实现连接,Host 会为每一个 MCP Server 创建一个对应的 MCP Client,并维持一对一连接。

MCP 架构中主要有三个角色:

  • MCP Host:协调和管理一个或多个 MCP Client 的 AI 应用。
  • MCP Client:负责维护与 MCP Server 的连接,并为 Host 获取上下文。
  • MCP Server:向 MCP Client 提供上下文的程序。

以 Visual Studio Code 为例:它可以作为 MCP Host。当它连接到某个 MCP Server(例如 Sentry MCP Server)时,运行时会实例化一个 MCP Client 来维持该连接;如果随后再连接另一个 MCP Server(例如本地文件系统服务器),则会再实例化一个新的 MCP Client。由此可见,MCP Client 与 MCP Server 之间通常是一对一关系。

需要注意的是,MCP Server 只是“提供上下文的程序”,至于它运行在本地还是远端并不重要:

  • 如果 Claude Desktop 启动了本地 filesystem server,并通过 STDIO 通信,那么它就是“本地 MCP Server”。
  • 如果服务运行在 Sentry 平台并通过 Streamable HTTP 提供能力,那么它就是“远程 MCP Server”。

image.png

3.5 MCP 的传输协议

MCP supports two transport mechanisms:

  • Stdio transport: Uses standard input/output streams for direct process communication between local processes on the same machine, providing optimal performance with no network overhead.
  • Streamable HTTP transport: Uses HTTP POST for client-to-server messages with optional Server-Sent Events for streaming capabilities. This transport enables remote server communication and supports standard HTTP authentication methods including bearer tokens, API keys, and custom headers. MCP recommends using OAuth to obtain authentication tokens.

The transport layer abstracts communication details from the protocol layer, enabling the same JSON-RPC 2.0 message format across all transport mechanisms. [3]

Stdio 传输

Stdio 传输本质上是一种本地进程间通信(IPC),其常见底层机制就是管道(pipe)。

  1. 什么是 stdio

    • stdio(standard I/O)是进程的标准输入输出接口。每个进程启动时,操作系统会为它分配三个文件描述符:
    0 → stdin  (标准输入,默认是键盘)
    1 → stdout (标准输出,默认是屏幕)
    2 → stderr (标准错误输出,默认是屏幕)
    
    • 程序中的 printfscanfcincoutreadwrite,本质上都在通过这些接口与外界交换数据。
  2. 什么是管道(pipe)

    • 管道是操作系统内核提供的一种进程间通信机制,允许一个进程的输出直接作为另一个进程的输入,从而实现数据在两个进程之间流动。
  3. 总结:什么是 Stdio 传输?

    • 所谓 Stdio 传输,就是通过标准输入标准输出这两个数据流来传输数据,并通过管道把两个进程的标准输入输出接口连接起来,使一个进程的输出直接进入另一个进程的输入,实现进程间数据传输。
    • 换句话说,stdio 是接口,管道是连接接口的通道。
  • 举例:
ps aux

命令

[键盘] → shell → ps(stdin) → ps(stdout) → [屏幕]

没有用管道

ps aux | grep python

命令

# ps 和 grep 在执行时都会成为各自独立的进程
# ps aux 列出进程,grep python 过滤后留下包含 "python" 的进程
# 管道 | 把 ps aux 的 stdout 作为 grep python 的 stdin
[键盘] → shell → ps(stdin) → ps(stdout) → grep(stdin) → grep(stdout) → [屏幕]

用了管道(Stdio 传输)

💡

为什么在这么多本地进程间通信方式中,选了 Stdio 传输?

HTTP + SSE 传输(旧方案,2024.10)

客户端通过 HTTP POST 向服务端发请求,服务端通过 SSE 通道返回响应结果。

  • SSE(Server-Sent Events,服务器发送事件)是一种服务器单向推送数据给客户端的技术,基于 HTTP 协议
  • 基本原理:
    • 客户端先向服务端发起一个普通的 HTTP 请求
    • 服务端保持这个连接不断开,并以 text/event-stream 作为响应类型,持续向连接中写入数据。
    • 客户端收到数据后,会触发相应的事件回调,例如浏览器前端实时更新界面。
  • 和普通 HTTP 的核心差异:
    • 支持服务端主动、流式推送消息。

💡

为什么在这么多远程服务调用协议中,选了 HTTP + SSE?

  • 服务端主动推送的一个典型场景是:MCP Server 中的工具发生更新,需要主动通知 MCP Client。

Why Notifications Matter

This notification system is crucial for several reasons:

  1. Dynamic Environments: Tools may come and go based on server state, external dependencies, or user permissions
  2. Efficiency: Clients don’t need to poll for changes; they’re notified when updates occur
  3. Consistency: Ensures clients always have accurate information about available server capabilities
  4. Real-time Collaboration: Enables responsive AI applications that can adapt to changing contexts

This notification pattern extends beyond tools to other MCP primitives, enabling comprehensive real-time synchronization between clients and servers. [4]

Streamable HTTP 传输(新方案,2025.03)

HTTP + SSE 传输方案的升级版,目前正在逐步取代原有的 HTTP + SSE 传输方案。

  • Streamable HTTP 不是一个严格的标准协议名,而是一个通用描述,指的是基于 HTTP 的可流式传输技术
  • 它的核心思想是:在一个 HTTP 连接中,服务端可以持续不断地发送数据,客户端边接收边处理,像“流”一样工作,而不是传统请求响应那样一次性结束。
  • 常见实现方式包括:
    • HTTP/1.1 长连接 + Chunked Transfer Encoding
    • HTTP/2 流式数据
    • HTTP/3 QUIC 流式传输

💡

为什么 HTTP + SSE 要升级成 Streamable HTTP?

  • 数据格式限制:SSE 的 Content-Type: text/event-stream 只支持文本;而 Streamable HTTP 可以支持 JSON、HTML、二进制等更多格式,更适合 AI 场景。
  • 跨平台兼容性:SSE 的客户端支持主要集中在浏览器与少量语言库;而 Streamable HTTP 的适配范围更广。
  • 性能因素:SSE 基于 HTTP/1.1 长连接;而 Streamable HTTP 可以建立在 HTTP/2/3 之上,支持多路复用与更好的流控制机制,也更容易获得高吞吐与低延迟。

必须选用这些传输协议吗?

未必。无论底层采用哪种传输方式,本质上都是为了把各种工具的不同接入方式统一起来,并对外暴露一致的协议接口。

MCP 协议的架构 |  by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

MCP 协议的架构 | by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

image.png

3.6 回顾:技术方案最终是怎么实现的

  1. 工具与 AI 应用必须解耦。

    ✅ 通过客户端 - 服务端架构实现,即 MCP Host、MCP Client、MCP Server。

  2. 工具与 AI 应用之间的交互必须标准化。

    1. AI 应用和工具服务的通信协议需要统一

      ✅ 本地使用 Stdio 传输;远程使用 HTTP + SSE 或 Streamable HTTP。

    2. AI 应用和工具服务的接口定义需要统一

      ✅ 工具服务需要提供的接口包括:
      tools/list(返回方法列表)、tools/call(执行方法并返回结果)、notifications/tools/list_changed(服务端主动通知客户端方法更新)。[5]

      ✅ 接口参数定义可参考官方文档。以 tools/list 为例:

      image.png

      image.png

    3. AI 应用和工具服务的数据交换格式需要统一

      ✅ 使用 JSON-RPC 2.0[6] [7]

      JSON-RPC 2.0 是一种轻量级远程过程调用协议,基于 JSON 格式进行通信,便于解析,也便于跨语言使用。

    4. AI 应用接入工具的配置内容需要标准化定义

      以接入高德地图 MCP Server 为例,可参考文档:
      https://lbs.amap.com/api/mcp-server/gettingstarted

      来源还提到了 ModelScope 的 MCP Server 市场:
      https://modelscope.cn/mcp

      ✅ 本地服务式接入(基于 Stdio 协议)

      // 在 mcpServers 配置中新增一个叫 "amap-maps"(你自己起名)的 MCP Server
      // 通过 npx -y @amap/amap-maps-mcp-server 命令,运行高德官方提供的 MCP server
      // 运行时的环境变量是 "AMAP_MAPS_API_KEY": "你申请的 API Key"
      {
        "mcpServers": {
          "amap-maps": {
            "command": "npx",
            "args": ["-y", "@amap/amap-maps-mcp-server"],
            "env": {
              "AMAP_MAPS_API_KEY": "您在高德官网上申请的key"
            }
          }
        }
      }
      

      Stdio 方式接入配置

      ✅ 远程服务式接入(基于 SSE 协议)

      {
        "mcpServers": {
          "amap-maps-streamableHTTP": {
            "url": "https://mcp.amap.com/mcp?key=您在高德官网上申请的key"
          }
        }
      }
      

      SSE 方式接入配置

    5. 所有工具服务必须提供标准化的接入方式

      ✅ 遵循 MCP 协议开发 MCP Server,对外提供标准接入方式。多语言 SDK 见:https://modelcontextprotocol.io/docs/sdk

    6. 所有 AI 应用内部都需要实现标准化的工具加载与调用逻辑

      ✅ 使用官方 SDK,在 AI 应用后端实例化 MCP Client,并通过 SDK 与 Server 交互。

3.7 MCP 工作流程

基于 MCP 协议的 Function Calling 工作流程  |  by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

基于 MCP 协议的 Function Calling 工作流程 | by @bilibili 堂吉诃德拉曼查的英豪(Carbon Based)

💡

值得一提的是,MCP 协议和 Function Calling 之间并不是“技术递进”关系。
“MCP 协议会取代 Function Calling”这种说法,并不严谨。

3.8 更多延伸

高级 RAG 技术学习笔记 2026-04-27

评论区