> ## Documentation Index
> Fetch the complete documentation index at: https://learn.orbexa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server部署与运维方案

> MCP Server部署与运维方案详解，涵盖本地stdio模式npm包发布、远程Streamable HTTP服务配置及Docker容器化打包流程。深入讲解Mcp-Session-Id会话管理、Redis状态存储与水平扩展负载均衡策略，提供从开发到生产环境的完整部署指南

# 部署指南

## 9.1 本地部署（stdio）

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

### npm包方式

把Server发布为npm包，用户全局安装后直接使用：

```bash theme={null}
# 发布
npm publish

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

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

### 直接运行方式

不发布npm包，直接指定脚本路径：

```json theme={null}
{
  "mcpServers": {
    "my-store": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"],
      "env": { "DB_URL": "..." }
    }
  }
}
```

### Python Server本地部署

```json theme={null}
{
  "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

```typescript theme={null}
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部署

```dockerfile theme={null}
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"]
```

### 云平台部署

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

## 9.3 会话管理

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

### 无状态部署（多实例）

如果Server部署了多个实例（如K8s Pod），需要外部会话存储：

```typescript theme={null}
// 使用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}`);
    // 恢复会话...
  }
});
```

### 会话清理

设置合理的会话超时和清理策略：

```typescript theme={null}
// 会话超时：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）

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

### 支持OAuth的远程Server

当Server实现了完整的OAuth 2.1流程时，Host应用会自动处理认证，用户只需提供Server URL：

```json theme={null}
{
  "mcpServers": {
    "my-oauth-store": {
      "url": "https://mcp.mystore.com/mcp"
    }
  }
}
```

Host会自动发现Server的OAuth配置（通过Protected Resource Metadata），引导用户完成授权。

## 9.5 监控和日志

生产环境推荐：

### 健康检查

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

### 请求日志

```typescript theme={null}
// 记录每次工具调用
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重连时从断点继续：

```typescript theme={null}
// 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);
}
```

***

**下一章**: [实战案例](/zh/book-5/ch10-case-studies) — 不同场景的MCP Server实现
