mcp-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMCP 服务器构建
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/sdkzodmy-mcp-server/
├── src/
│ ├── index.ts # Entry point, register tools/resources
│ ├── tools/ # Split by functionality
│ ├── resources/
│ └── lib/ # Client encapsulation, validation logic
├── tests/
├── package.json
└── tsconfig.jsonKey Dependencies: +
@modelcontextprotocol/sdkzodPython
Python
my-mcp-server/
├── src/my_mcp_server/
│ ├── server.py
│ ├── tools/
│ └── lib/
├── tests/
└── pyproject.toml关键依赖: +
mcppydanticmy-mcp-server/
├── src/my_mcp_server/
│ ├── server.py
│ ├── tools/
│ └── lib/
├── tests/
└── pyproject.tomlKey Dependencies: +
mcppydantic3. Tool 设计原则
3. Tool Design Principles
命名
Naming
- 格式,动词开头:
snake_case、search_users、create_issuedelete_file - 名称自解释,AI 助手靠名称选工具,模糊命名导致误调用
- Use format, starting with a verb:
snake_case,search_users,create_issuedelete_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 description
.describe() - 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,
};
}
});错误处理四原则:
- 永远不让服务器崩溃 — try/catch 包裹所有外部调用
- 返回可操作的错误信息 — 告诉 AI 问题是什么、能做什么
- 使用 — 让 AI 知道调用失败
isError: true - 区分错误类型 — 参数错误、权限不足、资源不存在、服务不可用
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:
- Never let the server crash — Wrap all external calls with try/catch
- Return actionable error messages — Tell the AI what the problem is and what can be done
- Use — Let the AI know the call failed
isError: true - 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.jsView 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 注入 → 参数化查询,绝不拼接
- 路径遍历 → 校验路径,禁止
../ - 命令注入 → 用 而非
execFileexec
敏感数据:
- 密钥通过环境变量传入,不硬编码
- 日志不打印完整敏感信息
- 返回数据做脱敏处理
沙箱: 文件操作限制目录、网络请求限制白名单、设置资源配额。
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 instead of
execFileexec
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.logtypescript
// 错误
console.log("debug");
// 正确
console.error("[DEBUG]", info);
// 更好
server.sendLoggingMessage({ level: "info", data: "处理中" });常见问题:
| 症状 | 原因 | 解决 |
|---|---|---|
| 启动无响应 | transport 未连接 | 检查 |
| Tool 不出现 | 注册在 connect 之后 | 先注册再 connect |
| AI 不调用 Tool | 描述不清晰 | 改善名称和描述 |
| 参数总错 | Schema 不明确 | 添加 |
| 调用超时 | 外部服务慢 | 加超时和缓存 |
调试流程: Inspector 验证基本功能 → 手动调用确认输入输出 → 连接真实 AI 客户端观察调用模式 → 根据实际行为调整设计。
Key Note: MCP uses stdio for communication; do not use as it will break the protocol flow.
console.logtypescript
// Wrong
console.log("debug");
// Correct
console.error("[DEBUG]", info);
// Better
server.sendLoggingMessage({ level: "info", data: "Processing" });Common Issues:
| Symptom | Cause | Solution |
|---|---|---|
| No response on startup | Transport not connected | Check |
| Tool does not appear | Registered after connect | Register first then connect |
| AI does not call Tool | Unclear description | Improve name and description |
| Parameter errors always occur | Ambiguous Schema | Add |
| Call timeout | Slow external service | Add 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 format, descriptions explain purpose and return content
verb_noun - Parameters are concise, optional parameters have reasonable default values
实现
Implementation
- 输入用 Zod/Pydantic 校验
- 外部调用有 try/catch 和超时
- 错误返回 并附可操作信息
isError: true - 不用 (用 stderr 或 SDK 日志)
console.log - 敏感数据走环境变量
- Use Zod/Pydantic for input validation
- External calls have try/catch and timeouts
- Errors return with actionable information
isError: true - Do not use (use stderr or SDK logging)
console.log - 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