基本理解

MCP 是一个开放协议,它规范了应用程序向 LLM 提供上下文的方式。当LLM想从应用程序获取信息,或者是使用应用程序,就需要应用程序提供一个规范,便于大模型进行使用和调用。

工具

LLM是大模型,大模型只有推理的能力,然而我们所见到的许多大模型应用有很多丰富的功能。例如,问大模型天气如何?

作为大模型而言,其自身是不携带例如查询天气等功能的,需要一定的第三方来协助大模型,例如当询问今天天气如何时,大模型应借助第三方查询天气的数据,再得到数据进行回复。那这些第三方我们可以称作工具。

MCP协议

由于需求不断变大,大模型需要各种各样的工具,然而工具提供的查询方法是不同的,为了让工具更好的统一和大模型进行对接,我们需要使用一个协议来统一这种对接方式,这就是MCP协议。

从发展角度理解

还是以LLM获取天气为例:

初始调用

借助提示词工程,让大模型生成请求所需参数,

例如生成请求json,给大模型一定的提示词要求,要求大模型根据某些个规定格式进行请求json生成,便于大模型获取天气数据。或者再通过正则匹配获取请求需要的参数,进行手工的构建。

缺点:依旧存在不稳定性,难以统一格式,提示词定制化

Function Calling

作为大模型早期带头人,为了让自家 ChatGPT 拥有更灵活的功能,Openai 提出了 Function Calling 概念:

  • 将工具信息全部告知大模型,让大模型自行判断选择工具使用。
  • 提供了自己函数的名称,描述,参数规范。
  • 自动生成符合格式的参数json。

以下就是一个Function Calling 例子

{
  "name": "get_weather", // 函数名
  "description": "获取某个城市当前的天气", // 函数功能
  "parameters": { // 参数格式要求
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市的名称"
      }
    },
    "required": ["city"]
  }
}

当用户问到涉及天气的问题时,大模型就会给出以下格式统一的内容

{
  "function_call": {
    "name": "get_weather",
    "arguments": "{ \\"city\\": \\"杭州\\" }"
  }
}

当获得结果后,大模型收到以下,便会以以下作为参考进行回复。

{
  "city": "杭州",
  "temperature": "21℃",
  "condition": "多云"
}

代码解析示例:

# 1. 定义函数 schema
functions = [ ... ]

# 2. 用户对话输入
messages = [ ... ]

# 3. 模型生成函数调用请求
response = openai.ChatCompletion.create(...)
function_call = response["choices"][0]["message"].get("function_call")

# 4. 你自己的函数处理逻辑 —— 写在这里
if function_call:
    name = function_call["name"]
    args = json.loads(function_call["arguments"])

    if name == "get_weather":
        city = args.get("city")
        # 你的逻辑:
        result = get_weather(city)  # 你自己定义的函数

这样的设计不容易出现参数错误,但随着更多更多功能的需求,就需要更多灵活的可移植的Function Calling 功能,因此产生ChatGPT插件功能。

ChatGPT插件

ChatGPT 插件 = Function Calling + 你提供的 HTTP API + OpenAPI 描述文档

也就是说:

  • 模型通过 Function Calling 机制调用一个你暴露的 HTTP 接口(你的服务)
  • 你写一个 API 接口 + 对应的 OpenAPI 文档(JSON)
  • ChatGPT 根据 OpenAPI 文档 知道你能提供哪些函数、参数长什么样
  • 它就能自动生成调用请求,发给你的 API

例:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Weather Plugin",
    "version": "1.0.0"
  },
  "paths": {
    "/weather": {
      "get": {
        "operationId": "getWeather",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "返回天气信息",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "temperature": { "type": "string" },
                    "condition": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

确实可以共享给更多人,但是其限制在了ChatGPT Web上,没有办法使用到自己的应用上,还需要维护自己提供的api接口,难以维护。

MCP协议

为了解决以上问题,提供出通用协议,提出一个大模型与应用程序的交互。

架构组成

MCP 采用 客户端-服务器(Client-Server)模型,包含三个主要组件:

  • MCP Hosts:运行大模型的应用程序(如 Claude Desktop、Cursor IDE)。
  • MCP Clients:维护与服务器的 1:1 连接,负责协议代理。
  • MCP Servers:提供标准化的数据访问和工具调用能力,支持本地(如数据库、文件)和远程(如 API、云存储)资源。
  • 本地数据源: MCP 服务器可以安全访问的计算机文件、数据库和服务
  • 远程服务: 通过互联网(例如,通过 APIs)可用的外部系统,MCP 服务器可以连接到这些系统

功能接口

并为了结合大模型的使用,MCP 有三个基本的服务器端功能:提示、资源、工具

  • 提示:指导与 LLM 交互的预定义模板或说明。
  • 资源:为模型提供额外背景的结构化数据或内容。
  • 工具:允许模型采取行动或检索信息的可执行函数。
控制实体 解释 例子
提示 用户控制 通过用户选择调用的交互式模板 斜线命令、菜单选项
资源 应用程序控制 客户端提供和管理的上下文数据 文件内容,Git 历史记录
工具 模型控制 LLM(大规模语言模型)中公开的执行函数 API POST请求,写入文件

MCP接口示例

  • tools/list: 获取可用工具列表
  • tools/call : 调用工具
  • resource/list : 获取可用资源
  • resource/read : 读取资源内容
  • prompts/list : 获取可用提示
  • prompts/get : 获取提示内容

MCP流程

我们定义有以下几个对象:

  • 用户:进行操作的对象,操作客户端
  • 客户端:负责协调和展示给用户,被用户操作
  • MCP服务器:提供MCP服务
  • LLM大模型:负责逻辑推理和决策

以获得天气为例:

  • 客户端 → MCP服务器:获取工具列表,预先要载入的资源,
  • 用户 → 客户端:向客户端发送查询天气的请求(“今天的天气如何?”)
  • 客户端 → LLM大模型 → 客户端:让LLM大模型推理处理用户问题产生工具使用申请
  • 客户端 → MCP服务器 → 客户端:客户端从MCP服务器调用工具函数获取结果值
  • 客户端 → LLM大模型 → 客户端:将结果值给予大模型,大模型再次结合推理给出结果
  • 客户端 → 用户:将结果显示给用户。

1. 客户端 → MCP服务器:获取工具列表

客户端 首先查询 MCP 服务器可用的工具和资源:

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": 1
}

服务器响应

{
  "jsonrpc": "2.0",
  "result": {
    "tools": [
      {
        "id": "get_weather",
        "description": "查询指定城市的天气",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {"type": "string", "description": "城市名称"}
          },
          "required": ["location"]
        }
      }
    ]
  },
  "id": 1
}

2. 用户 → 客户端:发送查询请求

用户输入自然语言请求:

"今天的天气如何?"

3. 客户端 → LLM 推理,生成工具调用请求

客户端 将用户输入传给 LLM,LLM 分析后返回结构化调用请求:

{
  "tool_use": {
    "tool_name": "get_weather",
    "parameters": {
      "location": "北京"  // LLM 推断用户可能想查询北京天气
    }
  }
}

4. 客户端 → MCP服务器:调用工具

客户端 按照 LLM 的指示,调用 get_weather 工具:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "tool_id": "get_weather",
    "args": {"location": "北京"}
  },
  "id": 2
}

服务器返回天气数据

{
  "jsonrpc": "2.0",
  "result": {
    "location": "北京",
    "temperature": "25°C",
    "condition": "晴",
    "humidity": "60%"
  },
  "id": 2
}

5. 客户端 → LLM:用工具结果生成最终回复

客户端 将天气数据传给 LLM,LLM 生成自然语言回复:

{
  "response": "北京今天的天气是晴天,气温 25°C,湿度 60%。"
}

6. 客户端 → 用户:返回最终结果

客户端 将 LLM 生成的回复展示给用户:

"北京今天的天气是晴天,气温 25°C,湿度 60%。"

这个流程体现了 MCP 的核心思想:

  • 标准化工具交互(JSON-RPC 格式)。
  • LLM 作为决策中枢(决定何时调用工具)。
  • 客户端作为协调者(连接用户、LLM 和 MCP 服务器)。