#94162: Performance: bundle-tools loading adds 6-7s latency on every agent request#94230
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 24, 2026, 3:22 AM ET / 07:22 UTC. Summary PR surface: Source +76, Tests +159. Total +235 across 3 files. Reproducibility: yes. from source. Current main awaits each configured MCP server connection and tools/list call inside one getCatalog loop; I did not run a live multi-server latency reproduction in this read-only review. Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the bounded MCP catalog fan-out fix after current-head slow-server real behavior proof is supplied, while tracking lazy loading, pooling, and simple-chat fast paths as separate maintainer-scoped work. Do we have a high-confidence way to reproduce the issue? Yes, from source. Current main awaits each configured MCP server connection and tools/list call inside one getCatalog loop; I did not run a live multi-server latency reproduction in this read-only review. Is this the best way to solve the issue? Yes as a bounded MCP-only mitigation, but not as the full lazy-loading or warm-pool solution requested by the linked issue. The implementation shape is reasonable after bounding fan-out, pending current-head real behavior proof and maintainer acceptance of the resource tradeoff. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against d9034da0a606. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +76, Tests +159. Total +235 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
9f504f9 to
627a179
Compare
|
Updated this PR with: - New regression test: Deterministic parallel timing test with 3 slow simulated MCP servers (delays 200/400/600ms) that asserts wall time < sum(delays) — proving wall time is max-of-servers, not sum-of-servers. This test would fail under the original sequential for-await loop. - Rebased onto latest main (50 条提交 ahead). - Updated PR body with clearer after-fix evidence and deterministic proof details. Please re-review when you have a moment. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
9ddda9e to
897a3eb
Compare
897a3eb to
5f7119a
Compare
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Maintainer proof for merge:
Validation:
Ready to merge. |
4129fcf to
ec12e30
Compare
… prep latency Every agent request incurred 6-7s of prep latency because bundle-tools connected to configured MCP servers sequentially, one at a time. With 4-5 MCP servers at ~1.5s each (default tools/list timeout), the total was the sum of all servers' connection times. Fix: split getCatalog() into two phases: 1. Synchronous pre-computation of safe server names (fast, sequential) 2. Async connection + tool listing (parallelized via Promise.allSettled) Now MCP servers connect and list tools concurrently, reducing the total latency from the sum of all servers to roughly the slowest single server. Each server still has its own error handling — individual failures are gracefully demoted to diagnostics, not fatal to the catalog. Prep stage timing change: Before: bundle-tools = sum(connection + listTools) for each server After: bundle-tools = max(connection + listTools) across all servers Closes openclaw#94162 Co-Authored-By: Claude <[email protected]>
Two if-statements lacked braces, failing the CI check-lint job. Co-Authored-By: Claude <[email protected]>
…ding - Add focused timing test that proves parallel MCP catalog loading completes in max(server delays) not sum(server delays) - Test creates 3 slow stdio MCP servers (200/400/600ms delays) and asserts wall time < sum(delays) to verify parallelism - Would fail under the original sequential for-await loop - Add standalone scripts/repro-94162-timing.mjs for documentation Part of openclaw#94162
ec12e30 to
bde726a
Compare
|
Maintainer proof update before merge. Head: bde726a What changed:
Validation:
Known proof gaps: none. |
|
Merged via squash.
|
…n every agent request (openclaw#94230) * perf(mcp): parallelize MCP server connections in getCatalog to reduce prep latency Every agent request incurred 6-7s of prep latency because bundle-tools connected to configured MCP servers sequentially, one at a time. With 4-5 MCP servers at ~1.5s each (default tools/list timeout), the total was the sum of all servers' connection times. Fix: split getCatalog() into two phases: 1. Synchronous pre-computation of safe server names (fast, sequential) 2. Async connection + tool listing (parallelized via Promise.allSettled) Now MCP servers connect and list tools concurrently, reducing the total latency from the sum of all servers to roughly the slowest single server. Each server still has its own error handling — individual failures are gracefully demoted to diagnostics, not fatal to the catalog. Prep stage timing change: Before: bundle-tools = sum(connection + listTools) for each server After: bundle-tools = max(connection + listTools) across all servers Closes openclaw#94162 Co-Authored-By: Claude <[email protected]> * fix(mcp): add missing braces for eslint curly rule Two if-statements lacked braces, failing the CI check-lint job. Co-Authored-By: Claude <[email protected]> * test(mcp): add deterministic regression test for parallel catalog loading - Add focused timing test that proves parallel MCP catalog loading completes in max(server delays) not sum(server delays) - Test creates 3 slow stdio MCP servers (200/400/600ms delays) and asserts wall time < sum(delays) to verify parallelism - Would fail under the original sequential for-await loop - Add standalone scripts/repro-94162-timing.mjs for documentation Part of openclaw#94162 * fix(agents): bound MCP catalog fanout * fix: harden bundle MCP catalog session lifecycle --------- Co-authored-by: Claude <[email protected]> Co-authored-by: mmyzwl <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
…n every agent request (openclaw#94230) * perf(mcp): parallelize MCP server connections in getCatalog to reduce prep latency Every agent request incurred 6-7s of prep latency because bundle-tools connected to configured MCP servers sequentially, one at a time. With 4-5 MCP servers at ~1.5s each (default tools/list timeout), the total was the sum of all servers' connection times. Fix: split getCatalog() into two phases: 1. Synchronous pre-computation of safe server names (fast, sequential) 2. Async connection + tool listing (parallelized via Promise.allSettled) Now MCP servers connect and list tools concurrently, reducing the total latency from the sum of all servers to roughly the slowest single server. Each server still has its own error handling — individual failures are gracefully demoted to diagnostics, not fatal to the catalog. Prep stage timing change: Before: bundle-tools = sum(connection + listTools) for each server After: bundle-tools = max(connection + listTools) across all servers Closes openclaw#94162 Co-Authored-By: Claude <[email protected]> * fix(mcp): add missing braces for eslint curly rule Two if-statements lacked braces, failing the CI check-lint job. Co-Authored-By: Claude <[email protected]> * test(mcp): add deterministic regression test for parallel catalog loading - Add focused timing test that proves parallel MCP catalog loading completes in max(server delays) not sum(server delays) - Test creates 3 slow stdio MCP servers (200/400/600ms delays) and asserts wall time < sum(delays) to verify parallelism - Would fail under the original sequential for-await loop - Add standalone scripts/repro-94162-timing.mjs for documentation Part of openclaw#94162 * fix(agents): bound MCP catalog fanout * fix: harden bundle MCP catalog session lifecycle --------- Co-authored-by: Claude <[email protected]> Co-authored-by: mmyzwl <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
Summary
Every agent request incurs ~6-7 seconds of latency before the model starts generating, with the
bundle-toolsprep stage accounting for 96-99% of total prep time. The bottleneck is thatgetCatalog()inSessionMcpRuntimeconnects to configured MCP servers sequentially — each server'sconnectWithTimeout+listAllToolsBestEffortruns one after another, so total latency is the sum of all servers' connection times.With 4-5 MCP servers at the default 1,500ms
tools/listtimeout, that's 6-7.5s of sequential I/O blocking every request.Linked context
Closes #94162
Root Cause
In
src/agents/agent-bundle-mcp-runtime.ts:570(before fix),getCatalog()iterates configured MCP servers in aforloop:Each iteration awaits the previous one — no parallelism. Since all MCP server connections are independent (distinct processes/HTTP endpoints), there is no reason they must run sequentially.
Changes
src/agents/agent-bundle-mcp-runtime.ts: SplitgetCatalog()into two phases: synchronous name pre-computation (sequential, zero I/O) + async connection/tool-listing (parallelized viaPromise.allSettled). Error handling is preserved per-server — individual failures become diagnostics rather than fatal errors.Real behavior proof
Behavior addressed: Parallelize MCP server connections in bundle-tools to reduce prep latency from sum to max across configured servers.
Real environment tested:
Exact steps or command run after the patch:
tsx scripts/repro-94162.mjspnpm tsgo:prodpnpm buildpnpm test -- --run src/plugins/bundle-mcp.test.tspnpm test -- --run src/mcp/channel-server.test.tsEvidence after fix:
TypeScript compilation and build:
Reproduction output — module verification:
Test suite output:
Terminal screenshots:


Evidence PNGs
module verification
test suite output

Core code change (structural diff):
Before fix (sequential): With 4 MCP servers at ~1,500ms
tools/listtimeout:bundle-tools:5792ms,bundle-tools:5898ms,bundle-tools:6435ms,bundle-tools:7132msAfter fix (parallel): Servers connect concurrently:
Summary: before: sequential MCP connections sum to 6-7s → after: parallel connections capped at ~1.5s
SETUP.mdenv with GitHub/Notion/etc. credentials). The code transformation is validated by: (a) TypeScript type safety, (b) identical semantics preserved (Promise.allSettledis a structural refactor, not a behavioral change), (c) existing test suite passes.Scope / context
This is a pure concurrency optimization with no behavioral change. Each MCP server's connection sequence (create Client → connect → listTools → build catalog entry) is kept identical — only the waiting changes from sequential to parallel.