@@ -36,10 +36,54 @@ import {
3636 SUPPORTED_PROTOCOL_VERSIONS ,
3737 type SubscribeRequest ,
3838 type Tool ,
39- type UnsubscribeRequest
39+ type UnsubscribeRequest ,
40+ ElicitResultSchema ,
41+ ElicitRequestSchema
4042} from '../types.js' ;
4143import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js' ;
4244import type { JsonSchemaType , JsonSchemaValidator , jsonSchemaValidator } from '../validation/types.js' ;
45+ import { ZodLiteral , ZodObject , z } from 'zod' ;
46+ import type { RequestHandlerExtra } from '../shared/protocol.js' ;
47+
48+ /**
49+ * Elicitation default application helper. Applies defaults to the data based on the schema.
50+ *
51+ * @param schema - The schema to apply defaults to.
52+ * @param data - The data to apply defaults to.
53+ */
54+ function applyElicitationDefaults ( schema : JsonSchemaType | undefined , data : unknown ) : void {
55+ if ( ! schema || data === null || typeof data !== 'object' ) return ;
56+
57+ // Handle object properties
58+ if ( schema . type === 'object' && schema . properties && typeof schema . properties === 'object' ) {
59+ const obj = data as Record < string , unknown > ;
60+ const props = schema . properties as Record < string , JsonSchemaType & { default ?: unknown } > ;
61+ for ( const key of Object . keys ( props ) ) {
62+ const propSchema = props [ key ] ;
63+ // If missing or explicitly undefined, apply default if present
64+ if ( obj [ key ] === undefined && Object . prototype . hasOwnProperty . call ( propSchema , 'default' ) ) {
65+ obj [ key ] = propSchema . default ;
66+ }
67+ // Recurse into existing nested objects/arrays
68+ if ( obj [ key ] !== undefined ) {
69+ applyElicitationDefaults ( propSchema , obj [ key ] ) ;
70+ }
71+ }
72+ }
73+
74+ if ( Array . isArray ( schema . anyOf ) ) {
75+ for ( const sub of schema . anyOf ) {
76+ applyElicitationDefaults ( sub , data ) ;
77+ }
78+ }
79+
80+ // Combine schemas
81+ if ( Array . isArray ( schema . oneOf ) ) {
82+ for ( const sub of schema . oneOf ) {
83+ applyElicitationDefaults ( sub , data ) ;
84+ }
85+ }
86+ }
4387
4488export type ClientOptions = ProtocolOptions & {
4589 /**
@@ -141,6 +185,64 @@ export class Client<
141185 this . _capabilities = mergeCapabilities ( this . _capabilities , capabilities ) ;
142186 }
143187
188+ /**
189+ * Override request handler registration to enforce client-side validation for elicitation.
190+ */
191+ public override setRequestHandler <
192+ T extends ZodObject < {
193+ method : ZodLiteral < string > ;
194+ } >
195+ > (
196+ requestSchema : T ,
197+ handler : (
198+ request : z . infer < T > ,
199+ extra : RequestHandlerExtra < ClientRequest | RequestT , ClientNotification | NotificationT >
200+ ) => ClientResult | ResultT | Promise < ClientResult | ResultT >
201+ ) : void {
202+ const method = requestSchema . shape . method . value ;
203+ if ( method === 'elicitation/create' ) {
204+ const wrappedHandler = async (
205+ request : z . infer < T > ,
206+ extra : RequestHandlerExtra < ClientRequest | RequestT , ClientNotification | NotificationT >
207+ ) : Promise < ClientResult | ResultT > => {
208+ const validatedRequest = ElicitRequestSchema . safeParse ( request ) ;
209+ if ( ! validatedRequest . success ) {
210+ throw new McpError ( ErrorCode . InvalidParams , `Invalid elicitation request: ${ validatedRequest . error . message } ` ) ;
211+ }
212+
213+ const result = await Promise . resolve ( handler ( request , extra ) ) ;
214+
215+ const validationResult = ElicitResultSchema . safeParse ( result ) ;
216+ if ( ! validationResult . success ) {
217+ throw new McpError ( ErrorCode . InvalidParams , `Invalid elicitation result: ${ validationResult . error . message } ` ) ;
218+ }
219+
220+ const validatedResult = validationResult . data ;
221+
222+ if (
223+ this . _capabilities . elicitation ?. applyDefaults &&
224+ validatedResult . action === 'accept' &&
225+ validatedResult . content &&
226+ validatedRequest . data . params . requestedSchema
227+ ) {
228+ try {
229+ applyElicitationDefaults ( validatedRequest . data . params . requestedSchema , validatedResult . content ) ;
230+ } catch {
231+ // gracefully ignore errors in default application
232+ }
233+ }
234+
235+ return validatedResult ;
236+ } ;
237+
238+ // Install the wrapped handler
239+ return super . setRequestHandler ( requestSchema , wrappedHandler as unknown as typeof handler ) ;
240+ }
241+
242+ // Non-elicitation handlers use default behavior
243+ return super . setRequestHandler ( requestSchema , handler ) ;
244+ }
245+
144246 protected assertCapability ( capability : keyof ServerCapabilities , method : string ) : void {
145247 if ( ! this . _serverCapabilities ?. [ capability ] ) {
146248 throw new Error ( `Server does not support ${ capability } (required for ${ method } )` ) ;
0 commit comments