mcp-builder

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MCP 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:设计

  1. Identify capabilities to expose (tools, resources, prompts)
  2. Define tool schemas with Zod/JSON Schema
  3. Plan resource URI patterns
  4. Design error handling strategy
  5. Choose transport layer (stdio for CLI, SSE for web)
STOP — Present the capability inventory and transport choice to user for approval.
  1. 确定需要暴露的能力(工具、资源、提示词)
  2. 使用Zod/JSON Schema定义工具Schema
  3. 规划资源URI格式
  4. 设计错误处理策略
  5. 选择传输层(CLI场景用stdio,Web场景用SSE)
停止 —— 向用户展示能力清单和传输层选择,等待确认后再继续。

Capability Selection Decision Table

功能选择决策表

What You HaveMCP PrimitiveExample
Actions that modify stateTool
create-issue
,
send-email
,
deploy-app
Actions that read/queryTool
search-documents
,
get-status
Data the AI should readResource
config://settings
,
docs://api/endpoints
Reusable prompt patternsPrompt
code-review
,
summarize-document
Real-time data feedsResource (subscribable)
metrics://cpu/current
现有内容对应MCP原语示例
修改状态的操作Tool
create-issue
send-email
deploy-app
读取/查询操作Tool
search-documents
get-status
AI需要读取的数据Resource
config://settings
docs://api/endpoints
可复用的提示词模式Prompt
code-review
summarize-document
实时数据流可订阅Resource
metrics://cpu/current

Transport Selection Decision Table

传输层选择决策表

ContextTransportWhy
CLI tool, local client (Claude Desktop)StdioSimple, no network overhead
Web application, remote clientsSSENetwork-accessible, real-time
Both local and remoteStdio + SSESupport both use cases
High-throughput, bidirectionalWebSocket (custom)Lower latency than SSE
场景传输层原因
CLI工具、本地客户端(Claude Desktop)Stdio简单,无网络开销
Web应用、远程客户端SSE可通过网络访问,支持实时传输
同时支持本地和远程Stdio + SSE覆盖两种使用场景
高吞吐量、双向通信WebSocket(自定义)延迟低于SSE

Phase 2: Implementation

阶段2:实现

  1. Set up MCP server project structure
  2. Implement tool handlers with input validation
  3. Implement resource providers
  4. Add prompt templates
  5. Configure transport and authentication
STOP — Run basic smoke tests before moving to hardening.
  1. 搭建MCP服务器项目结构
  2. 实现带输入校验的工具处理逻辑
  3. 实现资源提供器
  4. 添加提示词模板
  5. 配置传输层和认证
停止 —— 运行基础冒烟测试通过后,再进入加固阶段。

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.json
src/
  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.json

Tool 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

工具设计原则

PrincipleRule
Clear namingverb-noun format:
search-documents
,
create-issue
Descriptive descriptionsExplain what, when, and return value
Validated inputsZod schemas with
.describe()
on every field
Structured outputsWell-formatted text or JSON
Idempotent when possibleSame input produces same result
Actionable errorsSpecific error messages with
isError: true
原则规则
命名清晰动词-名词格式:
search-documents
create-issue
描述详尽说明功能、适用场景和返回值
输入校验每个字段都使用带
.describe()
的Zod Schema校验
输出结构化格式规范的文本或JSON
尽可能保证幂等相同输入产生相同结果
错误可落地附带
isError: true
的具体错误信息

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          — Documentation
file:///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:加固

  1. Add comprehensive error handling
  2. Implement rate limiting and timeouts
  3. Security review (input sanitization, permission checks)
  4. Write integration tests
  5. Document tools and resources for clients
STOP — All tests must pass and security review must be complete before deployment.
  1. 添加完整的错误处理逻辑
  2. 实现限流和超时配置
  3. 安全评审(输入 sanitization、权限校验)
  4. 编写集成测试
  5. 为客户端编写工具和资源文档
停止 —— 所有测试通过且安全评审完成后,才能部署。

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

错误处理规则

RuleWhy
Never expose stack traces to clientsSecurity risk
Return
isError: true
for all errors
Client can distinguish success/failure
Log unexpected errors server-sideDebugging and monitoring
Provide actionable error messagesClient can self-correct
Handle timeouts for external callsPrevent hanging requests
Validate all inputs before processingReject bad data early
规则原因
永远不要向客户端暴露堆栈信息存在安全风险
所有错误都返回
isError: true
客户端可以区分成功/失败状态
服务端记录非预期错误便于调试和监控
提供可落地的错误信息客户端可以自行修正问题
外部调用添加超时处理避免请求挂起
处理前校验所有输入提前拦截无效数据

Security Considerations

安全注意事项

CategoryRules
Input validationZod schemas, path traversal prevention, length limits
Permission modelLeast privilege, whitelist directories, separate read/write tools
SecretsEnv vars only, never in responses, mask in logs, rotate regularly
Rate limitingLimit tool invocations per client
AuditingLog 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-PatternWhy It Is WrongWhat to Do Instead
Tools that do too many thingsHard to use, hard to testSplit into focused single-purpose tools
Missing input validationCrashes, security holesAlways use Zod schemas
Returning raw stack tracesSecurity risk, confusing for AIReturn
isError: true
with clean message
No timeout on external callsHangs indefinitelySet timeouts on all I/O
Hardcoded secrets in sourceCredential exposureUse environment variables
Tools without descriptionsClients cannot discover purposeWrite clear descriptions
Blocking event loop with sync opsServer becomes unresponsiveUse async/await for all I/O
No testsRegressions go undetectedTest with InMemoryTransport
反模式问题正确做法
单个工具承担过多功能难以使用、难以测试拆分为聚焦单一功能的工具
缺少输入校验崩溃、安全漏洞始终使用Zod Schema
返回原始堆栈信息安全风险、AI难以理解返回
isError: true
和清晰的错误信息
外部调用没有超时请求无限挂起所有I/O操作都设置超时
源代码中硬编码密钥凭证泄露使用环境变量
工具缺少描述客户端无法发现功能编写清晰的描述信息
同步操作阻塞事件循环服务无响应所有I/O都使用async/await
没有测试回归问题无法被发现使用InMemoryTransport进行测试

Documentation Lookup (Context7)

文档查询(Context7)

Use
mcp__context7__resolve-library-id
then
mcp__context7__query-docs
for up-to-date docs. Returned docs override memorized knowledge.
  • @anthropic-ai/sdk
    — for Claude API client, tool definitions, or streaming

使用
mcp__context7__resolve-library-id
后调用
mcp__context7__query-docs
获取最新文档,返回的文档优先级高于记忆知识。
  • @anthropic-ai/sdk
    — 用于Claude API客户端、工具定义或流式传输

Integration Points

集成点

SkillIntegration
senior-devops
Containerize and deploy MCP servers
agent-development
MCP servers provide tools for agents
security-review
Security hardening of tool inputs/outputs
test-driven-development
TDD for tool implementation
deployment
CI/CD pipeline for MCP server releases
planning
MCP server design is part of the implementation plan
技能集成场景
senior-devops
容器化和部署MCP服务器
agent-development
MCP服务器为Agent提供工具
security-review
工具输入/输出的安全加固
test-driven-development
工具实现的TDD开发
deployment
MCP服务器发布的CI/CD流水线
planning
MCP服务器设计属于实现计划的一部分

Skill Type

技能类型

FLEXIBLE — Adapt project structure, transport choice, and tooling to the use case. Tool validation with Zod and error handling with
isError
are strongly recommended. Security review is recommended before production deployment.
灵活适配 — 根据使用场景调整项目结构、传输层选择和工具链。强烈推荐使用Zod进行工具校验和带
isError
的错误处理,生产部署前建议进行安全评审。