第2章 MCP协议深度解析

第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请求:

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

响应格式:

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

或者错误响应:

json 复制代码
{
  "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可以调用的功能,用于执行具体的操作或计算任务。

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

json 复制代码
{
  "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方案

bash 复制代码
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. 复用:跨项目、跨团队共享

提示模板示例

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

模板内容示例:

bash 复制代码
请作为销售分析专家,分析{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. 标准请求(期望响应)

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

2. 通知(不期望响应)

json 复制代码
{
  "jsonrpc": "2.0",
  "method": "notifications/resources/list_changed"
}

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

响应类型

1. 成功响应

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

2. 错误响应

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

JSON-RPC 2.0错误码标准

错误码 含义 说明
-32700 Parse error JSON解析失败
-32600 Invalid Request 请求格式不正确
-32601 Method not found 方法不存在
-32602 Invalid params 参数不合法
-32603 Internal error 服务器内部错误
-32000到-32099 Server 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匹配,顺序无关
多路复用示例
python 复制代码
# 伪代码:客户端同时发起多个请求
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验证

最简单的验证方式:

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

对于复杂的企业场景:

json 复制代码
{
  "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["删除"]

权限定义示例

json 复制代码
{
  "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["字段加密"]

个人数据脱敏示例

python 复制代码
# 返回客户信息前进行脱敏
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合规

对于医疗和欧洲客户:

json 复制代码
{
  "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 错误处理最佳实践

python 复制代码
# 客户端侧的完善错误处理
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中的请求处理

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的协同机制,包括工具调用、上下文注入、循环推理等核心互动方式。

相关推荐
回家路上绕了弯2 小时前
五分钟内重复登录 QQ 号定位:数据结构选型与高效实现方案
分布式·后端
Felix_XXXXL3 小时前
Spring Security安全框架原理与实战
java·后端
JaguarJack3 小时前
从零开始打造 Laravel 扩展包:开发、测试到发布完整指南
后端·php·laravel
星释3 小时前
Rust 练习册 :Minesweeper与二维数组处理
开发语言·后端·rust
小蒜学长3 小时前
springboot基于Java的校园导航微信小程序的设计与实现(代码+数据库+LW)
java·spring boot·后端·微信小程序
微学AI4 小时前
基于openEuler操作系统的Docker部署与AI应用实践操作与研究
后端
王元_SmallA4 小时前
IDEA + Spring Boot 的三种热加载方案
java·后端
LCG元4 小时前
实战:用 Shell 脚本自动备份网站和数据库,并上传到云存储
后端
Yeats_Liao4 小时前
时序数据库系列(四):InfluxQL查询语言详解
数据库·后端·sql·时序数据库