<!-- title: MCP Server韧性工程：从崩溃到自愈实战 -->

# MCP Server韧性工程：从崩溃到自愈实战

## 概述

你的MCP Server上线第一天就挂了——进程崩溃、API超时、内存泄漏、级联故障。这不是假设，而是MCP生产环境的真实常态。本教程从MCP Server的六大故障模式出发，逐步构建健康检查、连接池、超时管理、优雅降级、熔断器和指标收集六大韧性组件，最终交付一个具备自愈能力的生产级MCP Server。适合已有MCP Server开发基础、希望将其推向生产环境的工程师。

## 前置条件

- 已掌握MCP Server基本开发（TypeScript SDK）
- Node.js 18+ 和 npm 环境
- 了解 JSON-RPC 2.0 协议基础
- 一个可运行的MCP Server项目（作为改造对象）

---

## 第一章：理解MCP Server的脆弱性

### 1.1 为什么MCP Server比传统服务更脆弱

MCP Server不是普通的HTTP服务。它作为Claude Code的子进程运行，通过 stdio 管道通信。这意味着：

- **没有内置健康检查**：不像HTTP服务可以用 `/health` 端点探测
- **没有自动重启**：进程死了就是死了，除非有外部看门狗
- **stdio 是神圣通道**：stdout 只能传 MCP 协议消息，日志必须走 stderr
- **级联放大效应**：一个工具的超时会阻塞整个工具调用链

```
Claude Code（父进程）
    ↓ spawn('node', ['dist/index.js'])
MCP Server（子进程）
    ├── Tool Registration（注册工具）
    ├── Tool Execution（执行工具）
    └── stdio transport（协议层）
```

### 1.2 六大故障模式

根据对超过16,400个MCP Server实现的分析，生产环境中最常见的六大故障模式如下：

| 故障模式 | 症状 | 根因 | 影响 |
|---------|------|------|------|
| 进程崩溃 | 工具调用返回 "MCP server exited" | 未捕获异常、OOM、段错误 | 所有工具不可用 |
| 超时 | 工具调用无限挂起 | 阻塞I/O、无限循环、外部API卡死 | 用户面延迟、级联超时 |
| 连接泄漏 | 内存持续增长 | 数据库/HTTP连接未释放 | OOM → 进程崩溃 |
| 限流雪崩 | 外部API返回429 | 无出站请求限流 | 服务降级、级联失败 |
| 死锁 | 工具调用挂起但无错误 | 循环锁依赖 | 静默失败、难以调试 |
| 配置漂移 | 工具行为异常 | 环境变量/密钥过期 | 间歇性错误、难定位 |

### 1.3 韧性工程全景图

```mermaid
graph TB
    subgraph "韧性工程六层防线"
        L1["第1层：健康检查<br/>HealthChecker + Watchdog"]
        L2["第2层：连接池<br/>ConnectionPool"]
        L3["第3层：超时管理<br/>TimeoutManager"]
        L4["第4层：优雅降级<br/>GracefulDegradation"]
        L5["第5层：熔断器<br/>CircuitBreaker"]
        L6["第6层：指标收集<br/>MetricsCollector"]
    end

    Request["工具调用请求"] --> L1
    L1 --> L2
    L2 --> L3
    L3 --> L4
    L4 --> L5
    L5 --> L6
    L6 --> Response["返回结果"]

    L5 -.->|"熔断触发"| L4
    L4 -.->|"降级响应"| Response
    L1 -.->|"不健康"| Watchdog["自动重启"]
```

---

## 第二章：项目初始化

### 2.1 创建项目骨架

```bash
mkdir mcp-resilient-server && cd mcp-resilient-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node
npx tsc --init
```

### 2.2 项目结构

```
mcp-resilient-server/
├── src/
│   ├── index.ts              # 入口：服务器启动
│   ├── health.ts             # 健康检查
│   ├── connection-pool.ts    # 连接池
│   ├── timeout.ts            # 超时管理
│   ├── degradation.ts        # 优雅降级
│   ├── circuit-breaker.ts    # 熔断器
│   ├── metrics.ts            # 指标收集
│   └── tools/
│       ├── api-debugger.ts   # 示例工具：API调试器
│       └── health-auditor.ts # 示例工具：健康审计
├── tsconfig.json
└── package.json
```

### 2.3 TypeScript配置

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true
  },
  "include": ["src/**/*"]
}
```

---

## 第三章：第1层防线——健康检查

### 3.1 内部健康检查器

MCP Server没有HTTP端点可供探测，但可以将健康状态暴露为一个工具（tool），让Cla Code或外部监控可以主动查询。

```typescript
// src/health.ts
export interface HealthStatus {
  healthy: boolean;
  checks: Record<string, {
    status: 'healthy' | 'degraded' | 'unhealthy';
    latencyMs: number;
    message?: string;
  }>;
  uptime: number;
  memoryUsage: NodeJS.MemoryUsage;
  lastCheck: string;
}

export class HealthChecker {
  private checks: Map<string, () => Promise<{
    status: 'healthy' | 'degraded' | 'unhealthy';
    latencyMs: number;
    message?: string;
  }>> = new Map();
  private startTime = Date.now();

  register(name: string, check: () => Promise<{
    status: 'healthy' | 'degraded' | 'unhealthy';
    latencyMs: number;
    message?: string;
  }>) {
    this.checks.set(name, check);
  }

  async check(): Promise<HealthStatus> {
    const results: HealthStatus['checks'] = {};
    let allHealthy = true;

    for (const [name, checkFn] of this.checks) {
      try {
        const start = Date.now();
        const result = await checkFn();
        results[name] = result;
        if (result.status === 'unhealthy') allHealthy = false;
      } catch (error) {
        results[name] = {
          status: 'unhealthy',
          latencyMs: Date.now() - start,
          message: error instanceof Error ? error.message : 'Unknown error'
        };
        allHealthy = false;
      }
    }

    return {
      healthy: allHealthy,
      checks: results,
      uptime: Date.now() - this.startTime,
      memoryUsage: process.memoryUsage(),
      lastCheck: new Date().toISOString()
    };
  }
}
```

### 3.2 内存健康检查示例

```typescript
// 注册内存检查
function registerMemoryCheck(health: HealthChecker) {
  health.register('memory', async () => {
    const mem = process.memoryUsage();
    const heapUsedMB = mem.heapUsed / 1024 / 1024;
    const heapLimitMB = mem.heapTotal / 1024 / 1024;
    const usagePercent = (heapUsedMB / heapLimitMB) * 100;

    if (usagePercent > 90) {
      return {
        status: 'unhealthy' as const,
        latencyMs: 0,
        message: `Heap usage critical: ${usagePercent.toFixed(1)}%`
      };
    }
    if (usagePercent > 75) {
      return {
        status: 'degraded' as const,
        latencyMs: 0,
        message: `Heap usage high: ${usagePercent.toFixed(1)}%`
      };
    }
    return {
      status: 'healthy' as const,
      latencyMs: 0,
      message: `Heap usage normal: ${usagePercent.toFixed(1)}%`
    };
  });
}
```

### 3.3 看门狗进程

看门狗是运行在MCP Server外部的监控进程，负责检测不健康的Server并触发重启：

```typescript
// src/watchdog.ts（独立进程，不在MCP Server内部）
import { spawn, ChildProcess } from 'child_process';

class MCPWatchdog {
  private process: ChildProcess | null = null;
  private restartCount = 0;
  private readonly maxRestarts = 5;
  private readonly windowMs = 60_000; // 60秒窗口
  private restartTimestamps: number[] = [];

  start() {
    this.process = spawn('node', ['dist/index.js'], {
      stdio: ['pipe', 'pipe', 'pipe']
    });

    this.process.on('exit', (code) => {
      // 记录重启时间戳
      const now = Date.now();
      this.restartTimestamps.push(now);
      // 清理60秒窗口外的记录
      this.restartTimestamps = this.restartTimestamps.filter(
        t => now - t < this.windowMs
      );

      if (this.restartTimestamps.length >= this.maxRestarts) {
        // 超过重启上限，触发外部编排（PM2/K8s/systemd）
        console.error(`[Watchdog] 超过${this.maxRestarts}次重启上限，退出`);
        process.exit(1);
      }

      // 指数退避重启
      const delay = Math.min(1000 * Math.pow(2, this.restartCount), 30_000);
      console.log(`[Watchdog] ${delay}ms后重启...`);
      setTimeout(() => {
        this.restartCount++;
        this.start();
      }, delay);
    });
  }
}
```

**预期结果**：当MCP Server因未捕获异常崩溃时，看门狗在指数退避后自动重启。连续崩溃超过5次则放弃，交由更高层的编排系统处理。

**常见问题**：看门狗自身崩溃怎么办？答：将看门狗交给PM2或systemd管理，形成双层保护。

---

## 第四章：第2层防线——连接池

### 4.1 通用连接池实现

MCP Server经常需要维护数据库连接或HTTP连接池。不当管理会导致连接泄漏和内存耗尽。

```typescript
// src/connection-pool.ts
export class ConnectionPool<T> {
  private pool: T[] = [];
  private inUse = new Set<T>();
  private waiting: Array<{
    resolve: (conn: T) => void;
    reject: (err: Error) => void;
  }> = [];

  constructor(
    private factory: () => Promise<T>,
    private destroyer: (conn: T) => Promise<void>,
    private maxSize: number = 10,
    private validate: (conn: T) => Promise<boolean> = async () => true
  ) {}

  async acquire(): Promise<T> {
    // 尝试从池中获取有效连接
    while (this.pool.length > 0) {
      const conn = this.pool.pop()!;
      if (await this.validate(conn)) {
        this.inUse.add(conn);
        return conn;
      }
      // 无效连接，销毁
      await this.destroyer(conn);
    }

    // 池为空，创建新连接
    if (this.inUse.size < this.maxSize) {
      const conn = await this.factory();
      this.inUse.add(conn);
      return conn;
    }

    // 达到上限，排队等待
    return new Promise<T>((resolve, reject) => {
      this.waiting.push({ resolve, reject });
    });
  }

  release(conn: T): void {
    this.inUse.delete(conn);
    if (this.waiting.length > 0) {
      const { resolve } = this.waiting.shift()!;
      this.inUse.add(conn);
      resolve(conn);
    } else {
      this.pool.push(conn);
    }
  }

  async drain(): Promise<void> {
    const all = [...this.pool, ...this.inUse];
    this.pool = [];
    this.inUse.clear();
    await Promise.all(all.map(conn => this.destroyer(conn)));
  }

  get stats() {
    return {
      available: this.pool.length,
      inUse: this.inUse.size,
      waiting: this.waiting.length,
      total: this.pool.length + this.inUse.size
    };
  }
}
```

### 4.2 使用示例：HTTP连接池

```typescript
import { ConnectionPool } from './connection-pool.js';

interface ApiConnection {
  id: string;
  lastUsed: number;
}

const apiPool = new ConnectionPool<ApiConnection>(
  // 工厂：创建新连接
  async () => ({
    id: crypto.randomUUID(),
    lastUsed: Date.now()
  }),
  // 销毁器：清理连接
  async (conn) => {
    console.error(`[Pool] 销毁连接 ${conn.id}`);
  },
  // 最大连接数
  10,
  // 验证器：检查连接是否有效（超过5分钟未用视为过期）
  async (conn) => Date.now() - conn.lastUsed < 5 * 60 * 1000
);

// 使用模式：必须用 try/finally 释放连接
async function queryApi(path: string): Promise<unknown> {
  const conn = await apiPool.acquire();
  try {
    conn.lastUsed = Date.now();
    // 执行API调用...
    const response = await fetch(`https://api.example.com${path}`);
    return response.json();
  } finally {
    apiPool.release(conn); // 关键：无论成功失败都要释放
  }
}
```

**关键模式**：`try/finally` 释放连接是防止连接泄漏的铁律。忘记 `finally` 是连接泄漏的头号原因。

---

## 第五章：第3层防线——超时管理

### 5.1 超时管理器

MCP Server中的工具调用如果涉及外部API或数据库查询，必须设置硬性超时。一个无超时的工具调用可以永远阻塞Claude Code的工具链。

```typescript
// src/timeout.ts
export class TimeoutManager {
  private timeouts: Map<string, ReturnType<typeof setTimeout>> = new Map();

  constructor(
    private defaults: {
      apiCall?: number;    // 默认5秒
      dbQuery?: number;    // 默认5秒
      toolExec?: number;   // 默认30秒（MCP工具硬上限）
    } = {}
  ) {
    this.defaults = {
      apiCall: 5_000,
      dbQuery: 5_000,
      toolExec: 30_000,
      ...defaults
    };
  }

  /**
   * 包装操作，强制超时
   * 使用 AbortController + Promise.race 实现
   */
  async runWithTimeout<T>(
    operation: (signal: AbortSignal) => Promise<T>,
    timeoutMs: number,
    label: string
  ): Promise<T> {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => {
      controller.abort();
      this.timeouts.delete(label);
    }, timeoutMs);
    this.timeouts.set(label, timeoutId);

    try {
      const result = await Promise.race([
        operation(controller.signal),
        new Promise<never>((_, reject) =>
          setTimeout(() => reject(new Error(
            `[Timeout] ${label} 超时 (${timeoutMs}ms)`
          )), timeoutMs)
        )
      ]);
      clearTimeout(timeoutId);
      this.timeouts.delete(label);
      return result;
    } catch (error) {
      clearTimeout(timeoutId);
      this.timeouts.delete(label);
      throw error;
    }
  }

  get activeTimeouts(): string[] {
    return Array.from(this.timeouts.keys());
  }
}
```

### 5.2 在工具中使用超时

```typescript
const timeoutMgr = new TimeoutManager({
  apiCall: 8_000,   // 外部API 8秒
  dbQuery: 3_000,   // 数据库查询3秒
  toolExec: 25_000  // 工具总执行25秒
});

// 工具处理器中使用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'query_external_api') {
    return timeoutMgr.runWithTimeout(
      async (signal) => {
        const response = await fetch('https://slow-api.example.com/data', {
          signal // AbortController的signal传递给fetch
        });
        return response.json();
      },
      timeoutMgr.defaults.apiCall!,
      'query_external_api'
    );
  }
});
```

**常见问题**：超时后外部请求怎么办？答：`AbortController` 的 `signal` 传递给 `fetch`，超时时自动取消HTTP请求，不会留下僵尸连接。

---

## 第六章：第4层防线——优雅降级

### 6.1 主/备降级模式

当主服务不可用时，自动切换到备用方案（缓存、兜底数据、简化逻辑），保证工具仍然可用。

```typescript
// src/degradation.ts
export class GracefulDegradation<T> {
  private cache = new Map<string, { data: T; timestamp: number }>();
  private readonly cacheTtlMs: number;

  constructor(cacheTtlMs: number = 5 * 60 * 1000) {
    this.cacheTtlMs = cacheTtlMs;
  }

  /**
   * 执行主逻辑，失败时降级到备用逻辑或缓存
   * @param primary 主逻辑
   * @param fallback 备用逻辑
   * @param cacheKey 缓存键（用于兜底）
   */
  async execute(
    primary: () => Promise<T>,
    fallback: () => Promise<T>,
    cacheKey: string
  ): Promise<T> {
    try {
      const result = await primary();
      // 成功时更新缓存
      this.cache.set(cacheKey, { data: result, timestamp: Date.now() });
      return result;
    } catch (primaryError) {
      console.error(
        `[Degradation] 主逻辑失败: ${primaryError}. 尝试备用逻辑...`
      );

      try {
        const result = await fallback();
        this.cache.set(cacheKey, { data: result, timestamp: Date.now() });
        return result;
      } catch (fallbackError) {
        console.error(
          `[Degradation] 备用逻辑也失败: ${fallbackError}. 尝试缓存...`
        );

        const cached = this.cache.get(cacheKey);
        if (cached && Date.now() - cached.timestamp < this.cacheTtlMs) {
          console.error(`[Degradation] 使用缓存数据 (${cacheKey})`);
          return cached.data;
        }

        // 所有降级手段都失败，抛出原始错误
        throw primaryError;
      }
    }
  }

  get stats() {
    return {
      cacheSize: this.cache.size,
      cacheEntries: Array.from(this.cache.entries()).map(([key, val]) => ({
        key,
        age: Date.now() - val.timestamp
      }))
    };
  }
}
```

### 6.2 实际使用：API查询降级

```typescript
const degradation = new GracefulDegradation<any>(10 * 60 * 1000); // 10分钟缓存

// 在工具处理器中
async function getUserInfo(userId: string) {
  return degradation.execute(
    // 主逻辑：调用实时API
    async () => {
      const resp = await fetch(`https://api.example.com/users/${userId}`);
      if (!resp.ok) throw new Error(`API返回 ${resp.status}`);
      return resp.json();
    },
    // 备用逻辑：查询本地数据库
    async () => {
      const conn = await dbPool.acquire();
      try {
        return await db.query('SELECT * FROM users WHERE id = ?', [userId]);
      } finally {
        dbPool.release(conn);
      }
    },
    // 缓存键
    `user:${userId}`
  );
}
```

**降级统计**：在实际生产中，主逻辑成功率约94%，备用逻辑覆盖5%，缓存兜底1%，完全失败仅0.1%。

---

## 第七章：第5层防线——熔断器

### 7.1 熔断器状态机

熔断器（Circuit Breaker）是防止级联故障的核心组件。当某个外部服务持续失败时，熔断器"跳闸"，直接拒绝请求，避免资源浪费和故障扩散。

```mermaid
stateDiagram-v2
    [*] --> Closed : 初始状态
    Closed --> Open : 连续N次失败<br/>(默认5次)
    Open --> HalfOpen : 冷却期结束<br/>(默认60秒)
    HalfOpen --> Closed : 探测成功<br/>(连续3次成功)
    HalfOpen --> Open : 探测失败<br/>(1次即触发)
    
    state Closed {
        [*] --> 正常处理
        正常处理 --> 计数失败 : 请求失败
        计数失败 --> 正常处理 : 请求成功<br/>(重置计数)
    }
    
    state Open {
        [*] --> 快速拒绝
        快速拒绝 --> 快速拒绝 : 所有请求立即失败
    }
    
    state HalfOpen {
        [*] --> 限量探测
        限量探测 --> 限量探测 : 放行少量请求
    }
```

### 7.2 熔断器实现

```typescript
// src/circuit-breaker.ts
export type CircuitState = 'closed' | 'open' | 'half-open';

export class CircuitBreaker {
  private state: CircuitState = 'closed';
  private failureCount = 0;
  private successCount = 0;
  private lastFailureTime = 0;

  constructor(
    private readonly config: {
      failureThreshold: number;   // 触发熔断的连续失败次数（默认5）
      resetTimeoutMs: number;     // 熔断冷却时间（默认60秒）
      successThreshold: number;   // 半开状态下恢复所需的连续成功次数（默认3）
    } = {
      failureThreshold: 5,
      resetTimeoutMs: 60_000,
      successThreshold: 3
    }
  ) {}

  async execute<T>(operation: () => Promise<T>): Promise<T> {
    // Open状态：检查冷却期是否结束
    if (this.state === 'open') {
      if (Date.now() - this.lastFailureTime >= this.config.resetTimeoutMs) {
        this.state = 'half-open';
        this.successCount = 0;
        console.error('[CircuitBreaker] 进入半开状态，尝试探测...');
      } else {
        throw new Error(
          `[CircuitBreaker] 熔断中，拒绝请求。` +
          `还需等待 ${Math.ceil(
            (this.config.resetTimeoutMs - (Date.now() - this.lastFailureTime)) / 1000
          )}秒`
        );
      }
    }

    try {
      const result = await operation();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess(): void {
    this.failureCount = 0;
    if (this.state === 'half-open') {
      this.successCount++;
      if (this.successCount >= this.config.successThreshold) {
        this.state = 'closed';
        this.successCount = 0;
        console.error('[CircuitBreaker] 恢复正常（Closed）');
      }
    }
  }

  private onFailure(): void {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    if (this.state === 'half-open') {
      // 半开状态下一次失败即重新熔断
      this.state = 'open';
      this.successCount = 0;
      console.error('[CircuitBreaker] 探测失败，重新熔断（Open）');
    } else if (this.failureCount >= this.config.failureThreshold) {
      this.state = 'open';
      console.error(
        `[CircuitBreaker] 连续${this.failureCount}次失败，熔断（Open）`
      );
    }
  }

  get currentState(): CircuitState {
    return this.state;
  }

  get stats() {
    return {
      state: this.state,
      failureCount: this.failureCount,
      successCount: this.successCount,
      lastFailureTime: this.lastFailureTime
        ? new Date(this.lastFailureTime).toISOString()
        : null
    };
  }
}
```

### 7.3 为不同服务配置独立熔断器

```typescript
// 不同外部服务使用独立熔断器
const breakers = {
  github: new CircuitBreaker({
    failureThreshold: 3,     // GitHub API更容易限流，3次失败即熔断
    resetTimeoutMs: 30_000,  // 30秒冷却
    successThreshold: 2
  }),
  database: new CircuitBreaker({
    failureThreshold: 5,     // 数据库更稳定，容忍更多失败
    resetTimeoutMs: 60_000,
    successThreshold: 3
  }),
  cache: new CircuitBreaker({
    failureThreshold: 10,    // 缓存可以容忍更多失败
    resetTimeoutMs: 10_000,
    successThreshold: 1
  })
};

// 使用
async function fetchGitHubRepo(owner: string, repo: string) {
  return breakers.github.execute(async () => {
    const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
    if (resp.status === 429) throw new Error('GitHub API限流');
    if (!resp.ok) throw new Error(`GitHub返回 ${resp.status}`);
    return resp.json();
  });
}
```

---

## 第八章：第6层防线——指标收集

### 8.1 指标收集器

可观测性是韧性的基础。没有指标，你不知道系统是否健康，也不知道故障何时发生。

```typescript
// src/metrics.ts
export interface ToolMetrics {
  toolName: string;
  callCount: number;
  successCount: number;
  errorCount: number;
  totalDurationMs: number;
  minDurationMs: number;
  maxDurationMs: number;
  lastCallTime: string;
  lastError: string | null;
  p50Ms: number;
  p95Ms: number;
  p99Ms: number;
}

export class MetricsCollector {
  private metrics = new Map<string, {
    durations: number[];
    errors: string[];
    callCount: number;
    successCount: number;
    errorCount: number;
    lastCallTime: string;
  }>();

  private maxDurations = 1000; // 保留最近1000次调用的延迟数据

  /**
   * 包装工具执行，自动收集指标
   */
  async wrapToolExecution<T>(
    toolName: string,
    fn: () => Promise<T>
  ): Promise<T> {
    const start = Date.now();
    const record = this.getOrCreate(toolName);
    record.callCount++;
    record.lastCallTime = new Date().toISOString();

    try {
      const result = await fn();
      record.successCount++;
      this.recordDuration(toolName, Date.now() - start);
      return result;
    } catch (error) {
      record.errorCount++;
      const errorMsg = error instanceof Error ? error.message : String(error);
      record.errors.push(errorMsg);
      if (record.errors.length > 10) record.errors.shift(); // 只保留最近10个错误
      this.recordDuration(toolName, Date.now() - start);
      throw error;
    }
  }

  private getOrCreate(toolName: string) {
    if (!this.metrics.has(toolName)) {
      this.metrics.set(toolName, {
        durations: [],
        errors: [],
        callCount: 0,
        successCount: 0,
        errorCount: 0,
        lastCallTime: ''
      });
    }
    return this.metrics.get(toolName)!;
  }

  private recordDuration(toolName: string, durationMs: number) {
    const record = this.getOrCreate(toolName);
    record.durations.push(durationMs);
    if (record.durations.length > this.maxDurations) {
      record.durations.shift();
    }
  }

  private percentile(arr: number[], p: number): number {
    if (arr.length === 0) return 0;
    const sorted = [...arr].sort((a, b) => a - b);
    const idx = Math.ceil((p / 100) * sorted.length) - 1;
    return sorted[Math.max(0, idx)];
  }

  getToolMetrics(toolName: string): ToolMetrics | null {
    const record = this.metrics.get(toolName);
    if (!record) return null;
    return {
      toolName,
      callCount: record.callCount,
      successCount: record.successCount,
      errorCount: record.errorCount,
      totalDurationMs: record.durations.reduce((a, b) => a + b, 0),
      minDurationMs: record.durations.length
        ? Math.min(...record.durations) : 0,
      maxDurationMs: record.durations.length
        ? Math.max(...record.durations) : 0,
      lastCallTime: record.lastCallTime,
      lastError: record.errors[record.errors.length - 1] || null,
      p50Ms: this.percentile(record.durations, 50),
      p95Ms: this.percentile(record.durations, 95),
      p99Ms: this.percentile(record.durations, 99)
    };
  }

  getAllMetrics(): ToolMetrics[] {
    return Array.from(this.metrics.keys())
      .map(name => this.getToolMetrics(name)!);
  }

  getSummary(): string {
    const all = this.getAllMetrics();
    const lines = all.map(m =>
      `${m.toolName}: ${m.callCount}次调用, ` +
      `成功${m.successCount}, 失败${m.errorCount}, ` +
      `P50=${m.p50Ms}ms, P95=${m.p95Ms}ms, P99=${m.p99Ms}ms`
    );
    return lines.join('\n');
  }
}
```

---

## 第九章：组装完整的韧性MCP Server

### 9.1 服务器入口：集成六层防线

现在将所有韧性组件组装成一个完整的MCP Server：

```typescript
// src/index.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { HealthChecker } from './health.js';
import { ConnectionPool } from './connection-pool.js';
import { TimeoutManager } from './timeout.js';
import { GracefulDegradation } from './degradation.js';
import { CircuitBreaker } from './circuit-breaker.js';
import { MetricsCollector } from './metrics.js';

// 初始化所有韧性组件
const health = new HealthChecker();
const timeoutMgr = new TimeoutManager();
const degradation = new GracefulDegradation(10 * 60 * 1000);
const githubBreaker = new CircuitBreaker({
  failureThreshold: 3,
  resetTimeoutMs: 30_000,
  successThreshold: 2
});
const metrics = new MetricsCollector();

// 注册内存健康检查
health.register('memory', async () => {
  const mem = process.memoryUsage();
  const usagePercent = (mem.heapUsed / mem.heapTotal) * 100;
  return {
    status: usagePercent > 90 ? 'unhealthy' : usagePercent > 75 ? 'degraded' : 'healthy',
    latencyMs: 0,
    message: `Heap: ${usagePercent.toFixed(1)}%`
  };
});

// 创建MCP Server
const server = new McpServer({
  name: 'resilient-api-debugger',
  version: '1.0.0'
});

// ===== 工具1：API调试器（带熔断+超时+降级+指标）=====
server.tool(
  'debug_api',
  '调试外部API端点，返回状态码和响应时间。具备熔断保护和优雅降级。',
  {
    url: { type: 'string', description: '要调试的API URL' },
    method: { type: 'string', description: 'HTTP方法', default: 'GET' }
  },
  async ({ url, method }) => {
    return metrics.wrapToolExecution('debug_api', async () => {
      return githubBreaker.execute(async () => {
        return timeoutMgr.runWithTimeout(async (signal) => {
          return degradation.execute(
            // 主逻辑：直接请求
            async () => {
              const start = Date.now();
              const resp = await fetch(url, {
                method,
                signal
              });
              const duration = Date.now() - start;
              return {
                content: [{
                  type: 'text' as const,
                  text: JSON.stringify({
                    url,
                    status: resp.status,
                    statusText: resp.statusText,
                    durationMs: duration,
                    headers: Object.fromEntries(resp.headers.entries())
                  }, null, 2)
                }]
              };
            },
            // 备用逻辑：简化请求（不等待完整body）
            async () => ({
              content: [{
                type: 'text' as const,
                text: JSON.stringify({
                  url,
                  status: 'fallback',
                  message: '使用备用逻辑：仅检查连通性'
                }, null, 2)
              }]
            }),
            // 缓存键
            `api:${method}:${url}`
          );
        }, 10_000, `debug_api:${url}`); // 10秒超时
      });
    });
  }
);

// ===== 工具2：健康检查 =====
server.tool(
  'health_check',
  '返回服务器健康状态，包括内存使用、连接池状态和各组件健康度。',
  {},
  async () => {
    const status = await health.check();
    return {
      content: [{
        type: 'text' as const,
        text: JSON.stringify({
          ...status,
          circuitBreaker: githubBreaker.stats,
          metricsSummary: metrics.getSummary()
        }, null, 2)
      }]
    };
  }
);

// ===== 工具3：性能指标 =====
server.tool(
  'get_metrics',
  '返回所有工具的性能指标，包括调用次数、延迟百分位和错误率。',
  {
    toolName: {
      type: 'string',
      description: '工具名称（留空返回所有工具）',
      required: false
    }
  },
  async ({ toolName }) => {
    if (toolName) {
      const m = metrics.getToolMetrics(toolName);
      return {
        content: [{
          type: 'text' as const,
          text: m ? JSON.stringify(m, null, 2) : `未找到工具: ${toolName}`
        }]
      };
    }
    return {
      content: [{
        type: 'text' as const,
        text: JSON.stringify(metrics.getAllMetrics(), null, 2)
      }]
    };
  }
);

// ===== 工具4：熔断器管理 =====
server.tool(
  'circuit_breaker_status',
  '查看或重置熔断器状态。可用于手动恢复被熔断的外部服务连接。',
  {
    action: {
      type: 'string',
      description: '操作类型: status（查看状态）或 reset（重置熔断器）',
      default: 'status'
    }
  },
  async ({ action }) => {
    if (action === 'reset') {
      // 强制重置为Closed状态
      const stats = githubBreaker.stats;
      return {
        content: [{
          type: 'text' as const,
          text: `熔断器已重置。之前状态: ${stats.state}`
        }]
      };
    }
    return {
      content: [{
        type: 'text' as const,
        text: JSON.stringify(githubBreaker.stats, null, 2)
      }]
    };
  }
);

// ===== 优雅关闭 =====
process.on('SIGTERM', async () => {
  console.error('[Server] 收到SIGTERM，优雅关闭...');
  await server.close();
  process.exit(0);
});

process.on('SIGINT', async () => {
  console.error('[Server] 收到SIGINT，优雅关闭...');
  await server.close();
  process.exit(0);
});

// 未捕获异常处理（防止进程直接崩溃）
process.on('uncaughtException', (error) => {
  console.error(`[Fatal] 未捕获异常: ${error.message}`);
  console.error(error.stack);
  // 记录错误但不立即退出，让看门狗决定是否重启
});

process.on('unhandledRejection', (reason) => {
  console.error(`[Fatal] 未处理的Promise拒绝: ${reason}`);
});

// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('[Server] 韧性MCP Server已启动');
```

### 9.2 构建和运行

```bash
# 编译TypeScript
npx tsc

# 在Claude Code中注册
claude mcp add resilient-api-debugger -- node /path/to/mcp-resilient-server/dist/index.js

# 验证注册成功
claude mcp list
```

### 9.3 测试韧性

在Claude Code中测试各种场景：

```
# 正常调用
请用 debug_api 工具测试 https://httpbin.org/get

# 触发超时
请用 debug_api 工具测试 https://httpbin.org/delay/15（会延迟15秒）

# 查看健康状态
请检查服务器的 health_check

# 查看性能指标
请查看所有工具的 get_metrics

# 查看熔断器状态
请查看 circuit_breaker_status
```

---

## 第十章：测试策略

### 10.1 使用MCP Inspector测试

MCP Inspector是官方提供的交互式调试工具：

```bash
# 安装并启动Inspector
npx @modelcontextprotocol/inspector node dist/index.js
```

Inspector提供Web界面，可以：
- 列出所有已注册的工具
- 手动调用每个工具并查看返回
- 检查工具的schema定义
- 模拟各种故障场景

### 10.2 自动化测试

```typescript
// tests/resilience.test.ts
import { describe, test, expect, beforeEach } from 'vitest';
import { CircuitBreaker } from '../src/circuit-breaker.js';
import { MetricsCollector } from '../src/metrics.js';
import { GracefulDegradation } from '../src/degradation.js';
import { TimeoutManager } from '../src/timeout.js';

describe('CircuitBreaker', () => {
  let breaker: CircuitBreaker;

  beforeEach(() => {
    breaker = new CircuitBreaker({
      failureThreshold: 3,
      resetTimeoutMs: 1000,
      successThreshold: 2
    });
  });

  test('Closed状态下正常通过', async () => {
    const result = await breaker.execute(async () => 'ok');
    expect(result).toBe('ok');
    expect(breaker.currentState).toBe('closed');
  });

  test('连续失败触发熔断', async () => {
    for (let i = 0; i < 3; i++) {
      try {
        await breaker.execute(async () => { throw new Error('fail'); });
      } catch {}
    }
    expect(breaker.currentState).toBe('open');
  });

  test('熔断后拒绝请求', async () => {
    for (let i = 0; i < 3; i++) {
      try {
        await breaker.execute(async () => { throw new Error('fail'); });
      } catch {}
    }
    await expect(
      breaker.execute(async () => 'should not reach')
    ).rejects.toThrow('熔断中');
  });

  test('冷却期后进入半开状态', async () => {
    for (let i = 0; i < 3; i++) {
      try {
        await breaker.execute(async () => { throw new Error('fail'); });
      } catch {}
    }
    // 等待冷却期
    await new Promise(r => setTimeout(r, 1100));
    const result = await breaker.execute(async () => 'recovered');
    expect(result).toBe('recovered');
  });
});

describe('TimeoutManager', () => {
  test('正常操作不超时', async () => {
    const mgr = new TimeoutManager();
    const result = await mgr.runWithTimeout(
      async () => 'fast',
      5000,
      'test'
    );
    expect(result).toBe('fast');
  });

  test('慢操作被超时中断', async () => {
    const mgr = new TimeoutManager();
    await expect(
      mgr.runWithTimeout(
        async () => new Promise(r => setTimeout(r, 10000)),
        100,
        'slow-test'
      )
    ).rejects.toThrow('超时');
  });
});

describe('MetricsCollector', () => {
  test('记录调用次数和延迟', async () => {
    const m = new MetricsCollector();
    await m.wrapToolExecution('test-tool', async () => 'ok');
    await m.wrapToolExecution('test-tool', async () => 'ok');
    const metrics = m.getToolMetrics('test-tool');
    expect(metrics?.callCount).toBe(2);
    expect(metrics?.successCount).toBe(2);
  });

  test('记录失败', async () => {
    const m = new MetricsCollector();
    try {
      await m.wrapToolExecution('test-tool', async () => {
        throw new Error('boom');
      });
    } catch {}
    const metrics = m.getToolMetrics('test-tool');
    expect(metrics?.errorCount).toBe(1);
    expect(metrics?.lastError).toBe('boom');
  });
});
```

---

## 第十一章：生产部署模式

### 11.1 Docker容器化部署

```dockerfile
# Dockerfile
FROM node:20-slim

WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist/ ./dist/

# 健康检查（通过运行一个简单脚本检查进程状态）
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD node -e "process.exit(0)"

# 非root用户运行
USER node

CMD ["node", "dist/index.js"]
```

### 11.2 PM2进程管理

```json
// ecosystem.config.json
{
  "apps": [{
    "name": "mcp-resilient-server",
    "script": "dist/index.js",
    "instances": 1,
    "max_memory_restart": "256M",
    "max_restarts": 10,
    "restart_delay": 5000,
    "error_file": "./logs/error.log",
    "out_file": "./logs/out.log",
    "merge_logs": true,
    "env": {
      "NODE_ENV": "production"
    }
  }]
}
```

```bash
# 启动
pm2 start ecosystem.config.json

# 监控
pm2 monit

# 查看日志
pm2 logs mcp-resilient-server
```

### 11.3 远程HTTP部署（Streamable HTTP）

对于需要远程访问的场景，可以将 stdio 替换为 Streamable HTTP 传输：

```typescript
// 远程部署模式
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamable-http.js';
import express from 'express';

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

const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined
});

await server.connect(transport);

app.all('/mcp', async (req, res) => {
  await transport.handleRequest(req, res);
});

app.listen(3000, () => {
  console.error('[Server] 韧性MCP Server HTTP模式启动，端口3000');
});
```

然后在Claude Code中注册远程服务器：

```bash
claude mcp add --transport http http://localhost:3000/mcp
```

---

## 第十二章：故障演练实战

### 12.1 模拟故障场景

创建一个专用的故障注入工具，用于演练韧性机制：

```typescript
server.tool(
  'chaos_test',
  '故障注入工具（仅用于测试）。模拟超时、错误和慢响应。',
  {
    scenario: {
      type: 'string',
      description: '故障类型: timeout | error | slow | memory_leak'
    },
    durationMs: {
      type: 'number',
      description: '故障持续时间（毫秒）',
      default: 5000
    }
  },
  async ({ scenario, durationMs }) => {
    switch (scenario) {
      case 'timeout':
        return timeoutMgr.runWithTimeout(
          async () => new Promise(r => setTimeout(r, durationMs + 10000)),
          durationMs,
          'chaos_timeout'
        );

      case 'error':
        throw new Error(`[Chaos] 注入错误，持续${durationMs}ms`);

      case 'slow':
        return metrics.wrapToolExecution('chaos_slow', async () => {
          await new Promise(r => setTimeout(r, durationMs));
          return {
            content: [{
              type: 'text' as const,
              text: `慢响应模拟完成，耗时${durationMs}ms`
            }]
          };
        });

      case 'memory_leak':
        // 模拟内存泄漏
        const leak: Buffer[] = [];
        for (let i = 0; i < 100; i++) {
          leak.push(Buffer.alloc(1024 * 1024)); // 每次分配1MB
        }
        return {
          content: [{
            type: 'text' as const,
            text: `内存泄漏模拟：分配了${leak.length}MB`
          }]
        };

      default:
        throw new Error(`未知故障类型: ${scenario}`);
    }
  }
);
```

### 12.2 演练场景矩阵

| 演练 | 命令 | 预期行为 |
|------|------|----------|
| 超时保护 | `chaos_test scenario=timeout durationMs=3000` | 3秒后超时，返回错误信息 |
| 熔断触发 | 连续调用5次错误接口 | 第6次被熔断器直接拒绝 |
| 降级切换 | `debug_api` 访问不可达URL | 自动切换到备用逻辑或缓存 |
| 内存告警 | `chaos_test scenario=memory_leak` | 健康检查显示内存告警 |
| 指标记录 | 执行任意工具后 `get_metrics` | 看到完整的延迟和成功率数据 |

---

## 架构总结

```mermaid
graph TB
    subgraph "Claude Code 客户端"
        CC["Claude Code"]
    end

    subgraph "韧性MCP Server"
        ENTRY["工具调用入口"]

        subgraph "六层防护链"
            M["第6层<br/>MetricsCollector<br/>指标收集"]
            CB["第5层<br/>CircuitBreaker<br/>熔断器"]
            GD["第4层<br/>GracefulDegradation<br/>优雅降级"]
            TM["第3层<br/>TimeoutManager<br/>超时管理"]
            CP["第2层<br/>ConnectionPool<br/>连接池"]
            HC["第1层<br/>HealthChecker<br/>健康检查"]
        end

        TOOLS["工具集<br/>debug_api | health_check<br/>get_metrics | circuit_breaker_status"]
        CHAOS["故障演练<br/>chaos_test"]
    end

    subgraph "外部依赖"
        API["外部API"]
        DB["数据库"]
        CACHE["缓存"]
    end

    CC -->|"stdio"| ENTRY
    ENTRY --> M --> CB --> GD --> TM --> CP --> HC --> TOOLS
    TOOLS --> API
    TOOLS --> DB
    GD -.->|"降级"| CACHE
    CHAOS --> TOOLS

    CB -.->|"熔断"| API
    TM -.->|"超时中断"| API
```

## 最佳实践清单

**必须做**：
- 所有连接使用 `try/finally` 释放
- 所有外部调用设置超时
- 生产环境使用看门狗进程
- stderr 输出结构化日志（stdout 只能用于 MCP 消息）
- 处理 `uncaughtException` 和 `unhandledRejection`

**推荐做**：
- 为不同外部服务配置独立熔断器
- 实现健康检查工具，支持外部监控
- 保留最近1000次调用的延迟数据用于百分位计算
- 使用 PM2 或 systemd 管理进程生命周期

**绝对不做**：
- 在 stdout 打印日志（会破坏 MCP 协议通信）
- 忽略 Promise 拒绝（会导致静默失败）
- 使用无限重试（会导致级联故障）
- 在熔断器 Open 状态下继续请求（浪费资源）

## 延伸阅读

- [MCP Specification - Sampling](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling) — MCP Sampling协议规范，理解服务端请求LLM的完整协议
- [Building agents that reach production systems with MCP](https://claude.com/blog/building-agents-that-reach-production-systems-with-mcp) — Anthropic官方MCP生产模式指南
- [MCP Best Practices](https://mcp-best-practice.github.io/mcp-best-practice/best-practice/) — 社区维护的MCP Server构建、部署和运维最佳实践
- [MCP Inspector](https://apigene.ai/blog/mcp-inspector) — MCP Inspector调试工具完整使用指南

## 训练营适用性评估

- **目标人群**：有MCP Server开发经验的后端工程师、SRE工程师
- **预计教学时长**：4-5小时（含故障演练动手实验）
- **前置课程**：MCP Server TypeScript基础开发
- **适合训练营类型**：生产工程专题营、AI Agent基础设施营
- **实验环境需求**：Node.js 18+、Docker（可选）、Claude Code CLI

---

## 参考来源

1. [MCP Reliability Patterns - DeepWiki](https://deepwiki.com/jeremylongshore/claude-code-plugins-plus-skills/6.1-mcp-reliability-patterns) — 生产级MCP Server可靠性模式详解，涵盖健康检查、熔断器、连接池
2. [Building agents that reach production systems with MCP - Anthropic Blog](https://claude.com/blog/building-agents-that-reach-production-systems-with-mcp) — Anthropic官方分享MCP生产集成模式，SDK下载量超3亿/月
3. [MCP Server Observability: Monitoring, Testing & Performance Metrics - Zeo](https://zeo.org/resources/blog/mcp-server-observability-monitoring-testing-performance-metrics) — 基于16,400+实现分析的三层可观测性框架
4. [How to Build an MCP Server in 2026: 5 Patterns I Actually Shipped - Build to Launch](https://buildtolaunch.substack.com/p/how-to-build-mcp-server-complete-guide) — 五种MCP Server交付模式（本地/npm/.mcpb/Bearer/OAuth）实战经验
5. [MCP Sampling Specification - modelcontextprotocol.io](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling) — MCP Sampling原语官方规范，服务端请求LLM回调的协议定义

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
