mcp: add ServerSession.NotifyElicitationComplete (#1194)

Adds:

```go
func (ss *ServerSession) NotifyElicitationComplete(ctx context.Context, params *ElicitationCompleteParams) error
```

As according to
[spec](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts#L2493-L2501)
This commit is contained in:
Guglielmo Colombo 2026-08-24 10:14:28 +02:00 • committed by GitHub
commit 6f0a645c20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 6 deletions

View file

@ -12,6 +12,7 @@ import (
"testing/synctest"
"github.com/google/jsonschema-go/jsonschema"
"github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2"
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
)
@ -194,9 +195,9 @@ func TestElicitationCompleteNotification(t *testing.T) {
}
// 2. Server sends elicitation complete notification (simulating out-of-band completion)
err = handleNotify(ctx, notificationElicitationComplete, newServerRequest(ss, &ElicitationCompleteParams{
err = ss.NotifyElicitationComplete(ctx, &ElicitationCompleteParams{
ElicitationID: elicitID,
}))
})
if err != nil {
t.Fatalf("failed to send elicitation complete notification: %v", err)
}
@ -209,6 +210,41 @@ func TestElicitationCompleteNotification(t *testing.T) {
})
}
func TestNotifyElicitationCompleteInvalidParams(t *testing.T) {
ctx := context.Background()
ct, st := NewInMemoryTransports()
s := NewServer(testImpl, nil)
ss, err := s.Connect(ctx, st, nil)
if err != nil {
t.Fatal(err)
}
defer ss.Close()
c := NewClient(testImpl, nil)
cs, err := c.Connect(ctx, ct, nil)
if err != nil {
t.Fatal(err)
}
defer cs.Close()
// An empty elicitation ID cannot be matched to a pending elicitation by the
// client, so it is rejected rather than sent.
for _, tc := range []struct {
name string
params *ElicitationCompleteParams
}{
{"nil params", nil},
{"empty ID", &ElicitationCompleteParams{}},
} {
t.Run(tc.name, func(t *testing.T) {
err := ss.NotifyElicitationComplete(ctx, tc.params)
if !errors.Is(err, jsonrpc2.ErrInvalidParams) {
t.Errorf("NotifyElicitationComplete(%v) = %v, want %v", tc.params, err, jsonrpc2.ErrInvalidParams)
}
})
}
}
func TestElicitationNoValidationWithoutAccept(t *testing.T) {
ctx := context.Background()

View file

@ -300,10 +300,9 @@ func TestURLElicitationRequired(t *testing.T) {
// In a real scenario, this would happen out-of-band after the user
// completes the form submission.
go func() {
err := handleNotify(ctx, notificationElicitationComplete,
newServerRequest(ss, &ElicitationCompleteParams{
ElicitationID: elicitID,
}))
err := ss.NotifyElicitationComplete(ctx, &ElicitationCompleteParams{
ElicitationID: elicitID,
})
if err != nil {
t.Errorf("failed to send elicitation complete notification: %v", err)
}

View file

@ -1733,6 +1733,18 @@ func (ss *ServerSession) Elicit(ctx context.Context, params *ElicitParams) (*Eli
return res, nil
}
// NotifyElicitationComplete tells the client that the out-of-band ("url" mode)
// elicitation identified by params.ElicitationID has finished.
func (ss *ServerSession) NotifyElicitationComplete(ctx context.Context, params *ElicitationCompleteParams) error {
if params == nil {
return fmt.Errorf("%w: params cannot be nil", jsonrpc2.ErrInvalidParams)
}
if params.ElicitationID == "" {
return fmt.Errorf("%w: ElicitationID cannot be empty", jsonrpc2.ErrInvalidParams)
}
return handleNotify(ctx, notificationElicitationComplete, newServerRequest(ss, orZero[Params](params)))
}
// Log sends a log message to the client.
//
// For new-protocol (>= 2026-07-28) requests, the level is taken from the