{"slug":"mcp-builder-4","title":"mcp-builder","summary":"MCP 服务器构建方法论 — 系统化构建生产级 MCP 工具，让 AI 助手连接外部能力","platform":"Claude","tags":["mcp"],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-07T18:38:25.713169Z","repo":{"url":"https://github.com/jnMetaCode/superpowers-zh","stars":8205,"forks":767,"license":"MIT","updatedAt":"2026-09-17T09:11:21Z"},"bodyHtml":"<hr>\n<h2>name: mcp-builder\ndescription: MCP 服务器构建方法论 — 系统化构建生产级 MCP 工具，让 AI 助手连接外部能力\nversion: \"1.0.0\"\nlicense: MIT\nmetadata:\nhermes:\ntags: [mcp, development]</h2>\n<h1>MCP 服务器构建</h1>\n<p>系统化设计、实现、测试和部署 Model Context Protocol 服务器的方法论。</p>\n<h2>1. 协议核心概念</h2>\n<p>MCP 定义三种原语：</p>\n<ul>\n<li><strong>Tools（工具）</strong>：AI 助手主动调用的函数，有副作用。如搜索、创建、删除操作。</li>\n<li><strong>Resources（资源）</strong>：AI 助手只读访问的数据源，用 URI 标识。如 <code>users://{id}/profile</code>。</li>\n<li><strong>Prompts（提示词模板）</strong>：预定义交互模板，引导用户触发工作流。</li>\n</ul>\n<p><strong>选择原则：</strong> 执行操作 → Tool | 读取数据 → Resource | 引导交互 → Prompt</p>\n<h2>2. 项目结构规范</h2>\n<h3>TypeScript</h3>\n<pre><code>my-mcp-server/\n├── src/\n│   ├── index.ts          # 入口，注册 tools/resources\n│   ├── tools/             # 按功能拆分\n│   ├── resources/\n│   └── lib/               # 客户端封装、校验逻辑\n├── tests/\n├── package.json\n└── tsconfig.json\n</code></pre>\n<p>关键依赖：<code>@modelcontextprotocol/sdk</code> + <code>zod</code></p>\n<h3>Python</h3>\n<pre><code>my-mcp-server/\n├── src/my_mcp_server/\n│   ├── server.py\n│   ├── tools/\n│   └── lib/\n├── tests/\n└── pyproject.toml\n</code></pre>\n<p>关键依赖：<code>mcp</code> + <code>pydantic</code></p>\n<h2>3. Tool 设计原则</h2>\n<h3>命名</h3>\n<ul>\n<li><code>snake_case</code> 格式，动词开头：<code>search_users</code>、<code>create_issue</code>、<code>delete_file</code></li>\n<li>名称自解释，AI 助手靠名称选工具，模糊命名导致误调用</li>\n</ul>\n<h3>参数</h3>\n<ul>\n<li>每个参数有类型约束和 <code>.describe()</code> 描述</li>\n<li>可选参数给默认值，减少 AI 决策负担</li>\n<li>用枚举代替布尔开关</li>\n</ul>\n<pre><code>server.tool(\"search_issues\", {\n  query: z.string().describe(\"搜索关键词\"),\n  status: z.enum([\"open\", \"closed\", \"all\"]).default(\"open\").describe(\"状态筛选\"),\n  limit: z.number().min(1).max(100).default(20).describe(\"返回上限\"),\n}, async ({ query, status, limit }) =&gt; { /* ... */ });\n</code></pre>\n<h3>描述</h3>\n<p>说明<strong>用途 + 返回内容 + 限制</strong>，这是 AI 选择工具的关键依据：</p>\n<pre><code>server.tool(\"search_users\",\n  \"根据姓名或邮箱搜索用户。返回 ID、姓名、邮箱列表。模糊匹配，最多 50 条。\",\n  schema, handler);\n</code></pre>\n<h3>输出</h3>\n<ul>\n<li>结构化数据 → JSON，人类可读内容 → Markdown</li>\n<li>始终用 <code>content: [{ type: \"text\", text: \"...\" }]</code> 格式返回</li>\n</ul>\n<h2>4. 输入验证和错误处理</h2>\n<p>用 Zod/Pydantic 做 Schema 级校验，业务级校验放 handler 开头：</p>\n<pre><code>server.tool(\"get_user\", { id: z.string() }, async ({ id }) =&gt; {\n  try {\n    const user = await db.getUser(id);\n    if (!user) {\n      return {\n        content: [{ type: \"text\", text: `用户 ${id} 不存在，请检查 ID。` }],\n        isError: true,\n      };\n    }\n    return { content: [{ type: \"text\", text: JSON.stringify(user, null, 2) }] };\n  } catch (err) {\n    return {\n      content: [{ type: \"text\", text: `查询失败：${err.message}` }],\n      isError: true,\n    };\n  }\n});\n</code></pre>\n<p><strong>错误处理四原则：</strong></p>\n<ol>\n<li>永远不让服务器崩溃 — try/catch 包裹所有外部调用</li>\n<li>返回可操作的错误信息 — 告诉 AI 问题是什么、能做什么</li>\n<li>使用 <code>isError: true</code> — 让 AI 知道调用失败</li>\n<li>区分错误类型 — 参数错误、权限不足、资源不存在、服务不可用</li>\n</ol>\n<h2>5. 资源管理和生命周期</h2>\n<pre><code>// 资源注册\nserver.resource(\"user-profile\", \"users://{userId}/profile\", async (uri) =&gt; {\n  const profile = await db.getProfile(extractId(uri));\n  return { contents: [{ uri: uri.href, mimeType: \"application/json\", text: JSON.stringify(profile) }] };\n});\n\n// 生命周期：先初始化 → 再 connect → 监听关闭信号\nconst db = await Database.connect(config.dbUrl);\nawait server.connect(new StdioServerTransport());\nprocess.on(\"SIGINT\", async () =&gt; { await db.disconnect(); await server.close(); process.exit(0); });\n</code></pre>\n<p>关键点：使用连接池、所有外部调用设超时、优雅关闭清理资源。</p>\n<h2>6. 测试策略</h2>\n<h3>单元测试 — 业务逻辑与 MCP 注册分离</h3>\n<pre><code>// tools/search.ts 导出纯函数\nexport async function searchUsers(query: string, limit: number) { /* ... */ }\n\n// search.test.ts 独立测试\ntest(\"返回匹配结果\", async () =&gt; {\n  const results = await searchUsers(\"alice\", 10);\n  expect(results[0].name).toContain(\"Alice\");\n});\n</code></pre>\n<h3>集成测试 — 用 SDK Client 做端到端验证</h3>\n<pre><code>const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();\nawait server.connect(serverTransport);\nconst client = new Client({ name: \"test\", version: \"1.0.0\" });\nawait client.connect(clientTransport);\nconst result = await client.callTool(\"search_users\", { query: \"test\" });\nexpect(result.isError).toBeFalsy();\n</code></pre>\n<h3>MCP Inspector — 交互式调试</h3>\n<pre><code>npx @modelcontextprotocol/inspector node dist/index.js\n</code></pre>\n<p>在浏览器中查看所有 tools/resources，手动调用并查看结果。</p>\n<p><strong>测试要点：</strong> 每个 Tool 覆盖正常 + 异常路径、边界值、外部服务失败模拟。</p>\n<h2>7. 安全考虑</h2>\n<p><strong>权限控制：</strong></p>\n<ul>\n<li>最小权限原则，读写 Tool 分离</li>\n<li>危险操作要求确认参数（如 <code>confirm: true</code>）</li>\n</ul>\n<p><strong>输入安全：</strong></p>\n<ul>\n<li>SQL 注入 → 参数化查询，绝不拼接</li>\n<li>路径遍历 → 校验路径，禁止 <code>../</code></li>\n<li>命令注入 → 用 <code>execFile</code> 而非 <code>exec</code></li>\n</ul>\n<p><strong>敏感数据：</strong></p>\n<ul>\n<li>密钥通过环境变量传入，不硬编码</li>\n<li>日志不打印完整敏感信息</li>\n<li>返回数据做脱敏处理</li>\n</ul>\n<p><strong>沙箱：</strong> 文件操作限制目录、网络请求限制白名单、设置资源配额。</p>\n<h2>8. 部署和分发</h2>\n<h3>npm 发布</h3>\n<pre><code>{ \"bin\": { \"mcp-server-myservice\": \"dist/index.js\" }, \"files\": [\"dist\"] }\n</code></pre>\n<p>用户配置：</p>\n<pre><code>{ \"mcpServers\": { \"myservice\": { \"command\": \"npx\", \"args\": [\"@yourorg/mcp-server-myservice\"], \"env\": { \"API_KEY\": \"xxx\" } } } }\n</code></pre>\n<h3>pip 发布</h3>\n<pre><code>[project.scripts]\nmcp-server-myservice = \"my_mcp_server.server:main\"\n</code></pre>\n<h3>Docker — 适用于复杂依赖或隔离场景</h3>\n<pre><code>FROM node:20-slim\nWORKDIR /app\nCOPY package*.json ./ &amp;&amp; RUN npm ci --production\nCOPY dist ./dist\nENTRYPOINT [\"node\", \"dist/index.js\"]\n</code></pre>\n<h2>9. 调试技巧</h2>\n<p><strong>关键：MCP 用 stdio 通信，不能用 <code>console.log</code>，会破坏协议流。</strong></p>\n<pre><code>// 错误\nconsole.log(\"debug\");\n// 正确\nconsole.error(\"[DEBUG]\", info);\n// 更好\nserver.sendLoggingMessage({ level: \"info\", data: \"处理中\" });\n</code></pre>\n<p><strong>常见问题：</strong></p>\n<table>\n<thead>\n<tr>\n<th>症状</th>\n<th>原因</th>\n<th>解决</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>启动无响应</td>\n<td>transport 未连接</td>\n<td>检查 <code>server.connect()</code></td>\n</tr>\n<tr>\n<td>Tool 不出现</td>\n<td>注册在 connect 之后</td>\n<td>先注册再 connect</td>\n</tr>\n<tr>\n<td>AI 不调用 Tool</td>\n<td>描述不清晰</td>\n<td>改善名称和描述</td>\n</tr>\n<tr>\n<td>参数总错</td>\n<td>Schema 不明确</td>\n<td>添加 <code>.describe()</code></td>\n</tr>\n<tr>\n<td>调用超时</td>\n<td>外部服务慢</td>\n<td>加超时和缓存</td>\n</tr>\n</tbody>\n</table>\n<p><strong>调试流程：</strong> Inspector 验证基本功能 → 手动调用确认输入输出 → 连接真实 AI 客户端观察调用模式 → 根据实际行为调整设计。</p>\n<h2>10. 构建检查清单</h2>\n<h3>设计</h3>\n<ul>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 明确 Tools vs Resources vs Prompts 分工</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> Tool 命名 <code>动词_名词</code>，描述说明用途和返回内容</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 参数简洁，可选参数有合理默认值</li>\n</ul>\n<h3>实现</h3>\n<ul>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 输入用 Zod/Pydantic 校验</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 外部调用有 try/catch 和超时</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 错误返回 <code>isError: true</code> 并附可操作信息</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 不用 <code>console.log</code>（用 stderr 或 SDK 日志）</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 敏感数据走环境变量</li>\n</ul>\n<h3>测试</h3>\n<ul>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 核心逻辑有单元测试</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 有集成测试验证 MCP 协议交互</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 用 MCP Inspector 手动验证过</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 用真实 AI 客户端测试过</li>\n</ul>\n<h3>部署</h3>\n<ul>\n<li><input disabled=\"disabled\" type=\"checkbox\"> README 含安装和配置说明</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 提供客户端配置 JSON 示例</li>\n<li><input disabled=\"disabled\" type=\"checkbox\"> 遵循 semver，无硬编码密钥</li>\n</ul>\n","files":[{"path":"SKILL.md","sizeBytes":7986,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-12T07:22:29.9366Z","sha256":"E00DA5555C98DF3CF433A5A132A40B05CC5C9698D2C59DCDB53D872B877A04B3","sizeBytes":4346},"review":null,"source":{"repositoryUrl":"https://github.com/jnMetaCode/superpowers-zh","path":"skills/mcp-builder","license":"MIT","commit":"78cb4f68d691d516eb7216897053ea574212cdf6","subtreeSha":"6DABC6C58767DBA2265A866BCF5F77DF15C073C241E8D6FC7C2480BEF113675C","lastSyncedAt":"2026-09-25T06:49:13.086723Z"},"reviewedAt":"2026-09-12T07:23:52.611165Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/jnMetaCode/superpowers-zh/tree/main/skills/mcp-builder"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install jnmetacode-superpowers-zh@llmmart"},{"target":"git","command":"git clone https://github.com/jnMetaCode/superpowers-zh.git"}]}