perf(cli): instant MCP startup — WS connects in background
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

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) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-09 01:11:50 +01:00
parent c080bc517f
commit 4cb5a97512
2 changed files with 38 additions and 29 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "claudemesh-cli", "name": "claudemesh-cli",
"version": "0.9.0", "version": "0.9.1",
"description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.", "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
"keywords": [ "keywords": [
"claude-code", "claude-code",

View File

@@ -1733,12 +1733,21 @@ Your message mode is "${messageMode}".
} }
}); });
// Start broker clients for every joined mesh BEFORE MCP connects. // Start MCP transport IMMEDIATELY so Claude Code discovers tools/prompts/resources
await startClients(config); // 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(); const transport = new StdioServerTransport();
await server.connect(transport); await server.connect(transport);
// 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 // Wire WSS pushes → MCP channel notifications. Each inbound push on
// any mesh's broker connection becomes a <channel source="claudemesh"> // any mesh's broker connection becomes a <channel source="claudemesh">
// system reminder injected into Claude Code's context. // system reminder injected into Claude Code's context.
@@ -1890,12 +1899,11 @@ Your message mode is "${messageMode}".
} }
// Welcome notification: give Claude immediate context on connect. // Welcome notification: give Claude immediate context on connect.
// Triggers Claude to call mesh_info/list_peers without user input. // No delay needed — WS is already connected at this point.
setTimeout(async () => { const welcomeClient = allClients()[0];
const client = allClients()[0]; if (welcomeClient && welcomeClient.status === "open") {
if (!client || client.status !== "open") return;
try { try {
const peers = await client.listPeers(); const peers = await welcomeClient.listPeers();
const peerNames = peers const peerNames = peers
.filter(p => p.displayName !== myName) .filter(p => p.displayName !== myName)
.map(p => p.displayName) .map(p => p.displayName)
@@ -1903,12 +1911,13 @@ Your message mode is "${messageMode}".
await server.notification({ await server.notification({
method: "notifications/claude/channel", method: "notifications/claude/channel",
params: { 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.`, 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: client.meshSlug }, meta: { kind: "welcome", mesh_slug: welcomeClient.meshSlug },
}, },
}); });
} catch { /* best effort */ } } catch { /* best effort */ }
}, 3_000); // 3s delay: let WS connect + hello_ack complete first }
} // end wirePushHandlers
// Event loop keepalive: Node.js stdout to a pipe is buffered. Without // Event loop keepalive: Node.js stdout to a pipe is buffered. Without
// periodic event loop activity, stdout.write() from WS callbacks may not // periodic event loop activity, stdout.write() from WS callbacks may not