feat(client): return empty lists when server lacks capability (#1386)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com>
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
This commit is contained in:
Peder Holdgaard Pedersen 2026-01-26 16:44:21 +01:00 • committed by GitHub
commit 00249ce86d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -253,6 +253,7 @@ export class Client<
private _experimental?: { tasks: ExperimentalClientTasks<RequestT, NotificationT, ResultT> };
private _listChangedDebounceTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();
private _pendingListChangedConfig?: ListChangedHandlers;
private _enforceStrictCapabilities: boolean;
/**
* Initializes this client with the given name and version information.
@ -264,6 +265,7 @@ export class Client<
super(options);
this._capabilities = options?.capabilities ?? {};
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator();
this._enforceStrictCapabilities = options?.enforceStrictCapabilities ?? false;
// Store list changed config for setup after connection (when we know server capabilities)
if (options?.listChanged) {
@ -728,14 +730,31 @@ export class Client<
}
async listPrompts(params?: ListPromptsRequest['params'], options?: RequestOptions) {
if (!this._serverCapabilities?.prompts && !this._enforceStrictCapabilities) {
// Respect capability negotiation: server does not support prompts
console.debug('Client.listPrompts() called but server does not advertise prompts capability - returning empty list');
return { prompts: [] };
}
return this.request({ method: 'prompts/list', params }, ListPromptsResultSchema, options);
}
async listResources(params?: ListResourcesRequest['params'], options?: RequestOptions) {
if (!this._serverCapabilities?.resources && !this._enforceStrictCapabilities) {
// Respect capability negotiation: server does not support resources
console.debug('Client.listResources() called but server does not advertise resources capability - returning empty list');
return { resources: [] };
}
return this.request({ method: 'resources/list', params }, ListResourcesResultSchema, options);
}
async listResourceTemplates(params?: ListResourceTemplatesRequest['params'], options?: RequestOptions) {
if (!this._serverCapabilities?.resources && !this._enforceStrictCapabilities) {
// Respect capability negotiation: server does not support resources
console.debug(
'Client.listResourceTemplates() called but server does not advertise resources capability - returning empty list'
);
return { resourceTemplates: [] };
}
return this.request({ method: 'resources/templates/list', params }, ListResourceTemplatesResultSchema, options);
}
@ -860,6 +879,11 @@ export class Client<
}
async listTools(params?: ListToolsRequest['params'], options?: RequestOptions) {
if (!this._serverCapabilities?.tools && !this._enforceStrictCapabilities) {
// Respect capability negotiation: server does not support tools
console.debug('Client.listTools() called but server does not advertise tools capability - returning empty list');
return { tools: [] };
}
const result = await this.request({ method: 'tools/list', params }, ListToolsResultSchema, options);
// Cache the tools and their output schemas for future validation