MCP入门教程-第1章:MCP 核心架构 (Architecture)

850 阅读5分钟

概述

Model Context Protocol (MCP) 是基于灵活、可扩展架构构建的开放标准,它实现了 LLM 应用程序与集成服务之间的无缝通信。本章将详细介绍 MCP 的核心架构组件和设计理念。

1.1 客户端-服务器架构

MCP 遵循标准的客户端-服务器架构模式:

架构组件

  • 主机 (Hosts) : LLM 应用程序(如 Claude Desktop 或 IDE),负责发起连接
  • 客户端 (Clients) : 在主机应用程序内部,与服务器维持 1:1 连接关系
  • 服务器 (Servers) : 向客户端提供上下文、工具和提示的服务提供者

架构图示

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   LLM 应用      │    │   MCP 客户端    │    │   MCP 服务器    │
│   (主机)        │◄──►│                 │◄──►│                 │
│                 │    │  - 协议管理     │    │  - 资源提供     │
│  - Claude       │    │  - 连接维护     │    │  - 工具执行     │
│  - IDE          │    │  - 消息路由     │    │  - 提示模板     │
│  - AI Agent     │    │                 │    │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘

1.2 协议层 (Protocol Layer)

协议层负责处理消息帧、请求/响应链接和高级通信模式。

TypeScript SDK 核心类

import { Protocol, Client, Server } from '@modelcontextprotocol/sdk';

// 协议处理器
interface ProtocolOptions {
  timeout?: number;
  maxRetries?: number;
  logging?: boolean;
}

class MCPProtocol {
  private options: ProtocolOptions;

  constructor(options: ProtocolOptions = {}) {
    this.options = {
      timeout: 30000,
      maxRetries: 3,
      logging: false,
      ...options
    };
  }

  // 处理消息帧
  handleFrame(frame: any): void {
    // 消息验证和路由
  }

  // 链接请求和响应
  linkRequestResponse(requestId: string, response: any): void {
    // 请求响应匹配逻辑
  }
}

// 客户端实现
class MCPClient extends Protocol {
  private connectionId: string;
  private capabilities: ClientCapabilities;

  constructor(info: ClientInfo) {
    super();
    this.connectionId = crypto.randomUUID();
    this.capabilities = {
      experimental: {},
      sampling: {}
    };
  }

  // 连接到服务器
  async connect(transport: Transport): Promise<void> {
    await this.init(transport);
    await this.initialize();
  }

  // 初始化协议
  private async initialize(): Promise<InitializeResult> {
    const request: InitializeRequest = {
      protocolVersion: "2024-11-05",
      capabilities: this.capabilities,
      clientInfo: {
        name: "my-mcp-client",
        version: "1.0.0"
      }
    };

    const result = await this.request(
      { method: "initialize", params: request },
      InitializeResultSchema
    );

    // 发送初始化完成通知
    await this.notification({
      method: "notifications/initialized"
    });

    return result;
  }
}

// 服务器实现
class MCPServer extends Protocol {
  private tools = new Map<string, Tool>();
  private resources = new Map<string, Resource>();
  private prompts = new Map<string, Prompt>();

  constructor(info: ServerInfo) {
    super();
    this.setRequestHandler(InitializeRequestSchema, this.handleInitialize.bind(this));
    this.setRequestHandler(ListToolsRequestSchema, this.handleListTools.bind(this));
    this.setRequestHandler(CallToolRequestSchema, this.handleCallTool.bind(this));
  }

  // 处理初始化请求
  private async handleInitialize(
    request: InitializeRequest
  ): Promise<InitializeResult> {
    return {
      protocolVersion: "2024-11-05",
      capabilities: {
        tools: { listChanged: true },
        resources: { subscribe: true, listChanged: true },
        prompts: { listChanged: true },
        logging: {}
      },
      serverInfo: {
        name: "my-mcp-server",
        version: "1.0.0"
      }
    };
  }

  // 注册工具
  registerTool(name: string, description: string, handler: ToolHandler): void {
    const tool: Tool = {
      name,
      description,
      inputSchema: {
        type: "object",
        properties: {},
        required: []
      }
    };
    
    this.tools.set(name, tool);
    this.setRequestHandler(`tools/${name}`, handler);
  }
}

1.3 传输层 (Transport Layer)

传输层处理客户端和服务器之间的实际通信,所有传输都使用 JSON-RPC 2.0 协议。

Stdio 传输

import { StdioTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { spawn } from 'child_process';

// Stdio 传输 - 适用于本地进程通信
class StdioTransportExample {
  async createStdioConnection(): Promise<void> {
    // 启动服务器进程
    const serverProcess = spawn('node', ['./my-server.js'], {
      stdio: ['pipe', 'pipe', 'inherit']
    });

    // 创建 Stdio 传输
    const transport = new StdioTransport({
      stdin: serverProcess.stdin!,
      stdout: serverProcess.stdout!
    });

    // 创建客户端并连接
    const client = new MCPClient({
      name: "stdio-client",
      version: "1.0.0"
    });

    await client.connect(transport);

    // 使用连接
    const tools = await client.listTools();
    console.log('可用工具:', tools);

    // 清理资源
    process.on('exit', () => {
      serverProcess.kill();
    });
  }
}

HTTP with SSE 传输

import { SSETransport } from '@modelcontextprotocol/sdk/client/sse.js';

// HTTP with SSE 传输 - 适用于远程通信
class SSETransportExample {
  async createSSEConnection(): Promise<void> {
    // 创建 SSE 传输
    const transport = new SSETransport({
      url: 'http://localhost:3000/mcp',
      headers: {
        'Authorization': 'Bearer your-token-here',
        'Content-Type': 'application/json'
      }
    });

    // 创建客户端并连接
    const client = new MCPClient({
      name: "sse-client",
      version: "1.0.0"
    });

    await client.connect(transport);

    // 处理连接事件
    transport.onClose = () => {
      console.log('SSE 连接已关闭');
    };

    transport.onError = (error) => {
      console.error('SSE 连接错误:', error);
    };
  }
}

1.4 消息类型

MCP 定义了四种主要消息类型:

消息类型定义

// JSON-RPC 2.0 基础消息结构
interface JSONRPCMessage {
  jsonrpc: "2.0";
  id?: string | number | null;
}

// 1. 请求消息 - 期待响应
interface JSONRPCRequest extends JSONRPCMessage {
  method: string;
  params?: any;
  id: string | number;
}

// 2. 结果消息 - 成功响应
interface JSONRPCResult extends JSONRPCMessage {
  result: any;
  id: string | number;
}

// 3. 错误消息 - 失败响应
interface JSONRPCError extends JSONRPCMessage {
  error: {
    code: number;
    message: string;
    data?: any;
  };
  id: string | number | null;
}

// 4. 通知消息 - 单向消息
interface JSONRPCNotification extends JSONRPCMessage {
  method: string;
  params?: any;
}

// 消息处理器示例
class MessageHandler {
  // 处理请求
  async handleRequest(request: JSONRPCRequest): Promise<JSONRPCResult | JSONRPCError> {
    try {
      const result = await this.processRequest(request.method, request.params);
      return {
        jsonrpc: "2.0",
        result,
        id: request.id
      };
    } catch (error) {
      return {
        jsonrpc: "2.0",
        error: {
          code: -32603,
          message: "Internal error",
          data: error instanceof Error ? error.message : String(error)
        },
        id: request.id
      };
    }
  }

  // 发送通知
  sendNotification(method: string, params?: any): void {
    const notification: JSONRPCNotification = {
      jsonrpc: "2.0",
      method,
      params
    };
    
    this.transport.send(notification);
  }

  private async processRequest(method: string, params: any): Promise<any> {
    switch (method) {
      case "initialize":
        return this.handleInitialize(params);
      case "tools/list":
        return this.handleListTools(params);
      case "tools/call":
        return this.handleCallTool(params);
      default:
        throw new Error(`未知方法: ${method}`);
    }
  }
}

1.5 连接生命周期

MCP 连接遵循标准的生命周期管理:

生命周期实现

class ConnectionManager {
  private state: 'disconnected' | 'connecting' | 'initializing' | 'connected' | 'closing' = 'disconnected';
  private client: MCPClient;
  private transport: Transport;

  constructor(client: MCPClient) {
    this.client = client;
  }

  // 1. 初始化阶段
  async initialize(transport: Transport): Promise<void> {
    this.state = 'connecting';
    this.transport = transport;

    try {
      // 建立传输连接
      await this.transport.connect();
      
      this.state = 'initializing';

      // 发送初始化请求
      const initRequest: InitializeRequest = {
        protocolVersion: "2024-11-05",
        capabilities: {
          experimental: {},
          sampling: {}
        },
        clientInfo: {
          name: "typescript-client",
          version: "1.0.0"
        }
      };

      const initResult = await this.client.request(
        { method: "initialize", params: initRequest },
        InitializeResultSchema
      );

      console.log('服务器信息:', initResult.serverInfo);
      console.log('服务器能力:', initResult.capabilities);

      // 发送初始化完成通知
      await this.client.notification({
        method: "notifications/initialized"
      });

      this.state = 'connected';
      console.log('连接初始化完成');

    } catch (error) {
      this.state = 'disconnected';
      throw new Error(`初始化失败: ${error}`);
    }
  }

  // 2. 消息交换阶段
  async messageExchange(): Promise<void> {
    if (this.state !== 'connected') {
      throw new Error('连接未初始化');
    }

    // 请求-响应模式示例
    const tools = await this.client.listTools();
    console.log('可用工具:', tools.tools);

    // 调用工具
    if (tools.tools.length > 0) {
      const result = await this.client.callTool({
        name: tools.tools[0].name,
        arguments: {}
      });
      console.log('工具执行结果:', result);
    }

    // 通知模式示例
    await this.client.notification({
      method: "notifications/progress",
      params: {
        progress: 50,
        total: 100
      }
    });
  }

  // 3. 终止阶段
  async close(): Promise<void> {
    if (this.state === 'disconnected' || this.state === 'closing') {
      return;
    }

    this.state = 'closing';

    try {
      // 发送关闭通知
      await this.client.notification({
        method: "notifications/cancelled"
      });

      // 关闭传输
      await this.transport.close();

      this.state = 'disconnected';
      console.log('连接已关闭');

    } catch (error) {
      console.error('关闭连接时出错:', error);
      this.state = 'disconnected';
    }
  }

  // 获取连接状态
  getState(): string {
    return this.state;
  }

  // 检查连接是否活跃
  isConnected(): boolean {
    return this.state === 'connected';
  }
}

1.6 错误处理

MCP 定义了标准错误代码和处理机制:

错误处理实现

// MCP 标准错误代码
enum MCPErrorCode {
  // JSON-RPC 标准錯誤
  PARSE_ERROR = -32700,
  INVALID_REQUEST = -32600,
  METHOD_NOT_FOUND = -32601,
  INVALID_PARAMS = -32602,
  INTERNAL_ERROR = -32603,
  
  // MCP 特定錯誤
  RESOURCE_NOT_FOUND = -32001,
  TOOL_NOT_FOUND = -32002,
  INVALID_TOOL_INPUT = -32003,
  UNAUTHORIZED = -32004,
  RATE_LIMITED = -32005
}

class ErrorHandler {
  // 创建标准错误响应
  static createError(code: MCPErrorCode, message: string, data?: any): JSONRPCError {
    return {
      jsonrpc: "2.0",
      error: {
        code,
        message,
        data
      },
      id: null
    };
  }

  // 错误处理中间件
  static wrapHandler<T, R>(
    handler: (params: T) => Promise<R>
  ): (params: T) => Promise<R | JSONRPCError> {
    return async (params: T) => {
      try {
        return await handler(params);
      } catch (error) {
        if (error instanceof MCPError) {
          throw error;
        }

        // 未知错误转换为内部错误
        throw new MCPError(
          MCPErrorCode.INTERNAL_ERROR,
          "Internal server error",
          error instanceof Error ? error.message : String(error)
        );
      }
    };
  }

  // 传输层错误处理
  static handleTransportError(error: any): void {
    console.error('传输错误:', error);
    
    // 根据错误类型进行不同处理
    if (error.code === 'ECONNREFUSED') {
      console.error('连接被拒绝,请检查服务器是否运行');
    } else if (error.code === 'ETIMEDOUT') {
      console.error('连接超时');
    } else {
      console.error('未知传输错误:', error.message);
    }
  }
}

// 自定义 MCP 错误类
class MCPError extends Error {
  public code: MCPErrorCode;
  public data?: any;

  constructor(code: MCPErrorCode, message: string, data?: any) {
    super(message);
    this.name = 'MCPError';
    this.code = code;
    this.data = data;
  }

  toJSONRPCError(id: string | number | null = null): JSONRPCError {
    return {
      jsonrpc: "2.0",
      error: {
        code: this.code,
        message: this.message,
        data: this.data
      },
      id
    };
  }
}

1.7 完整示例

以下是一个完整的 MCP 客户端-服务器交互示例:

完整的客户端实现

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { spawn } from 'child_process';

class CompleteMCPClient {
  private client: Client;
  private transport: StdioClientTransport;

  async start(): Promise<void> {
    try {
      // 1. 启动服务器进程
      const serverProcess = spawn('node', ['./server.js'], {
        stdio: ['pipe', 'pipe', 'inherit']
      });

      // 2. 创建传输
      this.transport = new StdioClientTransport({
        stdin: serverProcess.stdin!,
        stdout: serverProcess.stdout!
      });

      // 3. 创建客户端
      this.client = new Client({
        name: "complete-client",
        version: "1.0.0"
      }, {
        capabilities: {
          experimental: {},
          sampling: {}
        }
      });

      // 4. 连接到服务器
      await this.client.connect(this.transport);

      console.log('✅ 客户端连接成功');

      // 5. 进行交互
      await this.interact();

    } catch (error) {
      console.error('❌ 客户端启动失败:', error);
    }
  }

  private async interact(): Promise<void> {
    try {
      // 列出可用工具
      const tools = await this.client.listTools();
      console.log('📋 可用工具:', tools.tools.map(t => t.name));

      // 列出可用资源
      const resources = await this.client.listResources();
      console.log('📁 可用资源:', resources.resources.map(r => r.name));

      // 列出可用提示
      const prompts = await this.client.listPrompts();
      console.log('💡 可用提示:', prompts.prompts.map(p => p.name));

      // 调用工具示例
      if (tools.tools.length > 0) {
        const result = await this.client.callTool({
          name: tools.tools[0].name,
          arguments: { message: "Hello from client!" }
        });
        console.log('🔧 工具执行结果:', result.content);
      }

    } catch (error) {
      console.error('❌ 交互过程中出错:', error);
    }
  }

  async close(): Promise<void> {
    if (this.client) {
      await this.client.close();
      console.log('👋 客户端已关闭');
    }
  }
}

// 使用示例
const client = new CompleteMCPClient();
client.start().then(() => {
  // 程序退出时清理资源
  process.on('SIGINT', async () => {
    await client.close();
    process.exit(0);
  });
});

小结

本章详细介绍了 MCP 的核心架构,包括:

  1. 客户端-服务器架构:清晰的角色分工和通信模式
  2. 协议层:消息处理和通信管理的核心组件
  3. 传输层:支持多种传输机制(Stdio、HTTP+SSE)
  4. 消息类型:四种标准 JSON-RPC 2.0 消息格式
  5. 连接生命周期:完整的连接建立、使用和关闭流程
  6. 错误处理:标准化的错误代码和处理机制

理解这些核心概念是有效使用 MCP 的基础。在接下来的章节中,我们将深入探讨 MCP 的具体功能模块:资源、提示、工具、采样、根和传输的详细实现。