从零理解 MCP:让 AI 大模型"万能插拔"的协议

0 阅读7分钟

一次搞懂 Model Context Protocol 是什么、为什么要用它、以及怎么用。


前言:从 LLM + Tools = Agent 说起

如果你玩过 AI 编程助手(Cursor、Trae 等)或者搭建过 LLM Agent,一定对下面这个公式不陌生:

LLM + Tools = Agent

大模型本身只会"说话",但配上读文件、写文件、执行命令等工具(Tools)之后,它就变成了一个能干活儿的 Agent。

这时候问题就来了:

  • 工具是硬编码在 Agent 里的,每加一个工具都要改代码、重新部署。
  • 外部团队 / 第三方想把他们的服务暴露给 AI 用,没有统一标准,对接成本极高。
  • 不同语言写的工具(Node 调 Python、调 Java、调 Rust)彼此割裂。
  • 本地工具和远程工具通信方式不一样,适配起来很痛苦。

MCP 就是为了解决这个"碎片化"问题而生的。

小思考:80% 的 App 未来可能会消失——因为当 AI 能直接通过 MCP 调用各种服务时,用户不再需要打开一个个 App,而是直接和 AI 对话就能完成所有操作。


一、MCP 是什么?

MCP(Model Context Protocol,模型上下文协议) 是由 Anthropic 于 2024 年底提出、2025 年贡献给开源社区的一套开放协议。它本质上是一个为 AI 大模型设计的通用接口标准

我用一个比喻来帮你理解:

在过去,AI 要调 10 个不同厂商的工具,就像你出门要带 10 个不同接口的充电器;有了 MCP,就像用上了"万能充电器",一套协议就能连接所有服务。

MCP 的核心思想是:把具体的工具(Tools)和"如何调用工具"的规则(Schema),打包成一个独立的服务器进程(Server)。

MCP 和 Tool 不是并列关系,而是**"容器"和"内容"**的关系:

  • 没有 MCP 之前:AI 调用 Tools 是硬编码的,每加一个工具都要写胶水代码。
  • 有了 MCP 之后:变成动态发现,Host 启动时自动扫描 Server 暴露了哪些工具,运行时按需调用。

二、MCP 的架构:Host / Client / Server 三者关系

MCP 采用经典的客户端-服务器(Client-Server)架构,包含三个核心角色:

角色说明举例
MCP Host(主机)需要连接外部世界的 AI 应用程序,是发起请求的"大脑"Cursor、Claude Desktop、Trae
MCP Client(客户端)在 Host 内部运行,负责与 MCP Server 建立一对一连接Cursor 内置的 MCP Client,读取 mcp.json 并建立通信
MCP Server(服务器)轻量级程序,通过标准协议对外暴露能力你自己写的 my-mcp-server.mjs

每一个 MCP Server 可以对外暴露三种核心能力

能力说明示例
Tools(工具)AI 可以执行的函数查询用户、发送邮件、读文件
Resources(资源)AI 可以引用的上下文数据数据库记录、本地文件、API 返回内容
Prompts(提示)可复用的预定义交互模板引导 AI 完成特定任务的对话模板

一句话总结 MCP 协议:Context = Tool + Resource + PromptTemplate


三、MCP 的调用流程(7 个步骤)

当你在 Cursor 里说了一句 "帮我查一下用户 002 的信息",背后到底发生了什么?

用户输入 prompt
      ↓
① Host 配置 mcp.json → 声明要连接哪些 Server
      ↓
② Client 发送 initialize 请求 → Server 返回所有 tools 列表(动态发现)
      ↓
③ 用户输入任务 prompt → Host 接收
      ↓
④ Host 分析 prompt → 从已发现的 tools 中检索匹配项
      ↓
⑤ Client 发起 tool 调用 → 通过 stdio / HTTP 传到 MCP Server
      ↓
⑥ Server 执行 tool 逻辑 → 返回结果
      ↓
⑦ 结果注入 LLM 上下文(ToolMessage) → LLM 继续推理 → 生成最终回复

关键一步:第 ② 步的"动态发现"

这是 MCP 最大的亮点。Host 不需要在代码里写死"有哪些工具可用",而是启动时主动问 Server:"你能提供什么能力?" Server 返回自己注册的所有 tools 的名称、描述、参数 schema。然后当用户发任务时,Host 就能自动匹配合适的工具去执行。

配置示例(mcp.json

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "node",
      "args": ["my-mcp-server.mjs"]
    }
  }
}

四、MCP 的通信方式

MCP 支持两种通信传输方式,覆盖本地和远程场景:

传输方式适用场景实现类
stdio(标准输入输出流)本地子进程调用StdioServerTransport
HTTP(Streamable HTTP)远程服务器调用HttpServerTransport / Streamable HTTP

这意味着你可以:

  • 本地:通过子进程调用一个 MCP Server(哪怕它是 Node / Python / Java / Rust 写的)
  • 远程:通过 HTTP 调用部署在云端的 MCP Server,享受第三方服务

MCP 最大的意义之一:跨进程、跨语言调用工具,让 LLM 能干更强大的任务。


五、手写一个 MCP Server(代码实战)

我们来看一个完整的例子:一个提供"查询用户"能力的 MCP Server。

5.1 依赖安装

pnpm add @modelcontextprotocol/sdk zod

5.2 完整代码:my-mcp-server.mjs

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

// 模拟数据库
const database = {
  users: {
    "001": { id: "001", name: "张三", email: "zhangsan@example.com", role: "admin" },
    "002": { id: "002", name: "李四", email: "lisi@example.com", role: "user" },
    "003": { id: "003", name: "王五", email: "wangwu@example.com", role: "user" },
  }
};

// ① 创建 MCP Server 实例
const server = new McpServer({
  name: 'my-mcp-server',
  version: '1.0.0',
});

// ② 注册 Tool:查询用户
server.registerTool('query-user', {
  description: '查询数据库中的用户信息。输入用户ID,返回该用户的详细信息(姓名、邮箱、角色)。',
  inputSchema: {
    userId: z.string().describe("用户 ID,例如:001, 002, 003"),
  },
}, async ({ userId }) => {
  const user = database.users[userId];
  if (!user) {
    return {
      content: [{
        type: 'text',
        text: `用户ID ${userId} 不存在。可用的ID: 001, 002, 003`,
      }],
    };
  }
  return {
    content: [{
      type: 'text',
      text: `用户信息:\n- ID: ${user.id}\n- 姓名: ${user.name}\n- 邮箱: ${user.email}\n- 角色: ${user.role}`,
    }],
  };
});

// ③ 注册 Resource:使用指南
server.registerResource('使用指南', 'docs://guide', {
  description: 'MCP Server 使用文档',
  mimeType: 'text/plain',
}, async () => {
  return {
    contents: [{
      uri: 'docs://guide',
      mimeType: 'text/plain',
      text: 'MCP Server 使用指南\n功能:提供用户查询等工具。',
    }],
  };
});

// ④ 选择通信方式并启动
const transport = new StdioServerTransport();
await server.connect(transport);

5.3 MCP 开发流程总结

new McpServer()              →  创建 Server 实例
server.registerTool()        →  注册工具(名称、描述、入参 schema、执行回调)
server.registerResource()    →  注册资源(名称、URI、描述、内容回调)
server.registerPrompt()      →  注册提示模板(可选)
new StdioServerTransport()   →  选择通信方式
server.connect(transport)    →  启动 Server,等待 Host 连接

六、把 MCP 集成到自己的 Agent 程序里

MCP Server 写好了,不仅可以在 Cursor 里用,还可以通过 LangChain 等框架直接嵌入到你自己的 Agent 程序中,实现"可拔插"的工具集成。

下面是用 LangChain + MCP Adapters 的示例:

import { MultiServerMCPClient } from '@langchain/mcp-adapters';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, ToolMessage } from '@langchain/core/messages';

const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  configuration: { baseURL: process.env.OPENAI_BASE_URL },
});

// ① 创建 MCP Client,连接 MCP Server
const mcpClient = new MultiServerMCPClient({
  mcpServers: {
    'my-mcp-server': {
      command: 'node',
      args: ['/path/to/my-mcp-server.mjs'],
    },
  },
});

// ② 动态发现 + 获取 tools
const tools = await mcpClient.getTools();

// ③ 把 tools 绑定到模型上
const modelWithTools = model.bindTools(tools);

// ④ Agent 循环:推理 → 调用 tool → 拿结果 → 再推理
async function runAgent(query, maxIterations = 30) {
  const messages = [new HumanMessage(query)];

  for (let i = 0; i < maxIterations; i++) {
    const response = await modelWithTools.invoke(messages);
    messages.push(response);

    // 没有 tool_calls 说明推理结束
    if (!response.tool_calls || response.tool_calls.length === 0) {
      return response.content;
    }

    // 执行每个 tool_call,把结果注入上下文
    for (const toolCall of response.tool_calls) {
      const foundTool = tools.find(t => t.name === toolCall.name);
      if (foundTool) {
        const toolResult = await foundTool.invoke(toolCall.args);
        messages.push(new ToolMessage({
          content: toolResult,
          tool_call_id: toolCall.id,
        }));
      }
    }
  }
  return messages[messages.length - 1].content;
}

// ⑤ 使用
const result = await runAgent("查一下用户 002 的信息");
console.log(result);

await mcpClient.close();

这个例子的关键点:

  1. getTools() 就是"动态发现"——你的程序不需要硬编码有哪些工具。
  2. bindTools(tools) 把这些动态发现的工具注入 LLM,让它"知道"自己能调什么。
  3. Agent 循环中,LLM 决定是否调 tool、调哪个 tool,执行后结果以 ToolMessage 的形式喂回 LLM,形成闭环。

七、MCP 的核心价值总结

维度传统方式MCP 方式
工具集成硬编码,一改全改动态发现,即插即用
跨语言调用需要自己写 IPC / RPC 胶水统一协议,天然跨语言
第三方接入各自定义接口,对接成本高一套规范,大家都遵守
本地 vs 远程需要分别适配stdio / HTTP 双模式原生支持
生态扩展封闭开放,大厂可将服务以 MCP 方式对外提供

八、写在最后

MCP 的野心远不止是"又一个工具调用协议"。它试图定义的是 AI 时代的**"USB-C 接口"**——一套通用的、开放的标准,让 AI 可以无缝连接这个世界的各种服务和数据。

理解了 MCP,你就能理解为什么有人说"80% 的 App 会消失":当 AI 成为一个超级入口,用户不再需要打开一个个独立的 App,而是直接对 AI 说"帮我查一下快递""帮我订一张票",AI 通过 MCP 在背后调用对应的服务,一切都变得透明。

希望这篇文章能帮你建立起对 MCP 的完整认知。现在就动手写一个自己的 MCP Server 吧,你会发现它比想象中简单得多。


本文参考: