MCP TypeScript SDK - Server 端源码深度解析

324 阅读5分钟

目录

  1. 整体架构概览
  2. 核心类详解
  3. 三大功能模块深入分析
  4. 传输层实现
  5. 认证与安全
  6. 最佳实践与使用建议

整体架构概览

MCP (Model Context Protocol) TypeScript SDK 采用分层架构设计,主要包含以下几个核心层次:

┌─────────────────────────────────────┐
│           应用层 (Application)       │
│  ┌─────────┬─────────┬─────────┐   │
│  │ Tools   │Resources│ Prompts │   │
│  └─────────┴─────────┴─────────┘   │
├─────────────────────────────────────┤
│         协议层 (Protocol)            │
│      McpServer / Server             │
├─────────────────────────────────────┤
│         传输层 (Transport)           │
│   Stdio / StreamableHTTP / SSE      │
└─────────────────────────────────────┘

关键设计理念

  1. 分离关注点: 将数据提供 (Resources)、功能执行 (Tools) 和交互模式 (Prompts) 完全分离
  2. 可插拔传输: 支持多种传输协议 (stdio, HTTP, SSE)
  3. 类型安全: 基于 Zod 的完整类型系统
  4. 异步优先: 所有操作都是异步的,支持流式处理

核心类详解

1. McpServer 类

McpServer 是高级封装类,提供了简化的 API 来注册和管理 MCP 功能。

export class McpServer {
  public readonly server: Server;  // 底层 Server 实例
  
  private _registeredResources: { [uri: string]: RegisteredResource } = {};
  private _registeredResourceTemplates: { [name: string]: RegisteredResourceTemplate } = {};
  private _registeredTools: { [name: string]: RegisteredTool } = {};
  private _registeredPrompts: { [name: string]: RegisteredPrompt } = {};
}

核心特性:

  • 提供高级 API (registerTool, registerResource, registerPrompt)
  • 自动处理协议协商和能力注册
  • 内部管理所有注册的功能组件

2. Server 类

Server 是底层协议处理类,继承自 Protocol,负责处理 MCP 协议的具体实现。

export class Server<
  RequestT extends Request = Request,
  NotificationT extends Notification = Notification,
  ResultT extends Result = Result,
> extends Protocol<...> {
  private _clientCapabilities?: ClientCapabilities;
  private _capabilities: ServerCapabilities;
  private _instructions?: string;
}

主要职责:

  • 处理初始化流程 (initialize, initialized)
  • 管理客户端能力协商
  • 提供底层请求/通知处理机制

三大功能模块深入分析

1. Tools 模块 - 功能执行引擎

Tools 是 MCP 中执行具体操作的组件,类似于 REST API 中的 POST 端点。

核心实现机制

// Tool 注册流程
server.setRequestHandler(CallToolRequestSchema, async (request, extra): Promise<CallToolResult> => {
  const tool = this._registeredTools[request.params.name];
  
  // 1. 参数验证
  if (tool.inputSchema) {
    const parseResult = await tool.inputSchema.safeParseAsync(request.params.arguments);
    if (!parseResult.success) {
      throw new McpError(ErrorCode.InvalidParams, `Invalid arguments...`);
    }
  }
  
  // 2. 执行回调
  const result = await Promise.resolve(tool.callback(args, extra));
  
  // 3. 错误处理
  return result;
});

Tool 类型定义

export type ToolCallback<
  InputArgs extends undefined | ZodRawShape = undefined,
  OutputArgs extends undefined | ZodRawShape = undefined
> = InputArgs extends ZodRawShape
  ? (args: z.objectOutputType<InputArgs, ZodTypeAny>, extra: RequestHandlerExtra) =>
      | TypedCallToolResult<OutputArgs>
      | Promise<TypedCallToolResult<OutputArgs>>
  : (extra: RequestHandlerExtra) =>
      | TypedCallToolResult<OutputArgs>
      | Promise<TypedCallToolResult<OutputArgs>>;

高级功能特性

  1. 输入/输出架构验证: 基于 Zod 的完整类型验证
  2. 工具注释系统: 支持 readOnlyHint, openWorldHint 等元数据
  3. 错误处理: 自动捕获异常并转换为标准错误响应
  4. 通知发送: 通过 extra.sendNotification 发送进度通知

实际使用示例

// 基础工具
server.registerTool(
  "greet",
  {
    title: "问候工具",
    description: "生成个性化问候语",
    inputSchema: {
      name: z.string().describe("要问候的人名"),
      language: z.enum(["zh", "en"]).default("zh")
    }
  },
  async ({ name, language }) => ({
    content: [{
      type: "text",
      text: language === "zh" ? `你好,${name}!` : `Hello, ${name}!`
    }]
  })
);

// 带通知的长时间运行工具
server.registerTool(
  "batch-process",
  {
    title: "批处理工具",
    description: "批量处理数据",
    inputSchema: { items: z.array(z.string()) }
  },
  async ({ items }, { sendNotification }) => {
    for (let i = 0; i < items.length; i++) {
      await sendNotification({
        method: "notifications/message",
        params: { 
          level: "info", 
          data: `处理进度: ${i + 1}/${items.length}` 
        }
      });
      
      // 处理逻辑...
      await processItem(items[i]);
    }
    
    return {
      content: [{ type: "text", text: `成功处理 ${items.length} 个项目` }]
    };
  }
);

2. Resources 模块 - 数据访问层

Resources 提供对数据的只读访问,相当于 REST API 中的 GET 端点。

核心架构

Resources 支持两种模式:

  1. 静态资源: 固定 URI 的资源
  2. 动态资源: 基于 URI 模板的参数化资源

ResourceTemplate 深入解析

export class ResourceTemplate {
  private _uriTemplate: UriTemplate;
  private _callbacks: {
    list: ListResourcesCallback | undefined;
    complete?: {
      [variable: string]: CompleteResourceTemplateCallback;
    };
  };
}

关键特性:

  • URI 模板: 支持 RFC 6570 标准的 URI 模板
  • 列表回调: 用于枚举模板匹配的所有资源
  • 自动补全: 为模板变量提供智能补全

资源处理流程

// 读取资源的处理流程
this.server.setRequestHandler(ReadResourceRequestSchema, async (request, extra) => {
  const uri = new URL(request.params.uri);

  // 1. 首先查找精确匹配的静态资源
  const resource = this._registeredResources[uri.toString()];
  if (resource) {
    return resource.readCallback(uri, extra);
  }

  // 2. 然后查找匹配的资源模板
  for (const template of Object.values(this._registeredResourceTemplates)) {
    const variables = template.resourceTemplate.uriTemplate.match(uri.toString());
    if (variables) {
      return template.readCallback(uri, variables, extra);
    }
  }

  throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`);
});

实际使用示例

// 静态资源
server.registerResource(
  "app-config",
  "config://app",
  {
    title: "应用配置",
    description: "应用程序配置信息",
    mimeType: "application/json"
  },
  async (uri) => ({
    contents: [{
      uri: uri.href,
      text: JSON.stringify({
        version: "1.0.0",
        features: ["auth", "logging"]
      }, null, 2),
      mimeType: "application/json"
    }]
  })
);

// 动态资源模板
server.registerResource(
  "user-profile",
  new ResourceTemplate("users://{userId}/profile", {
    list: async () => ({
      resources: [
        { name: "用户 123", uri: "users://123/profile" },
        { name: "用户 456", uri: "users://456/profile" }
      ]
    }),
    complete: {
      userId: (value) => ["123", "456", "789"].filter(id => id.startsWith(value))
    }
  }),
  {
    title: "用户资料",
    description: "用户详细信息"
  },
  async (uri, { userId }) => ({
    contents: [{
      uri: uri.href,
      text: JSON.stringify({
        id: userId,
        name: `用户${userId}`,
        email: `user${userId}@example.com`
      }, null, 2)
    }]
  })
);

// 文件系统资源
server.registerResource(
  "file-content",
  new ResourceTemplate("file://{path}", {
    list: async () => {
      const files = await fs.readdir("./docs");
      return {
        resources: files.map(file => ({
          name: file,
          uri: `file://${file}`
        }))
      };
    }
  }),
  {
    title: "文件内容",
    description: "读取文件系统中的文件"
  },
  async (uri, { path }) => {
    const content = await fs.readFile(`./docs/${path}`, 'utf-8');
    return {
      contents: [{
        uri: uri.href,
        text: content,
        mimeType: "text/plain"
      }]
    };
  }
);

3. Prompts 模块 - 交互模式定义

Prompts 定义了 LLM 交互的可重用模板,提供标准化的对话起点。

核心实现

// Prompt 处理器
this.server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => {
  const prompt = this._registeredPrompts[request.params.name];
  
  // 参数验证
  if (prompt.argsSchema) {
    const parseResult = await prompt.argsSchema.safeParseAsync(request.params.arguments);
    if (!parseResult.success) {
      throw new McpError(ErrorCode.InvalidParams, `Invalid arguments...`);
    }
  }
  
  return prompt.callback(args, extra);
});

Prompt 类型定义

export type PromptCallback<
  Args extends undefined | PromptArgsRawShape = undefined,
> = Args extends PromptArgsRawShape
  ? (args: z.objectOutputType<Args, ZodTypeAny>, extra: RequestHandlerExtra) => 
      GetPromptResult | Promise<GetPromptResult>
  : (extra: RequestHandlerExtra) => 
      GetPromptResult | Promise<GetPromptResult>;

实际使用示例

// 基础 Prompt
server.registerPrompt(
  "code-review",
  {
    title: "代码审查",
    description: "生成代码审查提示词",
    argsSchema: {
      language: z.string().describe("编程语言"),
      code: z.string().describe("要审查的代码")
    }
  },
  async ({ language, code }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `请审查以下 ${language} 代码,关注:
1. 代码质量和最佳实践
2. 潜在的bug和安全问题
3. 性能优化建议

代码:
\`\`\`${language}
${code}
\`\`\`

请提供详细的审查意见和改进建议。`
      }
    }]
  })
);

// 带资源链接的复杂 Prompt
server.registerPrompt(
  "documentation-helper",
  {
    title: "文档助手",
    description: "基于项目文档生成帮助提示",
    argsSchema: {
      topic: z.string().describe("需要帮助的主题"),
      context: z.string().optional().describe("额外上下文")
    }
  },
  async ({ topic, context }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `我需要关于"${topic}"的帮助。${context ? `额外信息:${context}` : ''}`
      }
    }],
    // 引用相关文档资源
    resources: [
      { type: "resource_link", uri: "docs://api-reference" },
      { type: "resource_link", uri: "docs://tutorials" }
    ]
  })
);

传输层实现

MCP SDK 支持多种传输协议,实现了完全可插拔的传输层架构。

1. Stdio Transport

// 标准输入输出传输,适用于命令行工具
const transport = new StdioServerTransport();
await server.connect(transport);

2. StreamableHTTP Transport

// HTTP 传输,支持流式处理
const transport = new StreamableHTTPServerTransport();
// 配置 Express 路由
app.post('/mcp', (req, res) => transport.handleRequest(req, res));

3. SSE (Server-Sent Events)

// 服务器推送事件,适用于实时通信
const transport = new SSEServerTransport();
app.get('/mcp/sse', (req, res) => transport.handleSSE(req, res));

认证与安全

OAuth 2.0 集成

MCP SDK 提供了完整的 OAuth 2.0 支持:

// 设置 OAuth 认证
const authMiddleware = requireBearerAuth(tokenVerifier);
app.use('/mcp', authMiddleware);

// 在处理器中访问认证信息
server.registerTool("secure-operation", {}, async (args, { authInfo }) => {
  if (!authInfo?.scopes?.includes('write')) {
    throw new McpError(ErrorCode.InvalidParams, "Insufficient permissions");
  }
  // 执行需要授权的操作...
});

资源访问控制

// 基于认证信息的资源访问控制
server.registerResource(
  "private-data",
  new ResourceTemplate("private://{userId}/data", { list: undefined }),
  {},
  async (uri, { userId }, { authInfo }) => {
    // 检查用户是否有权访问指定用户的数据
    if (authInfo?.sub !== userId && !authInfo?.scopes?.includes('admin')) {
      throw new McpError(ErrorCode.InvalidParams, "Access denied");
    }
    
    return {
      contents: [{ uri: uri.href, text: `Private data for ${userId}` }]
    };
  }
);

最佳实践与使用建议

1. 合理使用三大功能模块

  • Tools: 用于执行有副作用的操作(创建、更新、删除、计算)
  • Resources: 用于提供只读数据访问(配置、文档、状态查询)
  • Prompts: 用于定义标准化的 LLM 交互模式

2. 错误处理策略

// 在 Tool 中进行优雅的错误处理
server.registerTool("file-operation", {}, async ({ action, path }) => {
  try {
    const result = await performFileOperation(action, path);
    return {
      content: [{ type: "text", text: `操作成功: ${result}` }]
    };
  } catch (error) {
    // 返回结构化错误信息而不是抛出异常
    return {
      content: [{ 
        type: "text", 
        text: `操作失败: ${error.message}` 
      }],
      isError: true
    };
  }
});

3. 性能优化建议

// 使用缓存提高资源访问性能
const cache = new Map();

server.registerResource("expensive-data", "data://expensive", {}, 
  async (uri) => {
    const cacheKey = uri.href;
    if (cache.has(cacheKey)) {
      return cache.get(cacheKey);
    }
    
    const result = await expensiveDataComputation();
    cache.set(cacheKey, result);
    return result;
  }
);

// 定期清理缓存
setInterval(() => cache.clear(), 300000); // 5分钟清理一次

4. 监控和日志

// 添加请求监控
server.server.setRequestHandler = ((originalMethod) => {
  return function(schema, handler) {
    const wrappedHandler = async (request, extra) => {
      const startTime = Date.now();
      try {
        const result = await handler(request, extra);
        console.log(`请求处理成功: ${request.method} (${Date.now() - startTime}ms)`);
        return result;
      } catch (error) {
        console.error(`请求处理失败: ${request.method}`, error);
        throw error;
      }
    };
    return originalMethod.call(this, schema, wrappedHandler);
  };
})(server.server.setRequestHandler);

5. 开发调试技巧

// 添加调试中间件
if (process.env.NODE_ENV === 'development') {
  server.server.fallbackRequestHandler = async (request, extra) => {
    console.log('未处理的请求:', JSON.stringify(request, null, 2));
    throw new McpError(ErrorCode.MethodNotFound, `Method ${request.method} not found`);
  };
}

总结

MCP TypeScript SDK 提供了一个强大而灵活的框架来构建 LLM 应用的上下文提供者。通过合理使用 Tools、Resources 和 Prompts 三大模块,可以构建出功能丰富、性能优良的 MCP 服务器。

关键要点:

  1. 分层架构: 清晰的职责分离,便于维护和扩展
  2. 类型安全: 基于 Zod 的完整类型系统确保运行时安全
  3. 传输无关: 支持多种传输协议,适应不同部署场景
  4. 认证集成: 内置 OAuth 2.0 支持,满足企业级安全需求
  5. 扩展性强: 可插拔的架构设计,支持自定义扩展