mcp: allow any value for schemas (#517)

As discussed at length in
https://github.com/modelcontextprotocol/go-sdk/issues/518, coupling our
schema fields to
jsonschema.Schema limits our user's ability to use a different schema
package, and makes it impossible for our client to talk to some
backends.

This CL relaxes the type of `*jsonschema.Schema` fields to be of type
`any`, which is a breaking change but is unlikely to affect most server
code. (It *is* a nontrivial change for clients).

Fixes https://github.com/modelcontextprotocol/go-sdk/issues/518
This commit is contained in:
Robert Findley 2025-09-26 11:55:54 -04:00 • committed by GitHub
commit 964574b818
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -16,6 +16,49 @@ import (
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func ExampleServer_AddTool_rawSchema() {
// In some scenarios, you may want your server to be a pass-through, with
// JSON schema coming from another source. Or perhaps you want to implement
// tool validation using a different JSON schema library.
//
// For these cases, you can use [mcp.Server.AddTool], which is the "raw" form
// of the API. Note that it is the caller's responsibility to validate inputs
// and outputs.
server := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
server.AddTool(&mcp.Tool{
Name: "greet",
InputSchema: json.RawMessage(`{"type":"object","properties":{"user":{"type":"string"}}}`),
}, func(_ context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Note: no validation!
var args struct{ User string }
if err := json.Unmarshal(req.Params.Arguments, &args); err != nil {
// TODO: we should use a jsonrpc error here, to be consistent with other
// SDKs.
return nil, err
}
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.User}},
}, nil
})
ctx := context.Background()
session, err := connect(ctx, server)
if err != nil {
log.Fatal(err)
}
defer session.Close()
res, err := session.CallTool(ctx, &mcp.CallToolParams{
Name: "greet",
Arguments: map[string]any{"user": "you"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Content[0].(*mcp.TextContent).Text)
// Output: Hi you
}
func ExampleAddTool_customMarshalling() {
// Sometimes when you want to customize the input or output schema for a
// tool, you need to customize the schema of a single helper type that's used
@ -68,7 +111,7 @@ func ExampleAddTool_customMarshalling() {
}
// Output:
// my_tool {
// "type": "object",
// "additionalProperties": false,
// "properties": {
// "end": {
// "type": "string"
@ -80,7 +123,7 @@ func ExampleAddTool_customMarshalling() {
// "type": "string"
// }
// },
// "additionalProperties": false
// "type": "object"
// }
}
@ -200,9 +243,9 @@ func ExampleAddTool_complexSchema() {
}
// Formatting the entire schemas would be too much output.
// Just check that our customizations were effective.
fmt.Println("max days:", *t.InputSchema.Properties["days"].Maximum)
fmt.Println("max confidence:", *t.OutputSchema.Properties["confidence"].Maximum)
fmt.Println("weather types:", t.OutputSchema.Properties["dailyForecast"].Items.Properties["type"].Enum)
fmt.Println("max days:", jsonPath(t.InputSchema, "properties", "days", "maximum"))
fmt.Println("max confidence:", jsonPath(t.OutputSchema, "properties", "confidence", "maximum"))
fmt.Println("weather types:", jsonPath(t.OutputSchema, "properties", "dailyForecast", "items", "properties", "type", "enum"))
}
// Output:
// max days: 10
@ -218,3 +261,10 @@ func connect(ctx context.Context, server *mcp.Server) (*mcp.ClientSession, error
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
return client.Connect(ctx, t2, nil)
}
func jsonPath(s any, path ...string) any {
if len(path) == 0 {
return s
}
return jsonPath(s.(map[string]any)[path[0]], path[1:]...)
}