Live2D

熟知大模型中mcp概念 --by zk

MCP核心概念解析

什么是MCP:让大模型拥有“手和脚”

大模型本身只有对话和决策能力,没有执行调用工具,获取资源的能力。因此定义一套让大模型调用外部能力的通用协议很重要。
MCP(Model Context Protocol)是一套让大模型“安全、可控、可扩展”地调用外部能力的通用协议。
它统一了一件事:大模型只负责“决策”,真正的动作由你实现的工具去执行。

  • 你可以把 MCP 理解为“给大模型接插件”的标准化方式:HTTP 接口、数据库查询、浏览器调试、内部文档检索,都可以被包装成一个个工具。
  • 好处:规范的参数校验(JSON Schema)、一致的权限边界、可观测的调用链、更容易在 IDE/Agent/服务端之间迁移与复用。

MCP 与 tool_calls 的深层差异

在MCP早期,大模型实现外部能力调用,需要通过function callingtool_calls两种方式。

function calling是早期的调用方式,现在已经不推荐使用。
tool_calls则是他的替代品,允许在上下文中发送一个Tools列表,告诉大模型,我有这些工具,这些工具应该在什么时候被调用,我需要哪些入参,返回的结果应该是怎么样的。

示意:

const tools = [
  {
    type: 'function',
    function: {
      name: 'get_weather',
      description:
        '获取指定城市的实时天气信息,包括温度、天气状况、湿度、风速等。通过调用高德天气 API 获取真实数据。',
      parameters: {
        type: 'object',
        properties: {
          location: {
            type: 'string',
            description: '地理配置信息,例如 "北京"、"上海"',
          },
          unit: {
            type: 'string',
            enum: ['celsius', 'fahrenheit'],
            description: '温度单位,默认为 "celsius"',
          },
        },
        required: ['location'],
      },
    },
  },
];

模型返回:

{
  "choices": [
    {
      "finish_reason": "tool_calls",
      "index": 0,
      "message": {
        "content": null,
        "role": "assistant",
        "tool_calls": [
          {
            "id": "call_abc123",
            "function": {
              "arguments": "{\"location\":\"北京\",\"unit\":\"celsius\"}",
              "name": "get_weather"
            },
            "type": "function"
          }
        ]
      }
    }
  ],
  "created": 1694268198,
  "id": "chatcmpl-...",
  "model": "gpt-4-...",
  "object": "chat.completion",
  "system_fingerprint": "fp_..."
}
两者差异

为了彻底理解MCP和tool_calls的区别,我们用两张图来对比处理同一个用户请求(“北京今天天气怎么样?”)时的根本差异。


模式一:直接 tool_calls 调用

image

 

图解:您可以看到,为了完成这个简单任务,客户端和LLM之间进行了两次完整的来回交互。第一次是为了“决策”,第二次是为了“总结”。

模式二:MCP 调用

image

 

图解:从客户端的视角看,整个过程只有一次网络往返。它发出请求,然后等待最终结果。所有与 LLM 的多步交互、工具调用、结果回传都在服务端内部由 MCP Agent 完成。这才是 MCP 模式带来效率提升的关键。MCP通过自身的代码逻辑,“砍掉”了第一次用于决策的LLM调用,极大地优化了流程。

对比维度

模式一:直接tool_calls调用

模式二:MCP调用

决策主导者

LLM (凡事问LLM)

代码/MCP (小事自己定)

LLM交互次数

两次 (决策 + 总结)

一次 (仅总结)

核心优势

灵活性 (能处理模糊任务)

高效率 (快速处理明确任务)

一个设计精良的MCP,其真正的优势在于它是一个能够智能选择模式的调度中心

  • 遇到它能理解的简单任务,就采用模式二,追求极致效率。
  • 遇到它看不懂的复杂任务,就自动切换到模式一,发挥LLM的灵活性。

通过这种方式,MCP在效率灵活性之间取得了完美的平衡,为用户提供最佳的交互体验。

MCP的三种类型

MCP

特点

适用

局限

Stdio

通过标准输入/输出与子进程通信;零网络、延迟低、部署简单。

本地提供并执行所有能力。

如果往 stdout 打日志(会干扰协议),因此需要把日志打到 stderr 或文件。

SSE

HTTP 单向流(Server→Client),易部署,浏览器友好。

简单的云端推送、只需要“结果下行”的场景。

天然单向,复杂双向 RPC/并发多路复用需要额外回路与状态管理。

Streamable

支持“参数与结果的增量分片”“多通道并发”“可中断/可取消/背压控制”,把“文本流”和“工具结果流”统一起来。

需要边生成边调用工具、复杂 UI/长任务、多人协作与可观测的生产环境。

 

为什么用 Streamable 替代 SSE?

Streamable HTTP 相比于 SSE (Server-Sent Events) 提供了更简洁、高效且灵活的实现方式。以下是几个关键优势:

1. 实现更简洁,协议开销更低
  • SSE 的复杂性:SSE 协议要求服务器严格遵循 data: ...\n\n 的格式来封装消息。这意味着前端和后端都需要实现特定的解析逻辑来处理这种格式,增加了实现的复杂度。
  • Streamable 的简洁性:Streamable 本质上是一个标准的 HTTP 响应。服务器通过 Transfer-Encoding: chunked 持续向响应体中写入数据,客户端则像读取一个大文件一样持续接收。几乎所有的网络库都原生支持这种方式,无需额外的协议解析,代码逻辑更统一、简单。
2. 更好地利用 HTTP/2 的原生优势:多路复用 (Multiplexing)
  • SSE 的限制:SSE诞生于 HTTP/1.1 时代,其设计模型是“一个 TCP 连接专门服务于一个单向数据流”。即使在 HTTP/2 环境下运行,SSE 协议本身也限制了这个连接的用途。
  • Streamable 的高效:在 HTTP/2 环境下,Streamable 只是一个普通的 HTTP 请求。客户端可以在同一个 TCP 连接上发起这个流式请求,同时并行处理其他常规请求(如发送心跳、取消指令、获取元数据等)。所有通信都在一个连接上高效并发,极大地提升了网络资源利用率,这是 HTTP/2 的核心优势。
3. 更强的连接控制与可靠性
  • 断线重连:对于长时间运行的流式连接,断线重连至关重要。标准的 HTTP 请求可以更方便地利用现代网络库提供的重试和恢复机制,从而实现更稳健的断线重连逻辑。而 SSE 的内置重连机制则相对固定,自定义和控制能力较弱。

如何开发一个简单的mcp

目标:做一个“内部文档检索”工具,支持 stdio,本地在 Cursor/Claude Code 中直接调用。

1)定义工具 Schema(告诉 LLM 你提供什么、需要什么)

{
  "name": "get_internal_doc",
  "description": "根据关键词检索内部文档并返回摘要与链接",
  "parameters": {
    "type": "object",
    "properties": {
      "query": { "type": "string", "description": "检索关键词" },
      "limit": { "type": "number", "description": "返回条数", "default": 3 }
    },
    "required": ["query"]
  }
}

2)实现 handler(真正访问你的系统)

  • 约定:收到 tool_calls → 解析 arguments → 执行以下逻辑。
  • 示例流程:关键词 → 命中你的索引或 webhook 缓存 → 返回标准化结果
// 伪代码
async function getInternalDoc({ query, limit = 3 }) {
  const items = await searchIntranetDocs(query, limit);
  return items.map((i) => ({
    title: i.title,
    url: i.url,
    summary: i.snippet,
  }));
}

3)选择传输方式

  • 本地试用:stdio(最轻),注意 stdout 只输出协议数据,日志用 stderr。
  • 上云/协作:优先 Streamable;SSE 仅用于过渡或简单单向通知。
posted @ 2025-10-24 16:57  喻佳文  阅读(53)  评论(0)    收藏  举报