这就是MCP

背景

从上一篇文章 Agent工具调用:从正则解析到Function Calling 中可以看出,Function Calling 功能已经很了,但 Agent 需要调用更多的工具,最好像 App Store 中那么丰富,以便满足所有的用户。如何编写如此丰富的函数呢?Agent 的开发人员肯定没有这么多的精力,于是打算开发一个扩展系统,让其他开发人员来帮忙编写。

巧了,Sam Altman 也是这么想的。OpenAI 试图打造一个 AI 应用商店,让第三方开发者为 ChatGPT 开发插件,于是 ChatGPT Plugins 登场了。

用户可以在 ChatGPT 中安装各种插件:

  • Wolfram Alpha:数学计算
  • Zapier:自动化工作流
  • Expedia:订机票酒店

听起来很美好,但 Plugins 失败了。 2024 年,OpenAI 正式关闭了 ChatGPT Plugins。

为什么?因为它有几个致命问题:

  1. 闭源生态:只能在 ChatGPT 中使用,Claude、Gemini 用不了
  2. 中心化审核:插件要经过 OpenAI 审核才能上架
  3. 复杂的开发流程:需要部署服务器、配置 OAuth、编写 OpenAPI 规范

Plugins 的失败,为 MCP 的诞生埋下了伏笔。

MCP:工具界的 USB 协议

2024 年 11 月,Anthropic 开源了 MCP(Model Context Protocol)

MCP 的架构如下图所示:


mcp-architecture.png

一句话解释 MCP:让 AI 工具像 USB 设备一样即插即用。

USB 出现之前,键盘、鼠标、打印机各有各的接口。USB 出现后,所有设备都用同一个接口——这就是标准化的力量。

MCP 做的是同样的事:

USB 协议 MCP 协议
定义设备如何连接电脑 定义工具如何连接 Agent
键盘、鼠标、U盘都能用 任何 Agent 都能用同一套工具
即插即用 即插即用

MCP 架构解析

MCP 采用客户端-服务端架构:

                          ┌─────────────────────┐
                          │    LLM (Cloud)      │
                          └──────────┬──────────┘
                                     │
┌────────────────────────────────────┼────────────────────────────────┐
│                Agent               │                                │
│                                    ▼                                │
│                          ┌─────────────────┐                        │
│                          │   MCP Client    │                        │
│                          └────────┬────────┘                        │
│                                   │                                 │
└───────────────────────────────────┼─────────────────────────────────┘
                                    │
                          ┌─────────┴─────────┐
                          │                   │
                        stdio               HTTP
                       (local)            (remote)
                          │                   │
                          ▼                   ▼
             ┌──────────────────┐  ┌──────────────────┐
             │   MCP Server     │  │   MCP Server     │
             │   (local .py)    │  │   (remote API)   │
             └──────────────────┘  └──────────────────┘

核心概念:

  • MCP Server:提供工具的服务端,用 @mcp.tool() 装饰器注册工具
  • MCP Client:Agent 中负责连接 Server、调用工具的组件
  • MCP 协议:JSON-RPC 格式,通过 stdio 或 HTTP/SSE 传输
传输方式 说明 适用场景
stdio 本地进程,通过标准输入/输出通信 本地工具、IDE 插件
HTTP/SSE 远程服务,HTTP 请求 + SSE (Server-Sent Events) 响应 远端服务、共享工具

动手实现:MCP Server

让我们用 FastMCP 框架写一个 MCP Server,提供三个实用工具:

# mcp_server.py
from fastmcp import FastMCP
import hashlib
import base64
import uuid

# 创建 MCP 服务器实例
mcp = FastMCP(name="DevToolsServer")

# 工具 1:生成 UUID
@mcp.tool()
def generate_uuid(version: int = 4) -> str:
    """
    生成 UUID
    
    Args:
        version: UUID 版本 (1 或 4),默认 4
    """
    if version == 1:
        result = uuid.uuid1()
    else:
        result = uuid.uuid4()
    return f"🆔 UUID v{version}: {result}"

# 工具 2:生成哈希
@mcp.tool()
def generate_hash(text: str, algorithm: str = "md5") -> str:
    """
    生成哈希值
    
    Args:
        text: 要哈希的文本
        algorithm: 算法 (md5, sha1, sha256, sha512)
    """
    algorithms = {
        'md5': hashlib.md5,
        'sha1': hashlib.sha1,
        'sha256': hashlib.sha256,
        'sha512': hashlib.sha512
    }
    hash_obj = algorithms[algorithm](text.encode('utf-8'))
    return f"🔑 {algorithm.upper()} 哈希值:\n{hash_obj.hexdigest()}"

# 工具 3:Base64 编码
@mcp.tool()
def base64_encode(text: str) -> str:
    """
    Base64 编码
    
    Args:
        text: 要编码的文本
    """
    encoded = base64.b64encode(text.encode('utf-8')).decode('utf-8')
    return f"🔐 Base64 编码结果:\n{encoded}"

if __name__ == "__main__":
    mcp.run()

注意看这段代码的简洁之处:

  • @mcp.tool() 装饰器注册工具
  • 函数签名自动转成 JSON Schema
  • docstring 自动成为工具描述

对比 Function Calling 版本:

Function Calling MCP
手写 JSON Schema 自动从函数签名生成
工具定义和实现分离 @mcp.tool() 一步到位
和 Agent 强耦合 完全解耦,独立服务

动手实现:Agent 调用 MCP

现在写一个 Agent,通过 MCP 协议调用上面的工具:

# dev_agent.py
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

class MCPClient:
    """MCP 客户端"""
    
    async def connect(self, server_script: str):
        """连接到 MCP Server"""
        import sys
        server_params = StdioServerParameters(
            command=sys.executable,
            args=[server_script]
        )
        
        # 启动 Server 子进程,建立 stdio 通信
        self._stdio_transport = stdio_client(server_params)
        self._read, self._write = await self._stdio_transport.__aenter__()
        
        # MCP 协议握手
        self.session = ClientSession(self._read, self._write)
        await self.session.__aenter__()
        await self.session.initialize()
        
        # 获取工具列表
        response = await self.session.list_tools()
        self.tools = response.tools
        print(f"✅ 已连接,发现 {len(self.tools)} 个工具")
    
    async def call_tool(self, name: str, arguments: dict) -> str:
        """调用工具"""
        result = await self.session.call_tool(name, arguments)
        return result.content[0].text

关键代码解析(stdio 模式):

  1. 启动 MCP Server:MCP Client 用 subprocess 启动 MCP Server 作为子进程
  2. 建立通信:MCP Client 与 MCP Server 通过标准输入/输出管道传递 JSON-RPC 消息
  3. 协议握手:MCP Client 调用 initialize() 等待 MCP Server 就绪
  4. 获取工具:MCP Client 调用 list_tools() 获取 MCP Server 提供的工具列表
  5. 调用工具:MCP Client 调用 call_tool() 请求 MCP Server 执行具体工具

两种模式对比:

维度 stdio(本地服务) HTTP/SSE(远端服务)
启动顺序 MCP Client 拉起 MCP Server 子进程 MCP Server 先启动,MCP Client 再连接
就绪机制 MCP Client 调用 initialize() 等待 MCP Server MCP Client 连接 MCP Server 监听的端口
退出顺序 MCP Client 退出,MCP Server 随之结束 MCP Server 独立运行,不依赖 MCP Client
连接数 MCP Client 与 MCP Server 一一对应 多个 MCP Client 可连接同一个 MCP Server

运行效果

$ python3 dev_agent.py

🤖 程序员助手(MCP 版)
============================================================
✅ 已连接到 MCP Server,发现 3 个工具

📦 可用工具列表:
   • generate_uuid: 生成 UUID
   • generate_hash: 生成哈希值
   • base64_encode: Base64 编码

👤 用户: 帮我生成一个 UUID

============================================================
第 1 轮对话
============================================================

🔧 Action: generate_uuid(version=4)

📋 Observation:
🆔 UUID v4: f47ac10b-58cc-4372-a567-0e02b2c3d479

Answer: 已为您生成 UUID: f47ac10b-58cc-4372-a567-0e02b2c3d479

✅ 任务完成!
👤 用户: 计算 password123 的 MD5

🔧 Action: generate_hash(text='password123', algorithm='md5')

📋 Observation:
🔑 MD5 哈希值:
482c811da5d5b4bc6d497ffa98491e38

Answer: "password123" 的 MD5 值是 482c811da5d5b4bc6d497ffa98491e38

MCP 的真正威力

MCP 的真正威力在于:写一次工具,到处能用

对于 AI IDE,内置了 MCP Client,可以直接连接我们写的 MCP Server。

以 Cursor 为例,配置我们的 MCP Server:

1. 打开设置Cursor SettingsTools & MCPAdd Custom MCP

2. 编辑配置文件 ~/.cursor/mcp.json

{
  "mcpServers": {
    "dev-tools": {
      "command": "python3",
      "args": ["/绝对路径/mcp_server.py"]
    }
  }
}

3. 保存后自动生效(无需重启 Cursor)

Cursor 会监听 mcp.json 文件变化,检测到更新后自动重新加载配置。

MCP Server 加载流程(stdio 模式):

  1. Cursor 检测到 mcp.json 变化,解析新增的 MCP Server 配置
  2. Cursor 为该 MCP Server 创建独立的 MCP Client 实例
  3. MCP Client 拉起对应的 MCP Server 子进程(一对一)
  4. MCP Client 与 MCP Server 完成 initialize() 握手
  5. MCP Client 调用 list_tools() 获取 MCP Server 的工具列表
  6. Cursor 将工具列表合并到 Agent 的可用工具中,用户在交互区对话时即可调用

注:可配置多个 MCP Server,每个对应独立的 MCP Client 实例,互不影响。HTTP/SSE 模式同样会创建 MCP Client 实例,区别是 MCP Server 需提前独立启动。

我们在交互区验证一下:

cursor-uuid-sample.png

同一套工具,既能在自己的 Agent 中用,也能在 Cursor 中用,这就是 MCP 的价值。

总结

对比一下三种方案的演进:

维度 正则解析 Function Calling MCP
工具定义 写在 Prompt 里 JSON Schema @mcp.tool()
工具调用 正则解析文本 LLM 返回结构化 JSON 标准化协议
工具复用 ❌ 和 Prompt 耦合 ❌ 和 Agent 耦合 ✅ 即插即用
跨 Agent
生态 各家有各家的格式 统一标准

MCP 解决了 Agent 生态的一个核心问题:工具的标准化和复用

  • 对开发者:写一次工具,Cursor、Claude Desktop、自己的 Agent 都能用
  • 对用户:工具生态繁荣,选择更多
  • 对行业:有了统一标准,才能规模化

MCP 不是要取代 Function Calling,而是在它之上建立了一层标准。就像 TCP/IP 不取代电缆,而是定义了数据如何在电缆上传输。

MCP = Agent 世界的 USB 协议。


完整代码

本文代码已开源:GitHub - agent/03_mcp_practice

03_mcp_practice/
├── mcp_server.py    # MCP Server(3 个工具)
├── dev_agent.py     # Agent 示例
└── README.md        # 使用说明

运行方式:

# 安装依赖
pip install fastmcp mcp requests

# 运行 Agent
python3 dev_agent.py
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容