[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]);
故障序列
- 插件服务生成 MCP 客户端连接
- MCP 客户端启动与服务器的 `initialize` 握手
- 服务器开始重型初始化(例如,加载 sentence-transformers 等 ML 模型)
- 在服务器完成初始化之前达到 30 秒标记
- MCP 客户端以超时错误拒绝
- 插件服务将插件标记为失败并重试
- 重试时,模型被缓存,初始化在 10–15 秒内完成
配置传播缺口
配置层级不会传播到 MCP 客户端层:
plugins.entries.
代码位置参考
硬编码常量通常位于:
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 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: 混淆 timeout 与 initTimeoutMs
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: 超时值超出范围
实现会强制执行最小值和最大值边界。范围外的值会被限制:
| 配置值 | 实际值 | 原因 |
|---|---|---|
500 | 5000 | 强制执行最小值: 5000ms |
600000 | 300000 | 强制执行最大值: 300000ms |
NaN | 30000 | 默认回退值 |
陷阱 4: 配置文件与运行时覆盖优先级
当存在多个超时源时,优先级顺序为:
- 插件特定配置:
plugins.entries.<id>.config.initTimeoutMs - 环境变量:
OPENCLAW_MCP_INIT_TIMEOUT_MS - 全局配置:
plugins.mcpInitTimeoutMs - 硬编码默认值:
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 不暴露超时配置;在包装器层处理。