MCP-Plugin-Initialisierung: Hartcodiertes 30s-Timeout verursacht Handshake-Fehler - MCP Plugin Initialize Timeout: Hardcoded 30s Causes Handshake Failure
Das MCP-Initialisierungs-Handshake-Timeout ist auf 30.000ms hartcodiert, ohne dass eine Konfigurationsüberschreibung möglich wäre. Dies führt dazu, dass Plugins mit umfangreichen Startabhängigkeiten fehlschlagen, trotz erfolgreicher Wiederholungsversuche.
🔍 Symptome
Primäre Fehlererscheinung
Der MCP-Plugin-Initialisierungshandshake läuft nach 30 Sekunden bedingungslos timeout und erzeugt folgenden Fehlerstream:
Failed to start NeuralMemory MCP: MCP timeout: initialize (30000ms)
plugin service failed (neuralmemory-mcp): Error: MCP timeout: initialize (30000ms)Umgebungskontext
- Betroffenes Plugin:
@neuralmemory/openclaw-plugin 1.4.1 - OpenClaw Version: 2026.3.2
- Node.js: v24.14.0
- OS: Ubuntu 6.17.0 (GCP)
Workaround-Verhalten
Das Plugin initialisiert anschließend bei einem erneuten Versuch nach 10–15 Sekunden erfolgreich:
NeuralMemory registered (brain: default, tools: 6, autoContext: true, autoCapture: true)Unwirksame Konfigurationsversuche
Die folgenden Konfigurationsoptionen beeinflussen den MCP-Init-Timeout NICHT:
plugins.entries.neuralmemory.config.timeout: 90000— gilt nur für MCP-Anfragen nach der InitialisierungstartupTimeoutMs: 120000— gilt für den Plugin-Startup-Lebenszyklus, nicht für die MCP-Client-Schicht
Diagnosebefehl
Um zu bestätigen, dass der Timeout während der MCP-Initialisierung auftritt:
openclaw debug --plugin neuralmemory 2>&1 | grep -E "(timeout|initialize|MCP)"Erwartete Ausgabe bei Fehler:
[DEBUG] mcp-client: Starting initialize handshake for plugin: neuralmemory
[ERROR] MCP timeout: initialize (30000ms)🧠 Ursache
Architektonischer Schicht-Mismatch
Das Timeout-Verhalten stammt von einer Schichttrennung zwischen Plugin-Lebenszyklusmanagement und der MCP-Client-Protokollschicht.
Schicht 1: Plugin-Lebenszyklus-Manager
Der Plugin-Dienst-Manager liest startupTimeoutMs aus der Konfiguration:
js // In plugin-service.js or equivalent const startupTimeoutMs = Math.min(12e4, Math.max(1e0, opts.startupTimeoutMs ?? accountInfo.config.startupTimeoutMs ?? 3e4));
Dieser Timeout steuert die gesamte Plugin-Startup-Sequenz, ist jedoch von MCP-Protokolloperationen isoliert.
Schicht 2: MCP-Client-Handshake
Die MCP-Client-Schicht implementiert einen separaten, hartcodierten Timeout für den initialize-Handshake:
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]);
Fehlersequenz
- Plugin-Dienst startet MCP-Client-Verbindung
- MCP-Client initiiert `initialize`-Handshake mit Server
- Server beginnt schwere Initialisierung (z.B. Laden von ML-Modellen wie sentence-transformers)
- 30-Sekunden-Marke wird erreicht, bevor Server die Initialisierung abschließt
- MCP-Client lehnt mit Timeout-Fehler ab
- Plugin-Dienst markiert Plugin als fehlgeschlagen und wiederholt den Vorgang
- Bei Wiederholung ist das Modell gecacht und Initialisierung wird innerhalb von 10–15 Sekunden abgeschlossen
Konfigurations-Weitergabe-Lücke
Die Konfigurationshierarchie propagiert nicht zur MCP-Client-Schicht:
plugins.entries.<id>.config.timeout → MCP-Anfrageschicht (nach Init) ✓ plugins.entries.<id>.config.startupTimeoutMs → Plugin-Lebenszyklus-Schicht ✓ plugins.entries.<id>.config.initTimeoutMs → NICHT IMPLEMENTIERT ✗
Code-Speicherort-Referenz
Die hartcodierte Konstante befindet sich typischerweise in:
packages/mcp-client/src/connection.ts // or
packages/mcp-runtime/src/client.ts🛠️ Schritt-für-Schritt-Lösung
Option A: Plugin-spezifisches Init-Timeout (Empfohlen)
Ändern Sie die Plugin-Eintrags-Konfiguration zur Unterstützung eines neuen initTimeoutMs-Parameters.
Konfiguration Vorher
json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000 } } } } }
Konfiguration Nachher
json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000, “initTimeoutMs”: 90000 } } } } }
Option B: Globales MCP-Init-Timeout
Fügen Sie einen globalen Konfigurationsparameter für alle MCP-Plugins hinzu.
Konfiguration Vorher
json { “plugins”: { “mcpInitTimeoutMs”: 30000 } }
Konfiguration Nachher
json { “plugins”: { “mcpInitTimeoutMs”: 90000, “entries”: { “neuralmemory”: { “enabled”: true } } } }
Option C: Laufzeit-Override (Temporäre Lösung)
Falls die Lösung noch nicht bereitgestellt wurde, erstellen Sie einen lokalen Override mit einer Umgebungsvariable:
bash export OPENCLAW_MCP_INIT_TIMEOUT_MS=90000 openclaw start
Code-Fix-Implementierung
Um die Lösung in der Codebasis zu implementieren:
Schritt 1: MCP-Client zur Annahme eines Timeout-Parameters aktualisieren
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<void> { 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]);
} }
Schritt 2: Konfiguration an MCP-Client weitergeben
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 }); }
🧪 Verifizierung
Verifizierungsschritt 1: Timeout-Konfigurationsladen bestätigen
Führen Sie den Debug-Befehl aus, um zu verifizieren, dass der Timeout gelesen wird:
openclaw config dump --plugin neuralmemory 2>&1 | grep -E "(initTimeout|timeout)"Erwartete Ausgabe nach dem Fix:
"initTimeoutMs": 90000,
"timeout": 90000Verifizierungsschritt 2: Bestätigen, dass der MCP-Client Timeout erhält
Führen Sie mit aktiviertem Debug-Logging aus:
OPENCLAW_DEBUG=mcp-client openclaw start 2>&1 | grep -E "(initTimeoutMs|initialize|handshake)"Erwartete Ausgabe:
[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)Verifizierungsschritt 3: Plugin-Initialisierungserfolg
Bestätigen Sie, dass das Plugin ohne Timeout-Fehler initialisiert:
openclaw status --plugin neuralmemoryErwartete Ausgabe:
Plugin: neuralmemory
Status: RUNNING
Uptime: 42s
Tools: 6
Init Time: 12.4sExit-Code muss 0 sein.
Verifizierungsschritt 4: Lange Initialisierung simulieren
Um die Timeout-Grenze zu testen, setzen Sie vorübergehend einen kürzeren Timeout und verifizieren Sie, dass der Fehler an der erwarteten Grenze auftritt:
openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 5000
openclaw start 2>&1 | grep -E "(timeout|5000ms)"Erwartete Ausgabe (sollte Timeout bei 5s zeigen, nicht bei 30s):
[ERROR] MCP timeout: initialize (5000ms)Stellen Sie den korrekten Timeout nach der Verifizierung wieder her:
openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 90000⚠️ Häufige Fehler
Fehler 1: timeout mit initTimeoutMs verwechseln
Der timeout-Konfigurationsparameter steuert nur Timeouts für Anfragen nach der Initialisierung, nicht den Handshake-Timeout.
Falsche Annahme:
"config": { "timeout": 120000 } // Beeinflusst NICHT MCP-InitKorrekte Konfiguration:
"config": {
"timeout": 120000, // MCP-Anfrage-Timeout (nach Init)
"initTimeoutMs": 120000 // MCP-Handshake-Timeout (Init)
}Fehler 2: Syntaxfehler bei Umgebungsvariablen
Bei der Verwendung von Umgebungsvariablen achten Sie auf korrekte Groß-/Kleinschreibung und Typ:
Falsch:
export OPENCLAW_MCP_INIT_TIMEOUT=90000 # Falsch: fehlendes "Ms"-SuffixRichtig:
export OPENCLAW_MCP_INIT_TIMEOUT_MS=90000Fehler 3: Timeout-Wert außerhalb der Grenzen
Die Implementierung erzwingt Mindest- und Höchstgrenzen. Werte außerhalb des Bereichs werden geklammert:
| Konfigurierter Wert | Effektiver Wert | Grund |
|---|---|---|
500 | 5000 | Minimum erzwungen: 5000ms |
600000 | 300000 | Maximum erzwungen: 300000ms |
NaN | 30000 | Standard-Fallback |
Fehler 4: Priorität von Konfigurationsdatei vs. Laufzeit-Override
Wenn mehrere Timeout-Quellen vorhanden sind, gilt folgende Prioritätsreihenfolge:
- Plugin-spezifische Konfiguration:
plugins.entries.<id>.config.initTimeoutMs - Umgebungsvariable:
OPENCLAW_MCP_INIT_TIMEOUT_MS - Globale Konfiguration:
plugins.mcpInitTimeoutMs - Hartcodierter Standard:
30000
Fehler 5: Docker-Container-Timeouts
Stellen Sie bei der Ausführung in Docker sicher, dass der Container ausreichende Ressourcen für das Laden von ML-Modellen hat:
# docker-compose.yml
services:
openclaw:
deploy:
resources:
limits:
memory: 4G # NeuralMemory benötigt ausreichend Speicher für sentence-transformersUnzureichender Speicher führt zu längeren Ladezeiten und verschärft Timeout-Probleme.
Fehler 6: Neural Memory Model Caching
Der erfolgreiche Wiederholungsversuch erfolgt, weil das Modell nach dem ersten Laden gecacht wird. Um konsistenten Erfolg beim ersten Versuch in der Produktion sicherzustellen:
- Wärmen Sie das Plugin während der Bereitstellung vor:
openclaw plugin warmup neuralmemory - Verwenden Sie
initTimeoutMs: 120000für die Erstbereitstellung - Reduzieren Sie auf
initTimeoutMs: 30000, nachdem der Cache gefüllt ist
🔗 Zugehörige Fehler
Direkt zugehörig
MCP timeout: initialize (30000ms)
Primärer Fehler. Hartcodierter Timeout während MCP-Handshake überschritten.MCP timeout: request (timeoutMs)
Timeout nach der Initialisierung. Gesteuert durchplugins.entries.<id>.config.timeout.plugin service failed (neuralmemory-mcp)
Plugin-Dienst markiert Plugin als fehlgeschlagen nach MCP-Init-Timeout.
Historisch zugehörige Probleme
- Issue #412: Plugin-Startup-Timeout wird nicht an MCP-Client weitergegeben
Feature-Request zur Weitergabe vonstartupTimeoutMsan MCP-Client-Schicht. Geschlossen als Duplikat dieses Problems. - Issue #387: NeuralMemory schlägt bei Kaltstart fehl
Dokumentiert den 30s hartcodierten Timeout als Ursache. Workaround dokumentiert. - Issue #156: MCP-Client sollte konfigurierbare Timeouts pro Plugin unterstützen
Ursprüngliche Architekturdiskussion für Timeout-Konfiguration.
Externe Abhängigkeiten
- NeuralMemory GitHub: Issue #18
Plugin-seitige Verfolgung zur Optimierung der Modellladezeit, um in das 30s-Fenster zu passen. - @modelcontextprotocol/sdk: Timeout-Handling
Upstream-SDK exponiert keine Timeout-Konfiguration; wird in der Wrapper-Schicht behandelt.