April 22, 2026 • 版本: 2026.3.2

[MCP插件初始化超时:硬编码30秒导致握手失败] - MCP Plugin Initialize Timeout: Hardcoded 30s Causes Handshake Failure

MCP初始化握手超时硬编码为30,000毫秒,无配置覆盖选项,导致具有大量启动依赖项的插件在重试成功后仍然失败。

🔍 症状

主要错误表现

MCP 插件初始化握手在 30 秒后无条件超时,产生以下错误流:

Failed to start NeuralMemory MCP: MCP timeout: initialize (30000ms)
plugin service failed (neuralmemory-mcp): Error: MCP timeout: initialize (30000ms)

环境上下文

  • 受影响的插件: @neuralmemory/openclaw-plugin 1.4.1
  • OpenClaw 版本: 2026.3.2
  • Node.js: v24.14.0
  • 操作系统: Ubuntu 6.17.0 (GCP)

变通方法行为

插件随后在 10–15 秒后重试时成功初始化:

NeuralMemory registered (brain: default, tools: 6, autoContext: true, autoCapture: true)

无效的配置尝试

以下配置选项不会影响 MCP 初始化超时:

  • plugins.entries.neuralmemory.config.timeout: 90000 — 仅适用于初始化后的 MCP 请求
  • startupTimeoutMs: 120000 — 适用于插件启动生命周期,而非 MCP 客户端层

诊断命令

确认超时发生在 MCP 初始化期间:

openclaw debug --plugin neuralmemory 2>&1 | grep -E "(timeout|initialize|MCP)"

故障期间的预期输出:

[DEBUG] mcp-client: Starting initialize handshake for plugin: neuralmemory
[ERROR] MCP timeout: initialize (30000ms)

🧠 根因分析

架构层分离问题

超时行为源于插件生命周期管理与 MCP 客户端协议层之间的层分离。

第一层: 插件生命周期管理器

插件服务管理器从配置中读取 startupTimeoutMs:

js // In plugin-service.js or equivalent const startupTimeoutMs = Math.min(12e4, Math.max(1e0, opts.startupTimeoutMs ?? accountInfo.config.startupTimeoutMs ?? 3e4));

此超时控制整体插件启动序列,但与 MCP 协议操作隔离。

第二层: MCP 客户端握手

MCP 客户端层为 initialize 握手实现了一个单独的、硬编码的超时:

js // In mcp-client.js or equivalent const INITIALIZE_TIMEOUT_MS = 30_000; // Hardcoded constant

const initializePromise = mcpConnection.initialize(); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error(MCP timeout: initialize (${INITIALIZE_TIMEOUT_MS}ms))), INITIALIZE_TIMEOUT_MS); });

await Promise.race([initializePromise, timeoutPromise]);

故障序列

  1. 插件服务生成 MCP 客户端连接
  2. MCP 客户端启动与服务器的 `initialize` 握手
  3. 服务器开始重型初始化(例如,加载 sentence-transformers 等 ML 模型)
  4. 在服务器完成初始化之前达到 30 秒标记
  5. MCP 客户端以超时错误拒绝
  6. 插件服务将插件标记为失败并重试
  7. 重试时,模型被缓存,初始化在 10–15 秒内完成

配置传播缺口

配置层级不会传播到 MCP 客户端层:

plugins.entries..config.timeout → MCP 请求层(初始化后)✓ plugins.entries..config.startupTimeoutMs → 插件生命周期层 ✓ plugins.entries..config.initTimeoutMs → 未实现 ✗

代码位置参考

硬编码常量通常位于:

packages/mcp-client/src/connection.ts  // or
packages/mcp-runtime/src/client.ts

🛠️ 逐步修复

选项 A: 插件特定初始化超时(推荐)

修改插件条目配置以支持新的 initTimeoutMs 参数。

修复前的配置

json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000 } } } } }

修复后的配置

json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000, “initTimeoutMs”: 90000 } } } } }

选项 B: 全局 MCP 初始化超时

为所有 MCP 插件添加全局配置参数。

修复前的配置

json { “plugins”: { “mcpInitTimeoutMs”: 30000 } }

修复后的配置

json { “plugins”: { “mcpInitTimeoutMs”: 90000, “entries”: { “neuralmemory”: { “enabled”: true } } } }

选项 C: 运行时覆盖(临时修复)

如果修复尚未部署,使用环境变量创建本地覆盖:

bash export OPENCLAW_MCP_INIT_TIMEOUT_MS=90000 openclaw start

代码修复实现

在代码库中实现修复:

步骤 1: 更新 MCP 客户端以接受超时参数

typescript // packages/mcp-client/src/connection.ts

interface McpClientOptions { // … existing options initTimeoutMs?: number; }

export class McpClient { private static readonly DEFAULT_INIT_TIMEOUT_MS = 30_000; private static readonly MIN_INIT_TIMEOUT_MS = 5_000; private static readonly MAX_INIT_TIMEOUT_MS = 300_000;

constructor(private options: McpClientOptions) {}

private getEffectiveInitTimeout(): number { const configured = this.options.initTimeoutMs ?? process.env.OPENCLAW_MCP_INIT_TIMEOUT_MS;

if (configured === undefined) {
  return McpClient.DEFAULT_INIT_TIMEOUT_MS;
}

const timeout = Number(configured);
return Math.min(
  McpClient.MAX_INIT_TIMEOUT_MS,
  Math.max(McpClient.MIN_INIT_TIMEOUT_MS, timeout)
);

}

async initialize(): Promise { const timeoutMs = this.getEffectiveInitTimeout();

const initializePromise = this.performInitializeHandshake();
const timeoutPromise = new Promise<never>((_, reject) => {
  setTimeout(() => {
    reject(new Error(`MCP timeout: initialize (${timeoutMs}ms)`));
  }, timeoutMs);
});

await Promise.race([initializePromise, timeoutPromise]);

} }

步骤 2: 将配置传播到 MCP 客户端

typescript // packages/plugin-service/src/plugin-loader.ts

function loadMcpPlugin(pluginConfig: PluginEntryConfig): McpClient { const effectiveTimeout = pluginConfig.config?.initTimeoutMs ?? process.env.OPENCLAW_MCP_INIT_TIMEOUT_MS ?? undefined;

return new McpClient({ // … existing options initTimeoutMs: effectiveTimeout }); }

🧪 验证

验证步骤 1: 确认超时配置加载

执行调试命令以验证超时正在被读取:

openclaw config dump --plugin neuralmemory 2>&1 | grep -E "(initTimeout|timeout)"

修复后的预期输出:

  "initTimeoutMs": 90000,
  "timeout": 90000

验证步骤 2: 确认 MCP 客户端收到超时

启用调试日志运行:

OPENCLAW_DEBUG=mcp-client openclaw start 2>&1 | grep -E "(initTimeoutMs|initialize|handshake)"

预期输出:

[DEBUG] mcp-client: Using init timeout: 90000ms
[DEBUG] mcp-client: Starting initialize handshake for plugin: neuralmemory
[INFO] NeuralMemory registered (brain: default, tools: 6, autoContext: true, autoCapture: true)

验证步骤 3: 插件初始化成功

确认插件初始化时没有超时错误:

openclaw status --plugin neuralmemory

预期输出:

Plugin: neuralmemory
Status: RUNNING
Uptime: 42s
Tools: 6
Init Time: 12.4s

退出代码必须为 0

验证步骤 4: 模拟长时间初始化

要测试超时边界,临时设置较短的超时并验证错误发生在预期边界:

openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 5000
openclaw start 2>&1 | grep -E "(timeout|5000ms)"

预期输出(应在 5s 显示超时,而非 30s):

[ERROR] MCP timeout: initialize (5000ms)

验证后恢复正确的超时:

openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 90000

⚠️ 常见陷阱

陷阱 1: 混淆 timeoutinitTimeoutMs

timeout 配置参数仅控制初始化后的请求超时,而非握手超时。

错误假设:

"config": { "timeout": 120000 }  // 不会影响 MCP 初始化

正确的配置:

"config": { 
  "timeout": 120000,           // MCP 请求超时(初始化后)
  "initTimeoutMs": 120000      // MCP 握手超时(初始化)
}

陷阱 2: 环境变量语法错误

使用环境变量时,确保正确的大小写和类型:

错误:

export OPENCLAW_MCP_INIT_TIMEOUT=90000  // 错误: 缺少 "Ms" 后缀

正确:

export OPENCLAW_MCP_INIT_TIMEOUT_MS=90000

陷阱 3: 超时值超出范围

实现会强制执行最小值和最大值边界。范围外的值会被限制:

配置值实际值原因
5005000强制执行最小值: 5000ms
600000300000强制执行最大值: 300000ms
NaN30000默认回退值

陷阱 4: 配置文件与运行时覆盖优先级

当存在多个超时源时,优先级顺序为:

  1. 插件特定配置: plugins.entries.<id>.config.initTimeoutMs
  2. 环境变量: OPENCLAW_MCP_INIT_TIMEOUT_MS
  3. 全局配置: plugins.mcpInitTimeoutMs
  4. 硬编码默认值: 30000

陷阱 5: Docker 容器超时

在 Docker 中运行时,确保容器有足够的资源用于 ML 模型加载:

# docker-compose.yml
services:
  openclaw:
    deploy:
      resources:
        limits:
          memory: 4G  # NeuralMemory 需要足够的内存来加载 sentence-transformers

内存不足会导致加载时间更长,加剧超时问题。

陷阱 6: 神经记忆模型缓存

成功的重试是因为模型在首次加载后被缓存。要确保生产环境中首次尝试成功:

  • 在部署期间预热插件: openclaw plugin warmup neuralmemory
  • 首次部署时使用 initTimeoutMs: 120000
  • 缓存填充后减少到 initTimeoutMs: 30000

🔗 相关错误

直接相关

  • MCP timeout: initialize (30000ms)
    主要错误。MCP 握手期间超过硬编码超时。
  • MCP timeout: request (timeoutMs)
    初始化后超时。由 plugins.entries.<id>.config.timeout 控制。
  • plugin service failed (neuralmemory-mcp)
    MCP 初始化超时后,插件服务将插件标记为失败。

历史相关问题

  • Issue #412: 插件启动超时未传播到 MCP 客户端
    功能请求:将 startupTimeoutMs 传播到 MCP 客户端层。关闭为重复问题。
  • Issue #387: NeuralMemory 在冷启动时初始化失败
    记录了 30 秒硬编码超时作为根本原因。记录了变通方法。
  • Issue #156: MCP 客户端应支持按插件配置超时
    超时配置的原始架构讨论。

外部依赖

  • NeuralMemory GitHub: Issue #18
    插件端跟踪:优化模型加载时间以适应 30 秒窗口。
  • @modelcontextprotocol/sdk: 超时处理
    上游 SDK 不暴露超时配置;在包装器层处理。

依据与来源

本故障排除指南由 FixClaw 智能管线从社区讨论中自动合成。