🔌 MCP 协议深度解析

理解 Model Context Protocol 的设计思想与通信机制,掌握接入现有 MCP Server 和从零开发自定义 MCP Server 的完整方法。

MCP 协议 工具扩展 自定义 Server TypeScript / Python

🧩 1. MCP 是什么

Model Context Protocol(MCP)是 Anthropic 于 2024 年提出的开放标准协议,定义了 AI 模型与外部工具/数据源之间的标准通信接口。

在 MCP 出现之前,每个 AI 框架都有自己的工具接入方式,工具提供方需要为不同框架分别开发适配层。MCP 的目标是成为 AI 工具生态的"USB 接口"——一次开发,到处接入。

为什么需要 MCP

❌ MCP 之前(碎片化)
Claude GPT Hermes
→ 自定义格式 →工具 A(适配1)
→ 自定义格式 →工具 A(适配2)
→ 自定义格式 →工具 A(适配3)
重复开发,无法复用
VS
✅ MCP 之后(标准化)
Claude GPT Hermes
↓ MCP 标准协议
MCP Server A 工具 A(复用)
一次开发,到处接入
🔍 生态价值:MCP 的真正价值在于生态效应。当越来越多的工具提供方实现 MCP Server,所有支持 MCP 的 Agent 都能直接接入,形成正向循环。目前已有 GitHub、Slack、Google Drive、PostgreSQL 等数百个官方和社区 MCP Server。

📡 2. 协议通信机制

传输层

MCP 支持两种传输方式:

  • stdio(标准输入输出):最常用。Host 启动 MCP Server 进程,通过 stdin/stdout 通信。适合本地工具。
  • HTTP + SSE:Server 以 HTTP 服务形式运行,通过 Server-Sent Events 推送消息。适合远程服务或需要持久化连接的场景。
💡 Hermes 使用 stdio 模式:Hermes 在配置 MCP Server 时,通过 commandargs 字段启动 Server 进程,使用 stdio 通信。这意味着每次 Hermes 启动时,MCP Server 进程也会随之启动。

消息格式(JSON-RPC 2.0)

MCP 基于 JSON-RPC 2.0 协议,所有消息都是 JSON 格式:

bash
// 1. Host 向 Server 发送工具调用请求
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_stock_price",
    "arguments": { "symbol": "600519" }
  }
}

// 2. Server 返回结果
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{ "type": "text", "text": "贵州茅台(600519): ¥1,680.00 (+2.3%)" }]
  }
}

MCP Server 能力类型

  • Tools:可被模型调用的函数,有输入参数和返回值(最常用)
  • Resources:可读取的数据资源,如文件、数据库记录
  • Prompts:预定义的提示模板,可被 Host 调用

🔗 3. 接入现有 MCP Server

社区已有大量成熟的 MCP Server,直接接入即可扩展 Hermes 的能力:

📁 文件系统

读写本地文件和目录,支持权限控制

@modelcontextprotocol/server-filesystem

🐙 GitHub

操作仓库、Issue、PR、代码搜索

@modelcontextprotocol/server-github

🗄️ PostgreSQL

查询数据库,自动获取 Schema 信息

@modelcontextprotocol/server-postgres

🔍 Brave Search

通过 Brave Search API 进行网络搜索

@modelcontextprotocol/server-brave-search

☸️ Kubernetes

管理 K8s 集群、Pod、Service、Deployment

@modelcontextprotocol/server-kubernetes

🌐 Puppeteer

控制浏览器,截图、填表、自动化操作

@modelcontextprotocol/server-puppeteer

在 Hermes 中配置 MCP Server

yaml
# ~/.hermes/config.yaml
mcp_servers:

  # GitHub:需要 Personal Access Token
  github:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}"

  # PostgreSQL:连接字符串
  postgres:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-postgres",
           "postgresql://user:pass@localhost/mydb"]

  # 文件系统:限制访问目录
  filesystem:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem",
           "/home/user/documents"]   # 只允许访问此目录
⚠️ 安全注意:MCP Server 的权限即 Agent 的权限。配置 PostgreSQL MCP Server 时,建议使用只读账号;配置文件系统 MCP Server 时,严格限制可访问的目录范围。

🛠️ 4. 开发自定义 MCP Server

当现有 MCP Server 无法满足需求时,可以自己开发。以开发一个"A股实时行情查询"MCP Server 为例:

TypeScript 实现

typescript
// stock-mcp-server/src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "stock-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// 声明工具列表(模型会看到这些定义)
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "get_stock_price",
    description: "查询A股实时行情,输入股票代码返回最新价格和涨跌幅",
    inputSchema: {
      type: "object",
      properties: {
        symbol: { type: "string", description: "股票代码,如 600519" }
      },
      required: ["symbol"]
    }
  }]
}));

// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_stock_price") {
    const { symbol } = request.params.arguments as { symbol: string };
    // 调用行情 API(示例)
    const price = await fetchStockPrice(symbol);
    return {
      content: [{ type: "text", text: `${symbol}: ¥${price.current} (${price.change}%)` }]
    };
  }
  throw new Error(`Unknown tool: ${request.params.name}`);
});

// 启动 stdio 传输
const transport = new StdioServerTransport();
await server.connect(transport);

Python 实现

typescript
# stock_mcp_server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

app = Server("stock-server")

@app.list_tools()
async def list_tools():
    return [
        Tool(
            name="get_stock_price",
            description="查询A股实时行情",
            inputSchema={
                "type": "object",
                "properties": {
                    "symbol": {"type": "string", "description": "股票代码"}
                },
                "required": ["symbol"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_stock_price":
        symbol = arguments["symbol"]
        price = await fetch_stock_price(symbol)  # 你的行情获取逻辑
        return [TextContent(type="text", text=f"{symbol}: ¥{price}")]

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(read_stream, write_stream, app.create_initialization_options())

在 Hermes 中接入自定义 Server

yaml
# ~/.hermes/config.yaml
mcp_servers:
  stock:
    command: node
    args: ["/path/to/stock-mcp-server/dist/index.js"]
    # Python 版本:
    # command: python3
    # args: ["/path/to/stock_mcp_server.py"]

🔍 5. 调试与排错

使用 MCP Inspector 调试

官方提供了 MCP Inspector 工具,可以在浏览器中直接测试 MCP Server:

bash
npx @modelcontextprotocol/inspector node dist/index.js
# 打开 http://localhost:5173 进行可视化调试

常见问题

  • Server 启动失败:检查 commandargs 是否正确,手动在终端运行命令验证
  • 工具未出现在 Agent 中:检查 list_tools 返回格式是否符合 MCP 规范
  • 工具调用返回错误:在 Server 中添加详细日志(输出到 stderr,不影响 stdio 通信)
  • 环境变量未生效:MCP Server 进程的环境变量需要在 env 字段中显式传入
💡 调试技巧:MCP Server 的日志必须输出到 stderr,不能输出到 stdout。stdout 是 JSON-RPC 通信通道,任何非 JSON 输出都会导致协议解析失败。