mcp-builder

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MCP 服务器构建

MCP Server Construction

系统化设计、实现、测试和部署 Model Context Protocol 服务器的方法论。
A methodology for systematically designing, implementing, testing, and deploying Model Context Protocol servers.

1. 协议核心概念

1. Core Concepts of the Protocol

MCP 定义三种原语:
  • Tools(工具):AI 助手主动调用的函数,有副作用。如搜索、创建、删除操作。
  • Resources(资源):AI 助手只读访问的数据源,用 URI 标识。如
    users://{id}/profile
  • Prompts(提示词模板):预定义交互模板,引导用户触发工作流。
选择原则: 执行操作 → Tool | 读取数据 → Resource | 引导交互 → Prompt
MCP defines three primitives:
  • Tools: Functions actively invoked by AI assistants with side effects. Such as search, create, delete operations.
  • Resources: Read-only data sources accessed by AI assistants, identified by URIs. E.g.,
    users://{id}/profile
    .
  • Prompts: Pre-defined interaction templates that guide users to trigger workflows.
Selection Principles: Execute operations → Tool | Read data → Resource | Guide interactions → Prompt

2. 项目结构规范

2. Project Structure Specifications

TypeScript

TypeScript

my-mcp-server/
├── src/
│   ├── index.ts          # 入口,注册 tools/resources
│   ├── tools/             # 按功能拆分
│   ├── resources/
│   └── lib/               # 客户端封装、校验逻辑
├── tests/
├── package.json
└── tsconfig.json
关键依赖:
@modelcontextprotocol/sdk
+
zod
my-mcp-server/
├── src/
│   ├── index.ts          # Entry point, register tools/resources
│   ├── tools/             # Split by functionality
│   ├── resources/
│   └── lib/               # Client encapsulation, validation logic
├── tests/
├── package.json
└── tsconfig.json
Key Dependencies:
@modelcontextprotocol/sdk
+
zod

Python

Python

my-mcp-server/
├── src/my_mcp_server/
│   ├── server.py
│   ├── tools/
│   └── lib/
├── tests/
└── pyproject.toml
关键依赖:
mcp
+
pydantic
my-mcp-server/
├── src/my_mcp_server/
│   ├── server.py
│   ├── tools/
│   └── lib/
├── tests/
└── pyproject.toml
Key Dependencies:
mcp
+
pydantic

3. Tool 设计原则

3. Tool Design Principles

命名

Naming

  • snake_case
    格式,动词开头:
    search_users
    create_issue
    delete_file
  • 名称自解释,AI 助手靠名称选工具,模糊命名导致误调用
  • Use
    snake_case
    format, starting with a verb:
    search_users
    ,
    create_issue
    ,
    delete_file
  • Names should be self-explanatory. AI assistants select tools based on names; ambiguous naming leads to incorrect calls

参数

Parameters

  • 每个参数有类型约束和
    .describe()
    描述
  • 可选参数给默认值,减少 AI 决策负担
  • 用枚举代替布尔开关
typescript
server.tool("search_issues", {
  query: z.string().describe("搜索关键词"),
  status: z.enum(["open", "closed", "all"]).default("open").describe("状态筛选"),
  limit: z.number().min(1).max(100).default(20).describe("返回上限"),
}, async ({ query, status, limit }) => { /* ... */ });
  • Each parameter has type constraints and
    .describe()
    description
  • Optional parameters have default values to reduce AI decision-making burden
  • Use enums instead of boolean switches
typescript
server.tool("search_issues", {
  query: z.string().describe("Search keywords"),
  status: z.enum(["open", "closed", "all"]).default("open").describe("Status filter"),
  limit: z.number().min(1).max(100).default(20).describe("Return limit"),
}, async ({ query, status, limit }) => { /* ... */ });

描述

Description

说明用途 + 返回内容 + 限制,这是 AI 选择工具的关键依据:
typescript
server.tool("search_users",
  "根据姓名或邮箱搜索用户。返回 ID、姓名、邮箱列表。模糊匹配,最多 50 条。",
  schema, handler);
Explain purpose + return content + limitations, which is the key basis for AI to select tools:
typescript
server.tool("search_users",
  "Search users by name or email. Returns list of ID, name, email. Fuzzy matching, maximum 50 entries.",
  schema, handler);

输出

Output

  • 结构化数据 → JSON,人类可读内容 → Markdown
  • 始终用
    content: [{ type: "text", text: "..." }]
    格式返回
  • Structured data → JSON, human-readable content → Markdown
  • Always return in the format
    content: [{ type: "text", text: "..." }]

4. 输入验证和错误处理

4. Input Validation and Error Handling

用 Zod/Pydantic 做 Schema 级校验,业务级校验放 handler 开头:
typescript
server.tool("get_user", { id: z.string() }, async ({ id }) => {
  try {
    const user = await db.getUser(id);
    if (!user) {
      return {
        content: [{ type: "text", text: `用户 ${id} 不存在,请检查 ID。` }],
        isError: true,
      };
    }
    return { content: [{ type: "text", text: JSON.stringify(user, null, 2) }] };
  } catch (err) {
    return {
      content: [{ type: "text", text: `查询失败:${err.message}` }],
      isError: true,
    };
  }
});
错误处理四原则:
  1. 永远不让服务器崩溃 — try/catch 包裹所有外部调用
  2. 返回可操作的错误信息 — 告诉 AI 问题是什么、能做什么
  3. 使用
    isError: true
    — 让 AI 知道调用失败
  4. 区分错误类型 — 参数错误、权限不足、资源不存在、服务不可用
Use Zod/Pydantic for schema-level validation, and place business-level validation at the beginning of the handler:
typescript
server.tool("get_user", { id: z.string() }, async ({ id }) => {
  try {
    const user = await db.getUser(id);
    if (!user) {
      return {
        content: [{ type: "text", text: `User ${id} does not exist, please check the ID.` }],
        isError: true,
      };
    }
    return { content: [{ type: "text", text: JSON.stringify(user, null, 2) }] };
  } catch (err) {
    return {
      content: [{ type: "text", text: `Query failed: ${err.message}` }],
      isError: true,
    };
  }
});
Four Principles of Error Handling:
  1. Never let the server crash — Wrap all external calls with try/catch
  2. Return actionable error messages — Tell the AI what the problem is and what can be done
  3. Use
    isError: true
    — Let the AI know the call failed
  4. Distinguish error types — Parameter errors, insufficient permissions, non-existent resources, service unavailable

5. 资源管理和生命周期

5. Resource Management and Lifecycle

typescript
// 资源注册
server.resource("user-profile", "users://{userId}/profile", async (uri) => {
  const profile = await db.getProfile(extractId(uri));
  return { contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(profile) }] };
});

// 生命周期:先初始化 → 再 connect → 监听关闭信号
const db = await Database.connect(config.dbUrl);
await server.connect(new StdioServerTransport());
process.on("SIGINT", async () => { await db.disconnect(); await server.close(); process.exit(0); });
关键点:使用连接池、所有外部调用设超时、优雅关闭清理资源。
typescript
// Resource registration
server.resource("user-profile", "users://{userId}/profile", async (uri) => {
  const profile = await db.getProfile(extractId(uri));
  return { contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(profile) }] };
});

// Lifecycle: Initialize first → then connect → listen for shutdown signals
const db = await Database.connect(config.dbUrl);
await server.connect(new StdioServerTransport());
process.on("SIGINT", async () => { await db.disconnect(); await server.close(); process.exit(0); });
Key Points: Use connection pools, set timeouts for all external calls, and gracefully shut down to clean up resources.

6. 测试策略

6. Testing Strategy

单元测试 — 业务逻辑与 MCP 注册分离

Unit Testing — Separate business logic from MCP registration

typescript
// tools/search.ts 导出纯函数
export async function searchUsers(query: string, limit: number) { /* ... */ }

// search.test.ts 独立测试
test("返回匹配结果", async () => {
  const results = await searchUsers("alice", 10);
  expect(results[0].name).toContain("Alice");
});
typescript
// tools/search.ts exports pure functions
export async function searchUsers(query: string, limit: number) { /* ... */ }

// search.test.ts independent testing
test("Return matching results", async () => {
  const results = await searchUsers("alice", 10);
  expect(results[0].name).toContain("Alice");
});

集成测试 — 用 SDK Client 做端到端验证

Integration Testing — End-to-end verification with SDK Client

typescript
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await server.connect(serverTransport);
const client = new Client({ name: "test", version: "1.0.0" });
await client.connect(clientTransport);
const result = await client.callTool("search_users", { query: "test" });
expect(result.isError).toBeFalsy();
typescript
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await server.connect(serverTransport);
const client = new Client({ name: "test", version: "1.0.0" });
await client.connect(clientTransport);
const result = await client.callTool("search_users", { query: "test" });
expect(result.isError).toBeFalsy();

MCP Inspector — 交互式调试

MCP Inspector — Interactive debugging

bash
npx @modelcontextprotocol/inspector node dist/index.js
在浏览器中查看所有 tools/resources,手动调用并查看结果。
测试要点: 每个 Tool 覆盖正常 + 异常路径、边界值、外部服务失败模拟。
bash
npx @modelcontextprotocol/inspector node dist/index.js
View all tools/resources in the browser, manually call them and check results.
Test Key Points: Each Tool covers normal + abnormal paths, boundary values, and simulation of external service failures.

7. 安全考虑

7. Security Considerations

权限控制:
  • 最小权限原则,读写 Tool 分离
  • 危险操作要求确认参数(如
    confirm: true
输入安全:
  • SQL 注入 → 参数化查询,绝不拼接
  • 路径遍历 → 校验路径,禁止
    ../
  • 命令注入 → 用
    execFile
    而非
    exec
敏感数据:
  • 密钥通过环境变量传入,不硬编码
  • 日志不打印完整敏感信息
  • 返回数据做脱敏处理
沙箱: 文件操作限制目录、网络请求限制白名单、设置资源配额。
Permission Control:
  • Principle of least privilege, separate read and write Tools
  • Hazardous operations require confirmation parameters (e.g.,
    confirm: true
    )
Input Security:
  • SQL injection → Parameterized queries, never concatenate
  • Path traversal → Validate paths, prohibit
    ../
  • Command injection → Use
    execFile
    instead of
    exec
Sensitive Data:
  • Pass keys via environment variables, do not hardcode
  • Do not print complete sensitive information in logs
  • Desensitize returned data
Sandbox: Restrict directories for file operations, whitelist network requests, set resource quotas.

8. 部署和分发

8. Deployment and Distribution

npm 发布

npm Publishing

json
{ "bin": { "mcp-server-myservice": "dist/index.js" }, "files": ["dist"] }
用户配置:
json
{ "mcpServers": { "myservice": { "command": "npx", "args": ["@yourorg/mcp-server-myservice"], "env": { "API_KEY": "xxx" } } } }
json
{ "bin": { "mcp-server-myservice": "dist/index.js" }, "files": ["dist"] }
User configuration:
json
{ "mcpServers": { "myservice": { "command": "npx", "args": ["@yourorg/mcp-server-myservice"], "env": { "API_KEY": "xxx" } } } }

pip 发布

pip Publishing

toml
[project.scripts]
mcp-server-myservice = "my_mcp_server.server:main"
toml
[project.scripts]
mcp-server-myservice = "my_mcp_server.server:main"

Docker — 适用于复杂依赖或隔离场景

Docker — Suitable for complex dependencies or isolation scenarios

dockerfile
FROM node:20-slim
WORKDIR /app
COPY package*.json ./ && RUN npm ci --production
COPY dist ./dist
ENTRYPOINT ["node", "dist/index.js"]
dockerfile
FROM node:20-slim
WORKDIR /app
COPY package*.json ./ && RUN npm ci --production
COPY dist ./dist
ENTRYPOINT ["node", "dist/index.js"]

9. 调试技巧

9. Debugging Tips

关键:MCP 用 stdio 通信,不能用
console.log
,会破坏协议流。
typescript
// 错误
console.log("debug");
// 正确
console.error("[DEBUG]", info);
// 更好
server.sendLoggingMessage({ level: "info", data: "处理中" });
常见问题:
症状原因解决
启动无响应transport 未连接检查
server.connect()
Tool 不出现注册在 connect 之后先注册再 connect
AI 不调用 Tool描述不清晰改善名称和描述
参数总错Schema 不明确添加
.describe()
调用超时外部服务慢加超时和缓存
调试流程: Inspector 验证基本功能 → 手动调用确认输入输出 → 连接真实 AI 客户端观察调用模式 → 根据实际行为调整设计。
Key Note: MCP uses stdio for communication; do not use
console.log
as it will break the protocol flow.
typescript
// Wrong
console.log("debug");
// Correct
console.error("[DEBUG]", info);
// Better
server.sendLoggingMessage({ level: "info", data: "Processing" });
Common Issues:
SymptomCauseSolution
No response on startupTransport not connectedCheck
server.connect()
Tool does not appearRegistered after connectRegister first then connect
AI does not call ToolUnclear descriptionImprove name and description
Parameter errors always occurAmbiguous SchemaAdd
.describe()
Call timeoutSlow external serviceAdd timeout and caching
Debugging Process: Verify basic functions with Inspector → Manually call to confirm input and output → Connect to real AI client to observe call patterns → Adjust design based on actual behavior.

10. 构建检查清单

10. Construction Checklist

设计

Design

  • 明确 Tools vs Resources vs Prompts 分工
  • Tool 命名
    动词_名词
    ,描述说明用途和返回内容
  • 参数简洁,可选参数有合理默认值
  • Clarify the division of responsibilities between Tools vs Resources vs Prompts
  • Tool names follow
    verb_noun
    format, descriptions explain purpose and return content
  • Parameters are concise, optional parameters have reasonable default values

实现

Implementation

  • 输入用 Zod/Pydantic 校验
  • 外部调用有 try/catch 和超时
  • 错误返回
    isError: true
    并附可操作信息
  • 不用
    console.log
    (用 stderr 或 SDK 日志)
  • 敏感数据走环境变量
  • Use Zod/Pydantic for input validation
  • External calls have try/catch and timeouts
  • Errors return
    isError: true
    with actionable information
  • Do not use
    console.log
    (use stderr or SDK logging)
  • Sensitive data is passed via environment variables

测试

Testing

  • 核心逻辑有单元测试
  • 有集成测试验证 MCP 协议交互
  • 用 MCP Inspector 手动验证过
  • 用真实 AI 客户端测试过
  • Core logic has unit tests
  • Integration tests verify MCP protocol interactions
  • Manually verified with MCP Inspector
  • Tested with real AI clients

部署

Deployment

  • README 含安装和配置说明
  • 提供客户端配置 JSON 示例
  • 遵循 semver,无硬编码密钥
  • README includes installation and configuration instructions
  • Provide client configuration JSON examples
  • Follow semver, no hardcoded keys