fix(server): count a live session as connection evidence in initialize - #1192
Conversation
hasAnyProjectConnectedBefore alone decided whether the MCP `initialize` instructions led with the first-install steps. That durable memory can be empty or stale for a project the plugin wired without writing .reticle.json, so an agent attaching while a browser session was already live was told "no app has ever connected" and pointed at init/restart/load instead of getting straight to work. Both createMcpServer call sites (start(), startDaemon()) now OR in bridge.sessions.count() > 0, so a session connected right now outweighs empty or stale durable memory. Closes reticlehq#1138. AI-assisted: the added test was run against the unfixed tree and confirmed to fail before this change, per CONTRIBUTING.md's AI-assisted contributions policy. Signed-off-by: drakeo338 <paranoyouz@gmail.com>
|
Thanks for your first pull request to Reticle! A maintainer aims to review within two days. Before then, |
|
| profile, | ||
| hasAnyProjectConnectedBefore(reticleStateHome(), port, projectIdsAt(process.cwd())), | ||
| // See the sibling call in `start`: a live session outweighs empty/stale durable memory (#1138). | ||
| bridge.sessions.count() > 0 || |
There was a problem hiding this comment.
Other projects count as connected
A shared daemon can have a live browser session for project A while project B has never been wired. This count includes A’s session, so an agent attaching for B is treated as previously connected and does not receive B’s first-install instructions. The live-session check needs to consider the project or projects represented by the handshake.
| // stale for a project the plugin wired without writing `.reticle.json` (see connection-memory.ts's | ||
| // KNOWN LIMIT). Without this OR, that project's `initialize` led with the first-install steps | ||
| // while a real session was already connected — see #1138. | ||
| bridge.sessions.count() > 0 || |
There was a problem hiding this comment.
Standalone handshake misses later sessions
The standalone stdio path reads the session count once while constructing the MCP server, before the client’s initialize. If a browser connects afterward and durable memory is empty, the instructions still say no app has connected, even though that session is live by the time the agent initializes.
| port, | ||
| endpoint.data, | ||
| rpc(1, 'initialize', { | ||
| protocolVersion: '2024-11-05', |
There was a problem hiding this comment.
Handshake test inlines wire strings
The new test inlines the JSON-RPC version, initialize method, and protocol version in its request. The repository’s “No free strings” directive requires domain and wire strings to be named constants. Please use contract constants or named fixture constants before merging.
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
What & why
Closes #1138.
Both
initializecall sites used onlyhasAnyProjectConnectedBefore, durable memory that can be empty or stale for a project wired without.reticle.json. An agent attaching with a live browser session was still told "no app has ever connected" and pointed atinit/restart. The fix ORs inbridge.sessions.count() > 0, so a live session outweighs stale durable memory.I released my claim on the issue earlier while unsure a PR was wanted; since it is still open, here is the fix in case it helps. Happy to close it if you'd rather handle it differently.
How it was verified
New test in
server/src/index.mcp-live-session.test.tsfails on the unfixed tree and passes with the fix. Per CONTRIBUTING.md's AI-assisted policy: checked.Gates run
pnpm lint && pnpm typecheck && pnpm test:unit(~2 min — always)pnpm test:e2e(~8 min) — touched the tool surface,core, an observer, or telemetrypnpm gate:install(~15 min) — touchedreticle init,vite-plugin,next, orbabel-pluginpnpm test:e2e:desktop(~3 min) — touchedadapters/realm/electron,adapters/realm/tauri, or desktop captureChecklist
git commit -s) — CI's DCO check fails the PR without it. Already pushed?git rebase --signoff origin/main && git push --force-with-leaseany, no free strings (wire strings live in@reticlehq/core), no non-null!console.logor internal tracking codes left in the diff.changes/(never editCHANGELOG.md— that file is assembled at release time, and editing it is what makes PRs conflict; format in.changes/README.md)docs/telemetry.md) and are covered by a test — not applicable, no auth/redaction/trust-boundary change here🤖 Generated with Claude Code