mcp-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMCP Builder
MCP 构建器
Overview
概述
Build production-quality MCP (Model Context Protocol) servers that expose tools, resources, and prompts to AI clients. This skill covers the full development lifecycle: tool definition, resource management, prompt templates, transport configuration (stdio, SSE), error handling, security hardening, testing, and client integration.
构建生产级MCP(Model Context Protocol)服务器,向AI客户端暴露工具、资源和提示词。本技能覆盖完整开发生命周期:工具定义、资源管理、提示词模板、传输配置(stdio、SSE)、错误处理、安全加固、测试和客户端集成。
Phase 1: Design
阶段1:设计
- Identify capabilities to expose (tools, resources, prompts)
- Define tool schemas with Zod/JSON Schema
- Plan resource URI patterns
- Design error handling strategy
- Choose transport layer (stdio for CLI, SSE for web)
STOP — Present the capability inventory and transport choice to user for approval.
- 确定需要暴露的能力(工具、资源、提示词)
- 使用Zod/JSON Schema定义工具Schema
- 规划资源URI格式
- 设计错误处理策略
- 选择传输层(CLI场景用stdio,Web场景用SSE)
停止 —— 向用户展示能力清单和传输层选择,等待确认后再继续。
Capability Selection Decision Table
功能选择决策表
| What You Have | MCP Primitive | Example |
|---|---|---|
| Actions that modify state | Tool | |
| Actions that read/query | Tool | |
| Data the AI should read | Resource | |
| Reusable prompt patterns | Prompt | |
| Real-time data feeds | Resource (subscribable) | |
| 现有内容 | 对应MCP原语 | 示例 |
|---|---|---|
| 修改状态的操作 | Tool | |
| 读取/查询操作 | Tool | |
| AI需要读取的数据 | Resource | |
| 可复用的提示词模式 | Prompt | |
| 实时数据流 | 可订阅Resource | |
Transport Selection Decision Table
传输层选择决策表
| Context | Transport | Why |
|---|---|---|
| CLI tool, local client (Claude Desktop) | Stdio | Simple, no network overhead |
| Web application, remote clients | SSE | Network-accessible, real-time |
| Both local and remote | Stdio + SSE | Support both use cases |
| High-throughput, bidirectional | WebSocket (custom) | Lower latency than SSE |
| 场景 | 传输层 | 原因 |
|---|---|---|
| CLI工具、本地客户端(Claude Desktop) | Stdio | 简单,无网络开销 |
| Web应用、远程客户端 | SSE | 可通过网络访问,支持实时传输 |
| 同时支持本地和远程 | Stdio + SSE | 覆盖两种使用场景 |
| 高吞吐量、双向通信 | WebSocket(自定义) | 延迟低于SSE |
Phase 2: Implementation
阶段2:实现
- Set up MCP server project structure
- Implement tool handlers with input validation
- Implement resource providers
- Add prompt templates
- Configure transport and authentication
STOP — Run basic smoke tests before moving to hardening.
- 搭建MCP服务器项目结构
- 实现带输入校验的工具处理逻辑
- 实现资源提供器
- 添加提示词模板
- 配置传输层和认证
停止 —— 运行基础冒烟测试通过后,再进入加固阶段。
Project Structure
项目结构
src/
index.ts # Server entry point
tools/
search.ts # Tool implementations
create.ts
resources/
documents.ts # Resource providers
config.ts
prompts/
review.ts # Prompt templates
lib/
database.ts # Shared utilities
validation.ts
tests/
tools.test.ts
resources.test.ts
package.json
tsconfig.jsonsrc/
index.ts # 服务入口文件
tools/
search.ts # 工具实现代码
create.ts
resources/
documents.ts # 资源提供器
config.ts
prompts/
review.ts # 提示词模板
lib/
database.ts # 共享工具函数
validation.ts
tests/
tools.test.ts
resources.test.ts
package.json
tsconfig.jsonTool Definition Pattern
工具定义模式
typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
server.tool(
'search-documents',
'Search documents by query. Returns matching documents with relevance scores.',
{
query: z.string().describe('Search query string'),
limit: z.number().min(1).max(100).default(10).describe('Maximum results to return'),
filter: z.object({
type: z.enum(['article', 'page', 'note']).optional(),
dateAfter: z.string().datetime().optional(),
}).optional().describe('Optional filters'),
},
async ({ query, limit, filter }) => {
const results = await searchEngine.search(query, { limit, ...filter });
return {
content: [{
type: 'text',
text: JSON.stringify(results, null, 2),
}],
};
}
);typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
server.tool(
'search-documents',
'Search documents by query. Returns matching documents with relevance scores.',
{
query: z.string().describe('Search query string'),
limit: z.number().min(1).max(100).default(10).describe('Maximum results to return'),
filter: z.object({
type: z.enum(['article', 'page', 'note']).optional(),
dateAfter: z.string().datetime().optional(),
}).optional().describe('Optional filters'),
},
async ({ query, limit, filter }) => {
const results = await searchEngine.search(query, { limit, ...filter });
return {
content: [{
type: 'text',
text: JSON.stringify(results, null, 2),
}],
};
}
);Tool Design Principles
工具设计原则
| Principle | Rule |
|---|---|
| Clear naming | verb-noun format: |
| Descriptive descriptions | Explain what, when, and return value |
| Validated inputs | Zod schemas with |
| Structured outputs | Well-formatted text or JSON |
| Idempotent when possible | Same input produces same result |
| Actionable errors | Specific error messages with |
| 原则 | 规则 |
|---|---|
| 命名清晰 | 动词-名词格式: |
| 描述详尽 | 说明功能、适用场景和返回值 |
| 输入校验 | 每个字段都使用带 |
| 输出结构化 | 格式规范的文本或JSON |
| 尽可能保证幂等 | 相同输入产生相同结果 |
| 错误可落地 | 附带 |
Tool Response Patterns
工具响应模式
typescript
// Text response
return { content: [{ type: 'text', text: 'Operation completed successfully' }] };
// Structured data response
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
// Multi-part response
return {
content: [
{ type: 'text', text: `Found ${results.length} results:` },
{ type: 'text', text: results.map(r => `- ${r.title}: ${r.summary}`).join('\n') },
],
};
// Image response
return { content: [{ type: 'image', data: base64Data, mimeType: 'image/png' }] };
// Error response
return {
content: [{ type: 'text', text: `Error: ${error.message}` }],
isError: true,
};typescript
// 文本响应
return { content: [{ type: 'text', text: 'Operation completed successfully' }] };
// 结构化数据响应
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
// 多部分响应
return {
content: [
{ type: 'text', text: `Found ${results.length} results:` },
{ type: 'text', text: results.map(r => `- ${r.title}: ${r.summary}`).join('\n') },
],
};
// 图片响应
return { content: [{ type: 'image', data: base64Data, mimeType: 'image/png' }] };
// 错误响应
return {
content: [{ type: 'text', text: `Error: ${error.message}` }],
isError: true,
};Resource Management
资源管理
Resource Definition
资源定义
typescript
// Static resource
server.resource(
'config',
'config://app/settings',
{ mimeType: 'application/json' },
async () => ({
contents: [{
uri: 'config://app/settings',
mimeType: 'application/json',
text: JSON.stringify(appConfig),
}],
})
);
// Dynamic resource with URI template
server.resource(
'document',
new ResourceTemplate('docs://{category}/{id}', { list: undefined }),
{ mimeType: 'text/markdown' },
async (uri, { category, id }) => ({
contents: [{
uri: uri.href,
mimeType: 'text/markdown',
text: await getDocument(category, id),
}],
})
);typescript
// 静态资源
server.resource(
'config',
'config://app/settings',
{ mimeType: 'application/json' },
async () => ({
contents: [{
uri: 'config://app/settings',
mimeType: 'application/json',
text: JSON.stringify(appConfig),
}],
})
);
// 带URI模板的动态资源
server.resource(
'document',
new ResourceTemplate('docs://{category}/{id}', { list: undefined }),
{ mimeType: 'text/markdown' },
async (uri, { category, id }) => ({
contents: [{
uri: uri.href,
mimeType: 'text/markdown',
text: await getDocument(category, id),
}],
})
);Resource URI Conventions
资源URI规范
file:///path/to/file — Local files
https://api.example.com/data — Remote HTTP resources
db://database/table/id — Database records
config://app/settings — Configuration
docs://category/slug — Documentationfile:///path/to/file — 本地文件
https://api.example.com/data — 远程HTTP资源
db://database/table/id — 数据库记录
config://app/settings — 配置项
docs://category/slug — 文档Prompt Templates
提示词模板
typescript
server.prompt(
'code-review',
'Generate a code review for the given file',
{
filePath: z.string().describe('Path to the file to review'),
severity: z.enum(['strict', 'normal', 'lenient']).default('normal'),
},
async ({ filePath, severity }) => {
const code = await readFile(filePath, 'utf-8');
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Review this code with ${severity} standards:\n\n\`\`\`\n${code}\n\`\`\``,
},
}],
};
}
);typescript
server.prompt(
'code-review',
'Generate a code review for the given file',
{
filePath: z.string().describe('Path to the file to review'),
severity: z.enum(['strict', 'normal', 'lenient']).default('normal'),
},
async ({ filePath, severity }) => {
const code = await readFile(filePath, 'utf-8');
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Review this code with ${severity} standards:\n\n\`\`\`\n${code}\n\`\`\``,
},
}],
};
}
);Transport Layers
传输层
Stdio Transport (CLI tools, local development)
Stdio传输层(CLI工具、本地开发)
typescript
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const transport = new StdioServerTransport();
await server.connect(transport);typescript
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const transport = new StdioServerTransport();
await server.connect(transport);SSE Transport (Web applications, remote servers)
SSE传输层(Web应用、远程服务器)
typescript
import express from 'express';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
const app = express();
app.get('/sse', async (req, res) => {
const transport = new SSEServerTransport('/messages', res);
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
// Handle incoming messages
});
app.listen(3001);typescript
import express from 'express';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
const app = express();
app.get('/sse', async (req, res) => {
const transport = new SSEServerTransport('/messages', res);
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
// Handle incoming messages
});
app.listen(3001);Phase 3: Hardening
阶段3:加固
- Add comprehensive error handling
- Implement rate limiting and timeouts
- Security review (input sanitization, permission checks)
- Write integration tests
- Document tools and resources for clients
STOP — All tests must pass and security review must be complete before deployment.
- 添加完整的错误处理逻辑
- 实现限流和超时配置
- 安全评审(输入 sanitization、权限校验)
- 编写集成测试
- 为客户端编写工具和资源文档
停止 —— 所有测试通过且安全评审完成后,才能部署。
Error Handling
错误处理
typescript
server.tool('risky-operation', 'Performs an operation that might fail', {
input: z.string(),
}, async ({ input }) => {
try {
const result = await performOperation(input);
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
} catch (error) {
if (error instanceof ValidationError) {
return {
content: [{ type: 'text', text: `Invalid input: ${error.message}` }],
isError: true,
};
}
if (error instanceof NotFoundError) {
return {
content: [{ type: 'text', text: `Resource not found: ${error.message}` }],
isError: true,
};
}
console.error('Unexpected error:', error);
return {
content: [{ type: 'text', text: 'An unexpected error occurred. Please try again.' }],
isError: true,
};
}
});typescript
server.tool('risky-operation', 'Performs an operation that might fail', {
input: z.string(),
}, async ({ input }) => {
try {
const result = await performOperation(input);
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
} catch (error) {
if (error instanceof ValidationError) {
return {
content: [{ type: 'text', text: `Invalid input: ${error.message}` }],
isError: true,
};
}
if (error instanceof NotFoundError) {
return {
content: [{ type: 'text', text: `Resource not found: ${error.message}` }],
isError: true,
};
}
console.error('Unexpected error:', error);
return {
content: [{ type: 'text', text: 'An unexpected error occurred. Please try again.' }],
isError: true,
};
}
});Error Handling Rules
错误处理规则
| Rule | Why |
|---|---|
| Never expose stack traces to clients | Security risk |
Return | Client can distinguish success/failure |
| Log unexpected errors server-side | Debugging and monitoring |
| Provide actionable error messages | Client can self-correct |
| Handle timeouts for external calls | Prevent hanging requests |
| Validate all inputs before processing | Reject bad data early |
| 规则 | 原因 |
|---|---|
| 永远不要向客户端暴露堆栈信息 | 存在安全风险 |
所有错误都返回 | 客户端可以区分成功/失败状态 |
| 服务端记录非预期错误 | 便于调试和监控 |
| 提供可落地的错误信息 | 客户端可以自行修正问题 |
| 外部调用添加超时处理 | 避免请求挂起 |
| 处理前校验所有输入 | 提前拦截无效数据 |
Security Considerations
安全注意事项
| Category | Rules |
|---|---|
| Input validation | Zod schemas, path traversal prevention, length limits |
| Permission model | Least privilege, whitelist directories, separate read/write tools |
| Secrets | Env vars only, never in responses, mask in logs, rotate regularly |
| Rate limiting | Limit tool invocations per client |
| Auditing | Log all tool calls with timestamps |
| 分类 | 规则 |
|---|---|
| 输入校验 | Zod Schema、路径遍历防护、长度限制 |
| 权限模型 | 最小权限原则、目录白名单、读写工具分离 |
| 密钥管理 | 仅通过环境变量传递、永远不要出现在响应中、日志中脱敏、定期轮换 |
| 限流 | 限制每个客户端的工具调用次数 |
| 审计 | 记录所有工具调用和时间戳 |
Testing MCP Servers
MCP服务器测试
typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
describe('MCP Server', () => {
let server: McpServer;
let client: Client;
beforeEach(async () => {
server = createServer();
client = new Client({ name: 'test-client', version: '1.0.0' });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
server.connect(serverTransport),
client.connect(clientTransport),
]);
});
test('search-documents returns results', async () => {
const result = await client.callTool({
name: 'search-documents',
arguments: { query: 'test', limit: 5 },
});
expect(result.content[0].type).toBe('text');
const data = JSON.parse(result.content[0].text);
expect(data.length).toBeLessThanOrEqual(5);
});
test('handles invalid input gracefully', async () => {
const result = await client.callTool({
name: 'search-documents',
arguments: { query: '', limit: -1 },
});
expect(result.isError).toBe(true);
});
});typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
describe('MCP Server', () => {
let server: McpServer;
let client: Client;
beforeEach(async () => {
server = createServer();
client = new Client({ name: 'test-client', version: '1.0.0' });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
server.connect(serverTransport),
client.connect(clientTransport),
]);
});
test('search-documents returns results', async () => {
const result = await client.callTool({
name: 'search-documents',
arguments: { query: 'test', limit: 5 },
});
expect(result.content[0].type).toBe('text');
const data = JSON.parse(result.content[0].text);
expect(data.length).toBeLessThanOrEqual(5);
});
test('handles invalid input gracefully', async () => {
const result = await client.callTool({
name: 'search-documents',
arguments: { query: '', limit: -1 },
});
expect(result.isError).toBe(true);
});
});Client Integration
客户端集成
Claude Desktop Configuration
Claude Desktop配置
json
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/server/dist/index.js"],
"env": {
"API_KEY": "your-key-here"
}
}
}
}json
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/server/dist/index.js"],
"env": {
"API_KEY": "your-key-here"
}
}
}
}Anti-Patterns / Common Mistakes
反模式/常见错误
| Anti-Pattern | Why It Is Wrong | What to Do Instead |
|---|---|---|
| Tools that do too many things | Hard to use, hard to test | Split into focused single-purpose tools |
| Missing input validation | Crashes, security holes | Always use Zod schemas |
| Returning raw stack traces | Security risk, confusing for AI | Return |
| No timeout on external calls | Hangs indefinitely | Set timeouts on all I/O |
| Hardcoded secrets in source | Credential exposure | Use environment variables |
| Tools without descriptions | Clients cannot discover purpose | Write clear descriptions |
| Blocking event loop with sync ops | Server becomes unresponsive | Use async/await for all I/O |
| No tests | Regressions go undetected | Test with InMemoryTransport |
| 反模式 | 问题 | 正确做法 |
|---|---|---|
| 单个工具承担过多功能 | 难以使用、难以测试 | 拆分为聚焦单一功能的工具 |
| 缺少输入校验 | 崩溃、安全漏洞 | 始终使用Zod Schema |
| 返回原始堆栈信息 | 安全风险、AI难以理解 | 返回 |
| 外部调用没有超时 | 请求无限挂起 | 所有I/O操作都设置超时 |
| 源代码中硬编码密钥 | 凭证泄露 | 使用环境变量 |
| 工具缺少描述 | 客户端无法发现功能 | 编写清晰的描述信息 |
| 同步操作阻塞事件循环 | 服务无响应 | 所有I/O都使用async/await |
| 没有测试 | 回归问题无法被发现 | 使用InMemoryTransport进行测试 |
Documentation Lookup (Context7)
文档查询(Context7)
Use then for up-to-date docs. Returned docs override memorized knowledge.
mcp__context7__resolve-library-idmcp__context7__query-docs- — for Claude API client, tool definitions, or streaming
@anthropic-ai/sdk
使用后调用获取最新文档,返回的文档优先级高于记忆知识。
mcp__context7__resolve-library-idmcp__context7__query-docs- — 用于Claude API客户端、工具定义或流式传输
@anthropic-ai/sdk
Integration Points
集成点
| Skill | Integration |
|---|---|
| Containerize and deploy MCP servers |
| MCP servers provide tools for agents |
| Security hardening of tool inputs/outputs |
| TDD for tool implementation |
| CI/CD pipeline for MCP server releases |
| MCP server design is part of the implementation plan |
| 技能 | 集成场景 |
|---|---|
| 容器化和部署MCP服务器 |
| MCP服务器为Agent提供工具 |
| 工具输入/输出的安全加固 |
| 工具实现的TDD开发 |
| MCP服务器发布的CI/CD流水线 |
| MCP服务器设计属于实现计划的一部分 |
Skill Type
技能类型
FLEXIBLE — Adapt project structure, transport choice, and tooling to the use case. Tool validation with Zod and error handling with are strongly recommended. Security review is recommended before production deployment.
isError灵活适配 — 根据使用场景调整项目结构、传输层选择和工具链。强烈推荐使用Zod进行工具校验和带的错误处理,生产部署前建议进行安全评审。
isError