@@ -66,6 +66,11 @@ type ClientOptions struct {
6666 // Setting ElicitationHandler to a non-nil value causes the client to
6767 // advertise the elicitation capability.
6868 ElicitationHandler func (context.Context , * ElicitRequest ) (* ElicitResult , error )
69+ // ElicitationModes specifies the elicitation modes supported by the client.
70+ // If ElicitationHandler is set and ElicitationModes is empty, it defaults to ["form"].
71+ ElicitationModes []string
72+ // ElicitationCompleteHandler handles incoming notifications for notifications/elicitation/complete.
73+ ElicitationCompleteHandler func (context.Context , * ElicitationCompleteNotificationRequest )
6974 // Handlers for notifications from the server.
7075 ToolListChangedHandler func (context.Context , * ToolListChangedRequest )
7176 PromptListChangedHandler func (context.Context , * PromptListChangedRequest )
@@ -123,6 +128,15 @@ func (c *Client) capabilities() *ClientCapabilities {
123128 }
124129 if c .opts .ElicitationHandler != nil {
125130 caps .Elicitation = & ElicitationCapabilities {}
131+ modes := c .opts .ElicitationModes
132+ if len (modes ) == 0 || slices .Contains (modes , "form" ) {
133+ // Technically, the empty ElicitationCapabilities value is equivalent to
134+ // {"form":{}} for backward compatibility, but we explicitly set the form
135+ // capability.
136+ caps .Elicitation .Form = & FormElicitationCapabilities {}
137+ } else if slices .Contains (modes , "url" ) {
138+ caps .Elicitation .URL = & URLElicitationCapabilities {}
139+ }
126140 }
127141 return caps
128142}
@@ -297,40 +311,55 @@ func (c *Client) createMessage(ctx context.Context, req *CreateMessageRequest) (
297311
298312func (c * Client ) elicit (ctx context.Context , req * ElicitRequest ) (* ElicitResult , error ) {
299313 if c .opts .ElicitationHandler == nil {
300- // TODO: wrap or annotate this error? Pick a standard code?
301- return nil , jsonrpc2 .NewError (codeUnsupportedMethod , "client does not support elicitation" )
302- }
303-
304- // Validate that the requested schema only contains top-level properties without nesting
305- schema , err := validateElicitSchema (req .Params .RequestedSchema )
306- if err != nil {
307- return nil , jsonrpc2 .NewError (codeInvalidParams , err .Error ())
314+ return nil , jsonrpc2 .NewError (codeInvalidParams , "client does not support elicitation" )
308315 }
309316
310- res , err := c .opts .ElicitationHandler (ctx , req )
311- if err != nil {
312- return nil , err
317+ // Validate the elicitation parameters based on the mode.
318+ mode := req .Params .Mode
319+ if mode == "" {
320+ mode = "form"
313321 }
314322
315- // Validate elicitation result content against requested schema
316- if schema != nil && res .Content != nil {
317- // TODO: is this the correct behavior if validation fails?
318- // It isn't the *server's* params that are invalid, so why would we return
319- // this code to the server?
320- resolved , err := schema .Resolve (nil )
321- if err != nil {
322- return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("failed to resolve requested schema: %v" , err ))
323+ switch mode {
324+ case "form" :
325+ if req .Params .URL != "" {
326+ return nil , jsonrpc2 .NewError (codeInvalidParams , "URL must not be set for form elicitation" )
323327 }
324- if err := resolved .Validate (res .Content ); err != nil {
325- return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("elicitation result content does not match requested schema: %v" , err ))
328+ schema , err := validateElicitSchema (req .Params .RequestedSchema )
329+ if err != nil {
330+ return nil , jsonrpc2 .NewError (codeInvalidParams , err .Error ())
326331 }
327- err = resolved . ApplyDefaults ( & res . Content )
332+ res , err := c . opts . ElicitationHandler ( ctx , req )
328333 if err != nil {
329- return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("failed to apply schema defalts to elicitation result: %v" , err ))
334+ return nil , err
335+ }
336+ // Validate elicitation result content against requested schema.
337+ if schema != nil && res .Content != nil {
338+ resolved , err := schema .Resolve (nil )
339+ if err != nil {
340+ return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("failed to resolve requested schema: %v" , err ))
341+ }
342+ if err := resolved .Validate (res .Content ); err != nil {
343+ return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("elicitation result content does not match requested schema: %v" , err ))
344+ }
345+ err = resolved .ApplyDefaults (& res .Content )
346+ if err != nil {
347+ return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("failed to apply schema defalts to elicitation result: %v" , err ))
348+ }
330349 }
350+ return res , nil
351+ case "url" :
352+ if req .Params .RequestedSchema != nil {
353+ return nil , jsonrpc2 .NewError (codeInvalidParams , "requestedSchema must not be set for URL elicitation" )
354+ }
355+ if req .Params .URL == "" {
356+ return nil , jsonrpc2 .NewError (codeInvalidParams , "URL must be set for URL elicitation" )
357+ }
358+ // No schema validation for URL mode, just pass through to handler.
359+ return c .opts .ElicitationHandler (ctx , req )
360+ default :
361+ return nil , jsonrpc2 .NewError (codeInvalidParams , fmt .Sprintf ("unsupported elicitation mode: %q" , mode ))
331362 }
332-
333- return res , nil
334363}
335364
336365// validateElicitSchema validates that the schema conforms to MCP elicitation schema requirements.
@@ -528,6 +557,7 @@ var clientMethodInfos = map[string]methodInfo{
528557 notificationResourceUpdated : newClientMethodInfo (clientMethod ((* Client ).callResourceUpdatedHandler ), notification | missingParamsOK ),
529558 notificationLoggingMessage : newClientMethodInfo (clientMethod ((* Client ).callLoggingHandler ), notification ),
530559 notificationProgress : newClientMethodInfo (clientSessionMethod ((* ClientSession ).callProgressNotificationHandler ), notification ),
560+ notificationElicitationComplete : newClientMethodInfo (clientMethod ((* Client ).callElicitationCompleteHandler ), notification | missingParamsOK ),
531561}
532562
533563func (cs * ClientSession ) sendingMethodInfos () map [string ]methodInfo {
@@ -692,6 +722,13 @@ func (cs *ClientSession) callProgressNotificationHandler(ctx context.Context, pa
692722 return nil , nil
693723}
694724
725+ func (c * Client ) callElicitationCompleteHandler (ctx context.Context , req * ElicitationCompleteNotificationRequest ) (Result , error ) {
726+ if h := c .opts .ElicitationCompleteHandler ; h != nil {
727+ h (ctx , req )
728+ }
729+ return nil , nil
730+ }
731+
695732// NotifyProgress sends a progress notification from the client to the server
696733// associated with this session.
697734// This can be used if the client is performing a long-running task that was
0 commit comments