Skip to content

perf(boot): parallelize independent boot I/O + drop redundant MCP probes (#130) - #265

Merged
cboettig merged 1 commit into
mainfrom
perf/boot-parallel-io-130
Jun 23, 2026
Merged

perf(boot): parallelize independent boot I/O + drop redundant MCP probes (#130)#265
cboettig merged 1 commit into
mainfrom
perf/boot-parallel-io-130

Conversation

@cboettig

Copy link
Copy Markdown
Member

Addresses Findings 1–3 of #130. Finding 4 (render the welcome screen early via an ui.setAgent() setter) is deferred to a focused follow-up — it's a browser-bound UX refactor of ChatUI.init() that needs visual verification, and I didn't want to bundle a boot-path regression risk with these safe wins.

Changes

1. Drop the listTools() health check in MCPClient.ensureConnected().
It ran a full round trip before every callTool / listPrompts / getPrompt. Now it trusts the connected flag; callTool's existing reconnect-and-retry branch is the real safety net for the only frequent, long-lived op, and boot-time prompt reads happen right after a fresh connect (never stale).

2. listMcpToolsWithRetry uses the connect cache.
Was await mcp.listTools() (ensureConnected probe + a second list round trip) even though connect() already cached the tools. Now await mcp.connect(); return mcp.getTools() — dedupes with the eager boot connect, no extra round trip.

3. Parallelize independent boot I/O.
mcp.connect() and the system-prompt.md fetch now fire at the top of main(), and the STAC catalog walk + map-style load run via Promise.all instead of two serial awaits. The MCP cold-start overlaps catalog + map + prompt I/O (previously connect fired only after both were awaited, so it never overlapped).

Net: removes per-call probe round trips and collapses several serialized boot I/Os into one overlapping wait.

Tests

test/mcp-client.test.js updated for the new no-probe contract: ensureConnected on a live connection makes no client and no extra listTools call; staleness now surfaces via callTool / explicit reconnect(); the two onReconnect specs drive reconnect() directly. Full suite green (334).

Verification

app/main.js is browser-bootstrap (0% harness). Per the issue's validation plan, verify with a cold-load network waterfall on padus/template (disable cache, throttle): confirm total time-to-ready is unchanged-or-better and the MCP connect now overlaps the catalog/map fetches. The MCP-client change is covered by unit tests.

Deferred

Finding 4 (early welcome render): split ChatUI.init() into agent-independent chrome + an setAgent() that wires callbacks/model-selector and enables the input. Worth its own PR with jsdom tests + a browser pass, since it changes boot ordering of the UI.

…bes (#130)

Findings 1-3 from #130 (Finding 4, early ChatUI render, deferred to a
browser-verified follow-up):

1. Drop the listTools() health check in MCPClient.ensureConnected() — it
   added a full round trip to every callTool/listPrompts/getPrompt. Trust
   the connected flag; callTool's reconnect-and-retry is the real safety net
   for the only frequent long-lived op, and boot-time prompt reads run right
   after a fresh connect.
2. listMcpToolsWithRetry now awaits connect() + getTools() (cache) instead
   of a second listTools() round trip — connect already cached the tools.
3. Fire mcp.connect() and the system-prompt fetch at the top of main(), and
   Promise.all the catalog walk + map-style load instead of awaiting them
   serially. The MCP cold-start now overlaps catalog+map+prompt I/O.

Tests: mcp-client ensureConnected/onReconnect specs updated for the new
no-probe contract (staleness now surfaces via callTool / explicit reconnect).
Full suite green (334).

main.js boot is browser-bootstrap (0% harness) — verify via the cold-load
network waterfall on padus per the issue's validation plan.
@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 51.31% 3746 / 7300
🔵 Statements 51.29% 3746 / 7303
🔵 Functions 59% 131 / 222
🔵 Branches 82.63% 752 / 910
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
app/main.js 0% 0% 0% 0% 1-468
app/mcp-client.js 99.14% 84.48% 100% 99.14% 197-198
Generated in workflow #113 for commit 24020ac by the Vitest Coverage Report Action

@cboettig
cboettig merged commit 6469f1a into main Jun 23, 2026
1 check passed
@cboettig
cboettig deleted the perf/boot-parallel-io-130 branch June 23, 2026 23:31
cboettig added a commit that referenced this pull request Jun 29, 2026
_doConnect() set this.connected=true before populating this.tools via
listTools(). Since #265 (v3.16.0) the boot retry path trusts the cache
(await mcp.connect(); mcp.getTools()) instead of a fresh listTools()
round trip. When the eager boot connect had flipped the flag but its
listTools() hadn't resolved, listMcpToolsWithRetry's connect() short-
circuited and getTools() returned []: zero remote tools registered, no
exception, so the hardcoded-query fallback never fired and there is no
onReconnect recovery on initial connect. The session ran local-only for
its whole life — register_hex_tiles and all 5 other MCP tools missing.

Proxy logs: tools_count=18 (=18 local + 0 MCP) first appears 6/28 as
apps adopted v3.16/v3.17; global-30x30 today is intermittent (24 healthy
/ 18 degraded), the signature of a race. qwen3 sessions on padus +
global-30x30 correctly reported 'register_hex_tiles isn't available'.

Fix: cache tools before flipping connected, so connected===true always
implies tools ready. Defense-in-depth: listMcpToolsWithRetry treats an
empty list as a failure (retry + query fallback). Regression test locks
the invariant that connect() never reports connected with an empty cache.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant