本教程面向希望为Claude Code构建自定义MCP服务器的中高级开发者。你将从零理解MCP(Model Context Protocol)协议架构,掌握Python(FastMCP)和TypeScript(官方SDK)两种开发路径,完成工具(Tools)、资源(Resources)、提示(Prompts)三大原语的实战开发,并学会调试、安全加固和生产部署的完整流程。学完后,你将具备为团队或社区构建生产级MCP服务器的能力。
适用人群:有Python或TypeScript基础的开发者、希望扩展Claude Code能力的AI应用工程师、需要将内部API暴露给LLM的企业开发者。
学习目标: - 深入理解MCP协议架构与通信机制 - 分别用FastMCP(Python)和TypeScript SDK构建完整的MCP服务器 - 掌握Tool、Resource、Prompt三大原语的设计与实现 - 学会使用MCP Inspector进行调试和测试 - 完成安全加固与生产环境部署
| 项目 | 要求 |
|---|---|
| Python | 3.10+(FastMCP要求) |
| Node.js | 18+(TypeScript SDK要求) |
| Claude Code | 最新版本(claude --version确认) |
| 基础知识 | JSON Schema、REST API概念、命令行操作 |
| 操作系统 | macOS / Linux / Windows(WSL2) |
Model Context Protocol(MCP)是Anthropic提出的开放协议,标准化了LLM应用与外部工具、数据源之间的通信方式。其核心理念可以用一句话概括:为AI提供一个通用的"插头"标准,让它能连接任何"插座"。
MCP采用经典的客户端-服务器架构,但在LLM场景中引入了三层模型:
graph TB
subgraph Host["Host 宿主应用"]
C1["Client 1"]
C2["Client 2"]
C3["Client N"]
end
subgraph Servers["MCP Servers"]
S1["Server A<br/>文件系统"]
S2["Server B<br/>数据库"]
S3["Server C<br/>API网关"]
end
C1 <-->|"stdio/SSE/HTTP"| S1
C2 <-->|"stdio/SSE/HTTP"| S2
C3 <-->|"stdio/SSE/HTTP"| S3
Host -->|"用户交互"| User["用户"]
S1 -->|"读写"| FS["本地文件"]
S2 -->|"查询"| DB["PostgreSQL"]
S3 -->|"调用"| API["REST API"]
style Host fill:#4A90D9,color:#fff
style Servers fill:#50C878,color:#fff
style User fill:#FFD700,color:#333
三层模型详解:
MCP定义了三种原语(Primitive),每种服务于不同的交互模式:
| 原语 | 类比 | 方向 | 典型用途 |
|---|---|---|---|
| Tools(工具) | 函数调用 | LLM → Server | 执行操作、查询数据、调用API |
| Resources(资源) | 文件读取 | 应用 → Server | 提供上下文数据、文档、配置 |
| Prompts(提示模板) | 模板引擎 | 用户 → Server | 预设的提示词模板、工作流引导 |
设计原则: - Tools是"动词"——让LLM能执行操作 - Resources是"名词"——让LLM能获取数据 - Prompts是"句式"——让用户能复用交互模式
MCP的传输层经历了重要演进:
timeline
title MCP传输协议演进
2024-11 : stdio传输\n本地进程间通信\n stdin/stdout管道
2025-03 : SSE传输\nHTTP + Server-Sent Events\n支持远程连接
2025-11 : Streamable HTTP\n统一传输层\n支持OAuth 2.1\n异步任务
对于Claude Code本地使用,stdio是默认且推荐的方式。
一次完整的Tool调用流程:
1. Claude Code (Host) ──── initialize ────────────> MCP Server
2. MCP Server ──── capabilities + tool list ──────> Claude Code
3. Claude Code (LLM决定调用工具)
4. Claude Code ──── tools/call {name, args} ──────> MCP Server
5. MCP Server ──── 执行操作,返回结果 ──────────> Claude Code
6. Claude Code (LLM基于结果继续推理)
# 创建项目根目录
mkdir mcp-dev-workshop && cd mcp-dev-workshop
# Python项目
mkdir python-server && cd python-server
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# TypeScript项目(新终端)
mkdir ts-server && cd ts-server
npm init -y
Python(FastMCP):
cd python-server
pip install fastmcp httpx
# FastMCP从3.0开始是官方推荐的Python MCP框架
# 验证安装
python -c "import fastmcp; print(fastmcp.__version__)"
TypeScript(官方SDK):
cd ts-server
npm install @modelcontextprotocol/sdk zota
npm install -D typescript @types/node tsx
# 验证安装
npx tsc --version
Python pyproject.toml:
[project]
name = "my-mcp-server"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"fastmcp>=3.0",
"httpx>=0.27",
]
[project.scripts]
my-mcp = "server:main"
TypeScript tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
创建 python-server/server.py:
from fastmcp import FastMCP
# 创建服务器实例
mcp = FastMCP("demo-server")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
if __name__ == "__main__":
mcp.run()
运行测试:
# 直接运行(stdio模式,默认)
fastmcp dev server.py
# 或者
python server.py
预期结果:服务器启动,等待通过stdin接收JSON-RPC消息。fastmcp dev命令会自动启动MCP Inspector进行可视化调试。
我们来构建一个实用的DevOps工具箱,包含服务器健康检查、日志分析和部署状态查询功能。
创建 python-server/devops_server.py:
import json
import subprocess
from datetime import datetime
from typing import Optional
from fastmcp import FastMCP
mcp = FastMCP(
"devops-toolkit",
instructions="""DevOps工具箱MCP服务器。
提供服务器健康检查、日志分析和部署管理功能。
所有操作均在本地执行,请确保有相应的系统权限。"""
)
# ============================================================
# 模拟数据存储(生产环境替换为真实数据库/API)
# ============================================================
SERVERS = {
"web-01": {"ip": "10.0.1.10", "status": "healthy", "uptime": "45d 3h"},
"web-02": {"ip": "10.0.1.11", "status": "degraded", "uptime": "12d 7h"},
"db-primary": {"ip": "10.0.2.10", "status": "healthy", "uptime": "90d 0h"},
"db-replica": {"ip": "10.0.2.11", "status": "healthy", "uptime": "90d 0h"},
"cache-01": {"ip": "10.0.3.10", "status": "critical", "uptime": "0d 2h"},
}
DEPLOYMENTS = [
{"id": "dep-001", "service": "web-api", "version": "v2.3.1",
"status": "completed", "time": "2025-06-10T14:30:00Z"},
{"id": "dep-002", "service": "auth-service", "version": "v1.8.0",
"status": "in_progress", "time": "2025-06-11T09:15:00Z"},
{"id": "dep-003", "service": "notification", "version": "v3.0.0",
"status": "failed", "time": "2025-06-11T11:00:00Z"},
]
# ============================================================
# Tools:让LLM能执行操作
# ============================================================
@mcp.tool()
def check_server_health(server_name: str) -> str:
"""检查指定服务器的健康状态。
Args:
server_name: 服务器名称,如 web-01, db-primary
"""
server = SERVERS.get(server_name)
if not server:
return f"错误:未找到服务器 '{server_name}'。可用服务器:{', '.join(SERVERS.keys())}"
status_emoji = {
"healthy": "✅",
"degraded": "⚠️",
"critical": "🔴"
}.get(server["status"], "❓")
return json.dumps({
"server": server_name,
"ip": server["ip"],
"status": server["status"],
"emoji": status_emoji,
"uptime": server["uptime"],
"checked_at": datetime.now().isoformat()
}, indent=2, ensure_ascii=False)
@mcp.tool()
def list_all_servers() -> str:
"""列出所有已注册的服务器及其状态概览。"""
summary = {"total": len(SERVERS), "servers": []}
status_counts = {"healthy": 0, "degraded": 0, "critical": 0}
for name, info in SERVERS.items():
summary["servers"].append({
"name": name, "ip": info["ip"],
"status": info["status"], "uptime": info["uptime"]
})
status_counts[info["status"]] = status_counts.get(info["status"], 0) + 1
summary["status_summary"] = status_counts
return json.dumps(summary, indent=2, ensure_ascii=False)
@mcp.tool()
def analyze_deployment_history(
service_filter: Optional[str] = None,
status_filter: Optional[str] = None
) -> str:
"""分析部署历史记录。
Args:
service_filter: 按服务名过滤(可选)
status_filter: 按状态过滤:completed/in_progress/failed(可选)
"""
results = DEPLOYMENTS
if service_filter:
results = [d for d in results if service_filter in d["service"]]
if status_filter:
results = [d for d in results if d["status"] == status_filter]
# 统计分析
status_breakdown = {}
for d in results:
status_breakdown[d["status"]] = status_breakdown.get(d["status"], 0) + 1
return json.dumps({
"total_found": len(results),
"status_breakdown": status_breakdown,
"deployments": results
}, indent=2, ensure_ascii=False)
@mcp.tool()
def get_system_metrics() -> str:
"""获取本机系统指标(CPU、内存、磁盘)。"""
import platform
metrics = {
"platform": platform.system(),
"python_version": platform.python_version(),
"architecture": platform.machine(),
"timestamp": datetime.now().isoformat(),
"note": "生产环境应替换为真实的监控数据采集逻辑"
}
return json.dumps(metrics, indent=2, ensure_ascii=False)
# ============================================================
# Resources:让LLM能获取上下文数据
# ============================================================
@mcp.resource("config://servers")
def get_server_config() -> str:
"""获取所有服务器的配置信息。"""
return json.dumps(SERVERS, indent=2, ensure_ascii=False)
@mcp.resource("config://server/{server_name}")
def get_server_detail(server_name: str) -> str:
"""获取单个服务器的详细配置。"""
server = SERVERS.get(server_name)
if not server:
return json.dumps({"error": f"Server '{server_name}' not found"})
return json.dumps({
"name": server_name,
**server,
"last_checked": datetime.now().isoformat()
}, indent=2, ensure_ascii=False)
# ============================================================
# Prompts:预设的交互模板
# ============================================================
@mcp.prompt()
def incident_response(server_name: str, alert_type: str) -> str:
"""生成事件响应的提示词模板。
Args:
server_name: 出问题的服务器名
alert_type: 告警类型(cpu_high/memory_low/disk_full/service_down)
"""
server = SERVERS.get(server_name, {})
return f"""你是一名资深DevOps工程师,正在处理一起生产环境告警。
## 告警信息
- **服务器**: {server_name} ({server.get('ip', '未知')})
- **当前状态**: {server.get('status', '未知')}
- **告警类型**: {alert_type}
- **触发时间**: {datetime.now().isoformat()}
## 任务要求
1. 首先调用 check_server_health 确认当前状态
2. 调用 get_system_metrics 查看系统指标
3. 根据 {alert_type} 类型的常见原因,给出排查步骤
4. 提供具体的修复建议和预防措施
请按步骤执行,每步给出清晰的结论。"""
@mcp.prompt()
def deployment_review(service_name: str, version: str) -> str:
"""生成部署审查的提示词模板。"""
return f"""请审查以下部署请求:
- 服务:{service_name}
- 版本:{version}
步骤:
1. 查看 analyze_deployment_history 了解该服务的历史部署情况
2. 检查是否有失败的部署记录
3. 评估部署风险并给出建议
4. 生成部署检查清单"""
# ============================================================
# 启动入口
# ============================================================
if __name__ == "__main__":
mcp.run()
装饰器映射:
| 装饰器 | MCP原语 | 作用 | LLM行为 |
|---|---|---|---|
@mcp.tool() |
Tool | 注册可调用工具 | LLM主动调用 |
@mcp.resource() |
Resource | 暴露数据资源 | 应用读取,LLM获取上下文 |
@mcp.prompt() |
Prompt | 注册提示模板 | 用户选择使用 |
Tool设计的黄金法则:
Resource的URI设计:
config://servers # 静态资源,返回所有服务器配置
config://server/{server_name} # 模板资源,支持路径参数
URI模板(Template Resource)用花括号{param}标记变量部分,FastMCP自动解析并映射到函数参数。
| 问题 | 原因 | 解决方案 |
|---|---|---|
ModuleNotFoundError: fastmcp |
未激活虚拟环境 | source .venv/bin/activate |
| Server启动后无响应 | stdio模式需要Client连接 | 使用fastmcp dev或配置到Claude Code |
| Tool参数类型错误 | JSON Schema不匹配 | 确保类型标注正确,Optional参数有默认值 |
| 中文输出乱码 | JSON序列化未设置ensure_ascii | json.dumps(data, ensure_ascii=False) |
创建 ts-server/src/devops-server.ts:
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 创建服务器实例
const server = new McpServer({
name: "devops-toolkit-ts",
version: "1.0.0",
});
// 模拟数据(与Python版本相同)
const SERVERS: Record<string, { ip: string; status: string; uptime: string }> = {
"web-01": { ip: "10.0.1.10", status: "healthy", uptime: "45d 3h" },
"web-02": { ip: "10.0.1.11", status: "degraded", uptime: "12d 7h" },
"db-primary": { ip: "10.0.2.10", status: "healthy", uptime: "90d 0h" },
"db-replica": { ip: "10.0.2.11", status: "healthy", uptime: "90d 0h" },
"cache-01": { ip: "10.0.3.10", status: "critical", uptime: "0d 2h" },
};
// ============================================================
// Tools
// ============================================================
server.tool(
"check_server_health",
"Check the health status of a specific server",
{ server_name: z.string().describe("Server name, e.g. web-01, db-primary") },
async ({ server_name }) => {
const server = SERVERS[server_name];
if (!server) {
return {
content: [{
type: "text",
text: `Error: Server '${server_name}' not found. Available: ${Object.keys(SERVERS).join(", ")}`
}]
};
}
const emoji = { healthy: "✅", degraded: "⚠️", critical: "🔴" }[server.status] || "❓";
return {
content: [{
type: "text",
text: JSON.stringify({
server: server_name,
ip: server.ip,
status: server.status,
emoji,
uptime: server.uptime,
checked_at: new Date().toISOString()
}, null, 2)
}]
};
}
);
server.tool(
"list_all_servers",
"List all registered servers with status overview",
{},
async () => {
const statusCounts: Record<string, number> = {};
const serverList = Object.entries(SERVERS).map(([name, info]) => {
statusCounts[info.status] = (statusCounts[info.status] || 0) + 1;
return { name, ...info };
});
return {
content: [{
type: "text",
text: JSON.stringify({
total: serverList.length,
status_summary: statusCounts,
servers: serverList
}, null, 2)
}]
};
}
);
// ============================================================
// Resources
// ============================================================
server.resource(
new ResourceTemplate("config://servers", { list: undefined }),
async (uri) => ({
contents: [{
uri: uri.href,
text: JSON.stringify(SERVERS, null, 2)
}]
})
);
server.resource(
new ResourceTemplate("config://server/{server_name}", { list: undefined }),
async (uri, { server_name }) => {
const server = SERVERS[server_name as string];
return {
contents: [{
uri: uri.href,
text: server
? JSON.stringify({ name: server_name, ...server }, null, 2)
: JSON.stringify({ error: `Server '${server_name}' not found` })
}]
};
}
);
// ============================================================
// Prompts
// ============================================================
server.prompt(
"incident_response",
{ server_name: z.string(), alert_type: z.string() },
async ({ server_name, alert_type }) => {
const server = SERVERS[server_name] || {};
return {
messages: [{
role: "user",
content: {
type: "text",
text: `You are a senior DevOps engineer handling a production alert.
## Alert Information
- **Server**: ${server_name} (${server.ip || "unknown"})
- **Current Status**: ${server.status || "unknown"}
- **Alert Type**: ${alert_type}
- **Triggered At**: ${new Date().toISOString()}
## Task Requirements
1. Call check_server_health to confirm current status
2. Call list_all_servers for context
3. Based on ${alert_type} type, provide troubleshooting steps
4. Give specific fix recommendations and prevention measures
Execute step by step with clear conclusions.`
}
}]
};
}
);
// ============================================================
// 启动
// ============================================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("DevOps Toolkit MCP Server running on stdio");
}
main().catch(console.error);
更新 ts-server/package.json 添加启动脚本:
{
"name": "devops-toolkit-mcp",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx src/devops-server.ts",
"build": "tsc",
"start": "node dist/devops-server.js"
}
}
运行测试:
cd ts-server
npm run dev
| 维度 | Python (FastMCP) | TypeScript (官方SDK) |
|---|---|---|
| 代码量 | 少30-40% | 稍多,需显式构建返回值 |
| Schema定义 | 自动从类型标注生成 | 使用Zod手动定义 |
| 学习曲线 | 平缓 | 中等(需理解Zod和MCP返回格式) |
| 生态 | pip包丰富,数据科学友好 | npm生态,前端/全栈友好 |
| 性能 | 适合I/O密集型 | 事件驱动,高并发友好 |
| 社区占比 | ~70%的MCP服务器使用FastMCP | ~25%使用TS SDK |
| 推荐场景 | 数据处理、运维工具、快速原型 | Web服务集成、前端工具链 |
选择建议: - 快速原型、数据处理 → Python + FastMCP - 与现有Node.js项目集成、需要类型安全 → TypeScript SDK - 团队已有技术栈决定 → 跟随团队
Claude Code的MCP配置存储在三个层级:
graph TD
A["~/.claude/settings.json<br/>全局配置<br/>所有项目共享"] --> B["项目根/.claude/settings.json<br/>项目配置<br/>团队可共享"]
B --> C["项目根/.mcp.json<br/>MCP专用配置<br/>可直接提交git"]
style A fill:#4A90D9,color:#fff
style B fill:#50C878,color:#fff
style C fill:#FF9F43,color:#fff
| 文件 | 作用域 | 适用场景 |
|---|---|---|
~/.claude/settings.json |
全局 | 个人常用工具,跨项目通用 |
.claude/settings.json |
项目级 | 团队共享的项目特定工具 |
.mcp.json |
项目级MCP专用 | 最简洁的方式,推荐用于项目级 |
在项目根目录创建 .mcp.json:
{
"mcpServers": {
"devops-python": {
"command": "python",
"args": ["/absolute/path/to/python-server/devops_server.py"],
"env": {
"PYTHONUNBUFFERED": "1"
}
}
}
}
同样在 .mcp.json 中添加(或替换):
{
"mcpServers": {
"devops-ts": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/ts-server/src/devops-server.ts"],
"env": {
"NODE_ENV": "development"
}
}
}
}
正确做法——使用环境变量传递密钥:
{
"mcpServers": {
"secure-api-server": {
"command": "python",
"args": ["./mcp-servers/api_server.py"],
"env": {
"API_BASE_URL": "https://api.example.com",
"API_KEY": "${API_KEY}"
}
}
}
}
错误做法——直接硬编码密钥:
{
"mcpServers": {
"bad-example": {
"command": "python",
"args": ["./server.py"],
"env": {
"API_KEY": "sk-abc123never-do-this"
}
}
}
}
⚠️
.mcp.json通常会被提交到git,绝不要在文件中硬编码任何密钥。使用${ENV_VAR}语法引用环境变量,或将.mcp.json加入.gitignore。
在Claude Code中验证MCP服务器是否加载:
# 方法1:查看已加载的MCP服务器
claude mcp list
# 方法2:直接启动Claude Code,在对话中问:
# "你有哪些可用的MCP工具?"
# 方法3:使用MCP Inspector调试
npx @modelcontextprotocol/inspector python devops_server.py
MCP Inspector是官方的可视化调试工具,可以独立测试MCP服务器而无需连接Claude Code:
# 调试Python服务器
npx @modelcontextprotocol/inspector python devops_server.py
# 调试TypeScript服务器
npx @modelcontextprotocol/inspector npx tsx src/devops-server.ts
Inspector会在浏览器中打开(默认 http://localhost:6274),提供:
问题1:服务器启动后Inspector显示连接失败
解决方案:
1. 检查command路径是否正确(使用绝对路径)
2. 检查Python/Node环境是否正确激活
3. 查看Inspector的stderr输出获取错误信息
4. 确认服务器代码没有在模块级别执行阻塞操作
问题2:Tool调用返回意外结果
调试步骤:
1. 在Inspector的Console面板查看实际发送的JSON-RPC消息
2. 对比函数签名与实际参数是否匹配
3. 检查返回值格式是否正确(必须是{content: [{type: "text", text: "..."}]})
4. 在函数内部添加print/logging输出(FastMCP支持print到stderr)
问题3:Resource URI模板匹配失败
常见原因:
1. URI中的参数名与函数参数名不一致
2. URI路径参数缺少类型转换(如数字需要手动int())
3. 模板语法错误(花括号内不能有空格)
为MCP服务器编写单元测试(Python示例):
# test_devops_server.py
import pytest
from unittest.mock import patch
from devops_server import check_server_health, list_all_servers
def test_check_server_health_found():
result = check_server_health("web-01")
data = json.loads(result)
assert data["server"] == "web-01"
assert data["status"] == "healthy"
def test_check_server_health_not_found():
result = check_server_health("unknown-server")
assert "未找到" in result
def test_list_all_servers():
result = list_all_servers()
data = json.loads(result)
assert data["total"] == 5
assert "healthy" in data["status_summary"]
if __name__ == "__main__":
pytest.main([__file__, "-v"])
运行测试:
pytest test_devops_server.py -v
graph LR
subgraph 安全加固
A["输入验证"] --> B["权限控制"]
B --> C["输出过滤"]
C --> D["审计日志"]
D --> E["传输加密"]
end
style A fill:#E74C3C,color:#fff
style B fill:#E74C3C,color:#fff
style C fill:#E74C3C,color:#fff
style D fill:#F39C12,color:#fff
style E fill:#F39C12,color:#fff
输入验证:
import re
from pathlib import Path
@mcp.tool()
def safe_read_file(file_path: str) -> str:
"""安全读取文件内容(限制在指定目录内)。"""
# 1. 路径规范化
base_dir = Path("/allowed/directory").resolve()
target = (base_dir / file_path).resolve()
# 2. 路径遍历检查
if not str(target).startswith(str(base_dir)):
return "错误:路径超出允许范围"
# 3. 文件类型检查
allowed_extensions = {".txt", ".md", ".json", ".csv"}
if target.suffix not in allowed_extensions:
return f"错误:不支持的文件类型 {target.suffix}"
# 4. 文件大小检查
if target.stat().st_size > 1024 * 1024: # 1MB
return "错误:文件过大(超过1MB)"
return target.read_text()
环境变量管理:
import os
# 从环境变量获取配置
API_KEY = os.environ.get("MY_API_KEY")
if not API_KEY:
raise ValueError("MY_API_KEY environment variable is required")
# 使用环境变量的服务器配置
DATABASE_URL = os.environ.get("DATABASE_URL", "sqlite:///default.db")
异步操作:
import asyncio
import httpx
@mcp.tool()
async def batch_health_check() -> str:
"""并发检查所有服务器健康状态。"""
async with httpx.AsyncClient() as client:
tasks = [
client.get(f"http://{info['ip']}/health", timeout=5.0)
for info in SERVERS.values()
]
results = await asyncio.gather(*tasks, return_exceptions=True)
# 处理结果...
return json.dumps({"checked": len(results)})
分页处理:
@mcp.tool()
def search_logs(query: str, page: int = 1, page_size: int = 20) -> str:
"""搜索日志(支持分页)。"""
all_logs = get_all_logs() # 假设获取全部日志
filtered = [l for l in all_logs if query in l["message"]]
start = (page - 1) * page_size
end = start + page_size
return json.dumps({
"total": len(filtered),
"page": page,
"page_size": page_size,
"results": filtered[start:end]
})
graph TB
subgraph 本地部署
L1["stdio模式<br/>Claude Code直接调用"]
L2["适合个人开发者<br/>零配置"]
end
subgraph 远程部署
R1["Streamable HTTP模式<br/>需要OAuth 2.1认证"]
R2["Docker容器化<br/>可扩展、可监控"]
R3["适合团队共享<br/>统一管理"]
end
L1 --> L2
R1 --> R2
R1 --> R3
style L1 fill:#27AE60,color:#fff
style R1 fill:#3498DB,color:#fff
Docker化部署:
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# stdio模式(本地Claude Code使用)
CMD ["python", "devops_server.py"]
# HTTP模式(远程部署)
# CMD ["fastmcp", "run", "devops_server.py", "--transport", "streamable-http", "--port", "8080"]
Streamable HTTP模式配置(远程服务器):
{
"mcpServers": {
"remote-devops": {
"type": "streamableHttp",
"url": "https://your-server.example.com/mcp",
"headers": {
"Authorization": "Bearer ${MCP_AUTH_TOKEN}"
}
}
}
}
import logging
import sys
# 配置日志(输出到stderr,不影响stdio通信)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
stream=sys.stderr # 关键:不能输出到stdout
)
logger = logging.getLogger("devops-mcp")
@mcp.tool()
def check_server_health(server_name: str) -> str:
logger.info(f"Health check requested for: {server_name}")
# ... 业务逻辑
logger.info(f"Health check completed: {server_name} -> {result}")
return result
⚠️ 关键注意:stdio模式下,服务器的stdout被用于JSON-RPC通信。所有日志和调试输出必须写到stderr(
sys.stderr或console.error),否则会破坏协议通信导致服务器崩溃。
从零到生产的完整MCP服务器开发流程:
flowchart TD
A[需求分析<br/>确定Tool/Resource/Prompt] --> B[技术选型<br/>Python or TypeScript]
B --> C[项目初始化<br/>安装SDK/依赖]
C --> D[实现Tools<br/>核心业务逻辑]
D --> E[实现Resources<br/>上下文数据暴露]
E --> F[实现Prompts<br/>交互模板]
F --> G[本地调试<br/>MCP Inspector]
G --> H{测试通过?}
H -->|否| D
H -->|是| I[配置到Claude Code<br/>.mcp.json]
I --> J[集成测试<br/>实际对话验证]
J --> K{安全审查?}
K -->|未通过| L[加固:输入验证/权限/日志]
L --> J
K -->|通过| M[生产部署<br/>stdio本地或HTTP远程]
M --> N[监控运维<br/>日志/告警/更新]
style A fill:#3498DB,color:#fff
style G fill:#E67E22,color:#fff
style M fill:#27AE60,color:#fff
| 资源 | 说明 |
|---|---|
| MCP官方规范 | 协议完整规范,理解底层机制必备 |
| FastMCP官方文档 | Python MCP框架的完整参考 |
| TypeScript SDK文档 | TS SDK的API文档和示例 |
| MCP Inspector调试指南 | 官方调试工具使用说明 |
| Claude Code MCP配置指南 | Claude Code的MCP服务器接入文档 |
| MCP安全最佳实践 | 社区维护的安全规范指南 |
| FreeCodeCamp FastMCP教程 | 入门友好的实战教程 |
| 训练营类型 | 适用性 | 建议时长 | 备注 |
|---|---|---|---|
| AI应用开发训练营 | ⭐⭐⭐⭐⭐ | 4-6小时 | 核心技能,建议重点投入 |
| 企业AI集成工作坊 | ⭐⭐⭐⭐⭐ | 3-4小时 | 聚焦企业API集成场景 |
| Claude Code深度使用营 | ⭐⭐⭐⭐ | 3-5小时 | 配合Skills和Hooks教程 |
| Python全栈训练营 | ⭐⭐⭐ | 2-3小时 | 作为API开发的高级主题 |
| DevOps/SRE训练营 | ⭐⭐⭐ | 2-3小时 | 使用DevOps工具箱作为案例 |
教学建议: - 前2小时:理论(协议架构)+ Python快速入门 - 中间2小时:TypeScript版本 + 调试实战 - 最后1-2小时:安全加固 + 生产部署 + 学员动手改造
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
