自定义 MCP Server 开发


文档摘要

自定义 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 Server 覆盖了通用场景,但当你需要连接公司内部系统(如内部知识库、业务数据库、部署平台)时,就得自己写一个。好消息是:MCP SDK 把协议细节全部封装了,你只需要关注「暴露什么函数、函数做什么」。本节用 Python SDK 和 TypeScript SDK 各写一个最简 Server,讲清 Tool / Resource / Prompt 三种原语的开发方式,以及调试和排错技巧。

一、开发前的准备

选择语言:

  • Python SDK:pip install mcp(适合连接 Python 生态的系统)
  • TypeScript SDK:npm install @modelcontextprotocol/sdk(适合 Node.js 生态)

你需要想清楚的事:

  • 这个 Server 要暴露什么能力?(Tool 列表)
  • 每个 Tool 的输入参数和输出是什么?
  • 有没有需要暴露的静态数据?(Resource)

二、Python SDK:写一个最简 Server

假设你要写一个「查询公司内部员工信息」的 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() 装饰器把一个函数注册为 Tool
  • 函数的 docstring 会变成 AI 看到的「工具描述」——写清楚,AI 才知道什么时候该调用
  • 参数类型注解会变成 Tool 的输入 schema
  • mcp.run() 默认用 stdio 传输

三、TypeScript SDK:同样的 Server

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);

四、三种原语的开发方式

Tool(最常用)

上面已经演示。核心:定义函数 → 写清描述 → 注册。

Resource(暴露静态数据)

Python 示例:

@mcp.resource("config://app") def get_app_config() -> str: """返回应用当前配置。""" return "环境: production\n版本: 2.1.0\n区域: cn-east"

AI 可以在需要时读取这个 Resource,获取背景信息。

Prompt(预定义模板)

Python 示例:

@mcp.prompt() def review_code(code: str) -> str: """生成代码审查提示。""" return f"请审查以下代码,关注安全性、性能和可维护性:\n\n{code}"

五、在 IDE 中注册自定义 Server

写好 Server 后,在 IDE 中配置:

  • Command: python(或 node)
  • Args: 你的脚本路径,如 /path/to/employee_server.py

配置方式跟第 03 节完全一样——IDE 不关心 Server 是你写的还是官方提供的,只要它遵循 MCP 协议。

六、调试与排错

MCP Inspector

官方提供的调试工具,可以独立于 IDE 测试你的 Server:

npx @modelcontextprotocol/inspector python employee_server.py

它会打开一个 Web 界面,你可以:

  • 查看 Server 声明的所有 Tool / Resource / Prompt
  • 手动调用 Tool,查看输入输出
  • 查看完整的 JSON-RPC 消息流

常见问题

问题 原因 解法
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())。

本节要点回顾

  1. 开发极简:SDK 封装了协议细节,你只需定义函数 + 写清描述
  2. Python 用 @mcp.tool() 装饰器,TypeScript 用 server.tool() 方法
  3. docstring 即文档:AI 根据函数描述决定何时调用——写清楚很重要
  4. 调试用 Inspector:npx @modelcontextprotocol/inspector 独立测试
  5. stdio 铁律:stdout 只能有协议消息,调试输出走 stderr
  6. 注册方式:跟现成 Server 一样,在 IDE 中配置 command + args 即可

至此,第 3 章 MCP 五节全部完成。你从「什么是 MCP」走到了「自己开发 Server」。下一章我们讲另一个核心机制:Skills 与项目规范——如何让 AI 记住你的编程规矩。


发布者: 作者: 灏天文库 转发
评论区 (0)
U