MCP Server 开发实战
问题
如何从零开发一个 MCP Server?TypeScript 和 Python SDK 怎么用?
答案
一、开发流程
二、TypeScript 完整示例
以下实现一个天气查询 MCP Server:
- npm
- Yarn
- pnpm
- Bun
npm install @modelcontextprotocol/sdk zod
yarn add @modelcontextprotocol/sdk zod
pnpm add @modelcontextprotocol/sdk zod
bun add @modelcontextprotocol/sdk zod
weather-server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 1. 创建 Server 实例
const server = new McpServer({
name: "weather-server",
version: "1.0.0",
});
// 2. 注册 Tool
server.tool(
"get_weather", // 工具名称
"获取指定城市的天气信息", // 描述(模型据此决定是否调用)
{
city: z.string().describe("城市名称"), // 参数 Schema(Zod)
unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
},
async ({ city, unit }) => {
// 3. 实现逻辑
const weather = await fetchWeather(city, unit);
return {
content: [
{
type: "text",
text: `${city} 当前天气:${weather.temp}°${unit === "celsius" ? "C" : "F"},${weather.condition}`,
},
],
};
}
);
// 4. 注册 Resource
server.resource(
"weather://cities",
"支持查询的城市列表",
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(["北京", "上海", "广州", "深圳"]),
},
],
})
);
// 5. 启动 Server(stdio 传输)
const transport = new StdioServerTransport();
await server.connect(transport);
三、Python 完整示例
pip install mcp
weather_server.py
from mcp.server.fastmcp import FastMCP
# 1. 创建 Server
mcp = FastMCP("weather-server")
# 2. 注册 Tool(装饰器语法)
@mcp.tool()
async def get_weather(city: str, unit: str = "celsius") -> str:
"""获取指定城市的天气信息
Args:
city: 城市名称
unit: 温度单位(celsius 或 fahrenheit)
"""
weather = await fetch_weather(city, unit)
symbol = "°C" if unit == "celsius" else "°F"
return f"{city} 当前天气:{weather['temp']}{symbol},{weather['condition']}"
# 3. 注册 Resource
@mcp.resource("weather://cities")
async def list_cities() -> str:
"""支持查询的城市列表"""
return json.dumps(["北京", "上海", "广州", "深圳"])
# 4. 注册 Prompt
@mcp.prompt()
async def weather_report(city: str) -> str:
"""生成天气报告的 Prompt 模板"""
return f"请查询 {city} 的天气信息,并生成一份简要的天气报告。"
# 5. 启动
mcp.run(transport="stdio")
四、配置到 Claude Desktop
claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/path/to/weather-server.js"],
"env": {
"API_KEY": "your-api-key"
}
}
}
}
五、错误处理
server.tool("risky_operation", "可能失败的操作", {}, async () => {
try {
const result = await doSomething();
return {
content: [{ type: "text", text: result }],
};
} catch (error) {
// 返回错误信息而非抛异常
return {
content: [{ type: "text", text: `操作失败:${error.message}` }],
isError: true, // 标记为错误
};
}
});
六、调试技巧
# 使用 MCP Inspector 调试
npx @modelcontextprotocol/inspector node ./weather-server.js
MCP Inspector 提供 Web UI,可以:
- 查看 Server 注册的 Tools/Resources/Prompts
- 手动调用工具并查看响应
- 查看 JSON-RPC 消息日志
常见面试问题
Q1: MCP Server 开发有哪些最佳实践?
答案:
- 工具命名:使用
动词_名词格式(query_database、send_email) - 描述精确:模型根据 description 决定是否调用,要具体说明用途和限制
- Schema 完整:类型、枚举、必填字段都要定义,减少模型幻觉
- 幂等性:工具调用应尽量幂等,防止重复执行问题
- 错误处理:用
isError: true返回错误,不要抛未捕获异常
Q2: TypeScript SDK 和 Python SDK 有什么区别?
答案:
- TypeScript SDK:使用 Zod schema 定义参数,
McpServer+server.tool()API - Python SDK:使用
FastMCP高级 API,装饰器语法更简洁(@mcp.tool()) - Python SDK 的参数 schema 自动从函数签名 + docstring 推断,开发体验更好
- 两者功能完全对等