fix(state): dispatch connect event after capabilities are populated#1405
Conversation
The list-state managers (tools/prompts/resources/templates) refresh on the "connect" event and, since #1395, gate their list RPC on getCapabilities(). But connect() dispatched "connect" before fetchServerInfo() populated this.capabilities, and refresh() runs synchronously up to its first await — so the capability gate executed during the synchronous dispatch, read undefined capabilities (reset to undefined on every disconnect), tripped, and wiped every list to empty on connect. Tools, prompts, resources, and resource templates all vanished against any server. Move the "connect" dispatch to after the awaited fetchServerInfo() so capabilities are in place before any manager refreshes. The unit tests missed this because their FakeInspectorClient has capabilities populated from construction; added an integration regression test wiring a real ManagedToolsState on connect and asserting the tool list is non-empty (fails without the fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude review |
|
Claude finished @cliffhall's task in 1m 55s —— View job Claude PR Review
SummaryThe fix is correct, minimal, and well-targeted. Diagnosis lines up with the code: the gate added in #1395 reads Correctness of the fix
Test
Minor observations (non-blocking)
VerdictApprove in spirit (the action cannot literally approve). The fix is correct, the regression test catches the bug, and the inline comment will prevent regressions. |
Address the two non-blocking suggestions from the PR review: - Replace the 100ms settle sleep with waitForEvent() on the managers' change events, so the test is deterministic against slow CI instead of racing a fixed timeout. - Assert a second affected primitive (ManagedResourcesState alongside ManagedToolsState). The fix is a single shared line, but covering two distinct capabilities guards against a future change gating one primitive differently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the review! Both non-blocking test suggestions were good — addressed in 1ba24b5:
Verified: integration suite still 176 passed, I left observation #3 (the "why" comment tying the ordering to #1395 and the synchronous gate) as-is since it was called out as correct. |
|
@claude review |
|
Claude finished @cliffhall's task in 1m 41s —— View job Claude PR Review (round 2)
SummaryBoth follow-ups landed cleanly. The test is now deterministic (no fixed sleep) and covers two distinct capabilities through the same shared fix. The production fix in Test changes (
|
Closes #1406
Problem
Tools, prompts, resources, and resource templates were disappearing for some servers on the v2 web Inspector — nothing showed under any primitive.
Root cause
Regression from #1395 ("gate list fetches on server capability"). That PR added a capability gate to each list-state manager's
refresh():The gate is correct, but it exposed an ordering race in
InspectorClient.connect():The managed states refresh on the
"connect"event.refresh()runs synchronously up to its firstawait(the reallistToolscall), so the capability gate executes during the synchronousdispatch("connect")— beforefetchServerInfo()populatesthis.capabilities(which is reset toundefinedon every disconnect). The gate readsundefined, trips, and wipes the list to empty. All four primitives are gated identically, so all of them vanish.Before #1395
refresh()didn't read capabilities, so the ordering never mattered.Fix
Dispatch
"connect"after the awaitedfetchServerInfo(), so capabilities are populated before any manager refreshes.Tests
The unit tests missed this because their
FakeInspectorClienthas capabilities populated from construction. Added an integration regression test that wires a realManagedToolsStateon connect and asserts the tool list is non-empty — it fails without the fix, passes with it.npm run validate: pass🤖 Generated with Claude Code