第2章 MCP协议深度解析

126 阅读2分钟

第2章 MCP协议深度解析

前言

理解MCP协议是掌握整个生态的基础。本章将从通信原理、核心概念、消息格式、安全机制等多个维度深入解析MCP协议的设计哲学和实现细节。


2.1 MCP架构与设计原理

2.1.1 客户端-服务器模型

MCP采用经典的客户端-服务器(Client-Server)架构,但与传统Web API不同,它实现了双向异步通信

传统CS模型 vs MCP模型
graph TB
    subgraph "传统API模型"
        Client1["客户端"] -->|请求| Server1["服务器"]
        Server1 -->|响应| Client1
        Client1 -->|新请求| Server1
    end
    
    subgraph "MCP双向模型"
        Client2["客户端"] -->|调用工具| Server2["MCP服务器"]
        Server2 -->|结果| Client2
        Server2 -.->|资源更新通知| Client2
        Client2 -.->|订阅资源| Server2
    end
    
    style Client1 fill:#e3f2fd
    style Server1 fill:#c8e6c9
    style Client2 fill:#e3f2fd
    style Server2 fill:#c8e6c9

MCP模型的核心特点

  1. 双向通信

    • 客户端可以主动调用工具、访问资源
    • 服务器可以主动推送资源变化通知
    • 形成一个真正的对话通道,而不只是请求-响应
  2. 异步处理

    • 消息不需要立即响应
    • 支持长时间运行的操作
    • 请求可以被取消
  3. 多路复用

    • 单个连接上支持多个并发请求
    • 每个请求有唯一的ID来追踪
    • 避免了连接阻塞

2.1.2 通信机制与消息格式

MCP基于JSON-RPC 2.0规范构建,选择这个标准是因为:

  • 轻量级、易于解析
  • 平台无关、语言无关
  • 已有成熟的实现库
  • 广泛被业界接受
MCP通信的三个层级
graph LR
    A["应用层"] -->|MCP消息| B["传输层"]
    B -->|JSON-RPC 2.0| C["通信层"]
    C -->|HTTP/WebSocket/stdio| D["物理层"]
    
    A -->|定义| A1["工具、资源、提示"]
    B -->|定义| B1["请求、响应、通知"]
    C -->|定义| C1["JSON-RPC结构"]
    D -->|支持| D1["多种传输方式"]
JSON-RPC 2.0基础

一个标准的JSON-RPC请求:

{
  "jsonrpc": "2.0",
  "id": "req_123",
  "method": "tools/call",
  "params": {
    "name": "query_sales",
    "arguments": {
      "start_date": "2025-01-01",
      "end_date": "2025-12-31"
    }
  }
}

响应格式:

{
  "jsonrpc": "2.0",
  "id": "req_123",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "销售总额:$5,000,000"
      }
    ]
  }
}

或者错误响应:

{
  "jsonrpc": "2.0",
  "id": "req_123",
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "details": "start_date format must be YYYY-MM-DD"
    }
  }
}

关键概念

字段说明示例
jsonrpc协议版本"2.0"
id请求唯一标识"req_123"(字符串或数字)
method调用的方法"tools/call"、"resources/read"
params方法参数对象或数组
result成功时的结果任意JSON值
error失败时的错误{code, message, data}

2.1.3 MCP消息类型完整体系

graph TB
    A["MCP消息"] --> B["请求 Request"]
    A --> C["响应 Response"]
    A --> D["通知 Notification"]
    A --> E["资源更新 ResourceUpdated"]
    
    B --> B1["initialize"]
    B --> B2["tools/call"]
    B --> B3["resources/read"]
    B --> B4["resources/subscribe"]
    
    C --> C1["result"]
    C --> C2["error"]
    
    D --> D1["客户端发起"]
    D --> D2["服务器不期望响应"]
    
    E --> E1["资源内容变化"]
    E --> E2["资源列表变化"]
    
    style A fill:#ff6b6b
    style B fill:#4ecdc4
    style C fill:#4ecdc4
    style D fill:#95e1d3
    style E fill:#95e1d3

2.1.4 协议的演进与版本管理

timeline
    title MCP协议版本演进
    2024-03 : MCP 1.0 : 初始版本发布
    2024-06 : MCP 1.0.1 : 修复、澄清
    2024-09 : MCP 1.1 : 资源订阅、改进的错误处理
    2024-12 : MCP 1.2 : 性能优化、新的传输方式
    2025-03 : MCP 2.0 (规划) : 可能的重大更新

版本兼容性策略

  • 主版本号变更:不保证向后兼容
  • 次版本号变更:向后兼容
  • 修订号变更:bug修复和澄清

客户端和服务器应该在初始化时协商协议版本。


2.2 MCP的核心概念

2.2.1 工具(Tools)的定义与作用

定义:工具是LLM可以调用的功能,用于执行具体的操作或计算任务。

本质:工具 = 元数据 + 实现

{
  "name": "transfer_funds",
  "description": "转账资金到指定账户",
  "inputSchema": {
    "type": "object",
    "properties": {
      "from_account": {
        "type": "string",
        "description": "源账户ID"
      },
      "to_account": {
        "type": "string",
        "description": "目标账户ID"
      },
      "amount": {
        "type": "number",
        "description": "转账金额"
      },
      "reason": {
        "type": "string",
        "description": "转账原因"
      }
    },
    "required": ["from_account", "to_account", "amount"]
  }
}

工具的生命周期

sequenceDiagram
    participant LLM
    participant Client
    participant Server
    participant Backend
    
    LLM->>Client: 需要调用工具
    Client->>Server: 列出可用工具
    Server-->>Client: 工具元数据列表
    
    LLM->>Client: 调用具体工具
    Client->>Server: tools/call请求
    Server->>Backend: 执行业务逻辑
    Backend-->>Server: 返回结果
    Server-->>Client: 返回结果
    Client-->>LLM: 工具执行结果

工具的分类

类型特点示例
查询类只读,返回信息查询账户余额、搜索文档
操作类修改状态,有副作用创建订单、发送邮件
计算类数学运算、数据处理分析数据、生成报告
转换类格式转换、数据映射导出Excel、格式化文本

2.2.2 资源(Resources)的含义

定义:资源是LLM可以访问的数据源或信息,代表持久化的内容。

本质:资源 = 标识 + 内容 + 元数据

资源 vs 工具的区别

graph TB
    A["MCP组件"] --> B["工具 Tools"]
    A --> C["资源 Resources"]
    
    B --> B1["动态行为"]
    B --> B2["每次调用可能返回不同结果"]
    B --> B3["可能有副作用"]
    B --> B4["例:生成报告"]
    
    C --> C1["静态数据"]
    C --> C2["持久化内容"]
    C --> C3["通常是只读的"]
    C --> C4["例:报告文件"]
    
    style B fill:#ffcccc
    style C fill:#ccccff

资源URI方案

resources://authority/path?query=value

示例:
- resources://salesdb/reports/q1_2025.xlsx
- resources://docs/product_manual.pdf
- resources://config/system_settings.json
- resources://employees/dept_001/team_list.csv

资源的三种访问模式

graph TB
    A["资源访问"] --> B["列表模式"]
    A --> C["读取模式"]
    A --> D["订阅模式"]
    
    B --> B1["列出资源列表"]
    B --> B2["支持过滤和搜索"]
    B --> B3["返回资源元数据"]
    
    C --> C1["一次性读取"]
    C --> C2["完整内容"]
    C --> C3["无状态操作"]
    
    D --> D1["持续监听更新"]
    D --> D2["资源变化推送"]
    D --> D3["维持长连接"]

2.2.3 提示模板(Prompts)的角色

定义:提示模板是预定义的、参数化的提示词,用于指导LLM的行为。

作用

  1. 标准化:确保相同的任务获得一致的处理
  2. 参数化:支持动态参数注入
  3. 知识编码:将专家知识编码到提示中
  4. 复用:跨项目、跨团队共享

提示模板示例

{
  "name": "analyze_sales_trends",
  "description": "分析销售趋势的专家级提示",
  "arguments": [
    {
      "name": "time_period",
      "description": "分析的时间周期",
      "required": true
    },
    {
      "name": "product_category",
      "description": "产品类别(可选)",
      "required": false
    }
  ]
}

模板内容示例:

请作为销售分析专家,分析{time_period}的销售数据。
关注点:
1. 销售额趋势
2. 产品结构变化
3. 区域表现差异
{#if product_category}
4. 特别关注{product_category}类别的表现
{/if}

请提供具体的建议和预测。

2.2.4 取样(Sampling)机制

定义:取样是让服务器能够请求LLM进行推理的机制。这形成了一个循环

应用场景

  • 服务器需要LLM帮助做决策
  • 数据验证或复杂的业务逻辑判断
  • 需要LLM的自然语言理解能力

取样流程

sequenceDiagram
    participant Client as 客户端LLM
    participant Server as MCP服务器
    participant App as 业务系统
    
    Client->>Server: 调用工具处理数据
    Server->>App: 获取数据
    App-->>Server: 返回数据
    
    Note over Server: 需要LLM判断数据有效性
    Server->>Client: sampling/createMessage请求
    Client->>Client: 调用LLM推理
    Client-->>Server: 推理结果
    
    Server->>Server: 根据LLM结果做决策
    Server-->>Client: 工具执行结果

这创造了一个自反馈循环,让MCP系统具有更高的智能性。


2.3 MCP的通信协议

2.3.1 JSON-RPC 2.0详细解析

请求类型

1. 标准请求(期望响应)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": {},
      "sampling": {}
    },
    "clientInfo": {
      "name": "Claude Desktop",
      "version": "1.0.0"
    }
  }
}

2. 通知(不期望响应)

{
  "jsonrpc": "2.0",
  "method": "notifications/resources/list_changed"
}

通知用于单向通信,不需要响应。

响应类型

1. 成功响应

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "resources": {},
      "tools": {}
    },
    "serverInfo": {
      "name": "Sales Database",
      "version": "2.1.0"
    }
  }
}

2. 错误响应

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found",
    "data": {
      "method": "unknown_method"
    }
  }
}

JSON-RPC 2.0错误码标准

错误码含义说明
-32700Parse errorJSON解析失败
-32600Invalid Request请求格式不正确
-32601Method not found方法不存在
-32602Invalid params参数不合法
-32603Internal error服务器内部错误
-32000到-32099Server error服务器自定义错误

2.3.2 请求-响应模式详解

请求ID的重要性

在异步环境中,请求ID用于匹配请求和响应

sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: 请求1 (id=req1)
    Client->>Server: 请求2 (id=req2)
    Client->>Server: 请求3 (id=req3)
    
    Note over Server: 异步处理,顺序不定
    
    Server-->>Client: 响应2 (id=req2)
    Server-->>Client: 响应1 (id=req1)
    Server-->>Client: 响应3 (id=req3)
    
    Note over Client: 按ID匹配,顺序无关
多路复用示例
# 伪代码:客户端同时发起多个请求
async def call_multiple_tools():
    # 发起三个并发请求
    task1 = client.call_tool("query_sales", id="req1")
    task2 = client.call_tool("calculate_average", id="req2") 
    task3 = client.call_tool("fetch_report", id="req3")
    
    # 等待所有响应(无需按顺序)
    results = await asyncio.gather(task1, task2, task3)
    return results

2.3.3 流式与实时通信

MCP支持多种传输方式,适应不同场景:

graph TB
    A["MCP传输方式"] --> B["标准输入输出"]
    A --> C["HTTP"]
    A --> D["WebSocket"]
    A --> E["自定义"]
    
    B --> B1["本地进程通信"]
    B --> B2["低延迟"]
    B --> B3["例:Claude Desktop"]
    
    C --> C1["REST over HTTP"]
    C --> C2["无状态"]
    C --> C3["轻量级部署"]
    
    D --> D1["双向长连接"]
    D --> D2["实时推送"]
    D --> D3["高效复用"]
    
    E --> E1["gRPC"]
    E --> E2["自定义协议"]

各传输方式对比

方式延迟连接管理实时性适用场景
stdio极低简单同步本地工具、桌面应用
HTTP无状态REST API、无状态服务
WebSocket持久连接实时推送、长连接

2.4 MCP的安全性与权限控制

2.4.1 身份验证机制

多层验证体系
graph TB
    A["安全认证体系"] --> B["传输层"]
    A --> C["应用层"]
    A --> D["资源层"]
    
    B --> B1["TLS加密"]
    B --> B2["证书验证"]
    B --> B3["mTLS"]
    
    C --> C1["API Key"]
    C --> C2["OAuth 2.0"]
    C --> C3["JWT Token"]
    
    D --> D1["资源访问控制"]
    D --> D2["时间限制"]
    D --> D3["操作限制"]
API Key验证

最简单的验证方式:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "clientInfo": {
      "name": "Claude",
      "version": "1.0"
    },
    "authentication": {
      "type": "api_key",
      "key": "sk-1234567890abcdef"
    }
  }
}
OAuth 2.0集成

对于复杂的企业场景:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "authentication": {
      "type": "oauth2",
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "scope": ["tools:read", "resources:read"]
    }
  }
}

2.4.2 权限模型

细粒度权限控制
graph TB
    A["权限模型"] --> B["工具权限"]
    A --> C["资源权限"]
    A --> D["操作权限"]
    
    B --> B1["工具可见性"]
    B --> B2["工具调用权限"]
    B --> B3["工具参数限制"]
    
    C --> C1["资源读"]
    C --> C2["资源写"]
    C --> C3["资源删除"]
    
    D --> D1["创建"]
    D --> D2["查询"]
    D --> D3["更新"]
    D --> D4["删除"]

权限定义示例

{
  "version": "2024-11-05",
  "permissions": {
    "tools": {
      "query_sales": {
        "allowed": true,
        "timeRange": {
          "startDate": "2025-01-01",
          "endDate": "2025-12-31"
        }
      },
      "transfer_funds": {
        "allowed": true,
        "maxAmount": 100000,
        "requiresApproval": true
      }
    },
    "resources": {
      "resources://finance/*": {
        "read": true,
        "write": false
      }
    }
  }
}

2.4.3 数据隐私保护

数据敏感化策略
graph TB
    A["数据隐私保护"] --> B["数据分类"]
    A --> C["访问控制"]
    A --> D["审计日志"]
    A --> E["加密"]
    
    B --> B1["公开数据"]
    B --> B2["内部数据"]
    B --> B3["机密数据"]
    B --> B4["个人数据"]
    
    C --> C1["基于角色"]
    C --> C2["基于时间"]
    C --> C3["基于位置"]
    
    D --> D1["操作日志"]
    D --> D2["访问日志"]
    D --> D3["变更日志"]
    
    E --> E1["传输加密TLS"]
    E --> E2["存储加密"]
    E --> E3["字段加密"]

个人数据脱敏示例

# 返回客户信息前进行脱敏
def anonymize_customer_data(customer):
    return {
        "id": customer["id"],
        "email": mask_email(customer["email"]),  # 隐藏部分邮箱
        "phone": mask_phone(customer["phone"]),  # 隐藏部分电话
        "name": customer["name"],  # 完整名字
        # 注意:不返回敏感字段如密码、SSN等
    }

def mask_email(email):
    # example@domain.com -> exa***@domain.com
    parts = email.split("@")
    return parts[0][:3] + "***@" + parts[1]

def mask_phone(phone):
    # 13812345678 -> 1381****5678
    return phone[:4] + "****" + phone[-4:]
HIPAA/GDPR合规

对于医疗和欧洲客户:

{
  "security": {
    "encryption": {
      "transport": "TLS 1.3",
      "storage": "AES-256"
    },
    "compliance": ["HIPAA", "GDPR"],
    "dataRetention": {
      "logs": "30 days",
      "personalData": "user configurable"
    },
    "audit": {
      "enabled": true,
      "retention": "90 days",
      "immutable": true
    }
  }
}

2.5 MCP协议在实践中的应用

2.5.1 完整的工具调用流程

sequenceDiagram
    participant User
    participant LLM
    participant Client
    participant Server
    participant DB
    
    User->>LLM: "查询2025年销售数据"
    LLM->>Client: 需要调用query_sales工具
    
    Client->>Server: 请求 {method: "tools/call", name: "query_sales"}
    Server->>DB: 查询数据
    DB-->>Server: 返回销售记录
    Server-->>Client: 响应 {result: 销售数据}
    
    Client-->>LLM: 工具执行结果
    LLM->>LLM: 分析数据
    LLM-->>User: "2025年销售总额$5M,同比增长15%..."

2.5.2 错误处理最佳实践

# 客户端侧的完善错误处理
class MCPClient:
    async def call_tool(self, tool_name, arguments):
        try:
            request = {
                "jsonrpc": "2.0",
                "id": self.generate_request_id(),
                "method": "tools/call",
                "params": {
                    "name": tool_name,
                    "arguments": arguments
                }
            }
            
            # 发送请求并设置超时
            response = await self.send_request(request, timeout=30)
            
            # 检查是否有错误
            if "error" in response:
                error = response["error"]
                if error["code"] == -32602:
                    raise InvalidParamsError(error["message"])
                elif error["code"] == -32601:
                    raise MethodNotFoundError(error["message"])
                else:
                    raise MCPError(error["message"])
            
            return response.get("result")
            
        except asyncio.TimeoutError:
            raise TimeoutError(f"Tool {tool_name} execution timeout")
        except ConnectionError:
            raise ConnectionError("Lost connection to MCP server")

本章总结

核心概念关键点
架构模型客户端-服务器,双向异步通信
通信基础JSON-RPC 2.0,轻量级、标准化
核心概念工具、资源、提示、取样
消息类型请求、响应、通知、资源更新
多路复用单连接并发多个请求,按ID匹配
传输方式stdio、HTTP、WebSocket等
安全认证API Key、OAuth 2.0、JWT
权限控制细粒度的工具、资源、操作权限
数据隐私脱敏、加密、审计、合规

常见问题

Q1: 为什么MCP选择JSON-RPC而不是gRPC或REST? A: JSON-RPC相比gRPC更轻量级,更易于跨语言实现;相比REST更适合双向通信。这是在简洁性和功能性之间的最优平衡。

Q2: 单个连接如何支持并发请求? A: 通过为每个请求分配唯一的id,响应时返回相同的id,客户端据此匹配。这样即使响应顺序打乱也能正确处理。

Q3: 通知(notification)和请求有什么区别? A: 通知没有id字段,发送端不期望响应,用于单向消息。请求有id,发送端期望收到响应。

Q4: 资源和工具如何选择使用? A: 规则是:如果内容是持久的、不变的数据用资源;如果是动态计算、有副作用的操作用工具。

Q5: MCP如何保证消息安全? A: 多层防护:传输层TLS加密、应用层身份验证和授权、资源层细粒度权限控制、审计日志记录。


关键代码片段

TypeScript中的请求处理

interface MCPRequest {
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: Record<string, any>;
}

interface MCPResponse {
  jsonrpc: "2.0";
  id: string | number;
  result?: any;
  error?: {
    code: number;
    message: string;
    data?: any;
  };
}

async function sendRequest(request: MCPRequest): Promise<MCPResponse> {
  const response = await fetch("http://mcp-server/rpc", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(request)
  });
  
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  
  return response.json();
}

延伸阅读


下一章预告:第3章将讲述MCP与LLM的协同机制,包括工具调用、上下文注入、循环推理等核心互动方式。