Implement SEP-973 (#570)

mcp/protocol: Implement SEP-973

- Define Icon structure, which includes source, mimeType and sizes.
- sizes is any array of strings. refer
https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1531
- Support setting websiteUrl, icons for mcp.Implementation
- Support setting icons for mcp.Prompt
- Support setting icons for mcp.Tool
- Support setting icons for mcp.Resource

Fixes #552
This commit is contained in:
Suraj Bobade 2025-11-13 03:22:44 +05:30 • committed by GitHub
commit faddd764aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -111,6 +111,19 @@ func structuredTool(ctx context.Context, req *CallToolRequest, args *structuredI
return nil, &structuredOutput{"Ack " + args.In}, nil
}
func contentTool(ctx context.Context, req *CallToolRequest, args *structuredInput) (*CallToolResult, any, error) {
return &CallToolResult{
Content: []Content{
&ResourceLink{
Name: "Example Resource Link with Icons",
MIMEType: "text/plain",
URI: "https://example.com/resource/" + args.In,
Icons: []Icon{iconObj},
},
},
}, nil, nil
}
type tomorrowInput struct {
Now time.Time
}
@ -135,12 +148,26 @@ func incTool(_ context.Context, _ *CallToolRequest, args incInput) (*CallToolRes
return nil, incOutput{args.X + 1}, nil
}
var iconObj = Icon{
Source: "foobar",
MIMEType: "image/png",
Sizes: []string{"48x48", "96x96"},
Theme: "light",
}
// runServerTest runs the server conformance test.
// It must be executed in a synctest bubble.
func runServerTest(t *testing.T, test *conformanceTest) {
ctx := t.Context()
// Construct the server based on features listed in the test.
s := NewServer(&Implementation{Name: "testServer", Version: "v1.0.0"}, nil)
impl := &Implementation{Name: "testServer", Version: "v1.0.0"}
if test.name == "spec-sep-973-additional-metadata.txtar" {
impl.Icons = []Icon{iconObj}
impl.WebsiteURL = "https://github.com/modelcontextprotocol/go-sdk"
}
s := NewServer(impl, nil)
for _, tn := range test.tools {
switch tn {
case "greet":
@ -148,6 +175,12 @@ func runServerTest(t *testing.T, test *conformanceTest) {
Name: "greet",
Description: "say hi",
}, sayHi)
case "greetWithIcon":
AddTool(s, &Tool{
Name: "greetWithIcon",
Description: "say hi",
Icons: []Icon{iconObj},
}, sayHi)
case "structured":
AddTool(s, &Tool{Name: "structured"}, structuredTool)
case "tomorrow":
@ -159,6 +192,12 @@ func runServerTest(t *testing.T, test *conformanceTest) {
}
inSchema.Properties["x"].Default = json.RawMessage(`6`)
AddTool(s, &Tool{Name: "inc", InputSchema: inSchema}, incTool)
case "contentTool":
AddTool(s, &Tool{
Name: "contentTool",
Title: "contentTool",
Description: "return resourceLink content with Icon",
}, contentTool)
default:
t.Fatalf("unknown tool %q", tn)
}
@ -167,6 +206,13 @@ func runServerTest(t *testing.T, test *conformanceTest) {
switch pn {
case "code_review":
s.AddPrompt(codeReviewPrompt, codReviewPromptHandler)
case "code_reviewWithIcon":
s.AddPrompt(&Prompt{
Name: "code_review",
Description: "do a code review",
Arguments: []*PromptArgument{{Name: "Code", Required: true}},
Icons: []Icon{iconObj},
}, codReviewPromptHandler)
default:
t.Fatalf("unknown prompt %q", pn)
}
@ -177,6 +223,13 @@ func runServerTest(t *testing.T, test *conformanceTest) {
s.AddResource(resource1, readHandler)
case "info":
s.AddResource(resource3, handleEmbeddedResource)
case "infoWithIcon":
s.AddResource(&Resource{
Name: "info",
MIMEType: "text/plain",
URI: "embedded:info",
Icons: []Icon{iconObj},
}, handleEmbeddedResource)
default:
t.Fatalf("unknown resource %q", rn)
}