* Override request handler registration to enforce client-side validation for elicitation.
(
requestSchema: T,
handler: (
request: SchemaOutput<T>,
extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT>
) => ClientResult | ResultT | Promise<ClientResult | ResultT>
)
| 327 | * Override request handler registration to enforce client-side validation for elicitation. |
| 328 | */ |
| 329 | public override setRequestHandler<T extends AnyObjectSchema>( |
| 330 | requestSchema: T, |
| 331 | handler: ( |
| 332 | request: SchemaOutput<T>, |
| 333 | extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT> |
| 334 | ) => ClientResult | ResultT | Promise<ClientResult | ResultT> |
| 335 | ): void { |
| 336 | const shape = getObjectShape(requestSchema); |
| 337 | const methodSchema = shape?.method; |
| 338 | if (!methodSchema) { |
| 339 | throw new Error('Schema is missing a method literal'); |
| 340 | } |
| 341 | |
| 342 | // Extract literal value using type-safe property access |
| 343 | let methodValue: unknown; |
| 344 | if (isZ4Schema(methodSchema)) { |
| 345 | const v4Schema = methodSchema as unknown as ZodV4Internal; |
| 346 | const v4Def = v4Schema._zod?.def; |
| 347 | methodValue = v4Def?.value ?? v4Schema.value; |
| 348 | } else { |
| 349 | const v3Schema = methodSchema as unknown as ZodV3Internal; |
| 350 | const legacyDef = v3Schema._def; |
| 351 | methodValue = legacyDef?.value ?? v3Schema.value; |
| 352 | } |
| 353 | |
| 354 | if (typeof methodValue !== 'string') { |
| 355 | throw new Error('Schema method literal must be a string'); |
| 356 | } |
| 357 | const method = methodValue; |
| 358 | if (method === 'elicitation/create') { |
| 359 | const wrappedHandler = async ( |
| 360 | request: SchemaOutput<T>, |
| 361 | extra: RequestHandlerExtra<ClientRequest | RequestT, ClientNotification | NotificationT> |
| 362 | ): Promise<ClientResult | ResultT> => { |
| 363 | const validatedRequest = safeParse(ElicitRequestSchema, request); |
| 364 | if (!validatedRequest.success) { |
| 365 | // Type guard: if success is false, error is guaranteed to exist |
| 366 | const errorMessage = |
| 367 | validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error); |
| 368 | throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation request: ${errorMessage}`); |
| 369 | } |
| 370 | |
| 371 | const { params } = validatedRequest.data; |
| 372 | params.mode = params.mode ?? 'form'; |
| 373 | const { supportsFormMode, supportsUrlMode } = getSupportedElicitationModes(this._capabilities.elicitation); |
| 374 | |
| 375 | if (params.mode === 'form' && !supportsFormMode) { |
| 376 | throw new McpError(ErrorCode.InvalidParams, 'Client does not support form-mode elicitation requests'); |
| 377 | } |
| 378 | |
| 379 | if (params.mode === 'url' && !supportsUrlMode) { |
| 380 | throw new McpError(ErrorCode.InvalidParams, 'Client does not support URL-mode elicitation requests'); |
| 381 | } |
| 382 | |
| 383 | const result = await Promise.resolve(handler(request, extra)); |
| 384 | |
| 385 | // When task creation is requested, validate and return CreateTaskResult |
| 386 | if (params.task) { |