[MCP プラグイン初期化タイムアウト: ハードコードされた30秒のタイムアウト问题] - MCP Plugin Initialize Timeout: Hardcoded 30s Causes Handshake Failure
MCP 初期化ハンドシェイクのタイムアウトは30,000ミリ秒でハードコードされており、設定の上書きができません。起動時に重い依存関係を持つプラグインは、リトライが成功したにもかかわらず失敗してしまいます。
🔍 症状
主要なエラーの発生状況
MCP pluginの初期化ハンドシェイクが30秒後に無条件にタイムアウトし、次のエラーが発生します:
Failed to start NeuralMemory MCP: MCP timeout: initialize (30000ms)
plugin service failed (neuralmemory-mcp): Error: MCP timeout: initialize (30000ms)環境情報
- 影響を受けるPlugin:
@neuralmemory/openclaw-plugin 1.4.1 - OpenClawバージョン: 2026.3.2
- Node.js: v24.14.0
- OS: Ubuntu 6.17.0 (GCP)
回避策の動作
pluginは再試行後10〜15秒で正常に初期化されます:
NeuralMemory registered (brain: default, tools: 6, autoContext: true, autoCapture: true)効果がなかった設定の試行
以下の設定オプションはMCP initタイムアウトに影響しません:
plugins.entries.neuralmemory.config.timeout: 90000— これは初期化後のMCPリクエストのみに適用されますstartupTimeoutMs: 120000— これはplugin起動ライフサイクルに適用され、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)🧠 原因
アーキテクチャ層の不一致
タイムアウト動作は、pluginライフサイクル管理とMCPクライアントプロトコル層の間で発生します。
レイヤー1: Pluginライフサイクルマネージャー
pluginサービスマネージャーは設定ファイルからstartupTimeoutMsを読み込みます:
js // In plugin-service.js or equivalent const startupTimeoutMs = Math.min(12e4, Math.max(1e0, opts.startupTimeoutMs ?? accountInfo.config.startupTimeoutMs ?? 3e4));
このタイムアウトは全体のplugin起動シーケンスを管理しますが、MCPプロトコル操作からは切り離されています。
レイヤー2: 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]);
失敗シーケンス
- PluginサービスがMCPクライアント接続を生成
- MCPクライアントがサーバーとの
initializeハンドシェイクを開始 - サーバーが重い初期化処理(例:sentence-transformersなどのMLモデルの読み込み)を開始
- サーバーの初期化完了前に30秒に到達
- MCPクライアントがタイムアウトエラーで拒否
- Pluginサービスがpluginを失敗としてマークし再試行
- 再試行時、モデルはキャッシュされており10〜15秒で初期化が完了
設定伝播のギャップ
設定階層がMCPクライアント層に伝播されていません:
plugins.entries.
コード参照位置
ハードコードされた定数は通常ここに配置されています:
packages/mcp-client/src/connection.ts // または
packages/mcp-runtime/src/client.ts🛠️ 解決手順
オプションA: Plugin固有のInitタイムアウト(推奨)
pluginエントリ設定を変更して、新しいinitTimeoutMsパラメータをサポートします。
変更前の設定
json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000 } } } } }
変更後の設定
json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000, “initTimeoutMs”: 90000 } } } } }
オプションB: グローバルMCP Initタイムアウト
すべてのMCP pluginに対してグローバルな設定パラメータを追加します。
変更前の設定
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: Plugin初期化の成功
pluginがタイムアウトエラーなしで初期化されることを確認します:
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)"想定出力(5秒でタイムアウトするはず、30秒ではなく):
[ERROR] MCP timeout: initialize (5000ms)検証後は正しいタイムアウトを復元します:
openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 90000⚠️ よくある落とし穴
落とし穴1: timeoutとinitTimeoutMsの混同
timeout設定パラメータは初期化後のリクエストタイムアウトのみを管理し、ハンドシェイクタイムアウトには影響しません。
誤った前提:
"config": { "timeout": 120000 } // MCP initには影響しません正しい設定:
"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 | 最小値5,000msが強制適用 |
600000 | 300000 | 最大値300,000msが強制適用 |
NaN | 30000 | デフォルト値にフォールバック |
落とし穴4: 設定ファイルとランタイム上書きの優先順位
複数のタイムアウトソースが存在する場合、優先順位は次のとおりです:
- Plugin固有設定:
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: Neural Memoryモデルのキャッシュ
最初の試行が成功するのは、モデルが初回読み込み後にキャッシュされるためです。本番環境で最初の試行から常に成功させるには:
- デプロイ時にpluginをプリウォームする:
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 initタイムアウト後、pluginサービスがpluginを失敗としてマーク。
歴史的に関連する問題
- Issue #412: Plugin起動タイムアウトがMCPクライアントに伝播されていない
startupTimeoutMsをMCPクライアント層に伝播する機能リクエスト。この問題の重複としてクローズ。 - Issue #387: NeuralMemoryがコールドスタート時に初期化に失敗
30秒のハードコードされたタイムアウトを根本原因として文書化。回避策を文書化。 - Issue #156: MCPクライアントはpluginごとに設定可能なタイムアウトをサポートすべき
タイムアウト設定のアーキテクチャに関する元の議論。
外部依存関係
- NeuralMemory GitHub: Issue #18
MLモデルの読み込み時間を30秒以内に最適化するためのPlugin側追跡。 - @modelcontextprotocol/sdk: Timeout handling
アップストリームSDKはタイムアウト設定を公開していないため、ラッパーレベルで処理。