From 4cb5a97512b1d91b0249110ef83b51024282ee77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Thu, 9 Apr 2026 01:11:50 +0100 Subject: [PATCH] =?UTF-8?q?perf(cli):=20instant=20MCP=20startup=20?= =?UTF-8?q?=E2=80=94=20WS=20connects=20in=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move startClients() to run after server.connect(), not before. MCP server is available to Claude Code in <0.5s instead of ~30s. Tool handlers gracefully return errors until WS is ready. Push event wiring happens in background callback. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/cli/package.json | 2 +- apps/cli/src/mcp/server.ts | 65 ++++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 172f4eb..aaf5ce9 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "claudemesh-cli", - "version": "0.9.0", + "version": "0.9.1", "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.", "keywords": [ "claude-code", diff --git a/apps/cli/src/mcp/server.ts b/apps/cli/src/mcp/server.ts index 3ac06fc..871d2f1 100644 --- a/apps/cli/src/mcp/server.ts +++ b/apps/cli/src/mcp/server.ts @@ -1733,16 +1733,25 @@ Your message mode is "${messageMode}". } }); - // Start broker clients for every joined mesh BEFORE MCP connects. - await startClients(config); - + // Start MCP transport IMMEDIATELY so Claude Code discovers tools/prompts/resources + // without waiting for WS connections. Tool handlers gracefully return errors when + // not connected. WS connects in background; push wiring happens once ready. const transport = new StdioServerTransport(); await server.connect(transport); - // Wire WSS pushes → MCP channel notifications. Each inbound push on - // any mesh's broker connection becomes a - // system reminder injected into Claude Code's context. - for (const client of allClients()) { + // Connect to broker WS in background — don't block MCP startup. + startClients(config).then(() => { + wirePushHandlers(); + }).catch(() => { + // Connect failed — clients are in reconnecting state, push wiring still needed + wirePushHandlers(); + }); + + async function wirePushHandlers() { + // Wire WSS pushes → MCP channel notifications. Each inbound push on + // any mesh's broker connection becomes a + // system reminder injected into Claude Code's context. + for (const client of allClients()) { // Event-driven push: WS onPush fires immediately when a message arrives. // Claude Code's setNotificationHandler → enqueue → React useEffect pipeline // processes notifications instantly (no polling needed on Claude's side). @@ -1887,28 +1896,28 @@ Your message mode is "${messageMode}". }); } catch { /* best effort */ } }); - } + } - // Welcome notification: give Claude immediate context on connect. - // Triggers Claude to call mesh_info/list_peers without user input. - setTimeout(async () => { - const client = allClients()[0]; - if (!client || client.status !== "open") return; - try { - const peers = await client.listPeers(); - const peerNames = peers - .filter(p => p.displayName !== myName) - .map(p => p.displayName) - .join(", ") || "none"; - await server.notification({ - method: "notifications/claude/channel", - params: { - content: `[system] Connected as ${myName} to mesh ${client.meshSlug}. ${peers.length} peer(s) online: ${peerNames}. Call mesh_info for full details or set_summary to announce yourself.`, - meta: { kind: "welcome", mesh_slug: client.meshSlug }, - }, - }); - } catch { /* best effort */ } - }, 3_000); // 3s delay: let WS connect + hello_ack complete first + // Welcome notification: give Claude immediate context on connect. + // No delay needed — WS is already connected at this point. + const welcomeClient = allClients()[0]; + if (welcomeClient && welcomeClient.status === "open") { + try { + const peers = await welcomeClient.listPeers(); + const peerNames = peers + .filter(p => p.displayName !== myName) + .map(p => p.displayName) + .join(", ") || "none"; + await server.notification({ + method: "notifications/claude/channel", + params: { + content: `[system] Connected as ${myName} to mesh ${welcomeClient.meshSlug}. ${peers.length} peer(s) online: ${peerNames}. Call mesh_info for full details or set_summary to announce yourself.`, + meta: { kind: "welcome", mesh_slug: welcomeClient.meshSlug }, + }, + }); + } catch { /* best effort */ } + } + } // end wirePushHandlers // Event loop keepalive: Node.js stdout to a pipe is buffered. Without // periodic event loop activity, stdout.write() from WS callbacks may not