Skip to content

Commit 9d0ff02

Browse files
committed
filter tools if client capabilities are not supported
1 parent a5eaac9 commit 9d0ff02

File tree

2 files changed

+43
-18
lines changed

2 files changed

+43
-18
lines changed

packages/mcp-server-supabase/src/tools/account-tools.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export function getAccountTools({
126126
idempotentHint: true,
127127
openWorldHint: false,
128128
},
129+
isSupported: (clientCapabilities) => !clientCapabilities?.elicitation,
129130
parameters: z.object({
130131
type: z.enum(['project', 'branch']),
131132
recurrence: z.enum(['hourly', 'monthly']),

packages/mcp-utils/src/server.ts

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ export type Tool<
6161
description: Prop<string>;
6262
annotations?: Annotations;
6363
parameters: Params;
64+
/**
65+
* Optional predicate to determine if this tool should be listed/usable for the
66+
* connected client based on its declared capabilities. If omitted, the tool
67+
* is assumed to be supported by all clients.
68+
*
69+
* Example: Only show when the client supports elicitation
70+
* isSupported: (caps) => Boolean(caps?.elicitation)
71+
*/
72+
isSupported?: (
73+
clientCapabilities: ClientCapabilities | undefined
74+
) => boolean | Promise<boolean>;
6475
execute(
6576
params: z.infer<Params>,
6677
context?: ToolExecuteContext
@@ -443,28 +454,41 @@ export function createMcpServer(options: McpServerOptions) {
443454
ListToolsRequestSchema,
444455
async (): Promise<ListToolsResult> => {
445456
const tools = await getTools();
457+
const clientCapabilities = server.getClientCapabilities();
458+
459+
// Filter tools based on client capabilities when a predicate is provided
460+
const supportedToolEntries = (
461+
await Promise.all(
462+
Object.entries(tools).map(async ([name, tool]) => {
463+
const ok =
464+
typeof tool.isSupported === 'function'
465+
? await tool.isSupported(clientCapabilities)
466+
: true;
467+
return ok ? ([name, tool] as const) : null;
468+
})
469+
)
470+
).filter(Boolean) as Array<readonly [string, Tool]>;
446471

447472
return {
448473
tools: await Promise.all(
449-
Object.entries(tools).map(
450-
async ([name, { description, annotations, parameters }]) => {
451-
const inputSchema = zodToJsonSchema(parameters);
452-
453-
if (!('properties' in inputSchema)) {
454-
throw new Error('tool parameters must be a ZodObject');
455-
}
456-
457-
return {
458-
name,
459-
description:
460-
typeof description === 'function'
461-
? await description()
462-
: description,
463-
annotations,
464-
inputSchema,
465-
};
474+
supportedToolEntries.map(async ([name, tool]) => {
475+
const { description, annotations, parameters } = tool;
476+
const inputSchema = zodToJsonSchema(parameters);
477+
478+
if (!('properties' in inputSchema)) {
479+
throw new Error('tool parameters must be a ZodObject');
466480
}
467-
)
481+
482+
return {
483+
name,
484+
description:
485+
typeof description === 'function'
486+
? await description()
487+
: description,
488+
annotations,
489+
inputSchema,
490+
};
491+
})
468492
),
469493
} satisfies ListToolsResult;
470494
}

0 commit comments

Comments
 (0)