Skip to content
返回 AI 编程

构建自定义 MCP 服务器

端到端 MCP 服务器:工具、资源、提示、错误处理和客户端注册。

MCP 开发高级

为什么

MCP 于 2024 年 11 月发布;到 2025 年 12 月已有超过 9700 万月 SDK 下载量,到 2026 年 9 月 npm SDK 单包月下载量达约 2.04 亿(9 个月增长 5 倍),已发布服务器超 1 万个。现有入门仅覆盖一个工具。

真实服务器暴露多个工具、只读资源、可复用提示模板,且必须处理错误而不崩溃客户端。四者皆备才使服务器达到生产级,而非只成功一次并在边界情况静默失败的 demo。

何时使用

当 AI 客户端需跨多次会话持久访问你的系统(数据库、工单跟踪、部署 API)且单一工具不够时构建完整服务器。提示词加 curl 能覆盖的一次性脚本可跳过。

如何做

1. 编码前设计能力面。列出工具(动作)、资源(读取)和提示(模板)。 工具:query_db(sql)、insert_ticket(title, body) 资源:schema://db/tables、ticket://open-list 提示:"investigate-incident" 模板 2. 用 SDK 搭建并选传输: npm install @modelcontextprotocol/sdk 本地开发:stdio。共享/团队:SSE(HTTP)。 3. 实现工具,用严格 JSON Schema 和类型化错误: - inputSchema 为 AI 必须提供的每个参数加 'required'。 - 成功返回 { content: [{ type: 'text', text }] }。 - 失败返回 { isError: true, content: [...] },绝不抛异常。 4. 实现资源供 AI 浏览只读上下文: - uri 方案:db://tables、ticket://list - handler 返回文本或 JSON;保持小(每次读取约 4KB 以内)。 5. 实现提示为带参数的可复用模板: - name: "investigate-incident",arguments: { incident_id: string } - 返回客户端渲染的提示,预填资源数据。 6. 仅日志到 stderr。stdio 传输中 stdout 是协议通道。 console.error('[mcp] query_db', sql) // 安全 stdio 服务器中绝不 console.log。 7. 用 inspector 测试,然后注册到客户端: npx @modelcontextprotocol/inspector node dist/server.js 在 Claude Desktop 配置(claude_desktop_config.json)的 mcpServers 下添加。

示例

生产级 MCP 服务器(工具 + 资源 + 错误处理): import { Server } from "@modelcontextprotocol/sdk/server/Server.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server( { name: "ticket-server", version: "1.0.0" }, { capabilities: { tools: {}, resources: {}, prompts: {} } }, ); // 工具:类型化错误,绝不抛异常 server.setRequestHandler("tools/call", async (req) => { const { name, arguments: args } = req.params; if (name === "insert_ticket") { if (!args?.title) { return { isError: true, content: [{ type: "text", text: "title 为必填" }] }; } const id = await db.insert(args.title, args.body ?? ""); return { content: [{ type: "text", text: `已创建工单 #${id}` }] }; } return { isError: true, content: [{ type: "text", text: `未知工具 ${name}` }] }; }); // 资源:只读上下文 server.setRequestHandler("resources/read", async (req) => { if (req.params.uri === "ticket://open-list") { const open = await db.listOpen(); return { contents: [{ uri: req.params.uri, mimeType: "application/json", text: JSON.stringify(open) }] }; } }); console.error("[mcp] ticket-server ready"); // 仅 stderr await server.connect(new StdioServerTransport());

常见问题

  • ⚠在工具 handler 中抛异常会崩溃客户端会话。始终返回 { isError: true, content }。
  • ⚠"Error: transport closed" / 服务器未被客户端列出 → 确认两侧 stdio/HTTP 传输匹配;检查 `npx @modelcontextprotocol/inspector` 输出。
  • ⚠宽松 inputSchema(无 'required')让 AI 缺参调用工具并静默失败。
  • ⚠Error: Cannot find module '@modelcontextprotocol/sdk' → 运行 `npm install @modelcontextprotocol/sdk zod`;SDK 需 Node.js 18+。

最佳实践

  • ✓编码前在纸上设计能力面(工具/资源/提示)。
  • ✓通过 isError 返回类型化错误而非抛异常;客户端保持存活并可重试。
  • ✓固定 SDK 版本并在 CI 中运行 MCP Inspector 捕获协议回归。
  • ✓资源载荷保持在约 4KB 以内;更大数据分页或摘要。
  • ✓工具描述附使用示例,使 AI 可靠地选择正确工具。

常见问题解答

MCP 工具和资源有什么区别?

工具是 AI 可调用的动作(query_db、insert_ticket),有副作用;资源是 AI 可浏览的只读数据(schema://db/tables)。变更和查询用工具,AI 行动前应读的上下文用资源。

如何向 MCP 服务器添加可复用提示模板?

实现 prompts 能力:定义带参数的命名提示(如带 incident_id 的 'investigate-incident')和返回预填资源数据的渲染提示的 handler。客户端即可将其作为类斜杠命令模板提供。

部署前如何测试 MCP 服务器?

运行 MCP Inspector:`npx @modelcontextprotocol/inspector node dist/server.js`。它可交互调用工具、读取资源、检查协议消息。注册到真实客户端前始终先用 inspector 测试。

如何防止资源载荷撑爆上下文窗口?

每次资源读取限制约 4KB,更大数据分页或摘要。结构化数据返回 JSON 以便 AI 解析。超大无界载荷浪费上下文并降低 AI 后续轮次的推理。

如何处理缺参数的工具调用?

对照 inputSchema 验证并返回 { isError: true, content: [{ type: 'text', text: 'title 为必填' }] }。绝不抛异常——抛异常崩溃客户端会话。带 required 字段的严格 schema 防止 AI 缺必要参数调用工具。