feat: apply auto-refresh setting live + diff-aware list-changed indicator (#1444)#1445
feat: apply auto-refresh setting live + diff-aware list-changed indicator (#1444)#1445cliffhall wants to merge 9 commits into
Conversation
…ator (#1444) Two follow-ups to #1402. 1. Live setting application — InspectorClient gains setServerSettings() (on the protocol + fake); App pushes edited settings onto the live client when the Server Settings modal closes for the connected server, so toggling auto-refresh-on-list-changed takes effect without a reconnect (the managed state reads getServerSettings() at notification time). 2. Diff-aware indicator — in flag-only mode, on */list_changed the managed Tools/Prompts/Resources state now fetches the new list and compares it to the current one, lighting the indicator only when it actually differs. The displayed list stays put until the user pulls via Refresh (pull-on-demand preserved). Servers like everything re-send identical lists on list_changed; this suppresses the false-positive indicator. Refactors each manager's refresh into fetch + apply helpers shared with the peek path. Trade-off: item 2 reintroduces a fetch on */list_changed (to compare), but unlike the pre-#1402 behavior it never replaces the displayed list under the user — only the indicator reflects a real change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@claude review |
|
Claude finished @cliffhall's task in 1m 47s —— View job Code Review — PR #1445
Solid, well-scoped follow-up. The A few observations, all minor / non-blocking: 1.
|
#1444 review) peekForChange now sets the list-changed flag to whether the server list differs from the displayed one (setListChanged(differs)) instead of only ever setting it true. So a later notification that reverts the server back to the displayed list clears the indicator — nothing left to pull. Adds a clear-on-revert regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — good review. Addressed #1; reasoning on the rest below. 1. peek only lit, never cleared — fixed ( 2. 3. No in-flight guard on overlapping peeks — agreed it's redundant-but-correct; leaving it unless bursts are observed in practice. 4. Resource templates not in the comparison — confirmed pre-existing and a conscious omission; the Resources indicator tracks 5. Triplication — agreed; the three managers are now near-identical and a generic base parameterized by list/capabilityKey/changeEvent would be the right consolidation, but that's a broader refactor than this PR. Noting it as a standing item.
@claude review |
|
Claude finished @cliffhall's task in 54s —— View job Re-review — PR #1445 (after
|
… base (#1444) The four per-primitive list managers (tools, prompts, resources, resource templates) were near-identical copies of the same fetch/apply/peek/connect/ disconnect/list_changed machinery, a standing drift risk. Extract a generic ManagedListState<T, M> base parameterized by a config (change event, list_changed notification, capability key, page fetcher, and whether the list drives an indicator). Each manager is now a ~40-line subclass: its config plus a typed getter alias. Public contracts are unchanged — same per-manager *Change / listChangedChange events and get*/refresh/getListChanged/clearListChanged methods — so the hooks and all existing tests pass untouched. Templates opt out of the indicator (supportsIndicator: false): a list_changed auto-refreshes only when the server opts in, else does nothing. Net -245 lines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed Extracted a generic One small unavoidable cast: PR description updated to cover all three parts. @claude review |
|
Claude finished @cliffhall's task in 1m 23s —— View job Re-review — PR #1445 (after
|
A rapid burst of */list_changed notifications fired one concurrent paginated fetch per notification. Guard peekForChange with an in-flight flag: while a peek is fetching, a new notification queues a single re-run instead of launching a concurrent fetch, and the running peek loops once more on completion to capture the latest server state. Collapses a burst into sequential peeks. Adds a coalescing test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Implemented the remaining deferred nit (#3, in-flight peek guard) in
Added a regression test: hang the first peek's fetch, fire a 3-notification burst, assert only one fetch starts while hung and exactly one coalesced re-run after release (2 total, not 3).
@claude review |
|
Claude finished @cliffhall's task in 59s —— View job Re-review — PR #1445 (after
|
The in-flight guard only coalesced fetches that actually overlapped — when a server (e.g. everything) emits a burst of */list_changed spaced just far enough apart that each list call returns before the next notification, we still fired one list call per notification. Add a real debounce in the ManagedListState base: a notification (re)starts a timer and the refresh/peek runs once the burst settles, collapsing N notifications into one fetch. The in-flight coalescing guard stays as a second line of defense for a post-debounce notification landing during a slow paginated fetch. The delay is injectable (DEFAULT_LIST_CHANGED_DEBOUNCE_MS = 250; subclasses take an optional constructor arg) so tests run with 0 for fast, determinist ic behavior. Timer is cleared on disconnect, unsubscribe, and destroy. Adds debounce + in-flight regression tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed Added a real debounce in the Tests: debounce-burst-into-one-fetch + in-flight-coalescing regression tests. @claude review |
|
Claude finished @cliffhall's task in 1m 29s —— View job Re-review — PR #1445 (after
|
…1444 review) The overlap guard was only on the peek path; a burst-during-a-slow auto- refresh could let a slower older fetch clobber a newer list (applyItems is last-write-wins). Lift the guard from peekForChange up to runListChanged so it wraps both the auto-refresh and peek actions: while one is fetching, a new post-debounce notification queues a single re-run. Adds an auto-refresh coalescing test alongside the peek one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — addressed observation #1 in #1 (auto-refresh path had no in-flight guard) — fixed. Lifted the overlap guard out of #2 (single-notification latency, ~250ms) — intentional and imperceptible for an indicator; leaving as-is. #3 (templates arm a no-op debounce timer) — harmless (one cleared timer per settle), and gating it would complicate the base for no real benefit; leaving as-is.
@claude review |
|
Claude finished @cliffhall's task in 1m 33s —— View job Re-review — PR #1445 (after
|
… off (#1444) Per maintainer feedback: with auto-refresh off, a list_changed should not issue any list call. The diff-aware peek (fetch-to-compare) meant one tools/list went out per settled burst even with auto-refresh off, which the maintainer doesn't want. Revert flag-only mode to lighting the indicator blindly on a (debounced) list_changed — no network — and the user pulls the new list via Refresh. Trade-off (accepted): the indicator lights even when the server re-sent an identical list (the everything-server case), since we no longer fetch to compare. Keeps the debounce (one indicator light per burst), the auto-refresh path + its in-flight guard, the live setServerSettings, and the ManagedListState consolidation. Removes peekForChange and its tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed With auto-refresh off, a Kept: the debounce (one indicator light per burst), the auto-refresh path + its in-flight guard, live @claude review |
|
Claude finished @cliffhall's task in 1m 11s —— View job Re-review — PR #1445 (after
|
…ert (#1445 review) The diff-aware revert removed peekForChange's body but left its docstring, which orphaned onto destroy() and described fetch-to-compare/clear-on-revert behavior that no longer exists. Replace it with a one-line destroy() doc, and update clearListChanged's doc (peek -> blind light). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Good catch — scrubbed both stale docs in
Doc-only; @claude review |
|
Claude finished @cliffhall's task in 1m 14s —— View job Re-review — PR #1445 (after
|
…1445 review) Round-7's scrub missed four more peek/fetch-to-compare mentions in managedListState.ts (supportsIndicator, fetchAll, the debounce-default and onListChanged comments) plus six 'async peek' comments in the state tests. Update them all to the blind-indicator semantics. Doc/comment-only; no behavior change. grep -i peek over core/mcp/state now returns nothing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Finished the sweep in
Also scrubbed six "async peek" comments in the state tests. @claude review |
|
Claude finished @cliffhall's task in 1m 8s —— View job Re-review — PR #1445 (after
|
Summary
Closes #1444. Two follow-ups to #1402 (PR #1443), plus a consolidation refactor and burst handling.
1. Apply the Auto Refresh setting live (no reconnect)
InspectorClientgainssetServerSettings()(added toInspectorClientProtocol+ the fake). When the Server Settings modal closes for the currently connected server,Apppushes the edited settings onto the live client (alongside the existing live-rootsapply). Since the managed list state readsgetServerSettings()at notification time, toggling Auto Refresh on List Changed Notifications now takes effect on the nextlist_changedwithout a reconnect.2. List-changed indicator (auto-refresh off) — light without fetching
With auto-refresh off, a
*/list_changedlights the sidebar indicator with no list call — the displayed list is untouched and the user pulls the new one via Refresh. (This was initially built diff-aware — fetch + compare, light only on a real change — but a fetch-on-list_changedruns counter to "auto-refresh off = no automatic network," so it was reverted to the blind indicator. Accepted trade-off: the indicator lights even when the server re-sent an identical list.)3. Debounce + coalesce
list_changedburstsThe everything server emits a rapid burst of
*/list_changed; previously that produced one action per notification. The base now debounces (default 250ms, configurable): a notification restarts a timer and the action runs once the burst settles — collapsing N notifications into a single indicator light (off) or a single fetch (on). A second-line in-flight guard coalesces a post-debounce notification that lands during a slow auto-refresh. Timer cleared on disconnect / unsubscribe / destroy.4. Consolidate the managed-list state managers
The four per-primitive managers (
tools,prompts,resources,resource templates) were near-identical — extracted a genericManagedListState<T, M>base parameterized by a config (change-event name,list_changednotification, capability key, page fetcher,supportsIndicator,debounceMs). Each manager is now a ~45-line subclass. Public contracts unchanged, so the hooks and existing flow tests pass untouched. Templates opt out of the indicator.Testing
setServerSettingstoggle honored without reconnect; plus the preserved flow tests (pagination, capability-gate, connect/disconnect).getServerSettings()/setServerSettings()on the realInspectorClient.npm run validate(lint/build/format/typecheck + unit & integration coverage gate) andnpm run test:storybookgreen.🤖 Generated with Claude Code