Skip to content

Commit f44a0bf

Browse files
feat: apply elicitation defaults client-side
Override setRequestHandler to automatically apply defaults for elicitation responses. When clients return partial/empty content, the SDK fills in defaults from the schema before sending the response back to the server.
1 parent 159eef6 commit f44a0bf

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

src/client/index.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { mergeCapabilities, Protocol, type ProtocolOptions, type RequestOptions } from '../shared/protocol.js';
1+
import { mergeCapabilities, Protocol, type ProtocolOptions, type RequestOptions, type RequestHandlerExtra } from '../shared/protocol.js';
22
import type { Transport } from '../shared/transport.js';
33
import {
44
type CallToolRequest,
@@ -10,6 +10,8 @@ import {
1010
type CompatibilityCallToolResultSchema,
1111
type CompleteRequest,
1212
CompleteResultSchema,
13+
ElicitRequestSchema,
14+
type ElicitResult,
1315
EmptyResultSchema,
1416
ErrorCode,
1517
type GetPromptRequest,
@@ -40,6 +42,7 @@ import {
4042
} from '../types.js';
4143
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
4244
import type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator } from '../validation/types.js';
45+
import { ZodObject, ZodLiteral, z } from 'zod';
4346

4447
export type ClientOptions = ProtocolOptions & {
4548
/**
@@ -437,4 +440,71 @@ export class Client<
437440
async sendRootsListChanged() {
438441
return this.notification({ method: 'notifications/roots/list_changed' });
439442
}
443+
444+
/**
445+
* Override setRequestHandler to automatically apply defaults for elicitation responses.
446+
* When a handler is registered for ElicitRequestSchema, it wraps the handler to apply
447+
* defaults from the schema before returning the response.
448+
*/
449+
override setRequestHandler<
450+
T extends ZodObject<{
451+
method: ZodLiteral<string>;
452+
}>
453+
>(
454+
requestSchema: T,
455+
handler: (
456+
request: z.infer<T>,
457+
extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT>
458+
) => ClientResult | ResultT | Promise<ClientResult | ResultT>
459+
): void {
460+
const method = requestSchema.shape.method.value;
461+
462+
// Special handling for elicitation requests to apply defaults
463+
if (method === 'elicitation/create') {
464+
const wrappedHandler = async (
465+
request: z.infer<typeof ElicitRequestSchema>,
466+
extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT>
467+
): Promise<ElicitResult> => {
468+
// Call the original handler
469+
const result = (await handler(request as z.infer<T>, extra)) as ElicitResult;
470+
471+
// Only apply defaults if action is 'accept' and content exists
472+
if (result.action === 'accept' && result.content) {
473+
// Convert requestedSchema to JSON Schema format for validation
474+
const jsonSchema = {
475+
type: 'object' as const,
476+
properties: request.params.requestedSchema.properties,
477+
required: request.params.requestedSchema.required
478+
};
479+
480+
try {
481+
// Get validator which will apply defaults during validation
482+
const validator = this._jsonSchemaValidator.getValidator(jsonSchema);
483+
const validationResult = validator(result.content);
484+
485+
if (!validationResult.valid) {
486+
throw new McpError(
487+
ErrorCode.InvalidParams,
488+
`Elicitation response content does not match requested schema: ${validationResult.errorMessage}`
489+
);
490+
}
491+
} catch (error) {
492+
if (error instanceof McpError) {
493+
throw error;
494+
}
495+
// If validation fails, log but don't block - defaults were applied in-place
496+
// This handles edge cases where schema might not perfectly match
497+
}
498+
}
499+
500+
return result;
501+
};
502+
503+
// Register the wrapped handler using the parent's setRequestHandler
504+
super.setRequestHandler(ElicitRequestSchema, wrappedHandler);
505+
} else {
506+
// For all other request types, use default behavior
507+
super.setRequestHandler(requestSchema, handler);
508+
}
509+
}
440510
}

0 commit comments

Comments
 (0)