从本地到生产的三个跨越
本地 MCP Server 一条命令就能跑:python server.py。进入企业生产环境,三个问题必须解决:
- 认证:stdio 模式没有认证,所有进程都能连。生产环境需要明确的身份验证
- 进程守护:Python 进程崩溃后不重启,没有告警。需要守护进程和健康检查
- 平滑升级:多个 Agent 会话同时连接同一个 Server 时,升级不能中断已有连接
认证方案
API Key(内部服务首选)
最简单,适合企业内网中服务间调用:
python
import os
from mcp.server import Server
from mcp.server.stdio import stdio_server
# 启动时检查环境变量里的 Key
EXPECTED_API_KEY = os.environ.get("MCP_API_KEY")
if not EXPECTED_API_KEY:
raise RuntimeError("MCP_API_KEY environment variable is required")
server = Server("jira-tools")
# 在 initialize 处理里验证 client 传来的 key
@server.on_message
async def handle_message(message):
# 对于非 stdio 的 HTTP 传输,在请求头里验证
# stdio 模式下,通过部署层(Docker network)做隔离
pass
对于 HTTP 传输(非 stdio),在 HTTP 层验证:
python
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
class ApiKeyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
key = request.headers.get("X-API-Key") or request.headers.get("Authorization", "").removeprefix("Bearer ")
if key != os.environ["MCP_API_KEY"]:
return Response("Unauthorized", status_code=401)
return await call_next(request)
OAuth 2.0(跨组织、用户级权限)
需要细粒度权限控制时(不同用户能访问不同 Jira 项目):
python
from mcp.server.auth import OAuthServerProvider
from mcp.types import ClientCapabilities
# MCP 规范(2025 年版本)已定义 OAuth 集成接口
# Server 声明支持 OAuth
server = Server(
"jira-tools",
capabilities=ServerCapabilities(
experimental={"oauth": {"supported": True}}
)
)
实际场景中,OAuth Token 通常由 Host(Claude Desktop / Claude Code)在用户登录时获取,再在每次 MCP 连接时传给 Server。
mTLS(高安全内网通信)
金融、医疗等对数据安全有严格要求的场景,双向证书验证:
python
import ssl
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain("/certs/server.crt", "/certs/server.key")
ssl_context.load_verify_locations("/certs/ca.crt")
ssl_context.verify_mode = ssl.CERT_REQUIRED # 强制验证客户端证书
# 把 ssl_context 传给 HTTP Server
认证方案选择:
vbnet
内部服务间调用(同网络) → API Key + 网络隔离(Docker network)
跨组织或有用户级权限需求 → OAuth 2.0
高安全等级(金融、医疗、政务) → mTLS
Docker 化部署
最小 Dockerfile
dockerfile
FROM python:3.12-slim
WORKDIR /app
# 分离依赖安装(利用 Docker 缓存层)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 不以 root 运行(安全最佳实践)
RUN useradd -r -s /bin/false mcpuser
USER mcpuser
# 健康检查:stdio 模式发一个 ping
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD python -c "import sys; print('healthy')" || exit 1
CMD ["python", "jira_server.py"]
生产 Docker Compose 配置
yaml
# docker-compose.prod.yml
version: "3.9"
services:
jira-mcp:
build: .
image: jira-mcp-server:1.3.0
restart: unless-stopped # 崩溃后自动重启
environment:
- MCP_API_KEY=${MCP_API_KEY} # 从 .env 文件或 Secrets 注入
- JIRA_URL=${JIRA_URL}
- JIRA_TOKEN=${JIRA_TOKEN}
- LOG_LEVEL=INFO
volumes:
- ./logs:/app/logs # 持久化日志
networks:
- mcp-internal # 只在内部网络可访问
deploy:
resources:
limits:
cpus: "0.5"
memory: "256M"
reservations:
memory: "128M"
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
networks:
mcp-internal:
driver: bridge
internal: true # 不对外暴露
关键配置说明:
restart: unless-stopped:进程崩溃自动重启,手动 stop 才不重启internal: true:Docker 网络不连接外网,只有同网络的容器能访问 Server- 资源限制:防止 Server 因 Bug 消耗所有内存导致宿主机不稳定
- 日志轮转:防止日志文件无限增长
进程守护(非 Docker 环境)
ini
# /etc/systemd/system/jira-mcp.service
[Unit]
Description=Jira MCP Server
After=network.target
[Service]
Type=simple
User=mcpuser
WorkingDirectory=/opt/jira-mcp
ExecStart=/usr/bin/python3 /opt/jira-mcp/jira_server.py
Restart=always
RestartSec=5
Environment="MCP_API_KEY=your-key"
Environment="JIRA_TOKEN=your-token"
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
bash
systemctl enable jira-mcp
systemctl start jira-mcp
systemctl status jira-mcp
journalctl -u jira-mcp -f # 实时日志
多版本共存与平滑升级
问题场景
多个 Agent 会话连接同一个 MCP Server v1.2。此时需要发布 v1.3(新增一个工具),不能中断已有连接。
策略:并行版本 + 流量切换
yaml
# docker-compose.prod.yml(多版本并行)
services:
jira-mcp-stable:
image: jira-mcp-server:1.2.0 # 当前稳定版,处理已有连接
restart: unless-stopped
networks: [mcp-internal]
jira-mcp-canary:
image: jira-mcp-server:1.3.0 # 新版本,接受新连接
restart: unless-stopped
networks: [mcp-internal]
deploy:
replicas: 1 # 先只跑一个实例
Host 配置里先指向 canary 版本,观察一段时间后再切换 stable:
json
// Claude Code 配置(用于灰度)
{
"mcpServers": {
"jira": {
"command": "docker",
"args": ["exec", "jira-mcp-canary", "python", "jira_server.py"]
}
}
}
版本号规范
swift
MAJOR.MINOR.PATCH
MAJOR:破坏性变更
→ 删除工具、修改工具名、修改 inputSchema 的 required 字段
→ 已有 Agent 代码依赖这个工具,必须同步更新
MINOR:向后兼容的新增
→ 新增工具、新增可选参数、扩展返回字段
→ 已有 Agent 代码继续工作,新功能可选使用
PATCH:不影响行为的修复
→ Bug 修复、性能优化、日志改进
→ 透明升级
版本声明在 Server 代码里:
python
server = Server(
"jira-tools",
version="1.3.0" # 在 initialize 响应里返回给 Client
)
deprecation 流程:
python
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "search_jira": # 旧工具名
import warnings
logger.warning(
"Tool 'search_jira' is deprecated. Use 'search_issues' instead. "
"Will be removed in v2.0.0."
)
# 内部调用新实现
return await _search_issues(arguments)
保留旧工具名 90 天,日志里记录使用情况,90 天后在 MAJOR 版本里删除。
安全设计 Checklist
认证与授权
- 认证信息通过环境变量注入,不硬编码在代码或镜像里
- 内部服务用 API Key,跨组织或用户级权限用 OAuth
- HTTP 传输在 Middleware 层验证,不在工具层验证
网络隔离
- Docker network 设置
internal: true,Server 不直连外网 - 只开放必要的端口(stdio 模式不需要开放任何端口)
- 多个 Server 之间用独立 Docker network 隔离
工具安全
- 工具输入做类型验证和范围检查(见第 04 篇)
- 高风险工具(写操作、外部 API 调用)有操作记录
- Server 的文件系统访问限制在必要目录
运维
- 日志写到 stderr,格式结构化(JSON),有日志轮转
-
restart: unless-stopped或 systemd 守护确保高可用 - 资源限制(CPU/内存)防止 Server 异常消耗宿主资源
总结
- 认证选型按场景:内部服务 API Key 够用,跨组织上 OAuth,高安全等级上 mTLS------不要过度设计
- Docker 化三件套 :
restart: unless-stopped(崩溃恢复)+internal: true网络(隔离)+ 资源限制(稳定) - 平滑升级靠并行版本:MINOR 版本向后兼容,可以直接替换;MAJOR 版本并行跑旧版处理已有连接,新版接新连接,给 Agent 代码留足迁移时间
参考资料
欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页