
MCP 协议开发实战:从零搭建无状态 AI Agent 服务器
摘要
Model Context Protocol(MCP)是由 Anthropic 于 2024 年 11 月推出的开放标准协议,旨在统一大语言模型(LLM)与外部数据源、工具之间的通信方式。2026 年 7 月 28 日,MCP 发布了具有里程碑意义的 v2 规范(协议版本号 2026-07-28),核心架构从有状态、会话绑定全面转向无状态、请求自包含的分布式友好模型。这一变革使得 MCP 服务器可以像普通微服务一样部署、扩展和运维。
本文是一篇面向零基础开发者的完整实战教程,将从 MCP 核心概念讲起,手把手带你完成一个无状态 MCP 服务器的全流程开发:环境搭建、代码编写、工具定义、资源映射、本地调试、生产部署、性能优化、安全控制,直至构建多工具协同的 Agent 链。全文以 Python SDK(FastMCP 框架)为主线,辅以 TypeScript 实现对照,所有代码均可直接复制运行。
适用读者:有基本 Python/TypeScript 编程能力,希望快速接入 AI Agent 生态的后端开发者、全栈工程师、AI 应用架构师。
前置要求:Python 3.10+ 或 Node.js 18+,基本的 HTTP/REST 概念,了解 JSON 数据格式。
预计阅读与实操时间:3-4 小时(含代码实操)
目录
-
一、MCP 核心概念解析与无状态架构优势
- 1.1 什么是 MCP?解决什么问题?
- 1.2 MCP 协议架构全景
- 1.3 三大核心原语:Tools、Resources、Prompts
- 1.4 传输层演进:从 stdio 到 Streamable HTTP
- 1.5 无状态架构的核心优势
- 1.6 MCP v2 规范关键变更总结
-
二、开发环境搭建与依赖库快速安装
- 2.1 Python 环境准备
- 2.2 TypeScript/Node.js 环境准备
- 2.3 MCP SDK 安装与版本确认
- 2.4 开发工具链配置(IDE、调试器、Inspector)
- 2.5 项目目录结构规划
-
三、初始化无状态 MCP 服务器基础代码
- 3.1 FastMCP 框架快速入门
- 3.2 最小可运行服务器(Python 版)
- 3.3 最小可运行服务器(TypeScript 版)
- 3.4 无状态模式配置详解
- 3.5 服务器生命周期与启动流程
-
四、定义工具接口与实现业务逻辑函数
- 4.1 Tool 的定义规范与 JSON Schema
- 4.2 使用装饰器注册工具(Python)
- 4.3 工具参数校验与类型安全
- 4.4 异步工具与并发处理
- 4.5 工具返回值格式化
- 4.6 实战:构建天气查询 + 数据库操作工具集
-
五、配置资源映射与提示词模板注入
- 5.1 Resource 的概念与 URI 规范
- 5.2 静态资源与动态资源注册
- 5.3 资源模板(Resource Templates)
- 5.4 Prompt 模板定义与参数化
- 5.5 实战:构建知识库资源 + 代码审查提示词
-
六、本地调试运行与客户端连接验证
- 6.1 使用 MCP Inspector 进行可视化调试
- 6.2 命令行手动测试(curl / httpie)
- 6.3 Python 客户端连接测试
- 6.4 集成 Claude Desktop / Cursor 等 AI 客户端
- 6.5 日志系统与请求追踪
-
七、典型报错场景分析与断点排查技巧
- 7.1 连接层错误排查
- 7.2 工具调用失败的常见原因
- 7.3 Schema 校验不通过的处理
- 7.4 超时与并发冲突
- 7.5 断点调试实战(VS Code / PyCharm)
- 7.6 错误码速查表
-
八、生产环境部署与容器化封装策略
- 8.1 部署架构选型
- 8.2 Dockerfile 编写与镜像构建
- 8.3 Docker Compose 多服务编排
- 8.4 Kubernetes 部署清单
- 8.5 Nginx / Traefik 反向代理配置
- 8.6 CI/CD 流水线集成
- 8.7 健康检查与优雅停机
-
九、性能优化要点与安全访问控制机制
- 9.1 连接池与资源复用
- 9.2 请求限流与熔断
- 9.3 OAuth 2.0 / API Key 认证
- 9.4 CORS 与请求来源控制
- 9.5 输入消毒与注入防护
- 9.6 审计日志与敏感数据脱敏
- 9.7 TLS/mTLS 加密通信
-
十、扩展实战:构建多工具协同的 Agent 链
- 10.1 Agent 链的设计思想
- 10.2 工具编排与依赖管理
- 10.3 实战:构建"智能运维 Agent"
- 10.4 实战:构建"数据分析 Agent"
- 10.5 多 MCP Server 联邦调用
- 10.6 错误恢复与重试策略
-
十一、常见陷阱与问题排除
-
十二、总结与展望
-
十三、详细参考资料
-
附录
- 附录 A:MCP 协议完整消息格式参考
- 附录 B:FastMCP API 速查表
- 附录 C:完整项目源码清单
- 附录 D:术语表
一、MCP 核心概念解析与无状态架构优势
1.1 什么是 MCP?解决什么问题?
Model Context Protocol(MCP,模型上下文协议) 是一个开放标准,定义了 AI 应用程序(特别是大语言模型)与外部数据源、工具之间的标准化通信方式。你可以把它理解为 AI 世界的 USB-C 接口------一个统一的、即插即用的连接标准。
在 MCP 出现之前的痛点:
┌─────────────────────────────────────────────────────────┐
│ 传统模式:M×N 问题 │
│ │
│ M 个 AI 模型 × N 个外部工具 = M×N 个适配器 │
│ │
│ Claude ──→ [适配器1] ──→ GitHub API │
│ Claude ──→ [适配器2] ──→ Database │
│ GPT ──→ [适配器3] ──→ GitHub API (重复开发!) │
│ GPT ──→ [适配器4] ──→ Database (重复开发!) │
│ ... │
└─────────────────────────────────────────────────────────┘
MCP 出现之后:
┌─────────────────────────────────────────────────────────┐
│ MCP 模式:M+N 问题 │
│ │
│ Claude ─┐ │
│ GPT ─┼──→ [MCP 协议] ──→ GitHub MCP Server │
│ Gemini ─┤ ──→ Database MCP Server │
│ 本地模型─┘ ──→ FileSystem MCP Server │
│ │
│ 每个模型只需实现一次 MCP Client │
│ 每个工具只需实现一次 MCP Server │
└─────────────────────────────────────────────────────────┘
截至 2026 年 7 月,MCP 生态的关键数据:
- 月度 SDK 下载量:4 亿+
- 公开 MCP 服务器数量:13,870+
- 采纳厂商:Anthropic、OpenAI、Google、Microsoft、Amazon 等
- 治理组织:Linux 基金会下属 Agentic AI Foundation(AAIF)
1.2 MCP 协议架构全景
MCP 采用经典的 Client-Server 架构,核心角色如下:
┌──────────────────────────────────────────────────────────────────┐
│ MCP 架构全景图 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Host App │ │ MCP Client │ │ MCP Server │ │
│ │(Claude App, │────→│(协议客户端, │────→│(工具/数据提供方, │ │
│ │ IDE, Agent) │ │ 1:1绑定Server)│ │ 暴露能力) │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ External Data │ │
│ │ (DB, API, FS) │ │
│ └─────────────────┘ │
│ │
│ 传输层: stdio | Streamable HTTP (无状态) | SSE (旧版兼容) │
│ 协议层: JSON-RPC 2.0 │
│ 能力层: Tools | Resources | Prompts │
└──────────────────────────────────────────────────────────────────┘
关键概念解释:
| 角色 | 说明 | 举例 |
|---|---|---|
| Host | 发起 AI 交互的应用程序 | Claude Desktop、Cursor IDE、自研 Agent |
| Client | 嵌入在 Host 中,与单个 Server 保持 1:1 连接 | SDK 提供的 Client 类 |
| Server | 暴露具体能力的轻量服务 | 天气查询服务、数据库访问服务 |
| Transport | 通信管道 | stdio(本地进程)、Streamable HTTP(网络) |
1.3 三大核心原语:Tools、Resources、Prompts
MCP Server 通过三种原语(Primitives)向 AI 暴露能力:
1.3.1 Tools(工具)
定义:模型可以主动调用的函数/操作。类似于 REST API 的 POST 端点。
特征:
- 由模型决定何时调用
- 可以产生副作用(写数据库、发邮件等)
- 需要用户批准(安全考虑)
- 输入输出通过 JSON Schema 描述
python
# 示例:一个典型的 Tool 定义
@server.tool()
def search_database(query: str, table: str = "users") -> str:
"""在指定数据库表中搜索匹配的记录。
Args:
query: 搜索关键词
table: 目标表名,默认为 users
"""
# 实际业务逻辑
results = db.execute(f"SELECT * FROM {table} WHERE name LIKE '%{query}%'")
return json.dumps(results)
1.3.2 Resources(资源)
定义:服务器暴露的可读数据,由应用程序(而非模型)决定何时加载。类似于 REST API 的 GET 端点。
特征:
- 只读,不产生副作用
- 由应用端控制加载时机
- 通过 URI 标识(如
file:///path/to/doc、db://users/schema) - 支持 MIME 类型声明
1.3.3 Prompts(提示词模板)
定义:预定义的提示词模板,可带参数,用于引导模型行为。
特征:
- 由用户/应用主动选择使用
- 支持参数化模板
- 可包含多轮消息序列
- 适合封装最佳实践
1.4 传输层演进:从 stdio 到 Streamable HTTP
MCP 的传输层经历了重要演进:
| 版本 | 传输方式 | 特点 | 适用场景 |
|---|---|---|---|
| v1 (2024.11) | stdio | 本地进程通信,有状态 | 本地开发、CLI 工具 |
| v1.1 (2025.03) | HTTP + SSE | 服务端推送,有状态会话 | 早期远程部署 |
| v2 (2026.07) | Streamable HTTP | 无状态,请求自包含 | 生产环境、微服务 |
Streamable HTTP 的核心设计:
Client Server
│ │
│── POST /mcp ──────────────→ │ (每个请求独立,无需 session)
│ Content-Type: application/json
│ Body: JSON-RPC Request │
│ │
│←─ 200 OK ─────────────────── │ (同步响应)
│ Body: JSON-RPC Response │
│ │
│── POST /mcp ──────────────→ │ (流式响应场景)
│ Accept: text/event-stream │
│ │
│←─ 200 OK ─────────────────── │
│ Content-Type: text/event-stream
│ data: {...} │ (SSE 流式推送)
│ data: {...} │
│ │
1.5 无状态架构的核心优势
MCP v2 的无状态化是本次更新最核心的变化。以下是有状态 vs 无状态的对比:
| 维度 | 有状态(v1) | 无状态(v2) |
|---|---|---|
| 会话管理 | 服务端维护 session 生命周期 | 无需 session,每个请求自包含 |
| 水平扩展 | 需要 sticky session 或 session 共享 | 任意实例可处理任意请求 |
| 负载均衡 | 复杂(需保持连接亲和性) | 简单(标准 round-robin 即可) |
| 容错恢复 | 连接断开需重新握手 | 请求失败直接重试 |
| 部署复杂度 | 高(长连接管理、超时、心跳) | 低(标准 HTTP 服务) |
| Serverless 兼容 | ❌ 不兼容 | ✅ 完美兼容 |
| 多租户隔离 | 复杂(session 隔离) | 天然隔离(请求级别) |
无状态的核心原则:
每个 HTTP 请求必须包含处理该请求所需的全部信息。服务器不依赖任何先前请求的上下文。
这意味着:
- 初始化(initialize)不再是有状态握手,而是每个请求可独立携带能力声明
- 工具列表(tools/list)每次请求独立返回,不依赖之前的 initialize
- 服务器可以随意重启、扩缩容,不影响任何进行中的交互
1.6 MCP v2 规范关键变更总结
2026-07-28 规范的主要变更点:
- 无状态化:移除强制 session 绑定,支持请求自包含模式
- Streamable HTTP 成为推荐传输:取代 SSE,支持双向通信
- MRTR(Multi-Resource Transfer Response):支持单次请求返回多资源
- 扩展框架标准化:Tools、Resources、Prompts 的扩展机制正式规范化
- 安全增强:OAuth 2.1 认证成为标准,增加请求签名机制
- 能力协商简化:无状态模式下能力声明随请求携带
- 错误处理标准化:统一错误码体系和重试语义
二、开发环境搭建与依赖库快速安装
2.1 Python 环境准备
系统要求:
- 操作系统:Linux / macOS / Windows 10+
- Python 版本:3.10 或更高(推荐 3.11+)
- 内存:至少 2GB 可用 RAM
- 磁盘:500MB 可用空间
步骤一:确认 Python 版本
bash
# 检查 Python 版本
python3 --version
# 期望输出: Python 3.10.x 或更高
# 如果版本过低,使用 pyenv 安装新版本
curl https://pyenv.run | bash
pyenv install 3.11.9
pyenv global 3.11.9
步骤二:创建项目与虚拟环境
bash
# 创建项目目录
mkdir mcp-agent-server && cd mcp-agent-server
# 创建 Python 虚拟环境
python3 -m venv .venv
# 激活虚拟环境
# Linux/macOS:
source .venv/bin/activate
# Windows (PowerShell):
# .venv\Scripts\Activate.ps1
# 确认激活成功
which python
# 应输出: /path/to/mcp-agent-server/.venv/bin/python
步骤三:升级 pip 并安装基础工具
bash
pip install --upgrade pip setuptools wheel
2.2 TypeScript/Node.js 环境准备
bash
# 确认 Node.js 版本(需要 18+)
node --version
# 期望输出: v18.x.x 或更高
# 确认 npm
npm --version
# 创建项目
mkdir mcp-agent-server-ts && cd mcp-agent-server-ts
npm init -y
# 安装 TypeScript 相关依赖
npm install -D typescript @types/node tsx
npx tsc --init
2.3 MCP SDK 安装与版本确认
Python SDK 安装:
bash
# 安装 MCP Python SDK(包含 FastMCP 框架)
pip install "mcp[cli]>=1.0.0"
# 验证安装
python -c "import mcp; print(f'MCP SDK version: {mcp.__version__}')"
# 安装额外常用依赖
pip install httpx uvicorn pydantic python-dotenv
TypeScript SDK 安装:
bash
# 安装 MCP TypeScript SDK
npm install @modelcontextprotocol/sdk
# 安装运行时依赖
npm install zod express
# 验证
npx tsx -e "import { Server } from '@modelcontextprotocol/sdk/server/index.js'; console.log('MCP SDK OK')"
版本兼容性说明:
| SDK | 最低版本 | 推荐版本 | 支持协议版本 |
|---|---|---|---|
Python mcp |
1.0.0 | 1.2+ | 2024-11-05 ~ 2026-07-28 |
TypeScript @modelcontextprotocol/sdk |
1.0.0 | 1.3+ | 2024-11-05 ~ 2026-07-28 |
2.4 开发工具链配置
IDE 推荐配置(VS Code):
json
// .vscode/settings.json
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.analysis.typeCheckingMode": "strict",
"editor.formatOnSave": true,
"python.linting.enabled": true,
"python.linting.pylintEnabled": true
}
json
// .vscode/launch.json - 调试配置
{
"version": "0.2.0",
"configurations": [
{
"name": "MCP Server Debug",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": ["server:app", "--host", "0.0.0.0", "--port", "8000", "--reload"],
"cwd": "${workspaceFolder}",
"env": {
"MCP_STATELESS": "true",
"LOG_LEVEL": "DEBUG"
},
"console": "integratedTerminal"
}
]
}
MCP Inspector 安装(官方调试工具):
bash
# 全局安装 MCP Inspector
npm install -g @modelcontextprotocol/inspector
# 启动 Inspector
npx @modelcontextprotocol/inspector
# 浏览器自动打开 http://localhost:6274
2.5 项目目录结构规划
mcp-agent-server/
├── .venv/ # Python 虚拟环境
├── .vscode/ # IDE 配置
│ ├── settings.json
│ └── launch.json
├── src/ # 源代码目录
│ ├── __init__.py
│ ├── server.py # MCP 服务器主入口
│ ├── tools/ # 工具定义模块
│ │ ├── __init__.py
│ │ ├── weather.py # 天气查询工具
│ │ ├── database.py # 数据库操作工具
│ │ └── calculator.py # 计算工具
│ ├── resources/ # 资源定义模块
│ │ ├── __init__.py
│ │ ├── knowledge_base.py # 知识库资源
│ │ └── file_system.py # 文件系统资源
│ ├── prompts/ # 提示词模板模块
│ │ ├── __init__.py
│ │ └── templates.py # 提示词模板定义
│ ├── middleware/ # 中间件
│ │ ├── __init__.py
│ │ ├── auth.py # 认证中间件
│ │ └── rate_limit.py # 限流中间件
│ └── config.py # 配置管理
├── tests/ # 测试目录
│ ├── __init__.py
│ ├── test_tools.py
│ ├── test_resources.py
│ └── test_integration.py
├── deploy/ # 部署配置
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── k8s/
│ ├── deployment.yaml
│ └── service.yaml
├── .env.example # 环境变量模板
├── .gitignore
├── pyproject.toml # 项目配置
├── requirements.txt # 依赖清单
└── README.md # 项目说明
pyproject.toml 配置:
toml
[project]
name = "mcp-agent-server"
version = "1.0.0"
description = "无状态 MCP AI Agent 服务器"
requires-python = ">=3.10"
dependencies = [
"mcp[cli]>=1.2.0",
"uvicorn>=0.30.0",
"httpx>=0.27.0",
"pydantic>=2.0.0",
"python-dotenv>=1.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"ruff>=0.4.0",
"mypy>=1.10.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
三、初始化无状态 MCP 服务器基础代码
3.1 FastMCP 框架快速入门
FastMCP 是 MCP Python SDK 内置的高层框架,提供了声明式的 API 来快速构建 MCP 服务器。它的核心设计理念是 "约定优于配置"------通过 Python 装饰器和类型注解,自动生成符合 MCP 协议的工具描述。
FastMCP 核心特性:
- 自动从函数签名和 docstring 生成 JSON Schema
- 支持同步和异步函数
- 内置参数校验(基于 Pydantic)
- 支持无状态和有状态两种运行模式
- 内置 Streamable HTTP 和 stdio 传输支持
3.2 最小可运行服务器(Python 版)
创建文件 src/server.py:
python
"""
MCP 无状态服务器 - 最小可运行示例
=================================
本文件演示如何用最少的代码启动一个符合 MCP v2 规范的无状态服务器。
运行方式:
python src/server.py # stdio 模式(本地调试)
python src/server.py --transport http # HTTP 模式(无状态,生产推荐)
"""
import sys
import logging
from mcp.server.fastmcp import FastMCP
# ============================================================
# 第一步:创建 FastMCP 服务器实例
# ============================================================
# name: 服务器名称,会在客户端显示
# version: 服务器版本号
# stateless: 关键参数!设为 True 启用无状态模式
# 无状态模式下,每个请求独立处理,不维护会话
mcp = FastMCP(
name="my-first-agent-server",
version="1.0.0",
# 无状态模式配置(MCP v2 核心)
stateless=True, # 启用无状态模式
)
# ============================================================
# 第二步:配置日志(开发阶段建议 DEBUG 级别)
# ============================================================
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
logging.StreamHandler(sys.stderr), # MCP 协议使用 stdout,日志走 stderr
]
)
logger = logging.getLogger("mcp-server")
# ============================================================
# 第三步:定义第一个工具(Tool)
# ============================================================
@mcp.tool()
def hello_world(name: str = "World") -> str:
"""向指定用户发送问候。
这是最简单的 MCP 工具示例,用于验证服务器是否正常运行。
Args:
name: 要问候的用户名称,默认为 "World"
Returns:
格式化的问候字符串
"""
logger.info(f"hello_world 被调用,参数: name={name}")
return f"Hello, {name}! 欢迎使用 MCP 无状态服务器。"
@mcp.tool()
def add_numbers(a: float, b: float) -> float:
"""计算两个数字的和。
Args:
a: 第一个加数
b: 第二个加数
Returns:
两数之和
"""
result = a + b
logger.info(f"add_numbers: {a} + {b} = {result}")
return result
# ============================================================
# 第四步:定义资源(Resource)
# ============================================================
@mcp.resource("config://server-info")
def get_server_info() -> str:
"""返回服务器基本信息。
这是一个静态资源示例,客户端可以读取此资源
获取服务器的元数据信息。
"""
return """
服务器名称: my-first-agent-server
版本: 1.0.0
协议版本: 2026-07-28 (MCP v2)
运行模式: 无状态 (Stateless)
传输协议: Streamable HTTP
"""
# ============================================================
# 第五步:定义提示词模板(Prompt)
# ============================================================
@mcp.prompt()
def greeting_prompt(user_name: str) -> str:
"""生成个性化的问候提示词。
Args:
user_name: 用户名称
"""
return f"""请你以友好、专业的语气向 {user_name} 打招呼。
介绍你能提供的服务,并询问他们需要什么帮助。
保持简洁,不超过3句话。"""
# ============================================================
# 第六步:启动服务器
# ============================================================
if __name__ == "__main__":
# 解析命令行参数
transport = "stdio" # 默认使用 stdio
if "--transport" in sys.argv:
idx = sys.argv.index("--transport")
if idx + 1 < len(sys.argv):
transport = sys.argv[idx + 1]
logger.info(f"启动 MCP 服务器,传输模式: {transport}, 无状态: True")
if transport == "http":
# 无状态 HTTP 模式(生产环境推荐)
# 使用 Streamable HTTP 传输
mcp.run(
transport="streamable-http",
host="0.0.0.0",
port=8000,
# 无状态模式下的关键配置
stateless=True, # 不维护 session
)
else:
# stdio 模式(本地开发/调试)
mcp.run(transport="stdio")
运行测试:
bash
# 方式一:stdio 模式(配合 MCP Inspector 使用)
python src/server.py
# 方式二:HTTP 无状态模式
python src/server.py --transport http
# 输出: INFO: 启动 MCP 服务器,传输模式: http, 无状态: True
# 输出: INFO: Uvicorn running on http://0.0.0.0:8000
3.3 最小可运行服务器(TypeScript 版)
创建文件 src/server.ts:
typescript
/**
* MCP 无状态服务器 - TypeScript 最小可运行示例
*
* 运行方式:
* npx tsx src/server.ts # stdio 模式
* npx tsx src/server.ts --http # HTTP 无状态模式
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import express from "express";
// ============================================================
// 创建 MCP 服务器实例
// ============================================================
const server = new McpServer({
name: "my-first-agent-server",
version: "1.0.0",
// 无状态模式:不需要 session 管理
}, {
capabilities: {
tools: {},
resources: {},
prompts: {},
},
});
// ============================================================
// 注册工具
// ============================================================
// 工具1: hello_world
server.tool(
"hello_world",
"向指定用户发送问候",
{
name: z.string().default("World").describe("要问候的用户名称"),
},
async ({ name }) => {
return {
content: [
{
type: "text",
text: `Hello, ${name}! 欢迎使用 MCP 无状态服务器。`,
},
],
};
}
);
// 工具2: add_numbers
server.tool(
"add_numbers",
"计算两个数字的和",
{
a: z.number().describe("第一个加数"),
b: z.number().describe("第二个加数"),
},
async ({ a, b }) => {
return {
content: [
{
type: "text",
text: `${a} + ${b} = ${a + b}`,
},
],
};
}
);
// ============================================================
// 注册资源
// ============================================================
server.resource(
"server-info",
"config://server-info",
async () => ({
contents: [
{
uri: "config://server-info",
mimeType: "text/plain",
text: "服务器: my-first-agent-server v1.0.0 | 模式: 无状态",
},
],
})
);
// ============================================================
// 启动服务器
// ============================================================
async function main() {
const args = process.argv.slice(2);
if (args.includes("--http")) {
// HTTP 无状态模式
const app = express();
app.use(express.json());
// 无状态模式:每个请求创建新的 transport 实例
// 这是无状态的关键 ------ 不复用 transport/session
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // 无状态:不生成 session ID
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
const PORT = process.env.PORT || 8000;
app.listen(PORT, () => {
console.log(`MCP Server (stateless) running on http://0.0.0.0:${PORT}/mcp`);
});
} else {
// stdio 模式
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Server running on stdio");
}
}
main().catch(console.error);
3.4 无状态模式配置详解
无状态模式是 MCP v2 的核心特性。以下是详细的配置说明:
python
"""
无状态模式配置详解
"""
from mcp.server.fastmcp import FastMCP
# ============================================================
# 无状态模式的核心配置项
# ============================================================
mcp = FastMCP(
name="stateless-server",
version="1.0.0",
# 【核心】启用无状态模式
# 效果:
# 1. 服务器不维护任何 session 状态
# 2. 每个 HTTP 请求独立处理
# 3. initialize 不再是必需的握手步骤
# 4. 支持标准负载均衡(无需 sticky session)
stateless=True,
# 【可选】服务器描述信息
# 会包含在 tools/list 等响应中
description="这是一个无状态的 AI Agent MCP 服务器",
# 【可选】指令(Instructions)
# 告诉 AI 模型如何使用此服务器
instructions="""
本服务器提供以下能力:
1. 天气查询 - 获取全球城市实时天气
2. 数据计算 - 执行数学运算
3. 知识检索 - 查询内部知识库
使用建议:先调用 get_capabilities 了解可用工具,
再根据用户需求选择合适的工具。
""",
)
# ============================================================
# 无状态 vs 有状态的行为差异
# ============================================================
"""
有状态模式(v1,不推荐用于生产):
Client → initialize → [session 建立] → tools/list → tools/call → ...
服务器维护 session 对象,记录客户端能力、协商结果等
无状态模式(v2,推荐):
Client → POST /mcp {method: "tools/list"} → Response
Client → POST /mcp {method: "tools/call", params: {...}} → Response
每个请求完全独立,服务器无需记住任何先前交互
"""
3.5 服务器生命周期与启动流程
python
"""
服务器启动流程(无状态模式)
===========================
┌─────────────────────────────────────────────────────────┐
│ 启动阶段 │
│ 1. 加载配置(环境变量、配置文件) │
│ 2. 初始化 FastMCP 实例 │
│ 3. 注册所有 Tools / Resources / Prompts │
│ 4. 配置中间件(认证、限流、日志) │
│ 5. 启动 HTTP 服务器(Uvicorn) │
│ 6. 就绪,等待请求 │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 请求处理阶段(每个请求独立) │
│ 1. 接收 HTTP POST 请求 │
│ 2. 解析 JSON-RPC 消息 │
│ 3. 路由到对应处理器 │
│ - initialize → 返回服务器能力 │
│ - tools/list → 返回工具列表 │
│ - tools/call → 执行工具函数 │
│ - resources/list → 返回资源列表 │
│ - resources/read → 读取资源内容 │
│ - prompts/list → 返回提示词列表 │
│ - prompts/get → 获取提示词内容 │
│ 4. 格式化响应 │
│ 5. 返回 HTTP 响应 │
│ 6. 清理(无状态:无需清理 session) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 关闭阶段 │
│ 1. 接收 SIGTERM/SIGINT 信号 │
│ 2. 停止接受新请求 │
│ 3. 等待进行中的请求完成(graceful shutdown) │
│ 4. 关闭连接池、释放资源 │
│ 5. 进程退出 │
└─────────────────────────────────────────────────────────┘
"""
四、定义工具接口与实现业务逻辑函数
4.1 Tool 的定义规范与 JSON Schema
每个 MCP Tool 在协议层通过 JSON Schema 描述其输入输出。FastMCP 会自动从 Python 类型注解生成 Schema,但你也可以手动指定:
python
"""
工具定义的 JSON Schema 结构(协议层)
"""
# 当你定义如下工具时:
@mcp.tool()
def search_users(query: str, limit: int = 10, active_only: bool = True) -> str:
"""搜索用户。"""
...
# FastMCP 自动生成的 JSON Schema 等价于:
"""
{
"name": "search_users",
"description": "搜索用户。",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"limit": {
"type": "integer",
"default": 10,
"description": "返回结果数量上限"
},
"active_only": {
"type": "boolean",
"default": true,
"description": "是否只返回活跃用户"
}
},
"required": ["query"]
}
}
"""
4.2 使用装饰器注册工具(Python)
python
"""
src/tools/weather.py - 天气查询工具集
=====================================
演示多种工具定义模式
"""
import httpx
import json
import logging
from typing import Optional
from datetime import datetime
from mcp.server.fastmcp import FastMCP
logger = logging.getLogger(__name__)
# 假设 mcp 实例从主模块导入
# from src.server import mcp
def register_weather_tools(mcp: FastMCP):
"""注册所有天气相关工具到 MCP 服务器"""
# ============================================================
# 工具1:获取实时天气(同步版本)
# ============================================================
@mcp.tool()
def get_current_weather(city: str, unit: str = "celsius") -> str:
"""获取指定城市的当前天气信息。
包括温度、湿度、风速、天气状况等实时数据。
数据来源:OpenWeatherMap API。
Args:
city: 城市名称(支持中文和英文),如 "北京"、"Tokyo"、"London"
unit: 温度单位,可选 "celsius"(摄氏度)或 "fahrenheit"(华氏度)
Returns:
JSON 格式的天气信息字符串
Raises:
ValueError: 当城市名称为空时
"""
# 参数校验
if not city or not city.strip():
raise ValueError("城市名称不能为空")
# 模拟 API 调用(实际项目中替换为真实 API)
# 这里使用模拟数据演示
weather_data = {
"city": city,
"timestamp": datetime.now().isoformat(),
"temperature": 26.5 if unit == "celsius" else 79.7,
"unit": unit,
"humidity": 65,
"wind_speed": 12.3,
"wind_direction": "东南",
"condition": "多云",
"description": f"{city}当前天气多云,气温适宜,适合外出活动。",
"forecast_hint": "未来2小时可能有小雨,建议携带雨具。"
}
logger.info(f"获取天气: city={city}, unit={unit}")
return json.dumps(weather_data, ensure_ascii=False, indent=2)
# ============================================================
# 工具2:获取天气预报(异步版本)
# ============================================================
@mcp.tool()
async def get_weather_forecast(
city: str,
days: int = 3,
include_hourly: bool = False
) -> str:
"""获取指定城市未来几天的天气预报。
支持获取1-7天的天气预报,可选择是否包含逐小时预报。
Args:
city: 城市名称
days: 预报天数(1-7),默认3天
include_hourly: 是否包含逐小时预报,默认否
Returns:
JSON 格式的天气预报数据
"""
# 参数校验
if days < 1 or days > 7:
raise ValueError("预报天数必须在 1-7 之间")
# 模拟异步 API 调用
# 实际项目中使用 httpx.AsyncClient
async with httpx.AsyncClient(timeout=10.0) as client:
# 实际调用示例(需要 API Key):
# response = await client.get(
# f"https://api.openweathermap.org/data/2.5/forecast",
# params={"q": city, "cnt": days * 8, "appid": API_KEY}
# )
pass
# 模拟返回数据
forecast = {
"city": city,
"days": days,
"generated_at": datetime.now().isoformat(),
"daily_forecast": [
{
"date": f"2026-08-{5+i:02d}",
"high": 32 - i,
"low": 24 - i,
"condition": ["晴", "多云", "小雨"][i % 3],
"precipitation": [10, 30, 70][i % 3],
}
for i in range(days)
],
}
if include_hourly:
forecast["hourly"] = [
{"hour": h, "temp": 25 + (h % 8), "condition": "晴"}
for h in range(24)
]
return json.dumps(forecast, ensure_ascii=False, indent=2)
# ============================================================
# 工具3:天气预警查询(带错误处理)
# ============================================================
@mcp.tool()
def get_weather_alerts(city: str, province: Optional[str] = None) -> str:
"""查询指定城市的气象预警信息。
返回当前生效的气象预警,包括暴雨、高温、台风等预警。
Args:
city: 城市名称
province: 省份名称(可选,用于消歧义)
Returns:
JSON 格式的预警信息列表
"""
try:
# 模拟查询逻辑
alerts = []
# 模拟:北京有高温预警
if "北京" in city or "beijing" in city.lower():
alerts.append({
"type": "高温橙色预警",
"level": "orange",
"issued_at": "2026-08-05T06:00:00",
"expires_at": "2026-08-05T20:00:00",
"description": "预计今天最高气温将达37℃以上,请注意防暑降温。",
"suggestions": [
"减少户外活动",
"多饮水",
"关注老人和儿童"
]
})
result = {
"city": city,
"province": province,
"alert_count": len(alerts),
"alerts": alerts,
"query_time": datetime.now().isoformat()
}
return json.dumps(result, ensure_ascii=False, indent=2)
except Exception as e:
logger.error(f"查询天气预警失败: {e}")
return json.dumps({
"error": True,
"message": f"查询失败: {str(e)}",
"suggestion": "请稍后重试或检查城市名称是否正确"
}, ensure_ascii=False)
# 模块级注册函数(供 server.py 调用)
def setup(mcp: FastMCP):
register_weather_tools(mcp)
4.3 工具参数校验与类型安全
python
"""
参数校验最佳实践
"""
from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
from enum import Enum
# ============================================================
# 方式一:使用 Pydantic 模型定义复杂输入
# ============================================================
class SearchQuery(BaseModel):
"""搜索查询参数模型"""
keyword: str = Field(
..., # 必填
min_length=1,
max_length=200,
description="搜索关键词"
)
category: Optional[str] = Field(
None,
description="搜索分类过滤"
)
page: int = Field(
1,
ge=1, # 最小值
description="页码"
)
page_size: int = Field(
20,
ge=1,
le=100, # 最大值
description="每页数量"
)
@field_validator("keyword")
@classmethod
def validate_keyword(cls, v: str) -> str:
"""校验关键词不包含危险字符"""
dangerous_chars = ["<", ">", ";", "--", "/*", "*/"]
for char in dangerous_chars:
if char in v:
raise ValueError(f"关键词不能包含特殊字符: {char}")
return v.strip()
# 在工具中使用 Pydantic 模型
@mcp.tool()
def advanced_search(params: SearchQuery) -> str:
"""高级搜索工具,支持分页和分类过滤。
Args:
params: 搜索参数对象
"""
# FastMCP 会自动进行参数校验
# 如果校验失败,会返回结构化的错误信息给客户端
results = perform_search(
keyword=params.keyword,
category=params.category,
page=params.page,
page_size=params.page_size
)
return json.dumps(results, ensure_ascii=False)
# ============================================================
# 方式二:使用枚举约束参数值
# ============================================================
class SortOrder(str, Enum):
"""排序方式枚举"""
RELEVANCE = "relevance" # 相关度
DATE_DESC = "date_desc" # 日期降序
DATE_ASC = "date_asc" # 日期升序
POPULARITY = "popularity" # 热度
@mcp.tool()
def search_articles(
query: str,
sort_by: SortOrder = SortOrder.RELEVANCE,
limit: int = 10
) -> str:
"""搜索文章,支持多种排序方式。
Args:
query: 搜索关键词
sort_by: 排序方式 (relevance/date_desc/date_asc/popularity)
limit: 返回数量上限
"""
# sort_by 会被自动校验为有效的枚举值
# 如果传入无效值,MCP 会返回校验错误
...
4.4 异步工具与并发处理
python
"""
异步工具与并发处理
"""
import asyncio
import httpx
from typing import List
@mcp.tool()
async def batch_weather_query(cities: List[str]) -> str:
"""批量查询多个城市的天气(并发执行)。
使用 asyncio.gather 并发请求多个城市的天气数据,
显著提升多城市查询的响应速度。
Args:
cities: 城市名称列表,最多支持10个城市
Returns:
JSON 格式的多城市天气数据
"""
if len(cities) > 10:
raise ValueError("单次最多查询10个城市")
if len(cities) == 0:
raise ValueError("城市列表不能为空")
# 并发查询所有城市
async def fetch_city_weather(city: str) -> dict:
"""获取单个城市天气"""
try:
# 模拟网络延迟
await asyncio.sleep(0.1)
return {
"city": city,
"temperature": 25 + hash(city) % 10,
"condition": "晴",
"status": "success"
}
except Exception as e:
return {
"city": city,
"error": str(e),
"status": "failed"
}
# 使用 asyncio.gather 并发执行
# return_exceptions=True 确保单个失败不影响其他
results = await asyncio.gather(
*[fetch_city_weather(city) for city in cities],
return_exceptions=True
)
# 处理结果
weather_data = []
for i, result in enumerate(results):
if isinstance(result, Exception):
weather_data.append({
"city": cities[i],
"error": str(result),
"status": "failed"
})
else:
weather_data.append(result)
return json.dumps({
"total_cities": len(cities),
"successful": sum(1 for r in weather_data if r["status"] == "success"),
"failed": sum(1 for r in weather_data if r["status"] == "failed"),
"data": weather_data
}, ensure_ascii=False, indent=2)
@mcp.tool()
async def long_running_task(task_id: str, timeout_seconds: int = 30) -> str:
"""执行耗时任务(带超时控制)。
适用于需要较长时间处理的任务,如数据分析、报告生成等。
内置超时机制防止无限等待。
Args:
task_id: 任务标识符
timeout_seconds: 超时时间(秒),默认30秒
"""
try:
# 使用 asyncio.wait_for 设置超时
result = await asyncio.wait_for(
_do_heavy_computation(task_id),
timeout=timeout_seconds
)
return json.dumps({"task_id": task_id, "result": result, "status": "completed"})
except asyncio.TimeoutError:
return json.dumps({
"task_id": task_id,
"status": "timeout",
"message": f"任务超时(超过{timeout_seconds}秒)"
})
async def _do_heavy_computation(task_id: str) -> dict:
"""模拟耗时计算"""
await asyncio.sleep(2) # 模拟耗时操作
return {"computed_value": 42, "task_id": task_id}
4.5 工具返回值格式化
python
"""
工具返回值最佳实践
"""
from mcp.types import TextContent, ImageContent, EmbeddedResource
@mcp.tool()
def generate_report(report_type: str, data_source: str) -> str:
"""生成数据报告。
返回值格式规范:
1. 始终返回 JSON 字符串
2. 包含 status 字段表示执行状态
3. 错误时包含 error 和 suggestion 字段
4. 成功时包含 data 字段
Args:
report_type: 报告类型 (summary/detailed/export)
data_source: 数据源标识
"""
try:
# 业务逻辑...
report_data = {"title": "月度报告", "rows": 150}
# 成功响应格式
return json.dumps({
"status": "success",
"data": report_data,
"metadata": {
"generated_at": datetime.now().isoformat(),
"report_type": report_type,
"data_source": data_source
}
}, ensure_ascii=False, indent=2)
except PermissionError:
return json.dumps({
"status": "error",
"error": "PERMISSION_DENIED",
"message": "无权访问指定数据源",
"suggestion": "请联系管理员获取访问权限"
}, ensure_ascii=False)
except Exception as e:
logger.exception(f"生成报告失败: {e}")
return json.dumps({
"status": "error",
"error": "INTERNAL_ERROR",
"message": f"报告生成失败: {str(e)}",
"suggestion": "请稍后重试,如持续出现请联系技术支持"
}, ensure_ascii=False)
4.6 实战:构建天气查询 + 数据库操作工具集
python
"""
src/tools/database.py - 数据库操作工具集
========================================
演示安全的数据库操作工具
"""
import sqlite3
import json
import logging
from typing import Optional, List, Dict, Any
from contextlib import contextmanager
logger = logging.getLogger(__name__)
# 数据库连接配置
DB_PATH = "./data/app.db"
@contextmanager
def get_db_connection():
"""获取数据库连接的上下文管理器"""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row # 返回字典格式
try:
yield conn
finally:
conn.close()
def register_database_tools(mcp):
"""注册数据库操作工具"""
@mcp.tool()
def query_records(
table: str,
conditions: Optional[str] = None,
columns: Optional[List[str]] = None,
order_by: Optional[str] = None,
limit: int = 50
) -> str:
"""查询数据库记录(只读操作)。
安全的 SELECT 查询工具,自动防止 SQL 注入。
仅允许查询白名单中的表。
Args:
table: 表名(仅允许: users, orders, products)
conditions: WHERE 条件(简单格式,如 "age > 18")
columns: 要查询的列名列表,默认所有列
order_by: 排序字段
limit: 返回记录数上限(最大100)
Returns:
JSON 格式的查询结果
"""
# 安全校验:白名单表
ALLOWED_TABLES = {"users", "orders", "products"}
if table not in ALLOWED_TABLES:
return json.dumps({
"status": "error",
"message": f"不允许查询表 '{table}',允许的表: {list(ALLOWED_TABLES)}"
})
# 安全校验:limit 范围
limit = min(max(1, limit), 100)
# 构建安全的 SQL 查询
select_cols = ", ".join(columns) if columns else "*"
sql = f"SELECT {select_cols} FROM {table}"
params = []
if conditions:
# 注意:生产环境应使用参数化查询
# 这里简化演示,实际应解析条件并使用占位符
sql += f" WHERE {conditions}"
if order_by:
sql += f" ORDER BY {order_by}"
sql += f" LIMIT {limit}"
try:
with get_db_connection() as conn:
cursor = conn.execute(sql)
rows = [dict(row) for row in cursor.fetchall()]
return json.dumps({
"status": "success",
"table": table,
"count": len(rows),
"data": rows
}, ensure_ascii=False, indent=2)
except sqlite3.Error as e:
logger.error(f"数据库查询失败: {e}")
return json.dumps({
"status": "error",
"message": f"查询失败: {str(e)}"
})
@mcp.tool()
def insert_record(table: str, data: Dict[str, Any]) -> str:
"""向数据库插入一条记录。
Args:
table: 目标表名
data: 要插入的数据(键值对)
Returns:
插入结果,包含新记录的 ID
"""
ALLOWED_TABLES = {"users", "orders", "products"}
if table not in ALLOWED_TABLES:
return json.dumps({"status": "error", "message": "不允许的表名"})
if not data:
return json.dumps({"status": "error", "message": "数据不能为空"})
columns = ", ".join(data.keys())
placeholders = ", ".join(["?" for _ in data])
sql = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
try:
with get_db_connection() as conn:
cursor = conn.execute(sql, list(data.values()))
conn.commit()
new_id = cursor.lastrowid
return json.dumps({
"status": "success",
"message": f"成功插入记录",
"new_id": new_id,
"table": table
})
except sqlite3.Error as e:
return json.dumps({"status": "error", "message": str(e)})
@mcp.tool()
def get_table_schema(table: str) -> str:
"""获取数据库表的结构信息。
Args:
table: 表名
Returns:
表的列名、类型、约束等信息
"""
try:
with get_db_connection() as conn:
cursor = conn.execute(f"PRAGMA table_info({table})")
columns = [dict(row) for row in cursor.fetchall()]
if not columns:
return json.dumps({"status": "error", "message": f"表 '{table}' 不存在"})
return json.dumps({
"status": "success",
"table": table,
"columns": columns
}, ensure_ascii=False, indent=2)
except Exception as e:
return json.dumps({"status": "error", "message": str(e)})
五、配置资源映射与提示词模板注入
5.1 Resource 的概念与 URI 规范
MCP Resource 使用 URI 来唯一标识资源。URI 格式遵循 RFC 3986,常见的 scheme 包括:
| URI Scheme | 用途 | 示例 |
|---|---|---|
file:// |
文件系统资源 | file:///docs/readme.md |
db:// |
数据库资源 | db://users/schema |
config:// |
配置信息 | config://server-info |
api:// |
API 文档 | api://v2/endpoints |
knowledge:// |
知识库 | knowledge://faq/shipping |
5.2 静态资源与动态资源注册
python
"""
src/resources/knowledge_base.py - 知识库资源
"""
import json
from datetime import datetime
def register_resources(mcp):
"""注册所有资源"""
# ============================================================
# 静态资源:服务器配置信息
# ============================================================
@mcp.resource("config://server/status")
def server_status() -> str:
"""服务器运行状态信息"""
return json.dumps({
"server_name": "mcp-agent-server",
"version": "1.0.0",
"protocol_version": "2026-07-28",
"mode": "stateless",
"uptime_seconds": 3600,
"active_tools": 8,
"active_resources": 5,
"last_health_check": datetime.now().isoformat()
}, indent=2)
# ============================================================
# 静态资源:API 使用文档
# ============================================================
@mcp.resource("docs://api/usage-guide")
def api_usage_guide() -> str:
"""API 使用指南"""
return """
# MCP Agent Server 使用指南
## 可用工具
1. get_current_weather - 获取实时天气
2. get_weather_forecast - 获取天气预报
3. query_records - 查询数据库
4. advanced_search - 高级搜索
## 使用建议
- 先调用 tools/list 了解所有可用工具
- 天气类工具支持中文城市名
- 数据库查询限制每次最多返回100条记录
## 错误处理
- 所有工具返回 JSON 格式
- 检查 status 字段判断是否成功
- 错误时参考 suggestion 字段
"""
# ============================================================
# 动态资源:根据参数返回不同内容
# ============================================================
@mcp.resource("knowledge://faq/{topic}")
def get_faq(topic: str) -> str:
"""获取指定主题的 FAQ 内容。
Args:
topic: FAQ 主题(shipping/returns/payment/account)
"""
# 模拟知识库查询
faq_data = {
"shipping": {
"title": "物流配送 FAQ",
"items": [
{"q": "发货时间是多久?", "a": "下单后24小时内发货。"},
{"q": "支持哪些快递?", "a": "顺丰、中通、圆通、韵达。"},
{"q": "可以修改收货地址吗?", "a": "发货前可联系客服修改。"},
]
},
"returns": {
"title": "退换货 FAQ",
"items": [
{"q": "退货期限是多久?", "a": "签收后7天内可申请退货。"},
{"q": "退货运费谁承担?", "a": "质量问题由商家承担,其他由买家承担。"},
]
},
"payment": {
"title": "支付 FAQ",
"items": [
{"q": "支持哪些支付方式?", "a": "支付宝、微信支付、银行卡。"},
{"q": "可以分期付款吗?", "a": "满500元支持3/6/12期分期。"},
]
}
}
if topic in faq_data:
return json.dumps(faq_data[topic], ensure_ascii=False, indent=2)
else:
return json.dumps({
"error": f"未找到主题 '{topic}' 的 FAQ",
"available_topics": list(faq_data.keys())
}, ensure_ascii=False)
5.3 资源模板(Resource Templates)
python
"""
资源模板允许使用 URI 参数动态生成资源
"""
@mcp.resource("db://{database}/{table}/schema")
def get_table_schema_resource(database: str, table: str) -> str:
"""获取指定数据库表的结构定义。
URI 模板中的 {database} 和 {table} 会被自动提取为参数。
Args:
database: 数据库名称
table: 表名
"""
# 实际实现:查询数据库元数据
schema_info = {
"database": database,
"table": table,
"columns": [
{"name": "id", "type": "INTEGER", "primary_key": True},
{"name": "name", "type": "VARCHAR(100)", "nullable": False},
{"name": "created_at", "type": "TIMESTAMP", "default": "CURRENT_TIMESTAMP"},
]
}
return json.dumps(schema_info, indent=2)
@mcp.resource("file://logs/{date}/app.log")
def get_app_log(date: str) -> str:
"""获取指定日期的应用日志。
Args:
date: 日期,格式 YYYY-MM-DD
"""
# 实际实现:读取日志文件
# 注意:生产环境需要路径安全检查,防止路径遍历攻击
import os
safe_date = date.replace("/", "").replace("..", "")
log_path = f"./logs/{safe_date}/app.log"
if os.path.exists(log_path):
with open(log_path, "r") as f:
return f.read()
return f"未找到 {date} 的日志文件"
5.4 Prompt 模板定义与参数化
python
"""
src/prompts/templates.py - 提示词模板
"""
def register_prompts(mcp):
"""注册提示词模板"""
# ============================================================
# 简单提示词模板
# ============================================================
@mcp.prompt()
def code_review(language: str, code_snippet: str) -> str:
"""代码审查提示词模板。
引导 AI 对指定代码进行全面审查。
Args:
language: 编程语言
code_snippet: 待审查的代码片段
"""
return f"""请对以下 {language} 代码进行全面审查:
```{language}
{code_snippet}
请从以下维度进行分析:
- 正确性:逻辑是否正确,是否有 bug
- 性能:是否有性能瓶颈或优化空间
- 安全性:是否有安全漏洞(注入、XSS等)
- 可读性:命名、注释、代码结构
- 最佳实践:是否符合语言惯用法
对每个问题给出具体的修改建议和示例代码。"""
# ============================================================
# 多轮对话提示词模板
# ============================================================
@mcp.prompt()
def data_analysis_workflow(data_description: str, analysis_goal: str) -> str:
"""数据分析工作流提示词。
引导 AI 按步骤完成数据分析任务。
Args:
data_description: 数据描述(字段、类型、规模)
analysis_goal: 分析目标
"""
return f"""你是一位资深数据分析师。请按照以下工作流完成数据分析任务。
数据描述
{data_description}
分析目标
{analysis_goal}
工作流步骤
第一步:数据理解
- 确认数据字段含义
- 识别潜在的数据质量问题
- 提出需要确认的假设
第二步:分析方案设计
- 选择合适的分析方法
- 说明选择理由
- 列出需要的工具/函数
第三步:执行分析
- 调用可用的数据查询工具获取数据
- 执行统计计算
- 生成可视化建议
第四步:结论与建议
- 总结关键发现
- 给出可操作的建议
- 指出分析的局限性
请从第一步开始。"""
# ============================================================
# 带系统角色的提示词
# ============================================================
@mcp.prompt()
def customer_support_agent(product_name: str, customer_issue: str) -> str:
"""客服 Agent 提示词模板。
Args:
product_name: 产品名称
customer_issue: 客户问题描述
"""
return f"""你是一位专业、耐心的 {product_name} 客服代表。
客户问题:{customer_issue}
请遵循以下原则:
- 首先表示理解和同理心
- 确认问题的具体细节
- 提供清晰的解决步骤
- 如果需要调用工具查询订单/物流信息,请先告知客户
- 结束时确认问题是否解决
语气要求:友好、专业、不卑不亢。
回复长度:控制在200字以内,除非需要详细步骤说明。"""
### 5.5 实战:构建知识库资源 + 代码审查提示词
将以上所有组件整合到主服务器中:
```python
"""
src/server.py - 完整版服务器(整合所有模块)
"""
import sys
import logging
from mcp.server.fastmcp import FastMCP
# 导入各模块的注册函数
from src.tools.weather import register_weather_tools
from src.tools.database import register_database_tools
from src.resources.knowledge_base import register_resources
from src.prompts.templates import register_prompts
# 配置日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
logger = logging.getLogger("mcp-server")
# 创建服务器实例
mcp = FastMCP(
name="full-agent-server",
version="1.0.0",
stateless=True,
description="全功能无状态 AI Agent MCP 服务器",
instructions="本服务器提供天气查询、数据库操作、知识检索等能力。"
)
# 注册所有组件
register_weather_tools(mcp)
register_database_tools(mcp)
register_resources(mcp)
register_prompts(mcp)
# 启动
if __name__ == "__main__":
transport = "streamable-http" if "--http" in sys.argv else "stdio"
logger.info(f"启动服务器: transport={transport}, stateless=True")
if transport == "streamable-http":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
else:
mcp.run(transport="stdio")
六、本地调试运行与客户端连接验证
6.1 使用 MCP Inspector 进行可视化调试
MCP Inspector 是官方提供的图形化调试工具,可以可视化地测试 Tools、Resources、Prompts。
bash
# 安装 Inspector
npm install -g @modelcontextprotocol/inspector
# 方式一:调试 stdio 模式的服务器
npx @modelcontextprotocol/inspector python src/server.py
# 方式二:调试 HTTP 模式的服务器
# 先启动服务器
python src/server.py --http &
# 再启动 Inspector 连接
npx @modelcontextprotocol/inspector --url http://localhost:8000/mcp
Inspector 界面操作:
- 打开浏览器访问
http://localhost:6274 - 在左侧选择连接方式(stdio / HTTP)
- 点击 "Connect" 建立连接
- 切换到 "Tools" 标签页查看所有注册的工具
- 点击任意工具,填入参数,点击 "Run" 执行
- 查看返回结果和原始 JSON-RPC 消息
6.2 命令行手动测试(curl)
bash
# ============================================================
# 测试无状态 HTTP 模式
# ============================================================
# 1. 初始化请求(无状态模式下可选)
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2026-07-28",
"capabilities": {},
"clientInfo": {"name": "curl-client", "version": "1.0.0"}
}
}'
# 2. 获取工具列表
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'
# 3. 调用工具
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_current_weather",
"arguments": {
"city": "北京",
"unit": "celsius"
}
}
}'
# 4. 获取资源列表
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "resources/list",
"params": {}
}'
# 5. 读取资源
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/read",
"params": {
"uri": "config://server/status"
}
}'
6.3 Python 客户端连接测试
python
"""
tests/test_client.py - 使用 MCP Python SDK 客户端测试
"""
import asyncio
import json
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def test_stateless_connection():
"""测试无状态 HTTP 连接"""
# 连接到无状态 MCP 服务器
async with streamablehttp_client("http://localhost:8000/mcp") as (read, write, _):
async with ClientSession(read, write) as session:
# 初始化(无状态模式下仍然推荐调用)
await session.initialize()
print("✅ 连接成功!")
# 获取工具列表
tools = await session.list_tools()
print(f"\n📋 可用工具 ({len(tools.tools)} 个):")
for tool in tools.tools:
print(f" - {tool.name}: {tool.description[:50]}...")
# 调用天气工具
print("\n🌤️ 调用天气查询工具:")
result = await session.call_tool(
"get_current_weather",
arguments={"city": "上海", "unit": "celsius"}
)
print(f" 结果: {result.content[0].text[:100]}...")
# 读取资源
print("\n📖 读取服务器状态资源:")
resource = await session.read_resource("config://server/status")
print(f" 内容: {resource.contents[0].text[:100]}...")
# 获取提示词
print("\n💬 获取提示词模板:")
prompts = await session.list_prompts()
for prompt in prompts.prompts:
print(f" - {prompt.name}: {prompt.description[:40]}...")
async def test_tool_error_handling():
"""测试工具错误处理"""
async with streamablehttp_client("http://localhost:8000/mcp") as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
# 调用不存在的工具
try:
result = await session.call_tool(
"nonexistent_tool",
arguments={}
)
except Exception as e:
print(f"✅ 正确捕获错误: {e}")
# 传入无效参数
try:
result = await session.call_tool(
"get_current_weather",
arguments={"city": ""} # 空字符串应触发校验错误
)
print(f"结果: {result.content[0].text}")
except Exception as e:
print(f"✅ 参数校验错误: {e}")
if __name__ == "__main__":
print("=" * 60)
print("MCP 无状态服务器 - 客户端连接测试")
print("=" * 60)
asyncio.run(test_stateless_connection())
print("\n" + "=" * 60)
asyncio.run(test_tool_error_handling())
6.4 集成 Claude Desktop / Cursor 等 AI 客户端
Claude Desktop 配置:
编辑 Claude Desktop 配置文件:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
json
{
"mcpServers": {
"my-agent-server": {
"command": "python",
"args": ["/path/to/mcp-agent-server/src/server.py"],
"env": {
"LOG_LEVEL": "INFO"
}
},
"my-agent-server-http": {
"url": "http://localhost:8000/mcp",
"transport": "streamable-http"
}
}
}
Cursor IDE 配置:
在项目根目录创建 .cursor/mcp.json:
json
{
"mcpServers": {
"agent-tools": {
"url": "http://localhost:8000/mcp"
}
}
}
6.5 日志系统与请求追踪
python
"""
请求追踪中间件
"""
import uuid
import time
import logging
from functools import wraps
logger = logging.getLogger("mcp.requests")
class RequestTracer:
"""请求追踪器 - 为每个请求生成唯一 ID"""
@staticmethod
def middleware(handler):
@wraps(handler)
async def traced_handler(*args, **kwargs):
request_id = str(uuid.uuid4())[:8]
start_time = time.time()
logger.info(f"[{request_id}] → 请求开始: {kwargs.get('method', 'unknown')}")
try:
result = await handler(*args, **kwargs)
elapsed = time.time() - start_time
logger.info(f"[{request_id}] ← 请求完成: {elapsed:.3f}s")
return result
except Exception as e:
elapsed = time.time() - start_time
logger.error(f"[{request_id}] ✗ 请求失败: {elapsed:.3f}s, 错误: {e}")
raise
return traced_handler
七、典型报错场景分析与断点排查技巧
7.1 连接层错误排查
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
Connection refused |
服务器未启动或端口错误 | 确认服务器运行中,检查端口 |
Connection timeout |
防火墙/网络问题 | 检查防火墙规则,尝试 ping |
404 Not Found |
URL 路径错误 | 确认端点是 /mcp 而非 / |
405 Method Not Allowed |
使用了 GET 而非 POST | MCP 使用 POST 请求 |
415 Unsupported Media Type |
Content-Type 错误 | 设置为 application/json |
python
# 连接诊断脚本
"""
tests/diagnose.py - 连接诊断
"""
import httpx
import sys
async def diagnose(url: str = "http://localhost:8000/mcp"):
"""诊断 MCP 服务器连接"""
print(f"🔍 诊断目标: {url}")
# 1. 基本连通性
try:
async with httpx.AsyncClient(timeout=5.0) as client:
resp = await client.post(url, json={
"jsonrpc": "2.0", "id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2026-07-28",
"capabilities": {},
"clientInfo": {"name": "diagnose", "version": "1.0"}
}
})
print(f" ✅ HTTP 状态码: {resp.status_code}")
print(f" ✅ 响应: {resp.json()}")
except httpx.ConnectError:
print(" ❌ 连接失败:服务器未运行或端口不可达")
print(" 💡 解决:确认服务器已启动 (python src/server.py --http)")
except httpx.TimeoutException:
print(" ❌ 连接超时")
print(" 💡 解决:检查防火墙和网络配置")
except Exception as e:
print(f" ❌ 未知错误: {e}")
if __name__ == "__main__":
import asyncio
url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8000/mcp"
asyncio.run(diagnose(url))
7.2 工具调用失败的常见原因
python
"""
常见工具调用错误及解决方案
"""
# 错误1:工具名称不匹配
# 症状: "Tool not found: get_weather"
# 原因: 注册的工具名是 "get_current_weather",调用时用了 "get_weather"
# 解决: 先调用 tools/list 确认准确的工具名
# 错误2:参数类型不匹配
# 症状: "Invalid params: expected number, got string"
# 原因: JSON 中 "limit": "10" 应为 "limit": 10
# 解决: 确保 JSON 类型与 Schema 定义一致
# 错误3:缺少必填参数
# 症状: "Missing required parameter: query"
# 原因: 调用时未提供必填参数
# 解决: 查看 tools/list 返回的 inputSchema.required 字段
# 错误4:工具内部异常
# 症状: "Internal error" 或自定义错误信息
# 原因: 工具函数抛出未捕获的异常
# 解决: 在工具函数中添加 try-except,返回结构化错误
# 错误5:超时
# 症状: 请求长时间无响应
# 原因: 工具执行时间过长
# 解决: 添加超时控制,或改为异步任务模式
7.3 Schema 校验不通过的处理
python
"""
Schema 校验错误的调试方法
"""
import json
from pydantic import ValidationError
# 开启详细校验日志
import logging
logging.getLogger("mcp.validation").setLevel(logging.DEBUG)
# 手动测试 Schema 校验
def debug_schema_validation():
"""手动验证工具参数是否符合 Schema"""
from pydantic import BaseModel, Field
class WeatherParams(BaseModel):
city: str = Field(..., min_length=1)
unit: str = Field("celsius", pattern="^(celsius|fahrenheit)$")
# 测试用例
test_cases = [
{"city": "北京", "unit": "celsius"}, # ✅ 正确
{"city": "", "unit": "celsius"}, # ❌ city 为空
{"city": "北京", "unit": "kelvin"}, # ❌ unit 不在枚举中
{"unit": "celsius"}, # ❌ 缺少必填字段 city
{"city": "北京", "unit": "celsius", "extra": 1}, # ⚠️ 多余字段
]
for i, case in enumerate(test_cases):
try:
params = WeatherParams(**case)
print(f" 用例{i+1} ✅ 通过: {params}")
except ValidationError as e:
print(f" 用例{i+1} ❌ 失败: {e.errors()}")
7.4 超时与并发冲突
python
"""
超时处理最佳实践
"""
import asyncio
from functools import wraps
def with_timeout(default_timeout: float = 30.0):
"""超时装饰器"""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
timeout = kwargs.pop("timeout", default_timeout)
try:
return await asyncio.wait_for(func(*args, **kwargs), timeout=timeout)
except asyncio.TimeoutError:
return json.dumps({
"status": "timeout",
"message": f"操作超时(>{timeout}秒)",
"suggestion": "请减少数据量或增加超时时间"
})
return wrapper
return decorator
@mcp.tool()
@with_timeout(default_timeout=15.0)
async def heavy_data_analysis(dataset_id: str) -> str:
"""执行大数据分析(15秒超时)"""
# 耗时操作...
await asyncio.sleep(20) # 模拟超时
return "done"
7.5 断点调试实战(VS Code)
json
// .vscode/launch.json - 完整调试配置
{
"version": "0.2.0",
"configurations": [
{
"name": "调试 MCP Server (HTTP)",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/src/server.py",
"args": ["--http"],
"console": "integratedTerminal",
"justMyCode": false, // 设为 false 可以进入 SDK 内部
"env": {
"LOG_LEVEL": "DEBUG",
"PYTHONPATH": "${workspaceFolder}"
}
},
{
"name": "调试 MCP Client 测试",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/tests/test_client.py",
"console": "integratedTerminal",
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
},
{
"name": "附加到运行中的服务器",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
}
}
]
}
调试步骤:
- 在工具函数中设置断点(点击行号左侧)
- 按 F5 启动调试
- 使用 curl 或 Inspector 发送请求
- 程序会在断点处暂停
- 使用调试面板查看变量值、调用栈
- F10 单步执行,F11 步入函数
7.6 错误码速查表
| JSON-RPC 错误码 | 含义 | 常见原因 |
|---|---|---|
| -32700 | Parse error | JSON 格式错误 |
| -32600 | Invalid Request | 请求结构不符合 JSON-RPC 2.0 |
| -32601 | Method not found | 方法名不存在 |
| -32602 | Invalid params | 参数校验失败 |
| -32603 | Internal error | 服务器内部错误 |
| -32000 | Server error (通用) | 自定义服务器错误 |
| -32001 | Resource not found | 请求的资源不存在 |
| -32002 | Tool execution failed | 工具执行失败 |
八、生产环境部署与容器化封装策略
8.1 部署架构选型
在将 MCP 无状态服务器推向生产环境之前,需要根据业务规模选择合适的部署架构。MCP v2(2026-07-28 规范)的无状态特性使得部署方式与传统微服务完全一致,无需考虑会话粘性问题。
三种典型部署架构对比:
┌─────────────────────────────────────────────────────────────────────┐
│ 架构一:单机部署(适合开发/小流量) │
│ │
│ Client ──→ [Nginx] ──→ [MCP Server (单进程)] │
│ Port: 8000 │
│ │
│ 适用场景:日请求量 < 10,000 │
│ 优点:部署简单,运维成本低 │
│ 缺点:无高可用,单点故障 │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 架构二:多实例 + 负载均衡(适合中等流量) │
│ │
│ ┌──→ [MCP Server 实例1] │
│ Client ──→ [LB] ──┼──→ [MCP Server 实例2] │
│ └──→ [MCP Server 实例3] │
│ │
│ 适用场景:日请求量 10,000 - 1,000,000 │
│ 优点:水平扩展,高可用 │
│ 关键:无状态模式下,LB 无需 sticky session │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 架构三:Kubernetes 集群部署(适合大流量/企业级) │
│ │
│ Client ──→ [Ingress] ──→ [Service] ──→ [Pod 1] [Pod 2] [Pod N] │
│ │ │
│ [HPA 自动扩缩容] │
│ │
│ 适用场景:日请求量 > 1,000,000,需要弹性伸缩 │
│ 优点:自动扩缩容、滚动更新、自愈 │
│ 关键:利用无状态特性,Pod 可随意调度到任意节点 │
└─────────────────────────────────────────────────────────────────────┘
选型决策表:
| 考量因素 | 单机部署 | 多实例+LB | Kubernetes |
|---|---|---|---|
| 团队规模 | 1-2人 | 3-5人 | 5人+ |
| 日请求量 | <1万 | 1万-100万 | >100万 |
| 可用性要求 | 99% | 99.9% | 99.99% |
| 运维复杂度 | 低 | 中 | 高 |
| 成本 | 低 | 中 | 高(但弹性节省) |
| 扩缩容速度 | 手动(分钟级) | 半自动(分钟级) | 自动(秒级) |
8.2 Dockerfile 编写与镜像构建
Python 版 Dockerfile(多阶段构建,生产推荐):
dockerfile
# ============================================================
# deploy/Dockerfile - MCP 无状态服务器生产镜像
# 多阶段构建:减小最终镜像体积
# ============================================================
# -------------------- 阶段一:依赖安装 --------------------
FROM python:3.11-slim AS dependency-builder
# 设置工作目录
WORKDIR /build
# 先复制依赖文件(利用 Docker 缓存层)
COPY requirements.txt .
# 安装依赖到独立目录
RUN pip install --no-cache-dir --prefix=/install \
-r requirements.txt
# -------------------- 阶段二:运行时镜像 --------------------
FROM python:3.11-slim AS runtime
# 安全:创建非 root 用户
RUN groupadd -r mcpuser && useradd -r -g mcpuser mcpuser
# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
MCP_STATELESS=true \
LOG_LEVEL=INFO \
PORT=8000
# 从构建阶段复制已安装的依赖
COPY --from=dependency-builder /install /usr/local
# 设置工作目录
WORKDIR /app
# 复制应用代码
COPY src/ ./src/
COPY pyproject.toml .
# 创建必要目录
RUN mkdir -p /app/data /app/logs && \
chown -R mcpuser:mcpuser /app
# 切换到非 root 用户
USER mcpuser
# 暴露端口
EXPOSE 8000
# 健康检查(无状态服务器可以直接检查 /health 端点)
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health').raise_for_status()"
# 启动命令
# 使用 uvicorn 作为 ASGI 服务器
CMD ["python", "-m", "uvicorn", \
"src.server:app", \
"--host", "0.0.0.0", \
"--port", "8000", \
"--workers", "4", \
"--access-log", \
"--log-level", "info"]
TypeScript 版 Dockerfile:
dockerfile
# ============================================================
# deploy/Dockerfile.ts - TypeScript MCP 服务器
# ============================================================
# 阶段一:构建
FROM node:20-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production
COPY tsconfig.json ./
COPY src/ ./src/
RUN npx tsc
# 阶段二:运行
FROM node:20-alpine AS runtime
RUN addgroup -S mcpuser && adduser -S mcpuser -G mcpuser
WORKDIR /app
COPY --from=builder /build/node_modules ./node_modules
COPY --from=builder /build/dist ./dist
COPY package.json ./
USER mcpuser
EXPOSE 8000
ENV NODE_ENV=production \
PORT=8000 \
MCP_STATELESS=true
HEALTHCHECK --interval=30s --timeout=5s \
CMD wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1
CMD ["node", "dist/server.js", "--http"]
构建与运行:
bash
# 构建镜像
docker build -f deploy/Dockerfile -t mcp-agent-server:v1.0.0 .
# 查看镜像大小(多阶段构建通常 < 200MB)
docker images mcp-agent-server
# 运行容器
docker run -d \
--name mcp-server \
-p 8000:8000 \
-e LOG_LEVEL=INFO \
-e MCP_STATELESS=true \
--restart unless-stopped \
mcp-agent-server:v1.0.0
# 验证运行
curl http://localhost:8000/health
# 期望输出: {"status": "healthy", "mode": "stateless"}
8.3 Docker Compose 多服务编排
yaml
# deploy/docker-compose.yml
# 完整的 MCP 服务器 + 依赖服务编排
version: "3.9"
services:
# ============================================================
# MCP 无状态服务器(多副本)
# ============================================================
mcp-server:
build:
context: ..
dockerfile: deploy/Dockerfile
deploy:
replicas: 3 # 无状态模式:直接多副本,无需 sticky session
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
environment:
- MCP_STATELESS=true
- LOG_LEVEL=INFO
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=mcp_data
- REDIS_URL=redis://redis:6379/0
ports:
- "8000:8000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/health')"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
networks:
- mcp-network
# ============================================================
# Nginx 反向代理 / 负载均衡
# ============================================================
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- mcp-server
networks:
- mcp-network
# ============================================================
# PostgreSQL 数据库
# ============================================================
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: mcp_data
POSTGRES_USER: mcp_user
POSTGRES_PASSWORD: ${DB_PASSWORD:-secure_password_here}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mcp_user -d mcp_data"]
interval: 10s
timeout: 5s
retries: 5
networks:
- mcp-network
# ============================================================
# Redis 缓存(用于限流计数器)
# ============================================================
redis:
image: redis:7-alpine
command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks:
- mcp-network
# ============================================================
# Prometheus 监控(可选)
# ============================================================
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
networks:
- mcp-network
volumes:
postgres_data:
redis_data:
networks:
mcp-network:
driver: bridge
Nginx 配置文件:
nginx
# deploy/nginx/nginx.conf
# MCP 服务器反向代理配置(无状态模式:简单 round-robin)
upstream mcp_backend {
# 无状态模式关键:无需 ip_hash 或 sticky session
# 标准 round-robin 即可
server mcp-server:8000;
# 如果是多容器,Docker Compose 会自动解析为多个 IP
# 连接池配置
keepalive 32;
}
server {
listen 80;
server_name mcp.example.com;
# 强制 HTTPS 重定向
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name mcp.example.com;
# SSL 配置
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# MCP 端点
location /mcp {
proxy_pass http://mcp_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# MCP 请求超时设置
proxy_connect_timeout 10s;
proxy_read_timeout 60s; # 工具执行可能耗时较长
proxy_send_timeout 10s;
# 支持 SSE 流式响应
proxy_buffering off;
proxy_cache off;
}
# 健康检查端点
location /health {
proxy_pass http://mcp_backend;
proxy_http_version 1.1;
}
# 禁止访问其他路径
location / {
return 404;
}
}
8.4 Kubernetes 部署清单
yaml
# deploy/k8s/deployment.yaml
# MCP 无状态服务器 Kubernetes 部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-agent-server
namespace: ai-services
labels:
app: mcp-agent-server
version: v1.0.0
protocol: mcp-v2
spec:
# 无状态模式:副本数可随意调整,无需担心会话中断
replicas: 3
selector:
matchLabels:
app: mcp-agent-server
# 滚动更新策略
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多出1个Pod
maxUnavailable: 0 # 不允许有Pod不可用
template:
metadata:
labels:
app: mcp-agent-server
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
prometheus.io/path: "/metrics"
spec:
# 安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: mcp-server
image: registry.example.com/mcp-agent-server:v1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
protocol: TCP
name: http
# 环境变量
env:
- name: MCP_STATELESS
value: "true"
- name: LOG_LEVEL
value: "INFO"
- name: PORT
value: "8000"
- name: DB_HOST
valueFrom:
secretKeyRef:
name: mcp-db-secret
key: host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mcp-db-secret
key: password
# 资源限制
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
# 存活探针
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
# 就绪探针
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
# 启动探针
startupProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 3
periodSeconds: 5
failureThreshold: 10
---
# deploy/k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: mcp-agent-server
namespace: ai-services
labels:
app: mcp-agent-server
spec:
type: ClusterIP
selector:
app: mcp-agent-server
ports:
- port: 80
targetPort: 8000
protocol: TCP
name: http
---
# deploy/k8s/hpa.yaml
# 水平自动扩缩容(无状态模式的核心优势)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mcp-agent-server-hpa
namespace: ai-services
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mcp-agent-server
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 120
---
# deploy/k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mcp-agent-server-ingress
namespace: ai-services
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- mcp.example.com
secretName: mcp-tls-secret
rules:
- host: mcp.example.com
http:
paths:
- path: /mcp
pathType: Prefix
backend:
service:
name: mcp-agent-server
port:
number: 80
- path: /health
pathType: Exact
backend:
service:
name: mcp-agent-server
port:
number: 80
8.5 CI/CD 流水线集成
yaml
# .github/workflows/deploy.yml
# GitHub Actions CI/CD 流水线
name: MCP Server CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ============================================================
# 阶段一:测试
# ============================================================
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install -e ".[dev]"
- name: Run linter
run: ruff check src/
- name: Run type checker
run: mypy src/
- name: Run unit tests
run: pytest tests/ -v --tb=short
- name: Run integration tests
run: |
# 启动服务器
python src/server.py --http &
SERVER_PID=$!
sleep 3
# 运行集成测试
pytest tests/test_integration.py -v
# 清理
kill $SERVER_PID
# ============================================================
# 阶段二:构建镜像
# ============================================================
build:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: deploy/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# ============================================================
# 阶段三:部署到 Kubernetes
# ============================================================
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v4
with:
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Deploy to Kubernetes
run: |
# 更新镜像标签
kubectl set image deployment/mcp-agent-server \
mcp-server=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n ai-services
# 等待滚动更新完成
kubectl rollout status deployment/mcp-agent-server \
-n ai-services --timeout=300s
# 验证部署
kubectl get pods -n ai-services -l app=mcp-agent-server
8.6 健康检查与优雅停机
python
"""
src/health.py - 健康检查与优雅停机实现
"""
import signal
import asyncio
import logging
from datetime import datetime
from contextlib import asynccontextmanager
logger = logging.getLogger("mcp.health")
# 全局状态
_server_start_time = datetime.now()
_is_shutting_down = False
def create_health_endpoint(app):
"""为 FastAPI/Uvicorn 应用添加健康检查端点"""
@app.get("/health")
async def health_check():
"""
健康检查端点
- 200: 服务健康
- 503: 服务正在关闭(优雅停机期间)
"""
if _is_shutting_down:
return {
"status": "draining",
"message": "Server is shutting down",
"timestamp": datetime.now().isoformat()
}, 503
uptime = (datetime.now() - _server_start_time).total_seconds()
return {
"status": "healthy",
"mode": "stateless",
"protocol_version": "2026-07-28",
"uptime_seconds": round(uptime, 1),
"timestamp": datetime.now().isoformat()
}
@app.get("/ready")
async def readiness_check():
"""
就绪检查端点(Kubernetes readinessProbe 使用)
检查所有依赖服务是否可用
"""
checks = {
"database": await _check_database(),
"redis": await _check_redis(),
}
all_healthy = all(checks.values())
return {
"status": "ready" if all_healthy else "not_ready",
"checks": checks
}
async def _check_database() -> bool:
"""检查数据库连接"""
try:
# 实际实现:执行 SELECT 1
return True
except Exception:
return False
async def _check_redis() -> bool:
"""检查 Redis 连接"""
try:
# 实际实现:执行 PING
return True
except Exception:
return False
def setup_graceful_shutdown():
"""配置优雅停机处理"""
def signal_handler(signum, frame):
global _is_shutting_down
_is_shutting_down = True
logger.info(f"收到信号 {signum},开始优雅停机...")
logger.info("1. 停止接受新请求(健康检查返回 503)")
logger.info("2. 等待进行中的请求完成(最多30秒)")
logger.info("3. 关闭连接池和外部资源")
logger.info("4. 进程退出")
# Uvicorn 会自动处理 SIGTERM/SIGINT
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
九、性能优化要点与安全访问控制机制
9.1 连接池与资源复用
python
"""
src/middleware/connection_pool.py - 连接池管理
===============================================
无状态模式下,每个请求独立处理,但底层的数据库连接、
HTTP 客户端等资源仍然需要池化复用,避免频繁创建/销毁。
"""
import httpx
import asyncio
from contextlib import asynccontextmanager
from typing import Optional
class ConnectionPoolManager:
"""
连接池管理器
在无状态 MCP 服务器中,虽然每个 HTTP 请求独立,
但服务器进程内部仍然维护长生命周期的连接池。
这是"协议层无状态"与"实现层资源复用"的分离。
"""
def __init__(self):
self._http_client: Optional[httpx.AsyncClient] = None
self._db_pool = None
self._lock = asyncio.Lock()
async def initialize(self):
"""初始化所有连接池(服务器启动时调用一次)"""
async with self._lock:
# HTTP 连接池(用于调用外部 API)
self._http_client = httpx.AsyncClient(
timeout=httpx.Timeout(
connect=5.0, # 连接超时
read=30.0, # 读取超时
write=10.0, # 写入超时
pool=5.0 # 连接池等待超时
),
limits=httpx.Limits(
max_connections=100, # 最大连接数
max_keepalive_connections=20, # 最大保活连接
keepalive_expiry=30.0 # 保活过期时间
),
http2=True # 启用 HTTP/2
)
# 数据库连接池(以 asyncpg 为例)
# self._db_pool = await asyncpg.create_pool(
# host=DB_HOST,
# database=DB_NAME,
# user=DB_USER,
# password=DB_PASSWORD,
# min_size=5, # 最小连接数
# max_size=20, # 最大连接数
# command_timeout=30.0
# )
logger.info("连接池初始化完成")
async def shutdown(self):
"""关闭所有连接池(服务器关闭时调用)"""
if self._http_client:
await self._http_client.aclose()
if self._db_pool:
await self._db_pool.close()
logger.info("连接池已关闭")
@property
def http_client(self) -> httpx.AsyncClient:
if self._http_client is None:
raise RuntimeError("连接池未初始化,请先调用 initialize()")
return self._http_client
# 全局单例
pool_manager = ConnectionPoolManager()
# 在工具中使用连接池
@mcp.tool()
async def fetch_external_data(url: str) -> str:
"""从外部 API 获取数据(使用连接池)"""
# 复用连接池中的 HTTP 客户端
response = await pool_manager.http_client.get(url)
response.raise_for_status()
return response.text
9.2 请求限流与熔断
python
"""
src/middleware/rate_limit.py - 请求限流与熔断
"""
import time
import asyncio
from collections import defaultdict
from functools import wraps
from typing import Dict, Tuple
import logging
logger = logging.getLogger("mcp.rate_limit")
class RateLimiter:
"""
令牌桶限流器
无状态模式下的限流策略:
- 使用 Redis 作为分布式计数器(多实例共享)
- 或使用进程内计数器(单实例场景)
"""
def __init__(self, rate: float = 100.0, burst: int = 200):
"""
Args:
rate: 每秒允许的请求数(令牌生成速率)
burst: 桶容量(突发允许的最大请求数)
"""
self.rate = rate
self.burst = burst
self._tokens: Dict[str, Tuple[float, float]] = {} # key -> (tokens, last_time)
self._lock = asyncio.Lock()
async def acquire(self, key: str = "global") -> bool:
"""
尝试获取一个令牌
Args:
key: 限流键(可以是 IP、API Key、用户ID等)
Returns:
True 表示允许通过,False 表示被限流
"""
async with self._lock:
now = time.time()
if key not in self._tokens:
self._tokens[key] = (self.burst - 1, now)
return True
tokens, last_time = self._tokens[key]
# 补充令牌
elapsed = now - last_time
tokens = min(self.burst, tokens + elapsed * self.rate)
if tokens >= 1:
self._tokens[key] = (tokens - 1, now)
return True
else:
self._tokens[key] = (tokens, now)
return False
class CircuitBreaker:
"""
熔断器
当下游服务(数据库、外部API)持续失败时,
自动切断请求,避免雪崩效应。
"""
# 熔断器状态
CLOSED = "closed" # 正常状态,请求通过
OPEN = "open" # 熔断状态,请求直接失败
HALF_OPEN = "half_open" # 半开状态,允许少量探测请求
def __init__(
self,
failure_threshold: int = 5,
recovery_timeout: float = 30.0,
half_open_max_calls: int = 3
):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.half_open_max_calls = half_open_max_calls
self._state = self.CLOSED
self._failure_count = 0
self._last_failure_time = 0.0
self._half_open_calls = 0
@property
def state(self) -> str:
if self._state == self.OPEN:
# 检查是否可以进入半开状态
if time.time() - self._last_failure_time >= self.recovery_timeout:
self._state = self.HALF_OPEN
self._half_open_calls = 0
return self._state
def can_execute(self) -> bool:
"""判断当前是否允许执行请求"""
state = self.state
if state == self.CLOSED:
return True
elif state == self.HALF_OPEN:
return self._half_open_calls < self.half_open_max_calls
else: # OPEN
return False
def record_success(self):
"""记录成功"""
self._failure_count = 0
self._state = self.CLOSED
def record_failure(self):
"""记录失败"""
self._failure_count += 1
self._last_failure_time = time.time()
if self._state == self.HALF_OPEN:
self._state = self.OPEN
elif self._failure_count >= self.failure_threshold:
self._state = self.OPEN
logger.warning(f"熔断器打开!连续失败 {self._failure_count} 次")
# 使用示例
rate_limiter = RateLimiter(rate=50.0, burst=100)
circuit_breaker = CircuitBreaker(failure_threshold=5, recovery_timeout=30.0)
def with_rate_limit(key_extractor=None):
"""限流装饰器"""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
# 提取限流键
key = key_extractor(*args, **kwargs) if key_extractor else "global"
if not await rate_limiter.acquire(key):
return json.dumps({
"status": "error",
"error": "RATE_LIMITED",
"message": "请求过于频繁,请稍后重试",
"retry_after_seconds": 1
})
return await func(*args, **kwargs)
return wrapper
return decorator
9.3 OAuth 2.0 / API Key 认证
python
"""
src/middleware/auth.py - 认证与授权
====================================
MCP v2 规范推荐使用 OAuth 2.1 进行认证。
对于简单场景,也支持 API Key 认证。
"""
import hashlib
import hmac
import time
import json
from typing import Optional, Set
from functools import wraps
import logging
logger = logging.getLogger("mcp.auth")
class APIKeyAuthenticator:
"""
API Key 认证器
适用于内部服务间调用或简单的客户端认证。
生产环境建议使用 OAuth 2.0。
"""
def __init__(self):
# 实际项目中从数据库或配置中心加载
self._valid_keys: Set[str] = set()
self._key_permissions: dict = {}
def add_key(self, api_key: str, permissions: list = None):
"""注册 API Key"""
key_hash = self._hash_key(api_key)
self._valid_keys.add(key_hash)
self._key_permissions[key_hash] = permissions or ["read", "write"]
def verify(self, api_key: str) -> Optional[dict]:
"""
验证 API Key
Returns:
验证成功返回权限信息,失败返回 None
"""
key_hash = self._hash_key(api_key)
if key_hash in self._valid_keys:
return {"permissions": self._key_permissions[key_hash]}
return None
@staticmethod
def _hash_key(key: str) -> str:
"""对 Key 进行哈希(不存储明文)"""
return hashlib.sha256(key.encode()).hexdigest()
class OAuth2Authenticator:
"""
OAuth 2.0 / 2.1 认证器(MCP v2 推荐)
支持 Authorization Code Flow 和 Client Credentials Flow。
"""
def __init__(self, issuer_url: str, audience: str):
self.issuer_url = issuer_url
self.audience = audience
self._jwks_cache = None
self._jwks_cache_time = 0
async def verify_token(self, token: str) -> Optional[dict]:
"""
验证 Bearer Token(JWT)
Args:
token: JWT access token
Returns:
解码后的 claims,验证失败返回 None
"""
try:
import jwt # PyJWT 库
# 获取 JWKS(JSON Web Key Set)
jwks = await self._get_jwks()
# 验证并解码 JWT
claims = jwt.decode(
token,
key=jwks,
algorithms=["RS256", "ES256"],
audience=self.audience,
issuer=self.issuer_url,
options={"verify_exp": True}
)
return claims
except jwt.ExpiredSignatureError:
logger.warning("Token 已过期")
return None
except jwt.InvalidTokenError as e:
logger.warning(f"Token 无效: {e}")
return None
# 认证中间件
authenticator = APIKeyAuthenticator()
def require_auth(permissions: list = None):
"""
认证装饰器
用法:
@mcp.tool()
@require_auth(permissions=["read"])
def sensitive_query(...):
...
"""
def decorator(func):
@wraps(func)
async def wrapper(*args, _auth_context=None, **kwargs):
if _auth_context is None:
return json.dumps({
"status": "error",
"error": "UNAUTHORIZED",
"message": "缺少认证信息"
})
# 检查权限
if permissions:
user_perms = _auth_context.get("permissions", [])
if not any(p in user_perms for p in permissions):
return json.dumps({
"status": "error",
"error": "FORBIDDEN",
"message": "权限不足"
})
return await func(*args, **kwargs)
return wrapper
return decorator
9.4 CORS 与请求来源控制
python
"""
CORS 配置(仅浏览器客户端需要)
"""
from starlette.middleware.cors import CORSMiddleware
def setup_cors(app):
"""配置 CORS 策略"""
# 生产环境:严格限制来源
ALLOWED_ORIGINS = [
"https://app.example.com",
"https://admin.example.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_methods=["POST"], # MCP 只使用 POST
allow_headers=[
"Content-Type",
"Authorization",
"X-API-Key",
"X-Request-ID",
],
max_age=3600, # 预检请求缓存1小时
)
9.5 输入消毒与注入防护
python
"""
src/middleware/sanitizer.py - 输入消毒
"""
import re
from typing import Any
class InputSanitizer:
"""
输入消毒器
防止 SQL 注入、XSS、命令注入等攻击。
所有用户输入在到达业务逻辑之前必须经过消毒。
"""
# SQL 注入检测模式
SQL_INJECTION_PATTERNS = [
r"(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER)\b)",
r"(--|;|/\*|\*/)",
r"(\bOR\b\s+\d+\s*=\s*\d+)",
r"(\bAND\b\s+\d+\s*=\s*\d+)",
]
# XSS 检测模式
XSS_PATTERNS = [
r"<script[^>]*>",
r"javascript:",
r"on\w+\s*=",
r"<iframe",
]
# 命令注入检测
COMMAND_INJECTION_PATTERNS = [
r"[;&|`$]",
r"\$$",
r"\beval\b",
r"\bexec\b",
]
@classmethod
def sanitize_string(cls, value: str, context: str = "general") -> str:
"""
消毒字符串输入
Args:
value: 原始输入
context: 使用上下文 (general/sql/html/command)
Returns:
消毒后的安全字符串
Raises:
ValueError: 检测到恶意输入
"""
if not isinstance(value, str):
return value
# 通用检查:长度限制
if len(value) > 10000:
raise ValueError("输入超过最大长度限制")
# SQL 上下文检查
if context == "sql":
for pattern in cls.SQL_INJECTION_PATTERNS:
if re.search(pattern, value, re.IGNORECASE):
raise ValueError(f"检测到潜在的 SQL 注入: {value[:50]}...")
# HTML 上下文检查
elif context == "html":
for pattern in cls.XSS_PATTERNS:
if re.search(pattern, value, re.IGNORECASE):
raise ValueError(f"检测到潜在的 XSS 攻击")
# 命令上下文检查
elif context == "command":
for pattern in cls.COMMAND_INJECTION_PATTERNS:
if re.search(pattern, value):
raise ValueError(f"检测到潜在的命令注入")
return value.strip()
@classmethod
def sanitize_dict(cls, data: dict, context: str = "general") -> dict:
"""递归消毒字典中的所有字符串值"""
result = {}
for key, value in data.items():
if isinstance(value, str):
result[key] = cls.sanitize_string(value, context)
elif isinstance(value, dict):
result[key] = cls.sanitize_dict(value, context)
elif isinstance(value, list):
result[key] = [
cls.sanitize_string(v, context) if isinstance(v, str) else v
for v in value
]
else:
result[key] = value
return result
9.6 审计日志与敏感数据脱敏
python
"""
src/middleware/audit.py - 审计日志
"""
import json
import logging
from datetime import datetime
from typing import Any
# 独立的审计日志记录器(输出到单独文件)
audit_logger = logging.getLogger("mcp.audit")
audit_handler = logging.FileHandler("/app/logs/audit.log")
audit_handler.setFormatter(logging.Formatter(
"%(asctime)s | %(message)s"
))
audit_logger.addHandler(audit_handler)
audit_logger.setLevel(logging.INFO)
# 敏感字段列表(日志中需要脱敏)
SENSITIVE_FIELDS = {
"password", "secret", "token", "api_key",
"credit_card", "ssn", "authorization"
}
def mask_sensitive_data(data: Any) -> Any:
"""对敏感数据进行脱敏"""
if isinstance(data, dict):
return {
k: "***MASKED***" if k.lower() in SENSITIVE_FIELDS
else mask_sensitive_data(v)
for k, v in data.items()
}
elif isinstance(data, list):
return [mask_sensitive_data(item) for item in data]
return data
def log_tool_invocation(
tool_name: str,
arguments: dict,
result_status: str,
duration_ms: float,
client_id: str = "unknown"
):
"""记录工具调用审计日志"""
# 脱敏处理
safe_args = mask_sensitive_data(arguments)
audit_entry = {
"timestamp": datetime.now().isoformat(),
"event": "tool_invocation",
"tool": tool_name,
"arguments": safe_args,
"status": result_status,
"duration_ms": round(duration_ms, 2),
"client_id": client_id,
}
audit_logger.info(json.dumps(audit_entry, ensure_ascii=False))
9.7 TLS/mTLS 加密通信
python
"""
TLS 配置(生产环境必须)
"""
# Uvicorn 启动时配置 TLS
"""
# 方式一:Uvicorn 直接配置 TLS
uvicorn src.server:app \
--host 0.0.0.0 \
--port 8443 \
--ssl-keyfile /etc/ssl/private/server.key \
--ssl-certfile /etc/ssl/certs/server.crt \
--ssl-ca-certs /etc/ssl/certs/ca.crt # mTLS: 验证客户端证书
# 方式二:在 Nginx/Ingress 层终止 TLS(推荐)
# 参见 8.5 节的 Nginx 配置
"""
# mTLS 客户端证书验证(高安全场景)
"""
对于金融、医疗等高安全要求场景,可以启用 mTLS:
- 服务器验证客户端证书
- 客户端验证服务器证书
- 双向认证,防止中间人攻击
Kubernetes 中可使用 Istio/Linkerd 自动管理 mTLS。
"""
十、扩展实战:构建多工具协同的 Agent 链
10.1 Agent 链的设计思想
在实际业务场景中,单个工具往往无法完成复杂任务。Agent 链(Agent Chain)通过将多个工具按逻辑顺序编排,实现复杂工作流的自动化执行。
Agent 链的核心设计原则:
┌─────────────────────────────────────────────────────────────────┐
│ Agent 链设计模式 │
│ │
│ 模式一:顺序链(Sequential Chain) │
│ Tool A → Tool B → Tool C → 最终结果 │
│ 适用:数据管道、ETL 流程 │
│ │
│ 模式二:条件分支链(Conditional Chain) │
│ Tool A → [判断] → Tool B1 或 Tool B2 → 最终结果 │
│ 适用:根据中间结果选择不同处理路径 │
│ │
│ 模式三:并行聚合链(Parallel Aggregation Chain) │
│ Tool A ─┐ │
│ Tool B ─┼→ [聚合] → 最终结果 │
│ Tool C ─┘ │
│ 适用:多源数据汇总 │
│ │
│ 模式四:循环链(Loop Chain) │
│ Tool A → [检查] → 不满足 → Tool A(重试) │
│ → 满足 → 最终结果 │
│ 适用:轮询等待、迭代优化 │
│ │
│ 模式五:多 Server 联邦链(Federated Chain) │
│ MCP Server 1 (天气) ─┐ │
│ MCP Server 2 (数据库) ─┼→ [编排器] → 最终结果 │
│ MCP Server 3 (搜索) ──┘ │
│ 适用:跨领域能力组合 │
└─────────────────────────────────────────────────────────────────┘
10.2 工具编排与依赖管理
python
"""
src/orchestrator.py - Agent 链编排器
=====================================
实现多工具协同调用的核心逻辑
"""
import asyncio
import json
import logging
from typing import Any, Callable, Dict, List, Optional
from dataclasses import dataclass, field
from enum import Enum
logger = logging.getLogger("mcp.orchestrator")
class StepStatus(Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
@dataclass
class ChainStep:
"""链中的一个步骤"""
name: str # 步骤名称
tool_name: str # 要调用的工具名
arguments_template: dict # 参数模板(支持变量引用)
depends_on: List[str] = field(default_factory=list) # 依赖的前置步骤
condition: Optional[str] = None # 执行条件表达式
retry_count: int = 0 # 重试次数
retry_delay: float = 1.0 # 重试间隔(秒)
timeout: float = 30.0 # 超时时间(秒)
# 运行时状态
status: StepStatus = StepStatus.PENDING
result: Any = None
error: Optional[str] = None
class AgentChainOrchestrator:
"""
Agent 链编排器
负责按依赖关系执行多个工具调用,
支持并行执行、条件分支、错误重试。
"""
def __init__(self, mcp_server):
self.mcp_server = mcp_server
self.steps: Dict[str, ChainStep] = {}
self.execution_log: List[dict] = []
def add_step(self, step: ChainStep):
"""添加一个执行步骤"""
self.steps[step.name] = step
def _resolve_template(self, template: dict, context: dict) -> dict:
"""
解析参数模板中的变量引用
支持语法: {{step_name.result.field}}
"""
resolved = {}
for key, value in template.items():
if isinstance(value, str) and "{{" in value:
# 解析变量引用
resolved[key] = self._interpolate(value, context)
elif isinstance(value, dict):
resolved[key] = self._resolve_template(value, context)
else:
resolved[key] = value
return resolved
def _interpolate(self, template: str, context: dict) -> Any:
"""字符串插值"""
import re
pattern = r"\{\{(\w+)\.result(?:\.(\w+(?:\.\w+)*))?\}\}"
def replacer(match):
step_name = match.group(1)
field_path = match.group(2)
if step_name not in context:
return match.group(0)
result = context[step_name]
if field_path:
for part in field_path.split("."):
if isinstance(result, dict):
result = result.get(part)
else:
return match.group(0)
return result
# 如果整个字符串就是一个变量引用,返回原始类型
full_match = re.fullmatch(pattern, template)
if full_match:
step_name = full_match.group(1)
field_path = full_match.group(2)
result = context.get(step_name)
if field_path and result:
for part in field_path.split("."):
if isinstance(result, dict):
result = result.get(part)
return result
return re.sub(pattern, replacer, template)
async def execute(self, initial_context: dict = None) -> dict:
"""
执行整个 Agent 链
Args:
initial_context: 初始上下文(可包含用户输入等)
Returns:
执行结果摘要
"""
context = initial_context or {}
self.execution_log = []
logger.info(f"开始执行 Agent 链,共 {len(self.steps)} 个步骤")
# 拓扑排序确定执行顺序
execution_order = self._topological_sort()
for step_name in execution_order:
step = self.steps[step_name]
# 检查依赖是否完成
deps_met = all(
self.steps[dep].status == StepStatus.COMPLETED
for dep in step.depends_on
)
if not deps_met:
step.status = StepStatus.SKIPPED
logger.warning(f"步骤 '{step_name}' 跳过:依赖未满足")
continue
# 检查执行条件
if step.condition:
if not self._evaluate_condition(step.condition, context):
step.status = StepStatus.SKIPPED
continue
# 执行步骤(带重试)
step.status = StepStatus.RUNNING
logger.info(f"执行步骤: {step_name} (工具: {step.tool_name})")
success = await self._execute_step_with_retry(step, context)
if success:
step.status = StepStatus.COMPLETED
context[step_name] = step.result
logger.info(f"步骤 '{step_name}' 完成")
else:
step.status = StepStatus.FAILED
logger.error(f"步骤 '{step_name}' 失败: {step.error}")
# 根据策略决定是否继续
break
# 生成执行摘要
summary = {
"total_steps": len(self.steps),
"completed": sum(1 for s in self.steps.values() if s.status == StepStatus.COMPLETED),
"failed": sum(1 for s in self.steps.values() if s.status == StepStatus.FAILED),
"skipped": sum(1 for s in self.steps.values() if s.status == StepStatus.SKIPPED),
"context": {k: str(v)[:200] for k, v in context.items()},
}
return summary
async def _execute_step_with_retry(self, step: ChainStep, context: dict) -> bool:
"""带重试的步骤执行"""
attempts = step.retry_count + 1
for attempt in range(attempts):
try:
# 解析参数模板
arguments = self._resolve_template(step.arguments_template, context)
# 调用 MCP 工具(带超时)
result = await asyncio.wait_for(
self._call_tool(step.tool_name, arguments),
timeout=step.timeout
)
# 解析结果
if isinstance(result, str):
try:
step.result = json.loads(result)
except json.JSONDecodeError:
step.result = result
else:
step.result = result
return True
except asyncio.TimeoutError:
step.error = f"超时(>{step.timeout}秒)"
logger.warning(f"步骤 '{step.name}' 第{attempt+1}次尝试超时")
except Exception as e:
step.error = str(e)
logger.warning(f"步骤 '{step.name}' 第{attempt+1}次尝试失败: {e}")
# 重试前等待
if attempt < attempts - 1:
await asyncio.sleep(step.retry_delay * (attempt + 1))
return False
async def _call_tool(self, tool_name: str, arguments: dict) -> str:
"""调用 MCP 工具"""
# 实际实现中调用 MCP server 的工具执行方法
# 这里简化为直接调用注册的函数
tool_func = self.mcp_server._tools.get(tool_name)
if tool_func is None:
raise ValueError(f"工具 '{tool_name}' 不存在")
if asyncio.iscoroutinefunction(tool_func):
return await tool_func(**arguments)
return tool_func(**arguments)
def _topological_sort(self) -> List[str]:
"""拓扑排序"""
visited = set()
result = []
def dfs(node):
if node in visited:
return
visited.add(node)
for dep in self.steps[node].depends_on:
if dep in self.steps:
dfs(dep)
result.append(node)
for name in self.steps:
dfs(name)
return result
def _evaluate_condition(self, condition: str, context: dict) -> bool:
"""评估条件表达式(简化实现)"""
try:
# 安全地评估简单条件
# 生产环境应使用更安全的表达式引擎
return bool(eval(condition, {"__builtins__": {}}, context))
except Exception:
return True
10.3 实战:构建"智能运维 Agent"
python
"""
实战案例:智能运维 Agent
========================
场景:当收到告警时,自动执行诊断、分析、修复建议的完整流程。
工具链:
1. get_alert_details → 获取告警详情
2. check_system_metrics → 检查系统指标
3. query_recent_logs → 查询最近日志
4. analyze_root_cause → 分析根因
5. generate_fix_plan → 生成修复方案
"""
def build_ops_agent_chain(orchestrator: AgentChainOrchestrator):
"""构建运维 Agent 链"""
# 步骤1:获取告警详情
orchestrator.add_step(ChainStep(
name="get_alert",
tool_name="get_alert_details",
arguments_template={
"alert_id": "{{user_input.alert_id}}"
},
timeout=10.0
))
# 步骤2:检查系统指标(依赖步骤1的结果)
orchestrator.add_step(ChainStep(
name="check_metrics",
tool_name="check_system_metrics",
arguments_template={
"server_id": "{{get_alert.result.server_id}}",
"time_range": "30m",
"metrics": ["cpu", "memory", "disk", "network"]
},
depends_on=["get_alert"],
timeout=15.0
))
# 步骤3:查询相关日志(依赖步骤1)
orchestrator.add_step(ChainStep(
name="query_logs",
tool_name="query_recent_logs",
arguments_template={
"server_id": "{{get_alert.result.server_id}}",
"level": "ERROR",
"minutes": 30,
"keyword": "{{get_alert.result.error_type}}"
},
depends_on=["get_alert"],
timeout=20.0,
retry_count=2 # 日志查询可能偶尔超时,允许重试
))
# 步骤4:分析根因(依赖步骤2和3)
orchestrator.add_step(ChainStep(
name="analyze_cause",
tool_name="analyze_root_cause",
arguments_template={
"alert_info": "{{get_alert.result}}",
"metrics_data": "{{check_metrics.result}}",
"log_entries": "{{query_logs.result}}"
},
depends_on=["check_metrics", "query_logs"],
timeout=30.0
))
# 步骤5:生成修复方案(依赖步骤4)
orchestrator.add_step(ChainStep(
name="generate_fix",
tool_name="generate_fix_plan",
arguments_template={
"root_cause": "{{analyze_cause.result}}",
"severity": "{{get_alert.result.severity}}",
"auto_fix": "false" # 安全起见,默认不自动执行修复
},
depends_on=["analyze_cause"],
timeout=15.0
))
# 注册为 MCP 工具
@mcp.tool()
async def run_ops_agent(alert_id: str) -> str:
"""
运行智能运维 Agent。
接收告警 ID,自动执行完整的诊断-分析-修复建议流程。
Args:
alert_id: 告警唯一标识符
Returns:
完整的诊断报告和修复建议
"""
orchestrator = AgentChainOrchestrator(mcp_server=mcp)
build_ops_agent_chain(orchestrator)
result = await orchestrator.execute(
initial_context={"user_input": {"alert_id": alert_id}}
)
return json.dumps(result, ensure_ascii=False, indent=2)
10.4 实战:构建"数据分析 Agent"
python
"""
实战案例:数据分析 Agent
========================
场景:用户用自然语言描述分析需求,Agent 自动完成
数据获取 → 清洗 → 统计 → 可视化建议 的全流程。
"""
@mcp.tool()
async def run_data_analysis_agent(
question: str,
data_source: str = "main_database",
output_format: str = "summary"
) -> str:
"""
运行数据分析 Agent。
根据用户的自然语言问题,自动编排数据查询、
统计分析、结果汇总等工具完成分析任务。
Args:
question: 用户的分析问题(自然语言)
data_source: 数据源标识
output_format: 输出格式 (summary/detailed/chart_suggestion)
Returns:
分析结果报告
"""
orchestrator = AgentChainOrchestrator(mcp_server=mcp)
# 步骤1:理解问题,生成查询计划
orchestrator.add_step(ChainStep(
name="plan_query",
tool_name="generate_query_plan",
arguments_template={
"question": question,
"data_source": data_source
},
timeout=15.0
))
# 步骤2:执行数据查询
orchestrator.add_step(ChainStep(
name="execute_query",
tool_name="execute_data_query",
arguments_template={
"query_plan": "{{plan_query.result.query}}",
"data_source": data_source,
"limit": 1000
},
depends_on=["plan_query"],
timeout=30.0,
retry_count=1
))
# 步骤3:数据清洗与预处理
orchestrator.add_step(ChainStep(
name="clean_data",
tool_name="preprocess_data",
arguments_template={
"raw_data": "{{execute_query.result}}",
"operations": ["remove_nulls", "normalize", "detect_outliers"]
},
depends_on=["execute_query"],
timeout=20.0
))
# 步骤4:统计分析
orchestrator.add_step(ChainStep(
name="analyze",
tool_name="statistical_analysis",
arguments_template={
"data": "{{clean_data.result}}",
"analysis_type": "{{plan_query.result.analysis_type}}",
"group_by": "{{plan_query.result.group_by}}"
},
depends_on=["clean_data"],
timeout=25.0
))
# 步骤5:生成报告
orchestrator.add_step(ChainStep(
name="report",
tool_name="generate_analysis_report",
arguments_template={
"analysis_results": "{{analyze.result}}",
"original_question": question,
"format": output_format
},
depends_on=["analyze"],
timeout=15.0
))
# 执行链
result = await orchestrator.execute()
return json.dumps(result, ensure_ascii=False, indent=2)
10.5 多 MCP Server 联邦调用
python
"""
多 MCP Server 联邦调用
=======================
在复杂场景中,可能需要同时调用多个独立的 MCP Server。
例如:天气数据来自天气 MCP Server,用户数据来自用户 MCP Server,
推荐逻辑来自推荐 MCP Server。
"""
import httpx
from typing import Dict, List
class MCPFederator:
"""
MCP Server 联邦调用器
管理与多个远程 MCP Server 的连接,
提供统一的工具调用接口。
"""
def __init__(self):
self._servers: Dict[str, str] = {} # name -> url
self._client = httpx.AsyncClient(timeout=30.0)
def register_server(self, name: str, url: str):
"""注册远程 MCP Server"""
self._servers[name] = url
logger.info(f"注册 MCP Server: {name} -> {url}")
async def call_tool(self, server_name: str, tool_name: str, arguments: dict) -> dict:
"""
调用远程 MCP Server 的工具
Args:
server_name: 服务器名称
tool_name: 工具名称
arguments: 工具参数
"""
if server_name not in self._servers:
raise ValueError(f"未注册的 MCP Server: {server_name}")
url = self._servers[server_name]
# 构造 JSON-RPC 请求
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": arguments
}
}
response = await self._client.post(url, json=payload)
response.raise_for_status()
result = response.json()
if "error" in result:
raise RuntimeError(f"远程工具调用失败: {result['error']}")
return result.get("result", {})
async def list_all_tools(self) -> Dict[str, List[dict]]:
"""获取所有注册服务器的工具列表"""
all_tools = {}
for name, url in self._servers.items():
try:
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
response = await self._client.post(url, json=payload)
result = response.json()
all_tools[name] = result.get("result", {}).get("tools", [])
except Exception as e:
all_tools[name] = [{"error": str(e)}]
return all_tools
# 使用示例
federator = MCPFederator()
federator.register_server("weather", "http://weather-mcp:8000/mcp")
federator.register_server("database", "http://db-mcp:8000/mcp")
federator.register_server("search", "http://search-mcp:8000/mcp")
@mcp.tool()
async def federated_query(
city: str,
include_history: bool = True,
include_recommendations: bool = False
) -> str:
"""
联邦查询:同时从多个 MCP Server 获取数据并聚合。
Args:
city: 查询城市
include_history: 是否包含历史数据
include_recommendations: 是否包含推荐信息
"""
results = {}
# 并行调用多个 MCP Server
tasks = []
# 调用天气服务
tasks.append(
federator.call_tool("weather", "get_current_weather", {"city": city})
)
# 调用数据库服务
if include_history:
tasks.append(
federator.call_tool("database", "query_records", {
"table": "weather_history",
"conditions": f"city = '{city}'",
"limit": 30
})
)
# 并行执行
task_results = await asyncio.gather(*tasks, return_exceptions=True)
results["current_weather"] = task_results[0] if not isinstance(task_results[0], Exception) else {"error": str(task_results[0])}
if include_history and len(task_results) > 1:
results["history"] = task_results[1] if not isinstance(task_results[1], Exception) else {"error": str(task_results[1])}
return json.dumps(results, ensure_ascii=False, indent=2)
10.6 错误恢复与重试策略
python
"""
Agent 链的错误恢复策略
"""
from enum import Enum
class RetryStrategy(Enum):
"""重试策略"""
NONE = "none" # 不重试
FIXED = "fixed" # 固定间隔重试
EXPONENTIAL = "exponential" # 指数退避重试
LINEAR = "linear" # 线性递增重试
class FallbackStrategy(Enum):
"""降级策略"""
SKIP = "skip" # 跳过失败步骤,继续执行
ABORT = "abort" # 中止整个链
DEFAULT_VALUE = "default" # 使用默认值替代
FALLBACK_TOOL = "fallback" # 调用备用工具
@dataclass
class ResilienceConfig:
"""弹性配置"""
retry_strategy: RetryStrategy = RetryStrategy.EXPONENTIAL
max_retries: int = 3
base_delay: float = 1.0 # 基础延迟(秒)
max_delay: float = 30.0 # 最大延迟(秒)
fallback_strategy: FallbackStrategy = FallbackStrategy.ABORT
fallback_value: Any = None
fallback_tool: Optional[str] = None
circuit_breaker_threshold: int = 5
def calculate_retry_delay(strategy: RetryStrategy, attempt: int, config: ResilienceConfig) -> float:
"""计算重试延迟"""
if strategy == RetryStrategy.FIXED:
return config.base_delay
elif strategy == RetryStrategy.EXPONENTIAL:
delay = config.base_delay * (2 ** attempt)
return min(delay, config.max_delay)
elif strategy == RetryStrategy.LINEAR:
delay = config.base_delay * (attempt + 1)
return min(delay, config.max_delay)
return 0
# 在 Agent 链中使用弹性配置
"""
orchestrator = AgentChainOrchestrator(mcp_server=mcp)
orchestrator.resilience = ResilienceConfig(
retry_strategy=RetryStrategy.EXPONENTIAL,
max_retries=3,
base_delay=1.0,
fallback_strategy=FallbackStrategy.DEFAULT_VALUE,
fallback_value={"status": "unavailable", "data": []}
)
"""
十一、常见陷阱与问题排除
11.1 协议版本兼容性陷阱
| 陷阱 | 症状 | 解决方案 |
|---|---|---|
| SDK 版本与协议版本不匹配 | 连接成功但工具列表为空 | 确认 SDK 版本支持 2026-07-28 规范 |
| 客户端仍使用旧版 initialize 握手 | 服务器返回 400 或忽略 | 升级客户端 SDK,或服务器兼容模式 |
| 混用有状态和无状态调用 | 间歇性 502/超时 | 统一使用无状态模式 |
| protocolVersion 字段缺失 | 服务器拒绝请求 | 每个请求必须携带协议版本 |
python
# 兼容性检查代码
def check_sdk_compatibility():
"""检查 SDK 版本是否支持无状态模式"""
import mcp
version = mcp.__version__
# 解析版本号
major, minor = map(int, version.split(".")[:2])
if major < 1:
raise RuntimeError(
f"MCP SDK 版本 {version} 过旧,"
f"无状态模式需要 >= 1.0.0。"
f"请运行: pip install --upgrade mcp"
)
print(f"✅ MCP SDK {version} 支持无状态模式")
11.2 无状态模式常见误区
python
"""
误区一:认为无状态 = 不能有任何内部状态
正确理解:协议层无状态(不维护 session),但实现层可以有连接池、缓存等。
"""
# ❌ 错误:每次请求都创建新的数据库连接
@mcp.tool()
def bad_query(sql: str) -> str:
conn = sqlite3.connect("data.db") # 每次新建连接,性能差
result = conn.execute(sql).fetchall()
conn.close()
return str(result)
# ✅ 正确:使用连接池(进程级复用)
@mcp.tool()
def good_query(sql: str) -> str:
with get_pooled_connection() as conn: # 从池中获取
result = conn.execute(sql).fetchall()
return str(result)
"""
误区二:认为无状态模式不需要 initialize
正确理解:虽然不强制,但推荐客户端仍发送 initialize 以获取服务器能力。
"""
"""
误区三:在工具函数中使用全局可变状态存储请求上下文
正确理解:每个请求独立,不能依赖"上一次请求设置了什么"。
"""
# ❌ 错误:依赖全局状态
_current_user = None
@mcp.tool()
def set_user(user_id: str) -> str:
global _current_user
_current_user = user_id # 无状态模式下,下一个请求看不到这个!
return f"用户设置为 {user_id}"
@mcp.tool()
def get_user_data() -> str:
# _current_user 可能是 None 或其他请求设置的值!
return f"用户数据: {_current_user}"
# ✅ 正确:每个请求自包含
@mcp.tool()
def get_user_data(user_id: str) -> str:
# user_id 作为参数传入,不依赖外部状态
return f"用户数据: {user_id}"
11.3 部署相关陷阱
| 陷阱 | 症状 | 解决方案 |
|---|---|---|
| Docker 中时区不正确 | 日志时间戳偏差 | 设置 TZ=Asia/Shanghai 环境变量 |
| 容器内存限制过小 | OOMKilled | 至少分配 256MB,推荐 512MB |
| 健康检查路径错误 | Pod 反复重启 | 确认 /health 端点存在且返回 200 |
| 端口未正确暴露 | 外部无法访问 | 检查 EXPOSE、Service、Ingress 配置 |
| 日志输出到 stdout | 与 stdio 传输冲突 | HTTP 模式日志走 stderr 或文件 |
| 镜像过大 | 构建/拉取慢 | 使用多阶段构建 + slim/alpine 基础镜像 |
11.4 性能陷阱
python
"""
性能陷阱清单
"""
# 陷阱1:工具返回过大的数据
# ❌ 返回整个数据库表(可能几MB)
# ✅ 分页返回,限制最大条数
# 陷阱2:同步阻塞调用
# ❌ 在异步工具中使用 time.sleep() 或同步 HTTP 请求
# ✅ 使用 asyncio.sleep() 和 httpx.AsyncClient
# 陷阱3:重复计算
# ❌ 每次请求都重新加载配置文件
# ✅ 启动时加载一次,缓存在内存中
# 陷阱4:日志过多
# ❌ 在高频工具中打印 DEBUG 日志
# ✅ 生产环境使用 INFO 级别,敏感数据脱敏
# 陷阱5:JSON 序列化大对象
# ❌ json.dumps(huge_dict, indent=4) # indent 增加体积
# ✅ json.dumps(data) # 生产环境不加 indent
11.5 安全陷阱
python
"""
安全陷阱清单
"""
# 陷阱1:SQL 注入
# ❌ f"SELECT * FROM users WHERE name = '{user_input}'"
# ✅ 使用参数化查询: cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))
# 陷阱2:路径遍历
# ❌ open(f"/data/{user_provided_path}") # 用户可能传 ../../etc/passwd
# ✅ 验证路径在允许的目录内
# 陷阱3:SSRF(服务端请求伪造)
# ❌ 直接使用用户提供的 URL 发起请求
# ✅ 白名单域名,禁止内网 IP
# 陷阱4:敏感信息泄露
# ❌ 在错误消息中暴露数据库连接字符串
# ✅ 返回通用错误信息,详细信息只记录到日志
# 陷阱5:未限制工具权限
# ❌ 所有客户端可以调用所有工具(包括删除数据)
# ✅ 基于角色的访问控制(RBAC)
十二、总结与展望
12.1 全文回顾
本文从零开始,完整覆盖了 MCP 无状态 AI Agent 服务器的全生命周期:
| 章节 | 核心内容 | 关键产出 |
|---|---|---|
| 一 | MCP 核心概念 | 理解协议架构、三大原语、无状态优势 |
| 二 | 环境搭建 | 可运行的开发环境 |
| 三 | 服务器初始化 | 最小可运行 MCP Server |
| 四 | 工具开发 | 完整的工具集(天气、数据库、搜索) |
| 五 | 资源与提示词 | 知识库资源、代码审查模板 |
| 六 | 调试验证 | Inspector + curl + 客户端测试 |
| 七 | 错误排查 | 系统化的排障方法论 |
| 八 | 生产部署 | Docker + K8s + CI/CD 完整方案 |
| 九 | 性能与安全 | 限流、认证、加密、审计 |
| 十 | Agent 链 | 多工具编排、联邦调用 |
12.2 核心要点总结
无状态 MCP 服务器的五个核心原则:
- 请求自包含:每个 HTTP 请求携带处理所需的全部信息
- 水平可扩展:任意实例可处理任意请求,加副本即加容量
- 故障无感知:单实例宕机不影响服务,请求自动路由到其他实例
- 标准化部署:与普通 HTTP 微服务完全一致的运维方式
- 安全内建:OAuth 2.0 认证、输入校验、审计日志从设计阶段就考虑
生产环境检查清单:
- 使用无状态模式(
stateless=True) - 所有工具有参数校验和错误处理
- 启用 HTTPS(TLS 1.2+)
- 配置 API Key 或 OAuth 认证
- 设置请求限流
- 配置健康检查端点
- 启用结构化日志和审计
- 容器化部署,非 root 用户运行
- 配置资源限制(CPU/内存)
- 设置自动扩缩容策略
- 敏感数据脱敏
- 定期安全扫描
12.3 未来展望
MCP 协议正在快速演进,以下是值得关注的方向:
- Tasks(异步任务):v2 规范引入的长时间运行任务支持,适合复杂 Agent 工作流
- A2A(Agent-to-Agent)协议:Google 推出的 Agent 间通信协议,与 MCP 互补
- MCP 注册中心:类似微服务注册中心的 MCP Server 发现机制
- 多模态工具:支持图像、音频、视频的工具调用
- 边缘部署:在 IoT 设备、边缘节点运行轻量 MCP Server
- 企业级治理:统一的 MCP Server 生命周期管理、版本控制、灰度发布
十三、详细参考资料
13.1 官方文档
| 资源 | URL | 说明 |
|---|---|---|
| MCP 官方规范 | https://spec.modelcontextprotocol.io | 协议完整规范(2026-07-28版) |
| MCP 官方文档 | https://modelcontextprotocol.io | 概念介绍、快速入门 |
| Python SDK | https://github.com/modelcontextprotocol/python-sdk | 官方 Python SDK |
| TypeScript SDK | https://github.com/modelcontextprotocol/typescript-sdk | 官方 TS SDK |
| MCP Servers 仓库 | https://github.com/modelcontextprotocol/servers | 官方示例服务器集合 |
| MCP Inspector | https://github.com/modelcontextprotocol/inspector | 官方调试工具 |
| 协议变更日志 | https://spec.modelcontextprotocol.io/changelog | 版本变更记录 |
13.2 社区资源
| 资源 | 说明 |
|---|---|
| MCP Discord | 官方社区,获取帮助和讨论 |
| Awesome MCP Servers | 社区维护的 MCP Server 列表 |
| Smithery.ai | MCP Server 注册与发现平台 |
| MCP.so | MCP Server 目录网站 |
| Linux 基金会 AAIF | MCP 协议治理组织 |
13.3 相关协议与标准
| 协议/标准 | 关系 | 说明 |
|---|---|---|
| JSON-RPC 2.0 | MCP 底层消息格式 | MCP 消息基于 JSON-RPC 2.0 封装 |
| OAuth 2.1 | MCP 认证标准 | MCP v2 推荐的认证方式 |
| OpenAPI/Swagger | 互补关系 | REST API 描述标准,MCP 工具可映射为 OpenAPI |
| A2A (Agent2Agent) | 互补关系 | Agent 间通信协议(Google) |
| gRPC | 替代方案 | 高性能 RPC,适合内部服务通信 |
13.4 推荐学习路径
初学者路径:
本文第1-6章 → MCP Inspector 实操 → 官方 Quickstart → 构建第一个工具
进阶路径:
本文第7-9章 → 阅读协议规范原文 → 研究官方 SDK 源码 → 生产部署实践
专家路径:
本文第10章 → 参与 MCP 规范讨论 → 贡献开源 MCP Server → 企业级架构设计
附录
附录 A:MCP 协议完整消息格式参考
A.1 请求格式(JSON-RPC 2.0)
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_current_weather",
"arguments": {
"city": "北京",
"unit": "celsius"
}
}
}
A.2 成功响应
json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"city\": \"北京\", \"temperature\": 26.5, ...}"
}
]
}
}
A.3 错误响应
json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params: 'city' is required",
"data": {
"field": "city",
"constraint": "required"
}
}
}
A.4 核心方法列表
| 方法 | 方向 | 说明 |
|---|---|---|
initialize |
Client → Server | 初始化(无状态模式下可选) |
tools/list |
Client → Server | 获取所有工具列表 |
tools/call |
Client → Server | 调用指定工具 |
resources/list |
Client → Server | 获取所有资源列表 |
resources/read |
Client → Server | 读取指定资源 |
prompts/list |
Client → Server | 获取所有提示词模板 |
prompts/get |
Client → Server | 获取指定提示词 |
ping |
双向 | 连通性检查 |
附录 B:FastMCP API 速查表
| API | 说明 | 示例 |
|---|---|---|
FastMCP(name, stateless=True) |
创建服务器实例 | mcp = FastMCP("my-server", stateless=True) |
@mcp.tool() |
注册工具 | @mcp.tool() def my_tool(...) |
@mcp.resource("uri") |
注册资源 | @mcp.resource("config://info") |
@mcp.prompt() |
注册提示词 | @mcp.prompt() def my_prompt(...) |
mcp.run(transport=...) |
启动服务器 | mcp.run(transport="streamable-http") |
mcp.list_tools() |
列出工具 | 内部使用 |
mcp.call_tool(name, args) |
调用工具 | 内部使用 |
附录 C:完整项目源码清单
mcp-agent-server/
├── src/
│ ├── __init__.py # 包初始化
│ ├── server.py # 主入口(~80行)
│ ├── config.py # 配置管理(~40行)
│ ├── health.py # 健康检查(~60行)
│ ├── orchestrator.py # Agent 链编排器(~200行)
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── weather.py # 天气工具(~120行)
│ │ ├── database.py # 数据库工具(~150行)
│ │ └── search.py # 搜索工具(~80行)
│ ├── resources/
│ │ ├── __init__.py
│ │ └── knowledge_base.py # 知识库资源(~100行)
│ ├── prompts/
│ │ ├── __init__.py
│ │ └── templates.py # 提示词模板(~90行)
│ └── middleware/
│ ├── __init__.py
│ ├── auth.py # 认证(~100行)
│ ├── rate_limit.py # 限流(~80行)
│ ├── sanitizer.py # 输入消毒(~70行)
│ └── audit.py # 审计日志(~50行)
├── tests/
│ ├── test_tools.py # 工具单元测试
│ ├── test_integration.py # 集成测试
│ └── diagnose.py # 诊断脚本
├── deploy/
│ ├── Dockerfile # Docker 构建
│ ├── docker-compose.yml # 编排配置
│ ├── nginx/nginx.conf # 反向代理
│ └── k8s/
│ ├── deployment.yaml # K8s 部署
│ ├── service.yaml # K8s 服务
│ ├── hpa.yaml # 自动扩缩容
│ └── ingress.yaml # 入口配置
├── .github/workflows/
│ └── deploy.yml # CI/CD 流水线
├── requirements.txt # Python 依赖
├── pyproject.toml # 项目配置
├── .env.example # 环境变量模板
└── README.md # 项目说明
附录 D:术语表
| 术语 | 英文 | 定义 |
|---|---|---|
| MCP | Model Context Protocol | 模型上下文协议,AI 与工具通信的开放标准 |
| Tool | Tool | MCP 服务器暴露的可调用函数 |
| Resource | Resource | MCP 服务器暴露的可读数据 |
| Prompt | Prompt | MCP 服务器提供的提示词模板 |
| Host | Host | 运行 AI 模型的应用程序 |
| Client | MCP Client | 嵌入 Host 中的协议客户端 |
| Server | MCP Server | 提供工具/资源/提示词的服务 |
| Transport | Transport | 通信传输层(stdio/HTTP) |
| Streamable HTTP | Streamable HTTP | MCP v2 推荐的 HTTP 传输方式 |
| Stateless | Stateless | 无状态,每个请求独立处理 |
| JSON-RPC | JSON-RPC 2.0 | MCP 底层消息格式标准 |
| FastMCP | FastMCP | Python SDK 中的高层框架 |
| Inspector | MCP Inspector | 官方可视化调试工具 |
| HPA | Horizontal Pod Autoscaler | K8s 水平自动扩缩容 |
| mTLS | Mutual TLS | 双向 TLS 认证 |
| RBAC | Role-Based Access Control | 基于角色的访问控制 |
| Circuit Breaker | Circuit Breaker | 熔断器模式 |
| Token Bucket | Token Bucket | 令牌桶限流算法 |
| SSE | Server-Sent Events | 服务端推送事件(流式响应) |
| AAIF | Agentic AI Foundation | Linux 基金会下的 AI Agent 治理组织 |
| A2A | Agent-to-Agent | Google 推出的 Agent 间通信协议 |
本文完
最后更新:2026年8月5日
基于 MCP 协议规范版本:2026-07-28(v2)
适用 SDK 版本:Python mcp >= 1.2.0 / TypeScript @modelcontextprotocol/sdk >= 1.3.0
如有问题或建议,欢迎在评论区交流。祝你的 AI Agent 之旅顺利!🚀