fix(state): gate list fetches on server capability (#1350) (#1395)

The four list-fetching state managers fired their list RPC against every
server on connect regardless of advertised capabilities, so a server that
doesn't implement a given primitive replied -32601 "Method not found",
surfacing in the console on every connect. Mirrors the `tasks`-capability
gate added for ManagedRequestorTasksState in #1349.

- managedToolsState        → gate on capabilities.tools
- managedPromptsState      → gate on capabilities.prompts
- managedResourcesState    → gate on capabilities.resources
- managedResourceTemplatesState → gate on capabilities.resources
  (the spec defines no separate resourceTemplates capability;
  resources/templates/list is part of the resources surface)

When the capability is absent each manager sets an empty list, dispatches
its change event, and returns — the right semantics for "this server
doesn't support X."

Tests: existing flow tests now construct their FakeInspectorClient with
the relevant capability so the live list path is still exercised; added a
"refresh skips listX when capability absent" and a "connect against an
X-less server doesn't fire listX" test per manager. Updated the
useManagedX peers and resourceSubscriptionsState (which drives a
ManagedResourcesState refresh) to advertise the capability too.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cliff Hall 2026-05-31 22:06:55 -04:00 • committed by GitHub
commit 9e494544e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 206 additions and 9 deletions

View file

@ -70,6 +70,16 @@ export class ManagedPromptsState extends TypedEventTarget<ManagedPromptsStateEve
if (!client || client.getStatus() !== "connected") {
return this.getPrompts();
}
// Gate on the server's `prompts` capability — calling prompts/list against
// a server that doesn't advertise it returns -32601 "Method not found",
// which then surfaces in the console for every connect against a
// prompts-less server. Empty list is the right semantics for "this server
// doesn't support prompts."
if (!client.getCapabilities()?.prompts) {
this.prompts = [];
this.dispatchTypedEvent("promptsChange", this.prompts);
return this.getPrompts();
}
const effectiveMetadata = metadata ?? this._metadata;
this.prompts = [];
let cursor: string | undefined;

View file

@ -76,6 +76,21 @@ export class ManagedResourceTemplatesState extends TypedEventTarget<ManagedResou
if (!client || client.getStatus() !== "connected") {
return this.getResourceTemplates();
}
// Gate on the server's `resources` capability — the MCP spec doesn't define
// a separate `resourceTemplates` capability; resources/templates/list is
// part of the resources surface. Calling it against a server that doesn't
// advertise resources returns -32601 "Method not found", which then
// surfaces in the console for every connect against a resources-less
// server. Empty list is the right semantics for "this server doesn't
// support resources."
if (!client.getCapabilities()?.resources) {
this.resourceTemplates = [];
this.dispatchTypedEvent(
"resourceTemplatesChange",
this.resourceTemplates,
);
return this.getResourceTemplates();
}
const effectiveMetadata = metadata ?? this._metadata;
this.resourceTemplates = [];
let cursor: string | undefined;

View file

@ -73,6 +73,16 @@ export class ManagedResourcesState extends TypedEventTarget<ManagedResourcesStat
if (!client || client.getStatus() !== "connected") {
return this.getResources();
}
// Gate on the server's `resources` capability — calling resources/list
// against a server that doesn't advertise it returns -32601 "Method not
// found", which then surfaces in the console for every connect against a
// resources-less server. Empty list is the right semantics for "this
// server doesn't support resources."
if (!client.getCapabilities()?.resources) {
this.resources = [];
this.dispatchTypedEvent("resourcesChange", this.resources);
return this.getResources();
}
const effectiveMetadata = metadata ?? this._metadata;
this.resources = [];
let cursor: string | undefined;

View file

@ -67,6 +67,16 @@ export class ManagedToolsState extends TypedEventTarget<ManagedToolsStateEventMa
if (!client || client.getStatus() !== "connected") {
return this.getTools();
}
// Gate on the server's `tools` capability — calling tools/list against a
// server that doesn't advertise it returns -32601 "Method not found",
// which then surfaces in the console for every connect against a
// tools-less server. Empty list is the right semantics for "this server
// doesn't support tools."
if (!client.getCapabilities()?.tools) {
this.tools = [];
this.dispatchTypedEvent("toolsChange", this.tools);
return this.getTools();
}
const effectiveMetadata = metadata ?? this._metadata;
this.tools = [];
let cursor: string | undefined;