MCP TypeScript SDK
概述
什么是MCP TypeScript SDK
MCP TypeScript SDK是Model Context Protocol的官方TypeScript实现,为开发者提供了完整的类型定义和API接口,用于构建MCP客户端和服务器应用。它提供了类型安全、易于使用的接口,大大简化了MCP应用的开发过程。
SDK的优势
- 类型安全:完整的TypeScript类型定义,编译时错误检查
- 易于使用:简洁的API设计,降低学习成本
- 功能完整:支持MCP协议的所有核心功能
- 社区支持:官方维护,持续更新和改进
- 文档完善:详细的API文档和示例代码
适用场景
- 构建MCP客户端应用
- 开发MCP服务器工具
- 集成AI应用与外部工具
- 创建MCP开发工具和框架
- 学习和研究MCP协议
SDK安装配置
1. 环境要求
# Node.js版本要求
node --version # 需要16.0或更高版本
# TypeScript版本要求
npx tsc --version # 需要4.5或更高版本
# npm版本要求
npm --version # 需要7.0或更高版本
2. 安装SDK
# 使用npm安装
npm install @modelcontextprotocol/sdk
# 使用yarn安装
yarn add @modelcontextprotocol/sdk
# 使用pnpm安装
pnpm add @modelcontextprotocol/sdk
# 安装开发依赖
npm install -D @types/node typescript
3. 项目配置
// package.json
{
"name": "mcp-project",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
},
"devDependencies": {
"@types/node": "^18.0.0",
"typescript": "^5.0.0"
}
}
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
4. 开发环境配置
// .vscode/settings.json
{
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.suggest.autoImports": true,
"typescript.updateImportsOnFileMove.enabled": "always",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
}
}
核心API使用
1. 基础导入和类型
// 导入核心类型和接口
import {
Client,
Server,
StdioServerTransport,
StdioClientTransport,
ServerTransport,
ClientTransport,
CallRequest,
CallResult,
CallError,
ListToolsRequest,
ListToolsResult,
ListResourcesRequest,
ListResourcesResult,
ReadResourceRequest,
ReadResourceResult,
WatchResourceRequest,
WatchResourceResult,
SubscribeRequest,
SubscribeResult,
UnsubscribeRequest,
UnsubscribeResult,
NotifyRequest,
NotifyResult
} from '@modelcontextprotocol/sdk';
// 导入工具和资源类型
import {
Tool,
Resource,
ResourceMetadata,
ResourceUri,
ToolMetadata
} from '@modelcontextprotocol/sdk';
2. 创建MCP客户端
// 基础客户端创建
class MCPClient {
private client: Client;
private transport: ClientTransport;
constructor(command: string, args: string[] = []) {
// 创建stdio传输层
this.transport = new StdioClientTransport(command, args);
// 创建客户端
this.client = new Client(this.transport);
}
// 连接到服务器
async connect(): Promise<void> {
await this.client.connect();
console.log('MCP客户端连接成功');
}
// 调用工具方法
async callTool(name: string, params: Record<string, any>): Promise<any> {
try {
const result = await this.client.callTool(name, params);
return result;
} catch (error) {
console.error(`调用工具 ${name} 失败:`, error);
throw error;
}
}
// 列出可用工具
async listTools(): Promise<Tool[]> {
try {
const result = await this.client.listTools();
return result.tools;
} catch (error) {
console.error('获取工具列表失败:', error);
throw error;
}
}
// 读取资源
async readResource(uri: string): Promise<any> {
try {
const result = await this.client.readResource(uri);
return result.contents;
} catch (error) {
console.error(`读取资源 ${uri} 失败:`, error);
throw error;
}
}
// 关闭连接
async disconnect(): Promise<void> {
await this.client.disconnect();
console.log('MCP客户端已断开连接');
}
}
// 使用示例
async function main() {
const client = new MCPClient('python', ['file_tool.py']);
try {
await client.connect();
// 列出可用工具
const tools = await client.listTools();
console.log('可用工具:', tools);
// 调用工具方法
const result = await client.callTool('read_file', { path: '/test.txt' });
console.log('读取结果:', result);
} catch (error) {
console.error('操作失败:', error);
} finally {
await client.disconnect();
}
}
3. 创建MCP服务器
// 基础服务器创建
class MCPServer {
private server: Server;
private transport: ServerTransport;
private tools: Map<string, Tool> = new Map();
private resources: Map<string, Resource> = new Map();
constructor() {
// 创建stdio传输层
this.transport = new StdioServerTransport();
// 创建服务器
this.server = new Server(this.transport);
// 注册事件处理器
this.setupEventHandlers();
}
// 设置事件处理器
private setupEventHandlers(): void {
// 处理工具调用
this.server.onCallTool(async (request: CallRequest): Promise<CallResult | CallError> => {
try {
const tool = this.tools.get(request.name);
if (!tool) {
return {
jsonrpc: '2.0',
id: request.id,
error: {
code: -32601,
message: 'Method not found',
data: `Tool '${request.name}' not found`
}
};
}
const result = await tool.handler(request.arguments);
return {
jsonrpc: '2.0',
id: request.id,
result
};
} catch (error) {
return {
jsonrpc: '2.0',
id: request.id,
error: {
code: -32603,
message: 'Internal error',
data: error instanceof Error ? error.message : 'Unknown error'
}
};
}
});
// 处理工具列表请求
this.server.onListTools(async (request: ListToolsRequest): Promise<ListToolsResult> => {
return {
jsonrpc: '2.0',
id: request.id,
result: {
tools: Array.from(this.tools.values())
}
};
});
// 处理资源列表请求
this.server.onListResources(async (request: ListResourcesRequest): Promise<ListResourcesResult> => {
return {
jsonrpc: '2.0',
id: request.id,
result: {
resources: Array.from(this.resources.values())
}
};
});
// 处理资源读取请求
this.server.onReadResource(async (request: ReadResourceRequest): Promise<ReadResourceResult> => {
try {
const resource = this.resources.get(request.uri);
if (!resource) {
throw new Error(`Resource '${request.uri}' not found`);
}
const contents = await resource.reader(request.uri, request.arguments);
return {
jsonrpc: '2.0',
id: request.id,
result: {
contents
}
};
} catch (error) {
return {
jsonrpc: '2.0',
id: request.id,
error: {
code: -32603,
message: 'Internal error',
data: error instanceof Error ? error.message : 'Unknown error'
}
};
}
});
}
// 注册工具
registerTool(name: string, description: string, handler: (args: any) => Promise<any>): void {
const tool: Tool = {
name,
description,
inputSchema: {}, // 可以定义输入参数模式
handler
};
this.tools.set(name, tool);
console.log(`工具 ${name} 注册成功`);
}
// 注册资源
registerResource(uri: string, name: string, description: string, reader: (uri: string, args: any) => Promise<any>): void {
const resource: Resource = {
uri,
name,
description,
mimeType: 'text/plain', // 可以指定MIME类型
reader
};
this.resources.set(uri, resource);
console.log(`资源 ${uri} 注册成功`);
}
// 启动服务器
async start(): Promise<void> {
await this.server.start();
console.log('MCP服务器启动成功');
}
// 停止服务器
async stop(): Promise<void> {
await this.server.stop();
console.log('MCP服务器已停止');
}
}
// 使用示例
async function main() {
const server = new MCPServer();
// 注册文件操作工具
server.registerTool('read_file', '读取文件内容', async (args: { path: string }) => {
const fs = await import('fs/promises');
const content = await fs.readFile(args.path, 'utf8');
return { content, size: content.length };
});
server.registerTool('write_file', '写入文件内容', async (args: { path: string, content: string }) => {
const fs = await import('fs/promises');
await fs.writeFile(args.path, args.content);
return { success: true, path: args.path };
});
// 注册文件资源
server.registerResource('file://', '文件系统', '访问本地文件系统', async (uri: string, args: any) => {
const fs = await import('fs/promises');
const path = uri.replace('file://', '');
const content = await fs.readFile(path, 'utf8');
return { content, path };
});
// 启动服务器
await server.start();
// 保持服务器运行
process.on('SIGINT', async () => {
console.log('收到中断信号,正在停止服务器...');
await server.stop();
process.exit(0);
});
}
类型定义说明
1. 核心类型定义
// 基础请求类型
interface BaseRequest {
jsonrpc: '2.0';
id: string | number;
}
// 工具调用请求
interface CallRequest extends BaseRequest {
method: 'tools/call';
params: {
name: string;
arguments: Record<string, any>;
};
}
// 工具调用结果
interface CallResult extends BaseRequest {
result: any;
}
// 工具调用错误
interface CallError extends BaseRequest {
error: {
code: number;
message: string;
data?: any;
};
}
// 工具定义
interface Tool {
name: string;
description: string;
inputSchema?: Record<string, any>;
handler: (args: any) => Promise<any>;
}
// 资源定义
interface Resource {
uri: string;
name: string;
description: string;
mimeType: string;
reader: (uri: string, args: any) => Promise<any>;
}
2. 传输层类型
// 客户端传输接口
interface ClientTransport {
connect(): Promise<void>;
disconnect(): Promise<void>;
send(message: any): Promise<void>;
onMessage(handler: (message: any) => void): void;
onError(handler: (error: Error) => void): void;
onClose(handler: () => void): void;
}
// 服务器传输接口
interface ServerTransport {
start(): Promise<void>;
stop(): Promise<void>;
onConnection(handler: (transport: ClientTransport) => void): void;
onError(handler: (error: Error) => void): void;
}
3. 事件类型
// 服务器事件类型
interface ServerEvents {
onCallTool(handler: (request: CallRequest) => Promise<CallResult | CallError>): void;
onListTools(handler: (request: ListToolsRequest) => Promise<ListToolsResult>): void;
onListResources(handler: (request: ListResourcesRequest) => Promise<ListResourcesResult>): void;
onReadResource(handler: (request: ReadResourceRequest) => Promise<ReadResourceResult>): void;
onWatchResource(handler: (request: WatchResourceRequest) => Promise<WatchResourceResult>): void;
onSubscribe(handler: (request: SubscribeRequest) => Promise<SubscribeResult>): void;
onUnsubscribe(handler: (request: UnsubscribeRequest) => Promise<UnsubscribeResult>): void;
onNotify(handler: (request: NotifyRequest) => Promise<NotifyResult>): void;
}
高级功能实现
1. 中间件系统
// 中间件接口
interface Middleware {
(request: any, next: () => Promise<any>): Promise<any>;
}
// 中间件管理器
class MiddlewareManager {
private middlewares: Middleware[] = [];
// 添加中间件
use(middleware: Middleware): void {
this.middlewares.push(middleware);
}
// 执行中间件链
async execute(request: any, handler: (request: any) => Promise<any>): Promise<any> {
let index = 0;
const next = async (): Promise<any> => {
if (index >= this.middlewares.length) {
return await handler(request);
}
const middleware = this.middlewares[index++];
return await middleware(request, next);
};
return await next();
}
}
// 日志中间件
const loggingMiddleware: Middleware = async (request, next) => {
const startTime = Date.now();
console.log(`[${new Date().toISOString()}] 开始处理请求:`, request.method || request.name);
try {
const result = await next();
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] 请求处理完成:`, request.method || request.name, `耗时: ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - startTime;
console.error(`[${new Date().toISOString()}] 请求处理失败:`, request.method || request.name, `耗时: ${duration}ms`, error);
throw error;
}
};
// 认证中间件
const authMiddleware: Middleware = async (request, next) => {
const token = request.headers?.authorization || request.params?.token;
if (!token || !isValidToken(token)) {
throw new Error('Unauthorized');
}
return await next();
};
2. 错误处理系统
// 错误类型定义
enum MCPErrorCode {
PARSE_ERROR = -32700,
INVALID_REQUEST = -32600,
METHOD_NOT_FOUND = -32601,
INVALID_PARAMS = -32602,
INTERNAL_ERROR = -32603,
SERVER_ERROR_START = -32000,
SERVER_ERROR_END = -32099
}
// 自定义错误类
class MCPError extends Error {
constructor(
public code: number,
message: string,
public data?: any
) {
super(message);
this.name = 'MCPError';
}
// 转换为JSON-RPC错误响应
toJSONRPCError(id: string | number): CallError {
return {
jsonrpc: '2.0',
id,
error: {
code: this.code,
message: this.message,
data: this.data
}
};
}
}
// 错误处理器
class ErrorHandler {
// 处理同步错误
static handleSyncError(error: unknown, requestId: string | number): CallError {
if (error instanceof MCPError) {
return error.toJSONRPCError(requestId);
}
if (error instanceof Error) {
return {
jsonrpc: '2.0',
id: requestId,
error: {
code: MCPErrorCode.INTERNAL_ERROR,
message: error.message,
data: process.env.NODE_ENV === 'development' ? error.stack : undefined
}
};
}
return {
jsonrpc: '2.0',
id: requestId,
error: {
code: MCPErrorCode.INTERNAL_ERROR,
message: 'Unknown error',
data: error
}
};
}
// 处理异步错误
static async handleAsyncError<T>(
promise: Promise<T>,
requestId: string | number
): Promise<CallResult | CallError> {
try {
const result = await promise;
return {
jsonrpc: '2.0',
id: requestId,
result
};
} catch (error) {
return this.handleSyncError(error, requestId);
}
}
}
3. 连接管理
// 连接管理器
class ConnectionManager {
private connections: Map<string, Client> = new Map();
private connectionPool: Client[] = [];
private maxConnections: number;
private connectionTimeout: number;
constructor(maxConnections = 10, connectionTimeout = 30000) {
this.maxConnections = maxConnections;
this.connectionTimeout = connectionTimeout;
}
// 获取连接
async getConnection(command: string, args: string[]): Promise<Client> {
const key = `${command}:${args.join(' ')}`;
if (this.connections.has(key)) {
const connection = this.connections.get(key)!;
if (connection.isConnected()) {
return connection;
} else {
this.connections.delete(key);
}
}
if (this.connectionPool.length >= this.maxConnections) {
throw new Error('连接池已满');
}
const connection = new Client(new StdioClientTransport(command, args));
await connection.connect();
this.connections.set(key, connection);
this.connectionPool.push(connection);
return connection;
}
// 释放连接
releaseConnection(connection: Client): void {
const index = this.connectionPool.indexOf(connection);
if (index > -1) {
this.connectionPool.splice(index, 1);
}
}
// 关闭所有连接
async closeAll(): Promise<void> {
for (const connection of this.connections.values()) {
await connection.disconnect();
}
this.connections.clear();
this.connectionPool.length = 0;
}
}
最佳实践指南
1. 代码组织
// 项目结构建议
/*
src/
├── client/ # 客户端相关代码
│ ├── index.ts
│ ├── types.ts
│ └── utils.ts
├── server/ # 服务器相关代码
│ ├── index.ts
│ ├── tools/ # 工具实现
│ ├── resources/ # 资源实现
│ └── middleware/ # 中间件
├── shared/ # 共享代码
│ ├── types.ts
│ ├── constants.ts
│ └── utils.ts
└── index.ts # 主入口文件
*/
// 工具模块化
// tools/file-tool.ts
export class FileTool {
static readonly name = 'file_tool';
static readonly description = '文件操作工具';
static async readFile(args: { path: string }): Promise<{ content: string; size: number }> {
// 实现逻辑
}
static async writeFile(args: { path: string; content: string }): Promise<{ success: boolean }> {
// 实现逻辑
}
}
// 资源模块化
// resources/file-resource.ts
export class FileResource {
static readonly uri = 'file://';
static readonly name = '文件系统';
static readonly description = '访问本地文件系统';
static async read(uri: string, args: any): Promise<any> {
// 实现逻辑
}
}
2. 错误处理
// 统一的错误处理
class MCPToolError extends Error {
constructor(
message: string,
public code: number = MCPErrorCode.INTERNAL_ERROR,
public details?: any
) {
super(message);
this.name = 'MCPToolError';
}
}
// 工具包装器
function wrapTool<T extends any[], R>(
tool: (...args: T) => Promise<R>,
toolName: string
): (...args: T) => Promise<R> {
return async (...args: T): Promise<R> => {
try {
return await tool(...args);
} catch (error) {
if (error instanceof MCPToolError) {
throw error;
}
throw new MCPToolError(
`工具 ${toolName} 执行失败: ${error instanceof Error ? error.message : 'Unknown error'}`,
MCPErrorCode.INTERNAL_ERROR,
{ toolName, args, originalError: error }
);
}
};
}
3. 性能优化
// 连接池优化
class OptimizedConnectionPool {
private pool: Client[] = [];
private inUse: Set<Client> = new Set();
private maxSize: number;
private idleTimeout: number;
constructor(maxSize = 10, idleTimeout = 60000) {
this.maxSize = maxSize;
this.idleTimeout = idleTimeout;
}
async acquire(): Promise<Client> {
// 查找可用连接
const available = this.pool.find(client => !this.inUse.has(client));
if (available) {
this.inUse.add(available);
return available;
}
// 创建新连接
if (this.pool.length < this.maxSize) {
const client = await this.createClient();
this.pool.push(client);
this.inUse.add(client);
return client;
}
// 等待可用连接
return this.waitForConnection();
}
release(client: Client): void {
this.inUse.delete(client);
}
private async waitForConnection(): Promise<Client> {
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
const available = this.pool.find(client => !this.inUse.has(client));
if (available) {
clearInterval(checkInterval);
this.inUse.add(available);
resolve(available);
}
}, 100);
});
}
}
4. 测试策略
// 单元测试示例
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { MCPServer } from '../src/server';
describe('MCPServer', () => {
let server: MCPServer;
beforeEach(async () => {
server = new MCPServer();
await server.start();
});
afterEach(async () => {
await server.stop();
});
it('应该能够注册和调用工具', async () => {
// 注册测试工具
server.registerTool('test_tool', '测试工具', async (args) => {
return { message: 'Hello', args };
});
// 模拟工具调用请求
const request = {
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'test_tool',
arguments: { test: 'value' }
}
};
// 验证响应
const response = await server.handleRequest(request);
expect(response.result.message).toBe('Hello');
expect(response.result.args).toEqual({ test: 'value' });
});
});
总结
MCP TypeScript SDK为开发者提供了一个强大而灵活的工具集,用于构建MCP应用。通过本文的学习,你应该能够:
- 安装配置SDK:正确安装和配置MCP TypeScript SDK
- 使用核心API:掌握客户端和服务器的创建和使用方法
- 理解类型系统:了解SDK提供的类型定义和接口
- 实现高级功能:使用中间件、错误处理、连接管理等高级功能
- 遵循最佳实践:按照最佳实践组织代码和优化性能
掌握MCP TypeScript SDK,将大大提高你的MCP开发效率,帮助你构建更稳定、更高效的MCP应用。