1. MCP Server开发基础:理解Model Context Protocol
Model Context Protocol(MCP)是一种为大型语言模型(LLM)提供标准化上下文的协议,它将上下文提供与实际LLM交互的关注点分离。这个设计理念使得开发者可以专注于构建高质量的上下文服务,而不必担心底层模型交互的复杂性。
MCP的核心价值在于它定义了一套统一的接口规范,允许不同的应用程序以一致的方式向LLM提供上下文信息。这种标准化带来了几个关键优势:
- 上下文服务的可复用性:开发一次MCP服务,可以被多种LLM客户端使用
- 工具生态的互操作性:不同团队开发的工具可以无缝集成
- 开发效率的提升:专注于业务逻辑而非协议实现
当前MCP的最新规范是2026-07-28版本,TypeScript SDK的v2实现正处于beta阶段。对于生产环境,官方推荐使用稳定的v1.x版本,它将继续获得至少6个月的安全更新和维护。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 搭建MCP Server开发环境
2.1 环境准备与工具链选择
开发MCP Server需要准备以下基础环境:
- Node.js 18+ / Bun 1.0+ / Deno 1.30+(推荐使用最新LTS版本)
- 包管理器:npm/yarn/pnpm(推荐pnpm以获得最佳monorepo支持)
- 代码编辑器:VS Code + TypeScript插件(或其他支持TS的IDE)
对于依赖管理,MCP TypeScript SDK采用monorepo结构发布多个独立包:
bash复制# 核心服务器包
npm install @modelcontextprotocol/server
# 可选中间件(根据运行时选择)
npm install @modelcontextprotocol/node # Node.js HTTP
npm install @modelcontextprotocol/express # Express框架
npm install @modelcontextprotocol/hono # Hono框架
2.2 项目初始化与配置
创建一个新的MCP Server项目建议遵循以下步骤:
- 初始化项目并安装核心依赖
bash复制mkdir mcp-server-demo && cd mcp-server-demo
npm init -y
npm install typescript @types/node --save-dev
npm install @modelcontextprotocol/server zod
- 配置TypeScript(tsconfig.json)
json复制{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
- 添加基础目录结构
code复制├── src/
│ ├── server.ts # 主服务器文件
│ ├── tools/ # 工具实现
│ └── types/ # 类型定义
├── test/ # 测试代码
├── package.json
└── tsconfig.json
3. 构建基础MCP Server
3.1 创建最小化Server实例
以下是一个最基本的MCP Server实现,通过stdio传输协议提供服务:
typescript复制import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import { z } from 'zod';
// 初始化Server实例
const server = new McpServer({
name: 'demo-server',
version: '0.1.0',
description: 'A demo MCP server'
});
// 注册第一个工具
server.registerTool(
'greet',
{
description: 'Generate a greeting message',
inputSchema: z.object({
name: z.string().describe('The name to greet'),
language: z.enum(['en', 'es', 'fr']).optional()
})
},
async ({ name, language = 'en' }) => {
const greetings = {
en: `Hello, ${name}!`,
es: `¡Hola, ${name}!`,
fr: `Bonjour, ${name}!`
};
return {
content: [{ type: 'text', text: greetings[language] }]
};
}
);
// 启动stdio传输
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.log('MCP Server is running via stdio');
}
main().catch(console.error);
3.2 工具注册的深度解析
registerTool方法是MCP Server的核心API,它接受三个关键参数:
-
工具标识符(toolId):
- 必须是唯一的字符串
- 建议使用kebab-case命名(如
weather-forecast) - 将成为客户端调用时的端点名称
-
工具元数据:
description:人类可读的工具说明inputSchema:使用Zod等库定义的输入验证模式- 可选参数如
requireAuth、rateLimit等
-
工具处理器:
- 异步函数,接收已验证的输入参数
- 返回符合MCP规范的响应对象
- 可以生成多种类型的内容(文本、图像、结构化数据等)
重要提示:工具处理器应该保持纯净和无状态,任何持久化操作应该通过资源(Resources)机制实现,而不是直接修改外部状态。
4. 高级MCP Server功能实现
4.1 HTTP传输与中间件集成
除了stdio,MCP Server更常见的部署方式是通过HTTP提供服务。以下是使用Express中间件的示例:
typescript复制import express from 'express';
import { createExpressMiddleware } from '@modelcontextprotocol/express';
import { McpServer } from '@modelcontextprotocol/server';
const app = express();
const server = new McpServer({ name: 'http-server', version: '1.0.0' });
// 注册工具(同上)
server.registerTool(/* ... */);
// 添加Express中间件
app.use(
'/mcp',
createExpressMiddleware(server, {
validateHost: true,
maxBodySize: '1mb'
})
);
app.listen(3000, () => {
console.log('Server running on http://localhost:3000/mcp');
});
4.2 资源管理与上下文持久化
MCP的一个重要概念是资源(Resources),它允许服务器维护跨工具调用的持久化状态:
typescript复制// 定义资源类型
const userProfileResource = server.defineResource({
type: 'user-profile',
schema: z.object({
userId: z.string(),
preferences: z.object({
language: z.enum(['en', 'es', 'fr']),
timezone: z.string()
})
})
});
// 在工具中使用资源
server.registerTool('set-preferences', {
/* ... */
}, async (input, context) => {
await userProfileResource.set(context, {
userId: input.userId,
preferences: {
language: input.language,
timezone: input.timezone
}
});
return { content: [{ type: 'text', text: 'Preferences updated' }] };
});
4.3 认证与安全实践
生产环境的MCP Server需要实现适当的认证机制:
typescript复制// 使用JWT认证示例
server.configure({
auth: {
validate: async (token: string) => {
try {
const payload = verifyJwt(token);
return { userId: payload.sub };
} catch {
return null; // 认证失败
}
}
}
});
// 在工具中要求认证
server.registerTool('secure-action', {
requireAuth: true,
/* ... */
}, async (input, context) => {
console.log('Authenticated user:', context.auth.userId);
/* ... */
});
5. 调试与性能优化
5.1 日志与监控集成
完善的日志记录对MCP Server至关重要:
typescript复制import { createLogger } from '@modelcontextprotocol/server/log';
const logger = createLogger('demo-server');
server.configure({
hooks: {
beforeToolCall: (toolId, input) => {
logger.info(`Calling tool ${toolId}`, { input });
},
afterToolCall: (toolId, result, duration) => {
logger.info(`Tool ${toolId} completed in ${duration}ms`);
}
}
});
5.2 性能优化技巧
- 工具懒加载:对于初始化成本高的工具,使用动态导入
typescript复制server.registerLazyTool('heavy-tool', async () => {
const { heavyInit } = await import('./heavy-tool');
const tool = await heavyInit();
return {
description: '...',
inputSchema: z.object({/* ... */}),
handler: tool.handle.bind(tool)
};
});
- 响应流式传输:对于长时间运行的工具,支持流式响应
typescript复制server.registerTool('stream-demo', {
/* ... */
}, async function* ({ count }) {
for (let i = 1; i <= count; i++) {
yield { content: [{ type: 'text', text: `Chunk ${i}` }] };
await new Promise(r => setTimeout(r, 500));
}
});
- 缓存策略:对幂等操作实现缓存层
typescript复制import { createCache } from 'node-cache';
const cache = new createCache({ stdTTL: 300 });
server.registerTool('cached-query', {
/* ... */
}, async (input, context) => {
const cacheKey = JSON.stringify(input);
const cached = cache.get(cacheKey);
if (cached) return cached;
const result = await doExpensiveQuery(input);
cache.set(cacheKey, result);
return result;
});
6. 测试与部署策略
6.1 单元测试与集成测试
MCP Server的测试策略应该包含多个层次:
typescript复制// 工具单元测试示例
import { test } from 'vitest';
import { createTestMcpServer } from '@modelcontextprotocol/server/test';
test('greet tool works', async () => {
const testServer = createTestMcpServer();
testServer.registerTool(/* greet tool */);
const response = await testServer.callTool('greet', { name: 'Test' });
assert.equal(response.content[0].text, 'Hello, Test!');
});
// HTTP集成测试
import supertest from 'supertest';
test('HTTP endpoint', async () => {
const app = express();
app.use('/mcp', createExpressMiddleware(server));
const response = await supertest(app)
.post('/mcp/tools/greet')
.send({ name: 'HTTP' });
assert.equal(response.status, 200);
});
6.2 容器化与生产部署
生产环境部署建议使用Docker容器:
dockerfile复制# Dockerfile示例
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY dist ./dist
COPY public ./public
EXPOSE 3000
CMD ["node", "dist/server.js"]
配合Kubernetes部署时,注意以下配置:
- 就绪探针(readinessProbe):检查/mcp/health端点
- 存活探针(livenessProbe):检查服务器进程状态
- 资源限制:根据工具负载设置合理的CPU/内存限制
- 水平扩展:无状态工具可以水平扩展,有状态工具需要会话亲和性
7. 实际项目经验分享
在开发真实MCP Server项目时,有几个关键经验值得分享:
-
工具版本管理策略:
- 每个工具应该包含版本号(如
weather@v1) - 使用语义化版本控制
- 维护向后兼容性至少一个主要版本
- 每个工具应该包含版本号(如
-
输入验证的最佳实践:
- 始终使用Zod等库严格验证输入
- 为每个字段添加描述(
.describe())以生成更好的文档 - 使用
.brand()类型区分相似但语义不同的字段
-
错误处理的黄金法则:
- 使用标准的MCP错误代码(如
INVALID_INPUT) - 包含机器可读的错误详情
- 避免暴露堆栈跟踪等敏感信息
- 使用标准的MCP错误代码(如
-
文档生成的自动化:
typescript复制// 生成OpenAPI文档示例 import { generateOpenApi } from '@modelcontextprotocol/server/docs'; const openApiSpec = generateOpenApi(server, { title: 'Demo MCP Server', version: '1.0.0' }); fs.writeFileSync('openapi.json', JSON.stringify(openApiSpec, null, 2)); -
客户端SDK的协同开发:
- 为每个工具生成类型化的客户端SDK
- 发布配套的客户端库到内部npm registry
- 使用
@modelcontextprotocol/client作为基础
在开发过程中,我发现最常遇到的问题集中在工具接口设计上。一个常见的反模式是设计过于宽泛的接口,这会导致维护困难。好的MCP工具应该遵循Unix哲学——做好一件事,并通过组合实现复杂功能。
