跳转到主要内容

部署指南

9.1 本地部署(stdio)

适合个人工具和团队内部使用。

npm包方式

把Server发布为npm包,用户全局安装后直接使用:
# 发布
npm publish

# 用户安装
npm install -g my-commerce-server

# Claude Desktop配置
{
  "mcpServers": {
    "my-store": {
      "command": "my-commerce-server",
      "env": { "DB_URL": "..." }
    }
  }
}

直接运行方式

不发布npm包,直接指定脚本路径:
{
  "mcpServers": {
    "my-store": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"],
      "env": { "DB_URL": "..." }
    }
  }
}

Python Server本地部署

{
  "mcpServers": {
    "my-python-server": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"],
      "env": { "API_KEY": "..." }
    }
  }
}

9.2 远程部署(Streamable HTTP)

适合面向外部用户的SaaS服务。

完整的Express.js Streamable HTTP Server

import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";

const app = express();
app.use(express.json());

// 会话存储(生产环境应使用Redis等外部存储)
const sessions = new Map();

app.post("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"];

  if (sessionId && sessions.has(sessionId)) {
    // 已有会话,复用
    const transport = sessions.get(sessionId);
    await transport.handlePost(req, res);
  } else {
    // 新会话
    const transport = new StreamableHTTPServerTransport({
      endpoint: "/mcp"
    });
    const server = createMcpServer(); // 你的Server工厂函数
    await server.connect(transport);

    // 保存会话
    const newSessionId = transport.getSessionId();
    sessions.set(newSessionId, transport);

    await transport.handlePost(req, res);
  }
});

// SSE端点(可选,用于Server主动推送)
app.get("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"];
  const transport = sessions.get(sessionId);
  if (transport) {
    await transport.handleGet(req, res);
  } else {
    res.status(404).end();
  }
});

// 健康检查
app.get("/health", (req, res) => {
  res.json({ status: "ok", activeSessions: sessions.size });
});

app.listen(3000, () => {
  console.error("MCP Server (Streamable HTTP) running on port 3000");
});

Docker部署

FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production
COPY dist/ ./dist/
EXPOSE 3000
CMD ["node", "dist/server-http.js"]

云平台部署

平台方案优势注意事项
VercelEdge Function全球CDN,自动扩展无状态,需外部会话存储
RailwayDocker容器简单部署,持久连接适合有状态Server
AWS LambdaServerless按调用付费冷启动延迟,需注意超时
Cloudflare WorkersEdge Worker超低延迟无状态,需Durable Objects管理会话
自有服务器PM2 + Nginx完全控制需自行维护

9.3 会话管理

Streamable HTTP是有状态协议,Mcp-Session-Id 头用于维持会话。生产部署需要考虑:

无状态部署(多实例)

如果Server部署了多个实例(如K8s Pod),需要外部会话存储:
// 使用Redis存储会话状态
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);

app.post("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"];
  if (sessionId) {
    const sessionData = await redis.get(`mcp:session:${sessionId}`);
    // 恢复会话...
  }
});

会话清理

设置合理的会话超时和清理策略:
// 会话超时:30分钟无活动则清理
const SESSION_TTL = 30 * 60 * 1000;

setInterval(() => {
  for (const [id, session] of sessions) {
    if (Date.now() - session.lastActivity > SESSION_TTL) {
      session.transport.close();
      sessions.delete(id);
    }
  }
}, 60 * 1000); // 每分钟检查一次

9.4 Client连接配置

远程Server(Streamable HTTP)

{
  "mcpServers": {
    "my-remote-store": {
      "url": "https://mcp.mystore.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}

支持OAuth的远程Server

当Server实现了完整的OAuth 2.1流程时,Host应用会自动处理认证,用户只需提供Server URL:
{
  "mcpServers": {
    "my-oauth-store": {
      "url": "https://mcp.mystore.com/mcp"
    }
  }
}
Host会自动发现Server的OAuth配置(通过Protected Resource Metadata),引导用户完成授权。

9.5 监控和日志

生产环境推荐:

健康检查

app.get("/health", (req, res) => {
  res.json({
    status: "ok",
    version: "1.0.0",
    activeSessions: sessions.size,
    uptime: process.uptime()
  });
});

请求日志

// 记录每次工具调用
function logToolCall(method, params, duration, error) {
  console.error(JSON.stringify({
    timestamp: new Date().toISOString(),
    method,
    tool: params?.name,
    duration_ms: duration,
    error: error?.message
  }));
}

监控指标

指标说明告警阈值
响应时间P95工具调用的95分位延迟视业务而定,通常5s以内
错误率工具调用失败比例5%以上告警
活跃会话数当前连接的Client数量接近服务器容量时告警
初始化成功率MCP握手成功比例99%以下需排查

断线恢复

Streamable HTTP支持通过SSE的 Last-Event-ID 实现断线恢复。生产环境中,Server应维护事件ID序列,以便Client重连时从断点继续:
// Server端维护事件序列
let eventCounter = 0;

function sendNotification(sessionId, data) {
  eventCounter++;
  const event = {
    id: `evt-${eventCounter}`,
    data: JSON.stringify(data)
  };
  // 发送SSE事件并持久化(供重连恢复)
  sendSSEEvent(sessionId, event);
  persistEvent(sessionId, event);
}

下一章: 实战案例 — 不同场景的MCP Server实现