MCP协议 进阶 MCP 模型上下文协议 架构设计 AI工具

MCP 架构概览:模型上下文协议深度解析

AIEng Hub
阅读约 25 分钟

什么是 MCP?

MCP(Model Context Protocol,模型上下文协议)是 Anthropic 提出的开放协议,旨在标准化 AI 模型与外部工具、数据源之间的交互方式。

┌─────────────────────────────────────────────────────────────┐
│                  MCP 架构全景图                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────────────────────────────────────────────┐  │
│   │                    MCP Host                          │  │
│   │  (Claude Desktop / IDE / 自定义应用)                │  │
│   └────────────┬──────────────────────┬─────────────────┘  │
│                │                      │                     │
│                │ stdio / SSE          │ stdio / SSE         │
│                │                      │                     │
│   ┌────────────▼────────┐   ┌────────▼────────────┐       │
│   │    MCP Client       │   │    MCP Client       │       │
│   │  (Protocol Layer)   │   │  (Protocol Layer)   │       │
│   └────────────┬────────┘   └────────┬────────────┘       │
│                │                     │                    │
│                │ JSON-RPC 2.0        │ JSON-RPC 2.0       │
│                │                     │                    │
│   ┌────────────▼────────┐   ┌────────▼────────────┐       │
│   │    MCP Server       │   │    MCP Server       │       │
│   │  (File System)      │   │  (Database)         │       │
│   └─────────────────────┘   └─────────────────────┘       │
│                                                             │
│   核心概念:                                                │
│   • Host: 运行 LLM 的应用程序                                │
│   • Client: 与 Server 建立和维护 1:1 连接                  │
│   • Server: 向 Client 暴露特定能力的程序                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

MCP 的核心价值:

  • 标准化:统一的工具调用协议
  • 可组合:不同 Server 可自由组合
  • 安全:明确的权限控制模型
  • 开放:任何人可开发 MCP Server

MCP vs Function Calling

维度MCPFunction Calling
协议层级应用层协议模型能力
传输方式stdio / SSEAPI 参数
生命周期长连接单次调用
状态管理有状态无状态
发现机制能力协商预定义 Schema
安全性细粒度权限依赖实现
适用场景桌面/IDE 集成API 服务

协议基础

1. 传输层

MCP 支持两种传输方式:

┌─────────────────────────────────────────────────────────────┐
│                    MCP 传输层                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. stdio(标准输入输出)                                   │
│   ┌─────────────────────────────────────────────┐          │
│   │  Host ──fork──> Server Process             │          │
│   │       <──stdin/stdout──>                   │          │
│   │                                             │          │
│   │  特点:                                     │          │
│   │  • 本地进程间通信                           │          │
│   │  • 简单可靠                                 │          │
│   │  • 适合本地工具                             │          │
│   └─────────────────────────────────────────────┘          │
│                                                             │
│   2. SSE(Server-Sent Events)                               │
│   ┌─────────────────────────────────────────────┐          │
│   │  Host ──HTTP──> Remote Server              │          │
│   │       <──SSE──>                            │          │
│   │                                             │          │
│   │  特点:                                     │          │
│   │  • 远程服务通信                             │          │
│   │  • 支持网络隔离                             │          │
│   │  • 适合云服务                                 │          │
│   └─────────────────────────────────────────────┘          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2. 消息格式

MCP 使用 JSON-RPC 2.0 作为消息格式:

// 请求消息
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/path/to/file.txt"
    }
  }
}

// 响应消息
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "文件内容..."
      }
    ],
    "isError": false
  }
}

// 错误消息
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "details": "Path not found"
    }
  }
}

// 通知消息(无需响应)
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "abc123",
    "progress": 50,
    "total": 100
  }
}

协议流程

1. 初始化流程

┌─────────────────────────────────────────────────────────────┐
│                  MCP 初始化流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Client                                    Server          │
│     │                                         │             │
│     │ ────────── initialize ──────────────> │             │
│     │                                         │             │
│     │   {                                     │             │
│     │     "protocolVersion": "2024-11-05",   │             │
│     │     "capabilities": {                   │             │
│     │       "tools": {},                      │             │
│     │       "resources": {},                  │             │
│     │       "prompts": {}                     │             │
│     │     },                                  │             │
│     │     "clientInfo": {                     │             │
│     │       "name": "claude-desktop",        │             │
│     │       "version": "1.0.0"               │             │
│     │     }                                   │             │
│     │   }                                     │             │
│     │                                         │             │
│     │ <──────── initialized ──────────────── │             │
│     │                                         │             │
│     │   {                                     │             │
│     │     "protocolVersion": "2024-11-05",   │             │
│     │     "capabilities": {                   │             │
│     │       "tools": {                        │             │
│     │         "listChanged": true            │             │
│     │       },                                │             │
│     │       "resources": {                   │             │
│     │         "subscribe": true,            │             │
│     │         "listChanged": true            │             │
│     │       },                                │             │
│     │       "prompts": {                      │             │
│     │         "listChanged": true            │             │
│     │       }                                 │             │
│     │     },                                  │             │
│     │     "serverInfo": {                     │             │
│     │       "name": "filesystem-server",     │             │
│     │       "version": "1.0.0"               │             │
│     │     }                                   │             │
│     │   }                                     │             │
│     │                                         │             │
│     │ ────────── initialized ─────────────> │  (确认)      │
│     │                                         │             │
└─────────────────────────────────────────────────────────────┘

2. 能力协商

# capability_negotiation.py
from typing import Dict, Any, Optional
from dataclasses import dataclass

@dataclass
class ClientCapabilities:
    """客户端能力"""
    experimental: Optional[Dict[str, Any]] = None
    sampling: Optional[Dict] = None
    roots: Optional[Dict] = None

@dataclass
class ServerCapabilities:
    """服务端能力"""
    experimental: Optional[Dict[str, Any]] = None
    logging: Optional[Dict] = None
    prompts: Optional[Dict] = None
    resources: Optional[Dict] = None
    tools: Optional[Dict] = None

class CapabilityNegotiator:
    """能力协商器"""
    
    def __init__(self):
        self.client_caps: Optional[ClientCapabilities] = None
        self.server_caps: Optional[ServerCapabilities] = None
    
    def negotiate(
        self,
        client_caps: Dict,
        server_caps: Dict
    ) -> Dict:
        """执行能力协商"""
        
        negotiated = {
            "protocolVersion": self._select_version(
                client_caps.get("protocolVersion"),
                server_caps.get("protocolVersion")
            ),
            "capabilities": {}
        }
        
        # 协商工具能力
        if "tools" in client_caps.get("capabilities", {}):
            if "tools" in server_caps.get("capabilities", {}):
                negotiated["capabilities"]["tools"] = {
                    "listChanged": (
                        client_caps["capabilities"]["tools"].get("listChanged", False) and
                        server_caps["capabilities"]["tools"].get("listChanged", False)
                    )
                }
        
        # 协商资源能力
        if "resources" in client_caps.get("capabilities", {}):
            if "resources" in server_caps.get("capabilities", {}):
                negotiated["capabilities"]["resources"] = {
                    "subscribe": (
                        client_caps["capabilities"]["resources"].get("subscribe", False) and
                        server_caps["capabilities"]["resources"].get("subscribe", False)
                    ),
                    "listChanged": (
                        client_caps["capabilities"]["resources"].get("listChanged", False) and
                        server_caps["capabilities"]["resources"].get("listChanged", False)
                    )
                }
        
        return negotiated
    
    def _select_version(self, client_version: str, server_version: str) -> str:
        """选择共同支持的协议版本"""
        # 简单实现:选择较低的版本
        versions = [client_version, server_version]
        return min(versions)

核心原语

1. Resources(资源)

资源表示 Server 向 Client 暴露的数据源。

# resources.py
from typing import Optional, List
from dataclasses import dataclass

@dataclass
class Resource:
    """资源定义"""
    uri: str                    # 唯一标识符
    name: str                   # 可读名称
    description: Optional[str]  # 描述
    mimeType: Optional[str]     # MIME 类型
    size: Optional[int]         # 大小(字节)

@dataclass
class TextResourceContents:
    """文本资源内容"""
    uri: str
    mimeType: Optional[str]
    text: str

@dataclass
class BlobResourceContents:
    """二进制资源内容"""
    uri: str
    mimeType: Optional[str]
    blob: str  # base64 编码

class ResourceManager:
    """资源管理器"""
    
    def __init__(self):
        self.resources: Dict[str, Resource] = {}
        self.subscribers: Dict[str, List[str]] = {}
    
    def register(self, resource: Resource):
        """注册资源"""
        self.resources[resource.uri] = resource
    
    def list_resources(self) -> List[Resource]:
        """列出所有资源"""
        return list(self.resources.values())
    
    def read_resource(self, uri: str) -> Optional[Union[TextResourceContents, BlobResourceContents]]:
        """读取资源内容"""
        resource = self.resources.get(uri)
        if not resource:
            return None
        
        # 实际实现中根据 URI 读取内容
        # ...
        
    def subscribe(self, uri: str, client_id: str):
        """订阅资源更新"""
        if uri not in self.subscribers:
            self.subscribers[uri] = []
        self.subscribers[uri].append(client_id)
    
    def notify_update(self, uri: str):
        """通知订阅者资源更新"""
        if uri in self.subscribers:
            for client_id in self.subscribers[uri]:
                # 发送通知
                pass

2. Tools(工具)

工具是 Server 向 Client 暴露的可执行功能。

# tools.py
from typing import Dict, Any, Optional, List, Callable
from dataclasses import dataclass
import json

@dataclass
class Tool:
    """工具定义"""
    name: str
    description: str
    inputSchema: Dict[str, Any]  # JSON Schema

@dataclass
class CallToolRequest:
    """工具调用请求"""
    name: str
    arguments: Optional[Dict[str, Any]]

@dataclass
class CallToolResult:
    """工具调用结果"""
    content: List[Dict[str, Any]]
    isError: bool = False

class ToolRegistry:
    """工具注册表"""
    
    def __init__(self):
        self.tools: Dict[str, Tool] = {}
        self.handlers: Dict[str, Callable] = {}
    
    def register(
        self,
        name: str,
        description: str,
        input_schema: Dict[str, Any],
        handler: Callable
    ):
        """注册工具"""
        self.tools[name] = Tool(
            name=name,
            description=description,
            inputSchema=input_schema
        )
        self.handlers[name] = handler
    
    def list_tools(self) -> List[Tool]:
        """列出所有工具"""
        return list(self.tools.values())
    
    def call(self, request: CallToolRequest) -> CallToolResult:
        """调用工具"""
        handler = self.handlers.get(request.name)
        if not handler:
            return CallToolResult(
                content=[{
                    "type": "text",
                    "text": f"Tool '{request.name}' not found"
                }],
                isError=True
            )
        
        try:
            # 验证参数
            self._validate_arguments(request.name, request.arguments)
            
            # 执行工具
            result = handler(**(request.arguments or {}))
            
            # 格式化结果
            return self._format_result(result)
            
        except Exception as e:
            return CallToolResult(
                content=[{
                    "type": "text",
                    "text": f"Error: {str(e)}"
                }],
                isError=True
            )
    
    def _validate_arguments(self, tool_name: str, arguments: Optional[Dict]):
        """验证参数"""
        tool = self.tools.get(tool_name)
        if not tool:
            raise ValueError(f"Tool '{tool_name}' not found")
        
        schema = tool.inputSchema
        required = schema.get("required", [])
        
        for param in required:
            if not arguments or param not in arguments:
                raise ValueError(f"Missing required parameter: {param}")
    
    def _format_result(self, result: Any) -> CallToolResult:
        """格式化结果"""
        if isinstance(result, str):
            return CallToolResult(
                content=[{"type": "text", "text": result}]
            )
        elif isinstance(result, dict):
            return CallToolResult(
                content=[{"type": "text", "text": json.dumps(result, ensure_ascii=False)}]
            )
        elif isinstance(result, list):
            content = []
            for item in result:
                if isinstance(item, dict) and item.get("type") in ["text", "image", "resource"]:
                    content.append(item)
                else:
                    content.append({"type": "text", "text": str(item)})
            return CallToolResult(content=content)
        else:
            return CallToolResult(
                content=[{"type": "text", "text": str(result)}]
            )

3. Prompts(提示)

提示是 Server 向 Client 提供的预定义模板。

# prompts.py
from typing import Optional, List, Dict, Any
from dataclasses import dataclass

@dataclass
class Prompt:
    """提示定义"""
    name: str
    description: Optional[str]
    arguments: Optional[List[Dict[str, Any]]]  # 参数定义

@dataclass
class PromptMessage:
    """提示消息"""
    role: str  # "user" | "assistant"
    content: Dict[str, Any]

class PromptManager:
    """提示管理器"""
    
    def __init__(self):
        self.prompts: Dict[str, Prompt] = {}
        self.templates: Dict[str, Callable] = {}
    
    def register(
        self,
        name: str,
        description: str,
        arguments: Optional[List[Dict]] = None,
        template_func: Callable = None
    ):
        """注册提示"""
        self.prompts[name] = Prompt(
            name=name,
            description=description,
            arguments=arguments
        )
        self.templates[name] = template_func
    
    def get_prompt(self, name: str, arguments: Optional[Dict] = None) -> List[PromptMessage]:
        """获取提示内容"""
        template_func = self.templates.get(name)
        if not template_func:
            return []
        
        return template_func(**(arguments or {}))

安全模型

1. 权限控制

# security.py
from enum import Enum
from typing import Set, Dict, Optional

class PermissionLevel(Enum):
    """权限级别"""
    NONE = 0
    READ = 1
    WRITE = 2
    EXECUTE = 3
    ADMIN = 4

class SecurityPolicy:
    """安全策略"""
    
    def __init__(self):
        self.resource_permissions: Dict[str, PermissionLevel] = {}
        self.tool_permissions: Dict[str, PermissionLevel] = {}
        self.allowed_roots: Set[str] = set()
    
    def check_resource_access(self, uri: str, level: PermissionLevel) -> bool:
        """检查资源访问权限"""
        # 检查是否在允许的根目录下
        if not any(uri.startswith(root) for root in self.allowed_roots):
            return False
        
        # 检查具体权限
        current_level = self.resource_permissions.get(uri, PermissionLevel.NONE)
        return current_level.value >= level.value
    
    def check_tool_execution(self, tool_name: str) -> bool:
        """检查工具执行权限"""
        level = self.tool_permissions.get(tool_name, PermissionLevel.NONE)
        return level.value >= PermissionLevel.EXECUTE.value
    
    def configure_from_capabilities(self, capabilities: Dict):
        """从能力配置安全策略"""
        # 根据协商后的能力设置权限
        pass

2. 请求验证

# request_validation.py
import jsonschema
from typing import Dict, Any

class RequestValidator:
    """请求验证器"""
    
    # JSON-RPC 2.0 Schema
    JSONRPC_SCHEMA = {
        "type": "object",
        "required": ["jsonrpc"],
        "properties": {
            "jsonrpc": {"const": "2.0"},
            "id": {"type": ["string", "integer", "null"]},
            "method": {"type": "string"},
            "params": {"type": ["object", "array"]},
            "result": {},
            "error": {
                "type": "object",
                "required": ["code", "message"],
                "properties": {
                    "code": {"type": "integer"},
                    "message": {"type": "string"},
                    "data": {}
                }
            }
        }
    }
    
    @staticmethod
    def validate_request(request: Dict[str, Any]) -> bool:
        """验证请求格式"""
        try:
            jsonschema.validate(request, RequestValidator.JSONRPC_SCHEMA)
            
            # 请求必须有 method
            if "method" not in request:
                return False
            
            # 不能同时有 result 和 error
            if "result" in request and "error" in request:
                return False
            
            return True
        except jsonschema.ValidationError:
            return False
    
    @staticmethod
    def validate_tool_arguments(
        arguments: Dict[str, Any],
        schema: Dict[str, Any]
    ) -> tuple[bool, Optional[str]]:
        """验证工具参数"""
        try:
            jsonschema.validate(arguments, schema)
            return True, None
        except jsonschema.ValidationError as e:
            return False, str(e)

错误处理

# errors.py
from enum import IntEnum

class MCPErrorCode(IntEnum):
    """MCP 错误代码"""
    # 标准 JSON-RPC 错误
    PARSE_ERROR = -32700
    INVALID_REQUEST = -32600
    METHOD_NOT_FOUND = -32601
    INVALID_PARAMS = -32602
    INTERNAL_ERROR = -32603
    
    # MCP 特定错误
    RESOURCE_NOT_FOUND = -32001
    RESOURCE_UPDATE_FAILED = -32002
    TOOL_EXECUTION_FAILED = -32003
    TOOL_NOT_FOUND = -32004
    PROMPT_NOT_FOUND = -32005
    PROMPT_RENDERING_FAILED = -32006

class MCPError(Exception):
    """MCP 错误"""
    
    def __init__(
        self,
        code: MCPErrorCode,
        message: str,
        data: Optional[Dict] = None
    ):
        self.code = code
        self.message = message
        self.data = data
        super().__init__(message)
    
    def to_jsonrpc_error(self) -> Dict:
        """转换为 JSON-RPC 错误格式"""
        error = {
            "code": self.code,
            "message": self.message
        }
        if self.data:
            error["data"] = self.data
        return error

最佳实践

1. Server 设计原则

# server_best_practices.py
class MCPServerDesign:
    """MCP Server 设计最佳实践"""
    
    PRINCIPLES = {
        "granularity": {
            "title": "原子性原则",
            "description": "工具应该小而专注,而非大而全",
            "example": {
                "good": ["read_file", "write_file", "list_directory"],
                "bad": ["file_operations"]
            }
        },
        "discoverability": {
            "title": "可发现性原则",
            "description": "提供清晰的描述和 Schema",
            "tips": [
                "工具名称应该自描述",
                "描述应该说明用途和限制",
                "参数应该有明确的描述"
            ]
        },
        "statelessness": {
            "title": "无状态原则",
            "description": "工具调用应该是幂等的",
            "note": "除非有明确的会话管理需求"
        },
        "error_handling": {
            "title": "错误处理原则",
            "description": "提供详细的错误信息",
            "tips": [
                "区分用户错误和系统错误",
                "提供可操作的错误消息",
                "使用适当的错误代码"
            ]
        }
    }

2. 性能优化

# performance_optimization.py
class MCPPerformanceTips:
    """MCP 性能优化建议"""
    
    TIPS = {
        "connection_pooling": {
            "description": "使用连接池管理 Client-Server 连接",
            "implementation": "保持长连接,避免频繁创建销毁"
        },
        "caching": {
            "description": "缓存资源列表和工具列表",
            "ttl": "根据数据更新频率设置合理的 TTL"
        },
        "streaming": {
            "description": "大文件传输使用流式处理",
            "benefit": "减少内存占用"
        },
        "batching": {
            "description": "批量处理请求",
            "note": "注意 MCP 协议本身不支持批量调用,需要在应用层实现"
        }
    }

总结

MCP 的核心概念:

  1. 协议分层:传输层 → 协议层 → 应用层
  2. 三大原语:Resources、Tools、Prompts
  3. 能力协商:Client 和 Server 动态协商功能
  4. 安全模型:细粒度的权限控制

设计要点:

  • 工具设计要原子化
  • 资源 URI 要规范化
  • 错误信息要详细
  • 性能要考虑缓存

与 Function Calling 的关系:

  • MCP 是协议层,Function Calling 是模型能力
  • MCP 更适合桌面/IDE 场景
  • Function Calling 更适合 API 服务
  • 两者可以结合使用

相关资源: