mcp: include resultType on new-protocol responses (#1060)
## Summary - Add `resultType: "complete"` to non-MRTR server result responses for 2026-07-28 requests. - Cover `server/discover`, list methods, and completion responses, and update expected conformance/example output. ## Why Fixes #1032. The 2026-07-28 schema requires `resultType` on result objects, but only the MRTR-capable results were setting it. ## Validation - `go test ./mcp -run 'Test(ServerSessionHandle|CompleteResult|ClientConnectDiscover|InMemory_E2E_Discover)' -count=1` — passed\n- `go test ./mcp -count=1` — passed\n- `go test ./...` — passed\n- `go vet ./...` — passed --------- Co-authored-by: ychampion <ychampion@users.noreply.github.com> Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
This commit is contained in:
parent
40f034322f
commit
dd79ca3d44
1 changed files with 178 additions and 3 deletions
|
|
@ -1399,6 +1399,148 @@ func TestServerSessionHandle_RejectsInitializeOnNewProtocol(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestServerSessionHandle_SetsResultTypeOnNewProtocol(t *testing.T) {
|
||||
server := NewServer(testImpl, &ServerOptions{
|
||||
CompletionHandler: func(context.Context, *CompleteRequest) (*CompleteResult, error) {
|
||||
return &CompleteResult{
|
||||
Completion: CompletionResultDetails{
|
||||
Values: []string{"go"},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
AddTool(server, &Tool{Name: "tool"}, func(context.Context, *CallToolRequest, struct{}) (*CallToolResult, any, error) {
|
||||
return &CallToolResult{
|
||||
InputRequests: InputRequestMap{"confirm": &ElicitParams{Message: "Continue?"}},
|
||||
}, nil, nil
|
||||
})
|
||||
server.AddPrompt(&Prompt{Name: "prompt"}, func(context.Context, *GetPromptRequest) (*GetPromptResult, error) {
|
||||
return &GetPromptResult{
|
||||
InputRequests: InputRequestMap{"confirm": &ElicitParams{Message: "Continue?"}},
|
||||
}, nil
|
||||
})
|
||||
server.AddResource(&Resource{URI: "test://resource", Name: "resource"}, func(context.Context, *ReadResourceRequest) (*ReadResourceResult, error) {
|
||||
return &ReadResourceResult{
|
||||
InputRequests: InputRequestMap{"confirm": &ElicitParams{Message: "Continue?"}},
|
||||
}, nil
|
||||
})
|
||||
newProtocolParams := func(fields map[string]any) map[string]any {
|
||||
params := map[string]any{
|
||||
"_meta": map[string]any{
|
||||
MetaKeyProtocolVersion: protocolVersion20260728,
|
||||
MetaKeyClientInfo: map[string]any{"name": "c", "version": "1"},
|
||||
MetaKeyClientCapabilities: map[string]any{},
|
||||
},
|
||||
}
|
||||
for k, v := range fields {
|
||||
params[k] = v
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
method string
|
||||
params map[string]any
|
||||
want resultType
|
||||
}{
|
||||
{
|
||||
name: "discover",
|
||||
method: methodDiscover,
|
||||
params: newProtocolParams(nil),
|
||||
want: resultTypeComplete,
|
||||
},
|
||||
{
|
||||
name: "tools list",
|
||||
method: methodListTools,
|
||||
params: newProtocolParams(nil),
|
||||
want: resultTypeComplete,
|
||||
},
|
||||
{
|
||||
name: "prompts list",
|
||||
method: methodListPrompts,
|
||||
params: newProtocolParams(nil),
|
||||
want: resultTypeComplete,
|
||||
},
|
||||
{
|
||||
name: "resources list",
|
||||
method: methodListResources,
|
||||
params: newProtocolParams(nil),
|
||||
want: resultTypeComplete,
|
||||
},
|
||||
{
|
||||
name: "resource templates list",
|
||||
method: methodListResourceTemplates,
|
||||
params: newProtocolParams(nil),
|
||||
want: resultTypeComplete,
|
||||
},
|
||||
{
|
||||
name: "complete",
|
||||
method: methodComplete,
|
||||
params: newProtocolParams(map[string]any{
|
||||
"argument": map[string]any{
|
||||
"name": "language",
|
||||
"value": "g",
|
||||
},
|
||||
"ref": map[string]any{
|
||||
"type": "ref/prompt",
|
||||
"name": "code_review",
|
||||
},
|
||||
}),
|
||||
want: resultTypeComplete,
|
||||
},
|
||||
{
|
||||
name: "tool input required",
|
||||
method: methodCallTool,
|
||||
params: newProtocolParams(map[string]any{"name": "tool", "arguments": map[string]any{}}),
|
||||
want: resultTypeInputRequired,
|
||||
},
|
||||
{
|
||||
name: "prompt input required",
|
||||
method: methodGetPrompt,
|
||||
params: newProtocolParams(map[string]any{"name": "prompt"}),
|
||||
want: resultTypeInputRequired,
|
||||
},
|
||||
{
|
||||
name: "resource input required",
|
||||
method: methodReadResource,
|
||||
params: newProtocolParams(map[string]any{"uri": "test://resource"}),
|
||||
want: resultTypeInputRequired,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ss := &ServerSession{server: server}
|
||||
id, err := jsonrpc.MakeID("test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result, err := ss.handle(context.Background(), &jsonrpc.Request{
|
||||
ID: id,
|
||||
Method: tc.method,
|
||||
Params: mustMarshal(tc.params),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var got struct {
|
||||
ResultType string `json:"resultType"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.ResultType != string(tc.want) {
|
||||
t.Fatalf("resultType = %q, want %q; response = %s", got.ResultType, tc.want, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestServerSessionHandle_RejectsRemovedMethodsOnNewProtocol verifies that
|
||||
// the methods removed by SEP-2575 (`initialize`, `notifications/initialized`,
|
||||
// `ping`) all return Method not found when the request opts into the new
|
||||
|
|
|
|||
Loading…
Reference in a new issue