一、架构概览

1.1 MCP

Model Context Protocol(MCP)旨在解决 AI 应用与外部数据源/工具集成的 “N×M 问题”。它就像 AI 世界的"USB-C 接口"——任何符合 MCP 标准的 AI 应用都可以无缝连接任何符合标准的工具服务,无需为每个集成编写定制代码。

1.2 协议架构的三层模型

🖥️ Server(服务提供方)

🔌 Client(连接器)

JSON-RPC 2.0
Stateful Connection

JSON-RPC 2.0
Stateful Connection

JSON-RPC 2.0
Stateful Connection

管理多个

🏠 Host(宿主应用)

Claude Desktop

Cursor IDE

自定义 AI 应用

Client Instance 1

Client Instance 2

Client Instance 3

文件系统服务

GitHub 服务

数据库服务

核心角色说明:

角色 职责 示例
Host LLM 应用,负责协调多个 Client Claude Desktop、Cursor
Client 维持与 Server 的有状态连接 每个 Server 对应一个 Client 实例
Server 暴露具体能力(Tools/Resources/Prompts) 文件系统、API 服务、数据库

1.3 封装架构

📦 MCP Server Wrapper

1. 传输连接

2. 消息路由

3. 分发处理

传输层抽象

Standard I/O
本地进程

Streamable HTTP
远程服务
2025-11-25 标准

Server-Sent Events
兼容模式

协议处理层

JSON-RPC 2.0
消息处理器

能力协商
Capabilities

生命周期管理
Initialize/Shutdown

功能模块层

🔧 Tools
工具执行

📄 Resources
资源读取

💬 Prompts
提示模板

MCP Client


二、协议版本与规范更新

版本演进时间线

2024-11-05 初始稳定版 基础 Tools/Resources/Prompts 采样(Sampling)支持 2025-03-26 移除 JSON-RPC 批量处理 结构化工具输出 OAuth2 资源服务器分类 资源指示器(RFC 8707) 诱导(Elicitation)功能 2025-11-25 🎉 一周年版本(当前最新) 实验性 Tasks(异步任务) URL 模式诱导(安全外跳) OpenID Connect Discovery 增量范围同意 采样工具调用支持 MCP 协议版本演进

三、核心模块设计

3.1 项目结构

mcp-server-wrapper/
├── src/
│   ├── index.ts                    # 入口文件
│   ├── server/
│   │   ├── McpServer.ts            # 服务器主类
│   │   ├── ProtocolHandler.ts     # JSON-RPC 协议处理
│   │   ├── Transport.ts            # 传输层抽象(支持 Streamable HTTP)
│   │   └── TaskManager.ts          # 🆕 异步任务管理(2025-11-25)
│   ├── modules/
│   │   ├── ToolsModule.ts          # Tools 模块(支持结构化输出)
│   │   ├── ResourcesModule.ts      # Resources 模块(支持订阅)
│   │   ├── PromptsModule.ts        # Prompts 模块
│   │   └── SamplingModule.ts       # 🆕 采样请求处理
│   ├── types/
│   │   ├── index.ts                # 基础类型
│   │   ├── capabilities.ts         # 🆕 能力定义(2025-11-25)
│   │   └── tasks.ts                # 🆕 任务类型定义
│   ├── utils/
│   │   ├── logger.ts               # 日志工具(支持协议日志)
│   │   ├── validator.ts            # 输入验证(Zod 集成)
│   │   └── errors.ts               # 🆕 MCP 标准错误码
│   └── middleware/
│       ├── auth.ts                 # 🆕 OAuth2 中间件
│       └── rateLimit.ts            # 🆕 速率限制
├── examples/
│   ├── basic-stdio.ts              # 基础 stdio 示例
│   ├── http-server.ts              # 🆕 Streamable HTTP 示例
│   ├── async-tasks.ts              # 🆕 异步任务示例
│   └── elicitation-demo.ts         # 🆕 URL 诱导示例
└── package.json

3.2 核心类型定义

// src/types/capabilities.ts
// ⚠️ 关键修正:2025-11-25 完整能力定义

export interface McpCapabilities {
  // Server 能力
  tools?: {
    listChanged?: boolean;        // 支持工具列表变更通知
  };
  resources?: {
    subscribe?: boolean;          // 支持资源订阅(实时更新)
    listChanged?: boolean;        // 支持资源列表变更通知
  };
  prompts?: {
    listChanged?: boolean;        // 支持提示词列表变更通知
  };
  sampling?: {
    // 🆕 2025-11-25:采样能力需明确声明支持的工具调用
    supportedModels?: Array<{
      modelName: string;
      modelProvider: string;
    }>;
  };
  // 🆕 2025-11-25:实验性 Tasks 支持
  tasks?: {
    // 指示服务器支持异步任务执行
    supported?: boolean;
  };
  // 🆕 2025-11-25:诱导能力(安全采集用户信息)
  elicitation?: {
    form?: {};                    // 表单模式
    url?: {};                     // URL 外跳模式
  };
  // 🆕 2025-11-25:OAuth 支持
  oauth?: {
    providerMetadata?: string;    // .well-known/oauth-authorization-server URL
  };
  // 🆕 2025-11-25:图标元数据支持
  icons?: {
    // 服务器可声明图标资源
  };
}

// Client 能力(服务器需要了解客户端支持什么)
export interface ClientCapabilities {
  roots?: {
    listChanged?: boolean;        // 支持根目录列表变更通知
  };
  sampling?: {};                  // 客户端支持采样请求
  // ... 其他客户端能力
}

// Tool 定义修正:支持 2025-06-18 引入的结构化输出
export interface ToolDefinition {
  name: string;
  description: string;
  inputSchema: {
    type: 'object';
    properties: Record<string, any>;
    required?: string[];
  };
  // 🆕 2025-06-18:结构化输出模式(替代简单的文本返回)
  outputSchema?: {
    type: 'object';
    properties: Record<string, any>;
  };
  // 🆕 2025-11-25:工具图标
  icon?: {
    type: 'emoji' | 'url';
    value: string;
  };
  annotations?: {
    title?: string;
    readOnlyHint?: boolean;
    destructiveHint?: boolean;
    idempotentHint?: boolean;
    openWorldHint?: boolean;
    // 🆕 2025-11-25:性能提示
    timeoutHint?: number;           // 建议超时时间(毫秒)
    memoryHint?: number;          // 预估内存使用(MB)
  };
}

// 🆕 2025-11-25:实验性 Task 定义
export interface Task {
  id: string;
  status: 'queued' | 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
  createdAt: string;
  updatedAt: string;
  result?: any;
  error?: {
    code: number;
    message: string;
  };
  // 进度追踪
  progress?: {
    current: number;
    total: number;
    message?: string;
  };
}

// 标准错误码(原方案完全缺失)
export enum McpErrorCode {
  // JSON-RPC 标准错误
  ParseError = -32700,
  InvalidRequest = -32600,
  MethodNotFound = -32601,
  InvalidParams = -32602,
  InternalError = -32603,
  
  // MCP 特定错误(2025-11-25)
  ResourceNotFound = -32002,
  ResourceAccessDenied = -32003,
  ToolNotFound = -32004,
  ToolExecutionError = -32005,
  URLElicitationRequired = -32042,  // 🆕 URL 诱导必需
  TaskNotFound = -32050,            // 🆕 任务相关
  TaskCancelled = -32051,
}

3.3 MCP 服务器主类

// src/server/McpServer.ts
import { 
  McpCapabilities, 
  ClientCapabilities,
  ToolDefinition, 
  ResourceDefinition, 
  PromptDefinition,
  Task,
  McpErrorCode 
} from '../types';
import { ProtocolHandler } from './ProtocolHandler';
import { Transport, StdioTransport, StreamableHTTPTransport } from './Transport';
import { EventEmitter } from 'events';

export interface ServerOptions {
  name: string;
  version: string;
  transport?: 'stdio' | 'http';
  port?: number;                    // HTTP 模式端口
  capabilities?: McpCapabilities;
}

export class McpServer extends EventEmitter {
  private protocol: ProtocolHandler;
  private transport: Transport;
  private options: ServerOptions;    // 🐛 原方案缺失:保存配置
  private clientCapabilities?: ClientCapabilities;
  
  // 存储注册项
  private tools = new Map<string, ToolDefinition & { handler: Function }>();
  private resources = new Map<string, ResourceDefinition & { handler: Function }>();
  private resourceTemplates = new Map<string, { pattern: RegExp; handler: Function }>();
  private prompts = new Map<string, PromptDefinition & { handler: Function }>();
  
  // 🆕 2025-11-25:任务管理
  private tasks = new Map<string, Task>();
  private taskSubscriptions = new Map<string, Set<string>>(); // taskId -> clientIds

  constructor(options: ServerOptions) {
    super();
    this.options = options;
    
    // 初始化传输层
    if (options.transport === 'http') {
      this.transport = new StreamableHTTPTransport(options.port || 3000);
    } else {
      this.transport = new StdioTransport();
    }
    
    this.protocol = new ProtocolHandler(this);
    this.setupHandlers();
  }

  private setupHandlers() {
    // 生命周期
    this.protocol.on('initialize', this.handleInitialize.bind(this));
    this.protocol.on('initialized', this.handleInitialized.bind(this));
    
    // Tools
    this.protocol.on('tools/list', this.handleToolsList.bind(this));
    this.protocol.on('tools/call', this.handleToolsCall.bind(this));
    
    // Resources
    this.protocol.on('resources/list', this.handleResourcesList.bind(this));
    this.protocol.on('resources/read', this.handleResourcesRead.bind(this));
    this.protocol.on('resources/subscribe', this.handleResourcesSubscribe.bind(this));
    this.protocol.on('resources/unsubscribe', this.handleResourcesUnsubscribe.bind(this));
    
    // Prompts
    this.protocol.on('prompts/list', this.handlePromptsList.bind(this));
    this.protocol.on('prompts/get', this.handlePromptsGet.bind(this));
    
    // 🆕 2025-11-25:Tasks(实验性)
    this.protocol.on('tasks/create', this.handleTaskCreate.bind(this));
    this.protocol.on('tasks/get', this.handleTaskGet.bind(this));
    this.protocol.on('tasks/cancel', this.handleTaskCancel.bind(this));
    
    // 🆕 2025-11-25:Sampling(当 Client 支持时)
    this.protocol.on('sampling/createMessage', this.handleSampling.bind(this));
    
    // 日志
    this.protocol.on('logging/setLevel', this.handleSetLogLevel.bind(this));
  }

  // ========== 注册方法(链式调用) ==========
  
  registerTool(tool: ToolDefinition, handler: Function) {
    this.tools.set(tool.name, { ...tool, handler });
    this.emit('toolsChanged');
    return this;
  }

  registerResource(resource: ResourceDefinition, handler: Function) {
    this.resources.set(resource.uri, { ...resource, handler });
    this.emit('resourcesChanged');
    return this;
  }

  /**
   * 🆕 资源模板注册(支持动态 URI)
   * 例如:file:///{path} 匹配 file:///home/user/doc.txt
   */
  registerResourceTemplate(
    template: string,  // 如 "file:///{path}"
    meta: Omit<ResourceDefinition, 'uri'>,
    handler: (params: Record<string, string>) => Promise<any>
  ) {
    // 将模板转换为正则表达式
    const pattern = new RegExp(
      '^' + template.replace(/\{(\w+)\}/g, '(?<$1>[^/]+)') + '$'
    );
    this.resourceTemplates.set(template, { pattern, handler });
    return this;
  }

  registerPrompt(prompt: PromptDefinition, handler: Function) {
    this.prompts.set(prompt.name, { ...prompt, handler });
    this.emit('promptsChanged');
    return this;
  }

  // ========== 核心协议处理 ==========

  private async handleInitialize(params: {
    protocolVersion: string;
    capabilities: ClientCapabilities;
    clientInfo: { name: string; version: string };
  }) {
    this.clientCapabilities = params.capabilities;
    
    // 协议版本协商:优先使用客户端版本,但不超过服务器支持的最大版本
    const supportedVersions = ['2025-11-25', '2025-03-26', '2024-11-05'];
    const negotiatedVersion = supportedVersions.find(v => v === params.protocolVersion) 
      || '2025-03-26'; // 默认回退

    return {
      protocolVersion: negotiatedVersion,
      capabilities: this.options.capabilities || this.inferCapabilities(),
      serverInfo: {
        name: this.options.name,
        version: this.options.version
      }
    };
  }

  private async handleToolsCall(params: { 
    name: string; 
    arguments?: object;
    // 🆕 2025-11-25:任务提示(用于长时间运行工具)
    taskHint?: boolean;
  }) {
    const tool = this.tools.get(params.name);
    if (!tool) {
      throw this.createError(McpErrorCode.ToolNotFound, `Unknown tool: ${params.name}`);
    }

    try {
      // 如果工具可能长时间运行且客户端支持 Tasks,创建异步任务
      if (params.taskHint && this.clientCapabilities && tool.annotations?.timeoutHint && tool.annotations.timeoutHint > 5000) {
        return await this.executeAsTask(tool, params.arguments);
      }

      const result = await tool.handler(params.arguments);
      
      // 🆕 支持结构化输出(2025-06-18)
      if (tool.outputSchema) {
        return { output: result };  // 结构化输出
      }
      
      // 传统文本输出
      return {
        content: Array.isArray(result) ? result : [{ type: 'text', text: String(result) }],
        isError: false
      };
    } catch (error) {
      throw this.createError(
        McpErrorCode.ToolExecutionError,
        error instanceof Error ? error.message : 'Tool execution failed'
      );
    }
  }

  /**
   * 🆕 2025-11-25:异步任务执行
   */
  private async executeAsTask(tool: any, args: any): Promise<{ taskId: string }> {
    const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    const task: Task = {
      id: taskId,
      status: 'queued',
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString()
    };
    
    this.tasks.set(taskId, task);
    
    // 异步执行
    setImmediate(async () => {
      task.status = 'working';
      task.updatedAt = new Date().toISOString();
      
      try {
        const result = await tool.handler(args);
        task.status = 'completed';
        task.result = result;
        task.updatedAt = new Date().toISOString();
        
        // 通知订阅者
        this.notifyTaskUpdate(taskId);
      } catch (error) {
        task.status = 'failed';
        task.error = {
          code: McpErrorCode.ToolExecutionError,
          message: error instanceof Error ? error.message : 'Execution failed'
        };
        task.updatedAt = new Date().toISOString();
        this.notifyTaskUpdate(taskId);
      }
    });
    
    return { taskId };
  }

  // ========== 通知方法(修正版) ==========

  notifyToolsListChanged() {
    // 🐛 原方案错误:缺少 jsonrpc 字段
    this.transport.send({
      jsonrpc: '2.0',
      method: 'notifications/tools/list_changed',
      params: {}
    });
  }

  notifyResourcesListChanged() {
    this.transport.send({
      jsonrpc: '2.0',
      method: 'notifications/resources/list_changed',
      params: {}
    });
  }

  private notifyTaskUpdate(taskId: string) {
    const task = this.tasks.get(taskId);
    if (!task) return;
    
    this.transport.send({
      jsonrpc: '2.0',
      method: 'notifications/tasks/updated',
      params: { task }
    });
  }

  // ========== 辅助方法 ==========

  private createError(code: number, message: string) {
    return { code, message };
  }

  private inferCapabilities(): McpCapabilities {
    return {
      tools: { listChanged: true },
      resources: { 
        listChanged: true, 
        subscribe: true  // 假设支持订阅
      },
      prompts: { listChanged: true }
    };
  }

  async start() {
    await this.transport.connect();
    this.transport.onMessage((msg) => this.protocol.handleMessage(msg));
    console.error(`✅ MCP Server "${this.options.name}" v${this.options.version} started`);
  }
}

3.4 协议处理器

// src/server/ProtocolHandler.ts
import { JsonRpcRequest, JsonRpcResponse, JsonRpcNotification } from '../types';
import { McpServer } from './McpServer';
import { EventEmitter } from 'events';

export class ProtocolHandler extends EventEmitter {
  private handlers = new Map<string, Function>();
  private server: McpServer;

  constructor(server: McpServer) {
    super();
    this.server = server;
  }

  on(method: string, handler: Function) {
    this.handlers.set(method, handler);
  }

  async handleMessage(message: string) {
    let requestId: string | number | undefined;
    
    try {
      const data = JSON.parse(message);
      requestId = data.id;
      
      // 验证 JSON-RPC 格式
      if (data.jsonrpc !== '2.0') {
        throw this.createError(-32600, 'Invalid JSON-RPC version');
      }

      // 区分请求和通知
      if (data.id === undefined) {
        // 通知(Notification):无需回复
        await this.handleNotification(data as JsonRpcNotification);
      } else {
        // 请求(Request):必须回复
        const response = await this.handleRequest(data as JsonRpcRequest);
        this.server['transport'].send(JSON.stringify(response));
      }
    } catch (error) {
      // 🐛 原方案错误:错误时无法获取 id,现已修正
      const errorResponse: JsonRpcResponse = {
        jsonrpc: '2.0',
        id: requestId,  // 现在可以正确获取
        error: {
          code: error.code || -32603,
          message: error.message || 'Internal error'
        }
      };
      this.server['transport'].send(JSON.stringify(errorResponse));
    }
  }

  private async handleRequest(request: JsonRpcRequest): Promise<JsonRpcResponse> {
    const handler = this.handlers.get(request.method);
    if (!handler) {
      throw this.createError(-32601, `Method not found: ${request.method}`);
    }

    const result = await handler(request.params);
    
    return {
      jsonrpc: '2.0',
      id: request.id,
      result: result || {}
    };
  }

  private async handleNotification(notification: JsonRpcNotification) {
    // 处理客户端发来的通知(如取消请求)
    if (notification.method === 'notifications/cancelled') {
      // 处理取消逻辑
      this.emit('cancelled', notification.params);
    }
    // 其他通知...
  }

  private createError(code: number, message: string) {
    const error = new Error(message) as any;
    error.code = code;
    return error;
  }
}

3.5 传输层实现(Streamable HTTP 支持)

// src/server/Transport.ts
import { EventEmitter } from 'events';
import * as readline from 'readline';
import * as http from 'http';
import * as url from 'url';

export abstract class Transport extends EventEmitter {
  abstract connect(): Promise<void>;
  abstract send(message: object): void;
  abstract onMessage(callback: (message: string) => void): void;
  abstract close(): Promise<void>;
}

/**
 * Standard I/O 传输(本地进程通信)
 */
export class StdioTransport extends Transport {
  private messageCallback?: (message: string) => void;

  async connect(): Promise<void> {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
      terminal: false
    });

    rl.on('line', (line) => {
      if (this.messageCallback && line.trim()) {
        this.messageCallback(line);
      }
    });

    // 处理优雅关闭
    process.on('SIGINT', () => this.close());
    process.on('SIGTERM', () => this.close());
  }

  onMessage(callback: (message: string) => void) {
    this.messageCallback = callback;
  }

  send(message: object) {
    const json = JSON.stringify(message);
    process.stdout.write(json + '\n');
  }

  async close() {
    process.exit(0);
  }
}

/**
 * 🆕 Streamable HTTP 传输(2025-11-25 标准)
 * 替代旧版 SSE,支持双向流式通信
 */
export class StreamableHTTPTransport extends Transport {
  private server?: http.Server;
  private sessions = new Map<string, http.ServerResponse>();
  private messageCallback?: (message: string) => void;
  private port: number;

  constructor(port: number) {
    super();
    this.port = port;
  }

  async connect(): Promise<void> {
    this.server = http.createServer((req, res) => this.handleRequest(req, res));
    
    await new Promise<void>((resolve) => {
      this.server!.listen(this.port, () => {
        console.error(`🌐 Streamable HTTP server listening on port ${this.port}`);
        resolve();
      });
    });
  }

  private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
    const parsedUrl = url.parse(req.url!, true);
    const sessionId = parsedUrl.query.sessionId as string || 'default';

    // CORS 支持(2025-11-25 要求)
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id');

    if (req.method === 'OPTIONS') {
      res.writeHead(200);
      res.end();
      return;
    }

    // GET 请求:建立 SSE 流(服务器→客户端)
    if (req.method === 'GET' && parsedUrl.pathname === '/mcp') {
      res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Mcp-Session-Id': sessionId
      });

      this.sessions.set(sessionId, res);
      
      // 发送会话 ID
      res.write(`data: ${JSON.stringify({ type: 'session', id: sessionId })}\n\n`);

      req.on('close', () => {
        this.sessions.delete(sessionId);
      });
    }
    
    // POST 请求:接收客户端消息(客户端→服务器)
    else if (req.method === 'POST' && parsedUrl.pathname === '/mcp') {
      let body = '';
      req.on('data', chunk => body += chunk);
      req.on('end', () => {
        if (this.messageCallback) {
          this.messageCallback(body);
        }
        res.writeHead(202); // Accepted
        res.end();
      });
    }
    
    else {
      res.writeHead(404);
      res.end();
    }
  }

  onMessage(callback: (message: string) => void) {
    this.messageCallback = callback;
  }

  send(message: object) {
    const data = JSON.stringify(message);
    // 广播到所有会话(或根据 message 中的 sessionId 定向发送)
    for (const [id, res] of this.sessions) {
      res.write(`data: ${data}\n\n`);
    }
  }

  async close(): Promise<void> {
    for (const res of this.sessions.values()) {
      res.end();
    }
    this.sessions.clear();
    return new Promise((resolve) => {
      this.server?.close(() => resolve());
    });
  }
}

四、使用示例

4.1 基础服务器(stdio 模式)

// examples/basic-stdio.ts
import { McpServer } from '../src';
import { z } from 'zod';  // 🆕 使用 Zod 进行运行时验证

const server = new McpServer({
  name: 'weather-server',
  version: '1.0.0',
  transport: 'stdio',
  capabilities: {
    tools: { listChanged: true },
    resources: { listChanged: true, subscribe: true }
  }
});

// 使用 Zod 定义工具(类型安全 + 运行时验证)
server.registerTool(
  {
    name: 'get_weather',
    description: '获取指定城市的当前天气',
    inputSchema: {
      type: 'object',
      properties: {
        location: { 
          type: 'string', 
          description: '城市名称,如"北京"' 
        },
        unit: { 
          type: 'string', 
          enum: ['celsius', 'fahrenheit'],
          description: '温度单位'
        }
      },
      required: ['location']
    },
    // 🆕 2025-06-18:结构化输出
    outputSchema: {
      type: 'object',
      properties: {
        temperature: { type: 'number' },
        condition: { type: 'string' },
        humidity: { type: 'number' }
      }
    },
    annotations: {
      title: '天气查询',
      readOnlyHint: true,      // 只读操作
      idempotentHint: true,    // 幂等(可安全重试)
      timeoutHint: 3000        // 3秒超时建议
    }
  },
  async (args: { location: string; unit?: string }) => {
    // 模拟 API 调用
    const weather = {
      temperature: 25,
      condition: '晴朗',
      humidity: 60
    };
    
    // 返回结构化数据(根据 outputSchema)
    return weather;
  }
);

// 注册资源(带模板)
server.registerResourceTemplate(
  'weather://{city}/current',
  {
    name: '城市天气数据',
    description: '指定城市的实时天气',
    mimeType: 'application/json'
  },
  async (params: { city: string }) => {
    return {
      contents: [{
        uri: `weather://${params.city}/current`,
        mimeType: 'application/json',
        text: JSON.stringify({ temp: 25, condition: 'sunny' })
      }]
    };
  }
);

await server.start();

4.2 Streamable HTTP 服务器(远程部署)

// examples/http-server.ts
import { McpServer } from '../src';

const server = new McpServer({
  name: 'remote-api-server',
  version: '2.0.0',
  transport: 'http',
  port: 3000,
  capabilities: {
    tools: { listChanged: true },
    // 🆕 2025-11-25:声明支持的任务能力
    tasks: { supported: true },
    // 🆕 2025-11-25:诱导能力(OAuth 登录)
    elicitation: { url: {} }
  }
});

// 长时间运行任务示例
server.registerTool(
  {
    name: 'generate_report',
    description: '生成数据分析报告(可能需要几分钟)',
    inputSchema: {
      type: 'object',
      properties: {
        dataSource: { type: 'string' },
        analysisType: { type: 'string' }
      }
    },
    annotations: {
      timeoutHint: 120000,  // 2分钟
      memoryHint: 512      // 预估 512MB 内存
    }
  },
  async (args) => {
    // 实际实现会在后台运行
    return { reportUrl: 'https://...' };
  }
);

await server.start();

4.3 装饰器语法(高级封装)

// examples/decorator-pattern.ts
import { McpServer, ToolDefinition } from '../src';
import 'reflect-metadata';

const TOOL_METADATA_KEY = Symbol('tool');

// 改进的装饰器:自动收集元数据
function Tool(def: ToolDefinition) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 保存工具定义到类的元数据
    const existing = Reflect.getMetadata(TOOL_METADATA_KEY, target.constructor) || [];
    existing.push({
      name: def.name,
      definition: def,
      handler: descriptor.value.bind(target)
    });
    Reflect.defineMetadata(TOOL_METADATA_KEY, existing, target.constructor);
    return descriptor;
  };
}

class DataAnalysisTools {
  @Tool({
    name: 'analyze_csv',
    description: '分析 CSV 文件并返回统计信息',
    inputSchema: {
      type: 'object',
      properties: {
        filePath: { type: 'string' },
        columns: { type: 'array', items: { type: 'string' } }
      },
      required: ['filePath']
    }
  })
  async analyzeCSV(args: { filePath: string; columns?: string[] }) {
    // 实现...
    return { rowCount: 1000, columns: args.columns || [] };
  }

  @Tool({
    name: 'create_chart',
    description: '根据数据创建可视化图表',
    inputSchema: {
      type: 'object',
      properties: {
        data: { type: 'object' },
        chartType: { enum: ['bar', 'line', 'pie'] }
      }
    }
  })
  async createChart(args: { data: any; chartType: string }) {
    // 实现...
    return { chartUrl: 'https://...' };
  }
}

// 自动注册所有装饰过的工具
const server = new McpServer({ 
  name: 'analysis-server', 
  version: '1.0.0',
  transport: 'stdio'
});

const toolsInstance = new DataAnalysisTools();
const toolMetadata = Reflect.getMetadata(TOOL_METADATA_KEY, DataAnalysisTools) || [];

for (const tool of toolMetadata) {
  server.registerTool(tool.definition, tool.handler);
}

await server.start();

五、高级特性实现

5.1 异步任务系统(2025-11-25 实验性)

TaskRunner Server Client TaskRunner Server Client loop [进度更新] par [后台执行] [客户端轮询/订阅] tools/call (taskHint: true) 立即返回 {taskId: "task_123"} 启动异步任务 进度报告 notifications/tasks/updated 完成/失败 tasks/get (taskId) 当前状态 最终 notifications/tasks/updated tasks/get (获取结果)

5.2 资源订阅与实时通知

// 资源订阅实现示例
class ResourcesModule {
  private subscriptions = new Map<string, Set<string>>(); // uri -> clientIds
  private watchers = new Map<string, any>(); // uri -> fs.Watcher

  async subscribe(uri: string, clientId: string) {
    if (!this.subscriptions.has(uri)) {
      this.subscriptions.set(uri, new Set());
      
      // 启动文件监听(示例)
      this.watchers.set(uri, fs.watch(uri, () => {
        this.notifyUpdate(uri);
      }));
    }
    this.subscriptions.get(uri)!.add(clientId);
  }

  private notifyUpdate(uri: string) {
    const clients = this.subscriptions.get(uri);
    if (!clients) return;
    
    // 发送通知给所有订阅者
    for (const clientId of clients) {
      this.server.notifyResourceUpdated(uri, clientId);
    }
  }
}

5.3 URL 诱导(安全外跳)

2025-11-25 引入的 Elicitation 能力允许服务器安全地引导用户到外部 URL(如 OAuth 授权):

// 诱导用户进行 OAuth 登录
server.registerTool(
  {
    name: 'connect_github',
    description: '连接 GitHub 账号',
    inputSchema: { type: 'object', properties: {} }
  },
  async () => {
    // 当需要授权时,返回诱导错误
    throw {
      code: -32042, // URLElicitationRequired
      message: '需要 GitHub 授权',
      data: {
        elicitationId: 'auth_123',
        url: 'https://github.com/login/oauth/authorize?client_id=...',
        title: '授权访问 GitHub',
        description: '请点击链接完成 GitHub 授权'
      }
    };
  }
);

六、安全与最佳实践(增强版)

以下是基于 2025-11-25 规范的详细实践:

6.1 输入验证策略

// src/utils/validator.ts
import { z } from 'zod';
import { ToolDefinition } from '../types';

export function createValidator(schema: any) {
  return z.object(schema);
}

// 工具参数自动验证中间件
export function withValidation(
  tool: ToolDefinition, 
  handler: Function
) {
  const validator = z.object(tool.inputSchema.properties as any);
  
  return async (args: any) => {
    // 1. 类型验证
    const result = validator.safeParse(args);
    if (!result.success) {
      throw {
        code: -32602, // InvalidParams
        message: `参数验证失败: ${result.error.message}`
      };
    }
    
    // 2. 注入检查(防止命令注入)
    if (JSON.stringify(args).match(/[;&|`$]/)) {
      throw { code: -32602, message: '检测到潜在的危险字符' };
    }
    
    return handler(result.data);
  };
}

6.2 权限控制矩阵

操作类型 风险等级 控制措施
文件读取 🟡 中 限制在指定根目录(Roots)内
文件写入 🔴 高 需要用户确认,记录审计日志
网络请求 🔴 高 域名白名单,速率限制
代码执行 ⚫ 极高 沙箱环境,完全禁止或人工审核
数据库查询 🟡 中 只读账号,SQL 注入检查

6.3 OAuth2 集成(2025-11-25)

// 服务器能力声明
const capabilities = {
  oauth: {
    providerMetadata: 'https://auth.example.com/.well-known/oauth-authorization-server'
  }
};

// 动态客户端注册流程
server.on('oauth/register', async (params) => {
  // 实现动态客户端注册(RFC 7591)
  return {
    client_id: 'generated_client_id',
    client_secret: 'generated_secret',
    redirect_uris: params.redirect_uris
  };
});

七、NPM 包发布结构(优化版)

{
  "name": "@yourorg/mcp-server-wrapper",
  "version": "1.0.0",
  "description": "现代化的 MCP 服务器封装 SDK,支持 2025-11-25 规范",
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    },
    "./server": {
      "import": "./dist/server/index.js",
      "types": "./dist/server/index.d.ts"
    },
    "./transports": {
      "import": "./dist/transports/index.js",
      "types": "./dist/transports/index.d.ts"
    }
  },
  "scripts": {
    "build": "tsc --project tsconfig.build.json",
    "dev": "tsx watch src/index.ts",
    "test": "vitest",
    "lint": "eslint src/**/*.ts",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "zod": "^3.25.0",
    "events": "^3.3.0"
  },
  "peerDependencies": {
    "zod": "^3.25.0 || ^4.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.3.0",
    "vitest": "^1.0.0"
  },
  "engines": {
    "node": ">=18.0.0"
  },
  "keywords": [
    "mcp",
    "model-context-protocol",
    "ai",
    "llm",
    "anthropic",
    "claude"
  ],
  "license": "MIT"
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐