MCP Server 开发实战
理解了 MCP 协议的原理后,本章进入实战——如何从零开发一个 MCP Server。我们将使用 Python SDK 构建一个完整的 MCP Server,涵盖工具(Tools)、资源(Resources)和提示词模板(Prompts)的开发,并讲解调试和部署。
理解了 MCP 协议的原理后,本章进入实战——如何从零开发一个 MCP Server。我们将使用 Python SDK 构建一个完整的 MCP Server,涵盖工具(Tools)、资源(Resources)和提示词模板(Prompts)的开发,并讲解调试和部署。
建议先完成第 05 章
开发环境搭建、Python SDK 开发 MCP Server、配置与集成
连接协议 · MCP 与 Agent 生态
文章导航
点击图中节点可定位到对应正文。
引言
理解了 MCP 协议的原理后,本章进入实战——如何从零开发一个 MCP Server。我们将使用 Python SDK 构建一个完整的 MCP Server,涵盖工具(Tools)、资源(Resources)和提示词模板(Prompts)的开发,并讲解调试和部署。
1. 开发环境搭建
1.1 安装 SDK
# Python SDK
pip install mcp
# TypeScript SDK(二选一)
npm install @modelcontextprotocol/sdk
# 辅助工具
pip install mcp[cli] # MCP 开发调试 CLI1.2 项目结构
根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
my-mcp-server/
├── server.py # 主服务文件
├── tools/ # 工具实现
│ ├── __init__.py
│ ├── search.py # 搜索工具
│ └── analytics.py # 分析工具
├── resources/ # 资源实现
│ ├── __init__.py
│ └── data.py # 数据资源
├── prompts/ # 提示词模板
│ ├── __init__.py
│ └── templates.py
├── requirements.txt
└── mcp-config.json # MCP 配置文件2. Python SDK 开发 MCP Server
2.1 基础 Server 框架
根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.list_tools()
async def list_tools() -> list[Tool]:
"""返回所有可用工具的列表"""
return [
Tool(
name="query_data",
description="查询数据分析结果。当需要获取特定指标或统计数据时使用此工具。",
inputSchema={
"type": "object",
"properties": {
"metric": {
"type": "string",
"description": "要查询的指标名称,如 'revenue', 'users', 'conversion'",
"enum": ["revenue", "users", "conversion", "retention"]
},
"time_range": {
"type": "string",
"description": "时间范围",
"enum": ["today", "week", "month", "quarter", "year"]
},
"filters": {
"type": "object",
"description": "可选的过滤条件",
"properties": {
"region": {"type": "string"},
"product": {"type": "string"}
}
}
},
"required": ["metric", "time_range"]
}
),
Tool(
name="generate_report",
description="生成数据分析报告。支持多种格式和详细程度。",
inputSchema={
"type": "object",
"properties": {
"report_type": {
"type": "string",
"enum": ["daily", "weekly", "monthly", "custom"]
},
"format": {
"type": "string",
"enum": ["text", "markdown", "json"],
"default": "markdown"
},
"include_charts": {
"type": "boolean",
"default": True
}
},
"required": ["report_type"]
}
)
]根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""处理工具调用请求"""根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.list_resources()
async def list_resources() -> list[Resource]:
"""返回所有可用资源的列表"""
return [
Resource(
uri="data://metrics/overview",
name="数据概览",
description="所有核心指标的概览数据",
mimeType="application/json"
),
Resource(
uri="data://config/settings",
name="系统配置",
description="当前数据分析系统的配置信息",
mimeType="application/json"
)
]根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.list_resource_templates()
async def list_resource_templates() -> list[ResourceTemplate]:
"""返回动态资源模板"""
return [
ResourceTemplate(
uriTemplate="data://metrics/{metric_name}",
name="指标详情",
description="获取指定指标的详细数据",
mimeType="application/json"
)
]根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.read_resource()
async def read_resource(uri: str) -> str:
"""读取资源内容"""
if uri == "data://metrics/overview":
return json.dumps(get_overview_data())
elif uri.startswith("data://metrics/"):
metric_name = uri.split("/")[-1]
return json.dumps(get_metric_detail(metric_name))
else:
raise ValueError(f"未知资源: {uri}")根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.list_prompts()
async def list_prompts() -> list[Prompt]:
return [
Prompt(
name="analyze_trend",
description="分析数据趋势并提供洞察",
arguments=[
PromptArgument(
name="metric",
description="要分析的指标",
required=True
),
PromptArgument(
name="period",
description="分析周期(如 7d, 30d, 90d)",
required=True
)
]
)
]根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.get_prompt()
async def get_prompt(name: str, arguments: dict) -> dict:
if name == "analyze_trend":
return {
"messages": [
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"""请分析 {arguments['metric']} 指标在最近 {arguments['period']} 的趋势:根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
async def handle_query_data(arguments: dict) -> str:
"""查询数据的实际实现"""
metric = arguments["metric"]
time_range = arguments["time_range"]
filters = arguments.get("filters", {})根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
async def handle_generate_report(arguments: dict) -> str:
"""生成报告的实际实现"""
report_type = arguments["report_type"]
fmt = arguments.get("format", "markdown")2.2 TypeScript SDK 实现
根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
// 注册工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "query_data",
description: "查询数据分析结果",
inputSchema: {
type: "object",
properties: {
metric: { type: "string", enum: ["revenue", "users", "conversion"] },
time_range: { type: "string", enum: ["today", "week", "month"] },
},
required: ["metric", "time_range"],
},
},
],
}));根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;3. 配置与集成
3.1 Claude Desktop 配置
// claude_desktop_config.json
{
"mcpServers": {
"my-data-server": {
"command": "python",
"args": ["path/to/server.py"],
"env": {
"DATABASE_URL": "postgresql://localhost:5432/analytics",
"API_KEY": "your-api-key"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxx"
}
}
}
}3.2 多 Server 协同
根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
┌──────────────────────────────────────────────┐
│ Claude Desktop │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Client 1 │ │ Client 2 │ │ Client 3 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼─────────────┼──────────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│Database │ │ GitHub │ │ File │
│ Server │ │ Server │ │ Server │
└─────────┘ └─────────┘ └─────────┘根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
Claude 可以跨 Server 工作:
"查看 GitHub 上的 Issue,查询数据库中的相关数据,把分析结果写入报告文件"
→ GitHub Server 获取 Issue
→ Database Server 查询数据
→ Filesystem Server 写入文件4. 调试与测试
4.1 MCP Inspector
# 使用 MCP Inspector 调试 Server
npx @modelcontextprotocol/inspector python server.py
# Inspector 提供:
# - 工具列表和手动调用
# - 资源浏览和读取
# - 提示词模板测试
# - 消息日志查看4.2 单元测试
import pytest
from mcp.testing import MockClient
@pytest.fixture
async def client():
"""创建测试用的 Mock Client"""
return MockClient("server.py")
@pytest.mark.asyncio
async def test_list_tools(client):
"""测试工具列表"""
tools = await client.list_tools()
assert len(tools) >= 2
tool_names = [t.name for t in tools]
assert "query_data" in tool_names
assert "generate_report" in tool_names
@pytest.mark.asyncio
async def test_query_data(client):
"""测试数据查询工具"""
result = await client.call_tool("query_data", {
"metric": "revenue",
"time_range": "month"
})
assert not result.isError
assert "revenue" in result.content[0].text.lower()
@pytest.mark.asyncio
async def test_invalid_metric(client):
"""测试无效指标的错误处理"""
result = await client.call_tool("query_data", {
"metric": "invalid_metric",
"time_range": "month"
})
assert result.isError
@pytest.mark.asyncio
async def test_resources(client):
"""测试资源读取"""
resources = await client.list_resources()
assert len(resources) > 0
content = await client.read_resource("data://metrics/overview")
assert "revenue" in content or "users" in content5. 高级特性
5.1 工具进度通知
根据原图的箭头、并列、分层与循环关系选择对应图形;可展开核对原文结构。
查看原文结构
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "long_running_analysis":
# 发送进度通知
await server.send_progress("正在连接数据库...")
data = await fetch_data()5.2 资源订阅(变更通知)
# 客户端订阅资源变更
@server.subscribe_resource()
async def handle_subscribe(uri: str):
"""当客户端订阅某个资源时"""
# 注册变更通知
database.on_change(uri, lambda: server.send_resource_updated(uri))
# 当数据变更时,自动通知所有订阅者
async def on_data_change(table: str):
uri = f"data://metrics/{table}"
await server.send_notification(
"notifications/resources/updated",
{"uri": uri}
)5.3 采样能力(Server 请求 LLM)
# Server 可以请求 Host 的 LLM 能力(需要 Host 支持)
async def smart_tool_selection(task: str):
"""让 LLM 帮助选择最合适的工具组合"""
result = await server.create_message(
messages=[{
"role": "user",
"content": {
"type": "text",
"text": f"任务:{task}\n请从可用工具中选择最合适的执行方案"
}
}],
max_tokens=500
)
return result6. 部署方案
6.1 本地部署(stdio)
// 配置文件中直接使用
{
"mcpServers": {
"local-server": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}6.2 远程部署(HTTP + SSE)
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
# 创建 SSE 传输
sse = SseServerTransport("/mcp/")
async def handle_sse(request):
async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
await server.run(streams[0], streams[1], server.create_initialization_options())
async def handle_message(request):
await sse.handle_post_message(request.scope, request.receive, request._send)
app = Starlette(routes=[
Route("/mcp/sse", endpoint=handle_sse),
Route("/mcp/messages", endpoint=handle_message, methods=["POST"]),
])
# 部署
# uvicorn server_http:app --host 0.0.0.0 --port 80806.3 Docker 部署
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# stdio 模式
CMD ["python", "server.py"]// Docker 方式配置
{
"mcpServers": {
"docker-server": {
"command": "docker",
"args": ["run", "-i", "--rm", "my-mcp-server"],
"env": {
"DATABASE_URL": "postgresql://host.docker.internal:5432/analytics"
}
}
}
}7. 本章小结
| 维度 | 要点 |
|---|---|
| SDK 选择 | Python SDK(快速开发)/ TypeScript SDK(生态兼容) |
| 核心步骤 | 定义工具/资源/提示词 → 实现处理逻辑 → 配置启动 |
| 工具描述 | 越详细越好,直接影响 LLM 的选择准确性 |
| 调试 | MCP Inspector + 单元测试 |
| 部署 | stdio(本地)/ HTTP+SSE(远程)/ Docker(容器化) |
| 安全 | 最小权限、沙箱隔离、用户确认 |
相关章节
- MCP 协议详解 — 开发前应掌握的协议基础
- Agent 生态系统全景 — MCP Server 在整个生态中的定位
- LangChain 与 LangGraph — 将 MCP Server 集成到主流框架
延伸阅读
- MCP Python SDK: github.com/modelcontextprotocol/python-sdk
- MCP TypeScript SDK: github.com/modelcontextprotocol/typescript-sdk
- MCP Server 示例库: github.com/modelcontextprotocol/servers
- MCP Inspector: github.com/modelcontextprotocol/inspector