feat(web): gate header tabs on advertised server capabilities (#1516)#1520
Conversation
Header navigation tabs now appear only when the connected server
advertises the corresponding server capability (from the MCP
`initialize` result `capabilities`), instead of being shown
unconditionally or proxied through list content.
Previously the Tools and Logs tabs were always shown when connected,
regardless of whether the server advertised `tools` / `logging`. The
content-gated tabs (Prompts/Resources/Tasks) only appeared correct
because an unsupported capability yields an empty list — but a server
advertising a capability with a currently-empty list would have its
tab wrongly hidden.
InspectorView.availableTabs now gates each server-capability tab on
the matching field of `initializeResult.capabilities` (already
threaded from App via the handshake):
Tools -> capabilities.tools
Logs -> capabilities.logging
Prompts -> capabilities.prompts
Resources -> capabilities.resources
Tasks -> capabilities.tasks
Apps -> capabilities.tools AND >=1 app tool (Apps is a filtered
view of tools, so it keeps its content check)
Client-side sections are never gated on server capabilities: Servers
and History are always available, and Network stays transport-gated
(hidden for stdio). Tabs recompute live when reconnecting to a server
with a different capability set.
Tests: reworked the content-gating component tests into
capability-gating tests (including the Tools-hidden regression, Logs
present/absent, capability-present-but-empty-list, History-always, and
a reconnect-recomputes test). Connected story/fixtures advertise a
full capability set so the Connected story still shows every tab.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Related to #1522 (fixes #1521). Both PRs touch
They should merge cleanly in either order; whichever lands second is worth a quick rebase check. |
|
@claude review |
|
Claude finished @cliffhall's task in 2m 58s —— View job Review: gate header tabs on advertised capabilities (#1520)
Solid, well-scoped change. The core move — gating each header tab on One substantive point worth discussing — Tasks gating granularity
A server that advertises Consider tightening to match the screen's actual data source and the affordance: const hasTasks = capabilities?.tasks?.requests?.tools?.call !== undefined;Not a blocker — it's a judgment call on whether you want the tab to mirror "server supports tasks at all" vs. "the inspector can create the tasks this screen shows." But given the affordance already uses the narrower path, the broader gate here is a slight inconsistency. Fix this → Smaller notes
Nice test restructuring overall — the regression (Tools hidden despite a non-empty list) and the capability-present-but-empty-list cases are both explicitly covered. |
|
Thanks for the thorough review — and good catch on the Tasks granularity nuance. On the Tasks gate: keeping the broad This is a deliberate spec decision rather than an oversight. Issue #1516 explicitly calls it out:
i.e. the tab gate (broad "server supports tasks") and the affordance gate (narrow "inspector can create task-augmented tool calls") are deliberately different concerns. It's also consistent with the core's own list-RPC gating: You're right that a receiver-only-tasks server ( Leaving the gate as-is per the issue spec. Everything else in the review (Apps content-check, dropped list-changed tests, reconnect-recompute coverage, empty- |
|
@claude review |
|
Claude finished @cliffhall's task in 1m 38s —— View job Re-review: gate header tabs on advertised capabilities (#1520)
LGTM. Clean, well-scoped, and the test restructuring is thorough. Re-reviewed the current branch state including the follow-up to my earlier Tasks note. Tasks granularity — resolved, and I verified the rationaleYour decision to keep the broad // core/mcp/state/managedRequestorTasksState.ts:134
if (!client.getCapabilities()?.tasks) { this.tasks = []; … return; }So the Tasks tab is visible under precisely the condition where the screen will actually issue its list RPC. Tightening the tab to Fresh pass — confirmations
Tests & stories
No blocking issues, no nits worth holding the PR for. Nice work. |
Manual smoke test — capability gating verified in the running web InspectorDrove the dev web Inspector (this branch) against two stdio composable test servers built from SetupTwo configs for
Verified each server's advertised capabilities over stdio before connecting:
Results
The same UI renders Tools/Logs for Screenshots1.
2. |


Summary
Fixes #1516. Header navigation tabs now appear only when the connected server advertises the corresponding server capability (from the MCP
initializeresultcapabilities), instead of being shown unconditionally or proxied through list content.The reported bug: the Tools and Logs tabs were always shown when connected, even against a server that doesn't advertise
tools/logging. The content-gated tabs (Prompts/Resources/Tasks) only appeared correct because an unsupported capability yields an empty list — but a server advertising a capability with a currently-empty list would have its tab wrongly hidden, and the intent (gate on capability) wasn't expressed.What changed
InspectorView.availableTabsnow gates each server-capability tab on the matching field ofinitializeResult.capabilities. The capability object already rides oninitializeResult(App builds it from the handshake and already passes it in), so no new prop/threading was needed — the connected header only renders wheninitializeResultis truthy, so it stays consistent.capabilities.toolscapabilities.loggingcapabilities.promptscapabilities.resourcescapabilities.taskscapabilities.toolsAND ≥1 app tool (Apps is a filtered view of tools, so it keeps its content check)Client-side features (Sampling/Elicitation/Roots, when added) are inspector-offered client capabilities and must not be gated here. Tabs recompute live when reconnecting to a server with a different capability set.
Testing
Connectedstory still renders every tab.npm run validate(web): format, lint, build, 2003 unit tests pass; 370 Storybook play-function tests pass. InspectorView per-file coverage above gate (lines 96.7%, branches 98.2%, functions 81.8%).Manual verification
Drove the web Inspector against three stdio test servers with distinct advertised capability sets:
Servers · Tools · History(Logs correctly absent)Servers · Prompts · Logs · History(Tools correctly absent — the reported bug)Servers · Tools · Prompts · Resources · Tasks · Logs · History(Tasks shown with zero tasks, proving capability-gating over content-gating)🤖 Generated with Claude Code