本地 MCP Server 一条命令就能跑:python server.py。进入企业生产环境,三个问题必须解决:

最简单,适合企业内网中服务间调用:
复制代码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 层验证:
复制代码from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Requestclass 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)
需要细粒度权限控制时(不同用户能访问不同 Jira 项目):
复制代码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。
金融、医疗等对数据安全有严格要求的场景,双向证书验证:
复制代码import sslssl_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
认证方案选择:
复制代码内部服务间调用(同网络) → API Key + 网络隔离(Docker network)
跨组织或有用户级权限需求 → OAuth 2.0
高安全等级(金融、医疗、政务) → mTLS
复制代码FROM python:3.12-slimWORKDIR /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 1CMD ["python", "jira_server.py"]
复制代码# 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 复制代码# /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
复制代码systemctl enable jira-mcp
systemctl start jira-mcp
systemctl status jira-mcp
journalctl -u jira-mcp -f # 实时日志
多个 Agent 会话连接同一个 MCP Server v1.2。此时需要发布 v1.3(新增一个工具),不能中断已有连接。
复制代码# 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:
复制代码// Claude Code 配置(用于灰度)
{
"mcpServers": {
"jira": {
"command": "docker",
"args": ["exec", "jira-mcp-canary", "python", "jira_server.py"]
}
}
}
复制代码MAJOR.MINOR.PATCHMAJOR:破坏性变更
→ 删除工具、修改工具名、修改 inputSchema 的 required 字段
→ 已有 Agent 代码依赖这个工具,必须同步更新MINOR:向后兼容的新增
→ 新增工具、新增可选参数、扩展返回字段
→ 已有 Agent 代码继续工作,新功能可选使用PATCH:不影响行为的修复
→ Bug 修复、性能优化、日志改进
→ 透明升级
版本声明在 Server 代码里:
复制代码server = Server(
"jira-tools",
version="1.3.0" # 在 initialize 响应里返回给 Client
)
deprecation 流程:
复制代码@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 版本里删除。
认证与授权
网络隔离
internal: true,Server 不直连外网工具安全
运维
restart: unless-stopped 或 systemd 守护确保高可用restart: unless-stopped(崩溃恢复)+ internal: true 网络(隔离)+ 资源限制(稳定)欢迎访问 PrimeSkills —— 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页