自定义 MCP Server 开发 本节摘要:现成的 MCP Server 覆盖了通用场景,但当你需要连接公司内部系统(如内部知识库、业务数据库、部署平台)时,就得自己写一个。好消息是:MCP SDK 把协议细节全部封装了,你只需要关注「暴露什么函数、函数做什么」。本节用 Python SDK 和 TypeScript SDK 各写一个最简 Server,讲清 Tool / Resource / Prompt 三种原语的开发方式,以及调试和排错技巧。 一、开发前的准备 选择语言: Python SDK: (适合连接 Python 生态的系统) TypeScript SDK: (适合 Node.js 生态) 你需要想清楚的事: 这个 Server 要暴露什么能力?
本节摘要:现成的 MCP Server 覆盖了通用场景,但当你需要连接公司内部系统(如内部知识库、业务数据库、部署平台)时,就得自己写一个。好消息是:MCP SDK 把协议细节全部封装了,你只需要关注「暴露什么函数、函数做什么」。本节用 Python SDK 和 TypeScript SDK 各写一个最简 Server,讲清 Tool / Resource / Prompt 三种原语的开发方式,以及调试和排错技巧。
选择语言:
pip install mcp(适合连接 Python 生态的系统)npm install @modelcontextprotocol/sdk(适合 Node.js 生态)你需要想清楚的事:
假设你要写一个「查询公司内部员工信息」的 Server:
from mcp.server.fastmcp import FastMCP # 创建 Server 实例 mcp = FastMCP("employee-server") # 定义一个 Tool @mcp.tool() def get_employee(employee_id: str) -> str: """根据工号查询员工信息。 Args: employee_id: 员工工号,如 "EMP001" """ # 实际项目中这里查数据库或调内部 API employees = { "EMP001": {"name": "张三", "dept": "工程部", "email": "zhang@company.com"}, "EMP002": {"name": "李四", "dept": "产品部", "email": "li@company.com"}, } emp = employees.get(employee_id) if emp: return f"姓名: {emp['name']}, 部门: {emp['dept']}, 邮箱: {emp['email']}" return f"未找到工号 {employee_id} 的员工" # 定义另一个 Tool @mcp.tool() def list_departments() -> str: """列出公司所有部门及其人数。""" return "工程部(15人), 产品部(8人), 设计部(5人), 运营部(6人)" # 启动(stdio 模式) if __name__ == "__main__": mcp.run()
关键点:
@mcp.tool() 装饰器把一个函数注册为 Toolmcp.run() 默认用 stdio 传输import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; const server = new McpServer({ name: "employee-server", version: "1.0.0", }); // 定义 Tool server.tool( "get_employee", "根据工号查询员工信息", { employee_id: z.string().describe("员工工号,如 EMP001") }, async ({ employee_id }) => { const employees: Record<string, object> = { EMP001: { name: "张三", dept: "工程部" }, EMP002: { name: "李四", dept: "产品部" }, }; const emp = employees[employee_id]; return { content: [{ type: "text", text: emp ? JSON.stringify(emp) : "未找到" }], }; } ); // 启动 const transport = new StdioServerTransport(); await server.connect(transport);
上面已经演示。核心:定义函数 → 写清描述 → 注册。
Python 示例:
@mcp.resource("config://app") def get_app_config() -> str: """返回应用当前配置。""" return "环境: production\n版本: 2.1.0\n区域: cn-east"
AI 可以在需要时读取这个 Resource,获取背景信息。
Python 示例:
@mcp.prompt() def review_code(code: str) -> str: """生成代码审查提示。""" return f"请审查以下代码,关注安全性、性能和可维护性:\n\n{code}"
写好 Server 后,在 IDE 中配置:
python(或 node)/path/to/employee_server.py配置方式跟第 03 节完全一样——IDE 不关心 Server 是你写的还是官方提供的,只要它遵循 MCP 协议。
官方提供的调试工具,可以独立于 IDE 测试你的 Server:
npx @modelcontextprotocol/inspector python employee_server.py
它会打开一个 Web 界面,你可以:
| 问题 | 原因 | 解法 |
|---|---|---|
| Server 启动即退出 | 代码有语法错误 | 先单独 python server.py 看报错 |
| Tool 不出现在 IDE 中 | 函数没有 docstring | 加上描述文字 |
| 调用返回乱码 | 输出混入了 print 调试信息 | stdio 模式下 stdout 只能有协议消息,调试用 stderr |
| 参数类型报错 | schema 定义不匹配 | 检查类型注解/zod schema |
⚠️ 注意:stdio 模式下,stdout 是协议通道。任何
print()输出都会破坏协议通信。调试信息必须输出到 stderr(Python 用print(..., file=sys.stderr),Node 用console.error())。
@mcp.tool() 装饰器,TypeScript 用 server.tool() 方法npx @modelcontextprotocol/inspector 独立测试至此,第 3 章 MCP 五节全部完成。你从「什么是 MCP」走到了「自己开发 Server」。下一章我们讲另一个核心机制:Skills 与项目规范——如何让 AI 记住你的编程规矩。