MCP TypeScript SDK 架构
概述
MCP (Model Context Protocol) TypeScript SDK 实现了模型上下文协议,用于在 LLM 和外部数据源/工具之间建立标准化的通信。
架构图
arduino
┌─────────────────────────────────────────────────────────────────────────────┐
│ MCP Architecture │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐
│ LLM (AI) │
└────────┬────────┘
│
┌────────────▼────────────┐
│ MCP Client │
│ @modelcontextprotocol/ │
│ sdk/client │
└────────────┬────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌─────────▼─────────┐ ┌──────▼──────┐ ┌───────▼──────┐
│ Tools API │ │ Resources │ │ Prompts │
│ listTools() │ │ listResources│ │ listPrompts()│
│ callTool() │ │ readResource │ │ getPrompt() │
└─────────┬─────────┘ └──────┬──────┘ └───────┬──────┘
│ │ │
┌─────────▼───────────────────▼────────────────▼─────────┐
│ Transport Layer │
│ ┌──────────────┐ ┌──────────────────┐ ┌──────────┐ │
│ │ Stdio │ │ Streamable HTTP │ │ SSE │ │
│ │ (Local) │ │ (Remote) │ │(Legacy) │ │
│ └──────────────┘ └──────────────────┘ └──────────┘ │
└──────────────────────────┬──────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
┌─────────▼─────────┐ ┌─────────▼─────────┐
│ MCP Server │ │ MCP Client │
│ (Provider) │◄───────►│ (Consumer) │
└─────────┬─────────┘ RPC └───────────────────┘
│
┌─────────▼─────────────────────────────────────────┐
│ Capability Layer │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌───────┐ │
│ │ Tools │ │ Resources │ │Prompts │ │Sampling│ │
│ │ │ │ │ │ │ │Elicit │ │
│ └─────────┘ └──────────┘ └────────┘ └───────┘ │
└────────────────────────────────────────────────────┘
组件说明
| 组件 | 作用 | 职责说明 |
|---|---|---|
| LLM | 消费方 | 大语言模型,通过 MCP Client 发现和使用外部工具、资源。解析用户意图,决定调用哪些工具,处理返回结果 |
| MCP Client | 连接管理 | SDK 客户端库,负责:① 与服务器建立连接 ② 发现并列出可用工具/资源/提示器 ③ 调用工具并传递参数 ④ 读取资源内容 ⑤ 处理服务器通知 |
| MCP Server | 能力提供 | SDK 服务器端,负责:① 注册和暴露 Tools(可执行函数)② 注册和暴露 Resources(可读数据)③ 注册和暴露 Prompts(提示模板)④ 处理客户端请求并返回结果 |
| Transport | 通信通道 | 传输层,负责客户端与服务器之间的消息传递。支持多种传输方式:① Stdio - 本地进程间通信 ② HTTP Streamable - 远程 HTTP 通信,支持双向流 ③ SSE - 服务器单向推送事件 |
| Tools | 执行能力 | 服务器暴露的可调用函数,用于执行具体操作(如计算、查询、创建等)。带输入参数,返回执行结果 |
| Resources | 数据暴露 | 服务器暴露的只读数据,支持客户端读取和订阅变更。可以是配置文件、数据库记录、文件内容等 |
| Prompts | 模板复用 | 预定义的提示模板,支持参数化。客户端可以获取填充参数后的完整消息,用于标准化常见任务的提示 |
| Sampling | LLM 代理 | 允许服务器向客户端请求 LLM 推理能力。服务器可以请客户端的 LLM 生成文本、总结内容等 |
| Elicitation | 交互代理 | 允许服务器向客户端请求用户输入。用于需要用户确认、填写表单或做出选择的场景 |
SDK 模块结构
本项目使用 v1 版本的 @modelcontextprotocol/sdk 统一包。
| 模块 | 导入路径 | 用途 |
|---|---|---|
| Client | @modelcontextprotocol/sdk/client/index.js |
MCP 客户端实现,连接服务器、调用工具、读取资源 |
| Server | @modelcontextprotocol/sdk/server/mcp.js |
MCP 服务器实现,暴露工具、资源、提示器 |
协议层设计
Transport 层
Transport 层负责客户端与服务器之间的底层通信。
| 传输类型 | 类 | 适用场景 |
|---|---|---|
| Stdio | StdioServerTransport / StdioClientTransport |
本地进程通信,同一系统内 spawn 子进程 |
| HTTP Streamable | StreamableHTTPClientTransport |
远程服务器,支持 SSE 事件流(推荐) |
| SSE | SSEClientTransport |
Server-Sent Events 单向事件流 |
Stdio 传输
适合本地场景,服务器作为子进程启动:
typescript
// 服务器
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const transport = new StdioServerTransport();
await server.connect(transport);
// 客户端
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const transport = new StdioClientTransport({ command: 'node', args: ['server.js'] });
HTTP Streamable 传输
适合远程场景,服务器独立运行:
typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp'));
await client.connect(transport);
Capability 层
MCP 协议定义了四种主要能力。
Tools(工具)
工具是服务器暴露的可调用函数,LLM 可以通过工具执行实际操作。
服务器注册:
typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod';
server.registerTool(
'calculate-bmi',
{
title: 'BMI Calculator',
description: 'Calculate Body Mass Index',
inputSchema: z.object({
weightKg: z.number().describe('Weight in kilograms'),
heightM: z.number().describe('Height in meters')
}),
outputSchema: z.object({ bmi: z.number() })
},
async ({ weightKg, heightM }) => {
const bmi = weightKg / (heightM * heightM);
return {
content: [{ type: 'text', text: `BMI: ${bmi.toFixed(2)}` }],
structuredContent: { bmi }
};
}
);
客户端调用:
typescript
const result = await client.callTool({
name: 'calculate-bmi',
arguments: { weightKg: 70, heightM: 1.75 }
});
Resources(资源)
资源是服务器暴露的二进制或文本数据,客户端可以读取和订阅。
服务器注册:
typescript
server.registerResource(
'config://app',
{
title: 'App Config',
description: 'Application configuration',
mimeType: 'application/json'
},
async () => ({
contents: [{
uri: 'config://app',
text: JSON.stringify({ theme: 'dark', language: 'en' })
}]
})
);
客户端操作:
typescript
// 列出资源
const { resources } = await client.listResources();
// 读取资源
const { contents } = await client.readResource({ uri: 'config://app' });
// 订阅变更
await client.subscribeResource({ uri: 'config://app' });
client.setNotificationHandler('notifications/resources/updated', handler);
Prompts(提示器)
提示器是预定义的提示模板,可以携带参数生成消息。
服务器注册:
typescript
server.registerPrompt(
'review-code',
{
title: 'Code Review',
description: 'Review code for best practices',
argsSchema: z.object({ code: z.string() })
},
({ code }) => ({
messages: [{
role: 'user',
content: { type: 'text', text: `Please review:\n\n${code}` }
}]
})
);
客户端调用:
typescript
const { messages } = await client.getPrompt({ name: 'review-code', arguments: { code: '...' } });
Sampling(采样)
Sampling 允许服务器向客户端请求 LLM 推理。
服务器端请求:
typescript
server.registerTool(
'summarize',
{ inputSchema: z.object({ text: z.string() }) },
async ({ text }, ctx) => {
const response = await ctx.mcpReq.requestSampling({
messages: [{ role: 'user', content: { type: 'text', text: `Summarize: ${text}` } }],
maxTokens: 500
});
return { content: [{ type: 'text', text: JSON.stringify(response.content) }] };
}
);
客户端处理:
typescript
client.setRequestHandler('sampling/createMessage', async request => {
const lastMessage = request.params.messages.at(-1);
// 转发给 LLM 并返回
return { model: 'claude-3-5-sonnet', role: 'assistant', content: { type: 'text', text: '...' } };
});
Elicitation(请求用户输入)
Elicitation 允许服务器向客户端请求用户交互式输入。
服务器端请求:
typescript
const result = await ctx.mcpReq.elicitInput({
mode: 'form',
message: 'Please rate this:',
requestedSchema: {
type: 'object',
properties: { rating: { type: 'integer', minimum: 1, maximum: 5 } },
required: ['rating']
}
});
客户端处理:
typescript
client.setRequestHandler('elicitation/create', async request => ({
action: 'accept',
content: { rating: 5 }
}));
Auth 层
SDK 支持 OAuth 2.0 认证。
| Provider | 用途 |
|---|---|
ClientCredentialsProvider |
客户端凭证流,适用于服务间认证 |
PrivateKeyJwtProvider |
私钥 JWT,适用于更安全的场景 |
typescript
import { ClientCredentialsProvider } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const authProvider = new ClientCredentialsProvider({
clientId: 'my-service',
clientSecret: 'my-secret'
});
const transport = new StreamableHTTPClientTransport(
new URL('http://localhost:3000/mcp'),
{ authProvider }
);
错误处理
SDK 提供 ProtocolError 和 SdkError:
typescript
import { ProtocolError } from '@modelcontextprotocol/sdk/client/index.js';
if (error instanceof ProtocolError) {
console.log(error.code); // 错误码
console.log(error.message); // 错误消息
}