urlElicitationMiddleware returns middleware that automatically handles URL elicitation required errors by executing the elicitation handler, waiting for completion notifications, and retrying the operation. This middleware should be added to clients that want automatic URL elicitation handling: c
()
| 515 | // TODO(rfindley): this isn't strictly necessary for the SEP, but may be |
| 516 | // useful. Propose exporting it. |
| 517 | func urlElicitationMiddleware() Middleware { |
| 518 | return func(next MethodHandler) MethodHandler { |
| 519 | return func(ctx context.Context, method string, req Request) (Result, error) { |
| 520 | // Call the underlying handler. |
| 521 | res, err := next(ctx, method, req) |
| 522 | if err == nil { |
| 523 | return res, nil |
| 524 | } |
| 525 | |
| 526 | // Check if this is a URL elicitation required error. |
| 527 | var rpcErr *jsonrpc.Error |
| 528 | if !errors.As(err, &rpcErr) || rpcErr.Code != CodeURLElicitationRequired { |
| 529 | return res, err |
| 530 | } |
| 531 | |
| 532 | // Notifications don't support retries. |
| 533 | if strings.HasPrefix(method, "notifications/") { |
| 534 | return res, err |
| 535 | } |
| 536 | |
| 537 | // Extract the client session. |
| 538 | cs, ok := req.GetSession().(*ClientSession) |
| 539 | if !ok { |
| 540 | return res, err |
| 541 | } |
| 542 | |
| 543 | // Check if the client has an elicitation handler. |
| 544 | if cs.client.opts.ElicitationHandler == nil { |
| 545 | return res, err |
| 546 | } |
| 547 | |
| 548 | // Parse the elicitations from the error data. |
| 549 | var errorData struct { |
| 550 | Elicitations []*ElicitParams `json:"elicitations"` |
| 551 | } |
| 552 | if rpcErr.Data != nil { |
| 553 | if err := json.Unmarshal(rpcErr.Data, &errorData); err != nil { |
| 554 | return nil, fmt.Errorf("failed to parse URL elicitation error data: %w", err) |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | // Validate that all elicitations are URL mode. |
| 559 | for _, elicit := range errorData.Elicitations { |
| 560 | mode := elicit.Mode |
| 561 | if mode == "" { |
| 562 | mode = "form" // Default mode. |
| 563 | } |
| 564 | if mode != "url" { |
| 565 | return nil, fmt.Errorf("URLElicitationRequired error must only contain URL mode elicitations, got %q", mode) |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | // Register waiters for all elicitations before executing handlers |
| 570 | // to avoid race condition where notification arrives before waiter is registered. |
| 571 | type waiter struct { |
| 572 | await func(context.Context) error |
| 573 | cleanup func() |
| 574 | } |
searching dependent graphs…