Skip to content

[Performance]: Session switch blocks on synchronous MCP tool materialization (getCatalog + createRuntime) - 5-8s delay per switch #89408

Description

@q1387154-spec

Problem

Switching between sessions in the OpenClaw TUI/agent runtime takes 5-8 seconds due to synchronous MCP tool materialization in createRuntime. Every session switch spawns a new stdio MCP subprocess, fetches the full tool catalog, and registers all tools before the session becomes usable.

Environment

  • OpenClaw 2026.5.28 (e932160)
  • Windows 11 x64, Node v24.16.0
  • MCP: vibe-trading stdio server (70+ tools)

Root Cause (Source Analysis)

The session switch path in sessions-htxRTdBr.js:

switchSession(path)
-> teardownCurrent() (dispose old session, serial)
-> createRuntime() (rebuilds entire runtime)
-> materializeMcpToolsForAgent()
-> getOrCreateSessionMcpRuntime()
-> getCatalog() (SYNCHRONOUS blocking call)
-> spawn stdio child process (~1-3s)
-> connectWithTimeout(30s) (~0.5-1s)
-> listAllTools() (~1-2s, 70+ tool definitions)
-> register 70+ tools to registry (~0.5-1s)
-> finishSessionReplacement()

Key source evidence:

  1. getCatalog() is synchronous (agent-bundle-mcp-runtime--qlnxWNy.js:625-695): iterates all MCP servers, spawns stdio processes, connects, and lists tools - all awaited in series. Default connection timeout: 30s per server.

  2. Tool materialization blocks createRuntime (agent-bundle-mcp-materialize-DAJQADAM.js:81): catalog = await params.runtime.getCatalog() blocks until all tools fetched.

  3. No cross-session connection pooling (agent-bundle-mcp-runtime--qlnxWNy.js:789-830): runtimesBySessionId.get(params.sessionId) - each session gets its own key, so switching sessions always creates a new runtime with a new Python process.

  4. sweepIdleRuntimes() runs on every getOrCreate call - unnecessary overhead.

Performance Breakdown (Estimated)

Step Time %
Spawn stdio child process (Python startup) 1-3s 30%
stdio connection handshake 0.5-1s 10%
listAllTools (70+ tool JSON transfer+parse) 1-2s 25%
Register tools to registry 0.5-1s 15%
Transcript load + context rebuild 0.5-1s 15%
Total 3.5-8s

Suggested Fixes

P0: Cross-session MCP connection pool

Share a single stdio server connection across sessions instead of spawning per-session. The tool catalog for a given server config is identical across all sessions. Key change: use runtimesByConfigHash instead of runtimesBySessionId. Expected: 5-8s to less than 1s.

P1: Lazy tool materialization

Move getCatalog() out of the synchronous createRuntime path. Start catalog fetch in background, allow session to become usable with partial tool list. Expected: 5-8s to 1-2s.

P2: Tool definition cache

Cache resolved tool definitions to disk per server config hash. Avoid re-fetching identical 70+ tool definitions on every cold start. Expected: cold start 3-5s to 1-2s.

Related Issues

Reproduction

  1. Configure an MCP stdio server with many tools (e.g., vibe-trading with 70+ tools)
  2. Start OpenClaw, enter a session
  3. Switch to a different session
  4. Observe 5-8 second delay before the new session is ready

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions