Summary
When registering a tool with mcp.AddTool[In, Out], the SDK calls jsonschema.ForType and then unconditionally invokes internalSchema.Resolve(...) on the result. If ForType returns a partially-nil schema tree (which happens with certain jsonschema:"..." struct-tag descriptions — see linked upstream issue), the call panics with a nil-pointer dereference at server startup, taking down the whole process.
The SDK should defend against a nil or malformed schema returned by jsonschema-go and surface a clear error (or panic with an actionable message that names the offending Go type and field), not segfault inside a third-party library.
Versions
github.com/modelcontextprotocol/go-sdk v1.4.0 and v1.6.0 (same code path)
github.com/google/jsonschema-go v0.4.2 / v0.4.3
- Go 1.24, macOS arm64
Stack trace
[signal SIGSEGV: segmentation violation code=0x2 addr=0x10 pc=0x102b9c36c]
goroutine 1 [running]:
github.com/google/jsonschema-go/jsonschema.detectDraft(...)
.../jsonschema-go@v0.4.2/jsonschema/resolve.go:52
github.com/google/jsonschema-go/jsonschema.newResolved(...)
.../jsonschema-go@v0.4.2/jsonschema/resolve.go:43
github.com/google/jsonschema-go/jsonschema.(*resolver).resolve(0x..., 0x0, 0x...)
.../jsonschema-go@v0.4.2/jsonschema/resolve.go:211 +0x2c
github.com/google/jsonschema-go/jsonschema.(*Schema).Resolve(0x0, 0x...)
.../jsonschema-go@v0.4.2/jsonschema/resolve.go:185 +0x110
github.com/modelcontextprotocol/go-sdk/mcp.setSchema[...](...)
.../go-sdk@v1.4.0/mcp/server.go:466 +0x1a0
github.com/modelcontextprotocol/go-sdk/mcp.toolForErr[...](...)
.../go-sdk@v1.4.0/mcp/server.go:309 +0x230
github.com/modelcontextprotocol/go-sdk/mcp.AddTool[...](...)
.../go-sdk@v1.4.0/mcp/server.go:501 +0x38
The receiver of Resolve is 0x0 — a nil *jsonschema.Schema.
Where the SDK should guard
mcp/server.go setSchema (current code, v1.6.0 line ~436):
internalSchema, err = jsonschema.ForType(rt, &jsonschema.ForOptions{})
if err != nil {
return zero, err
}
*sfield = internalSchema
resolved, err := internalSchema.Resolve(&jsonschema.ResolveOptions{ValidateDefaults: true})
internalSchema is checked for err, never for nil. If ForType returns (nil, nil) (or a tree containing a nil sub-schema), the next call panics inside jsonschema-go.
Suggested fix
In setSchema (and the analogous *sfield/providedSchema branches further down):
internalSchema, err = jsonschema.ForType(rt, &jsonschema.ForOptions{})
if err != nil {
return zero, err
}
if internalSchema == nil {
return zero, fmt.Errorf("mcp: jsonschema.ForType returned nil schema for %s", rt)
}
*sfield = internalSchema
For added safety in toolForErr / AddTool, wrap the schema-related calls in a defer recover() that converts a panic to an error mentioning the tool name and the Go type, so a single bad tag in one tool does not take the whole server down at startup. Something like:
func toolForErr[In, Out any](t *Tool, h ToolHandlerFor[In, Out], cache *SchemaCache) (rt *Tool, rh ToolHandler, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("mcp: schema generation panicked for tool %q (In=%T): %v",
t.Name, *new(In), r)
}
}()
// ... existing code
}
That alone would have transformed our outage into a clear actionable error pointing at the tool name and input type.
Trigger
Reproducible by adding a tool whose input struct uses jsonschema:"..." descriptions containing combinations of commas, =, >, <, em/en dashes, parens, brackets, single quotes, or non-ASCII characters. The underlying jsonschema-go bug is reported separately at:
https://github.com/google/jsonschema-go/issues/NEW (link once filed)
The SDK fix is independent: regardless of whether jsonschema-go ever produces a nil schema, the SDK should not segfault as a result.
Workaround
Restrict every jsonschema:"..." tag description in tool input/output structs to ASCII letters and spaces only. No commas, no ,required directive, no parens, brackets, quotes, equals, angle brackets, em/en dashes, or non-ASCII characters.
Impact
This is a denial-of-service-grade bug for any server that registers tools dynamically based on user-authored Go types. A single malformed tag anywhere in the registered toolset prevents the server from starting and produces no actionable error message — just a stack trace pointing into a third-party library. Adding a nil-check and a panic-to-error conversion turns an outage into a clear, fixable error.
Summary
When registering a tool with
mcp.AddTool[In, Out], the SDK callsjsonschema.ForTypeand then unconditionally invokesinternalSchema.Resolve(...)on the result. IfForTypereturns a partially-nil schema tree (which happens with certainjsonschema:"..."struct-tag descriptions — see linked upstream issue), the call panics with a nil-pointer dereference at server startup, taking down the whole process.The SDK should defend against a nil or malformed schema returned by
jsonschema-goand surface a clear error (or panic with an actionable message that names the offending Go type and field), not segfault inside a third-party library.Versions
github.com/modelcontextprotocol/go-sdkv1.4.0 and v1.6.0 (same code path)github.com/google/jsonschema-gov0.4.2 / v0.4.3Stack trace
The receiver of
Resolveis0x0— a nil*jsonschema.Schema.Where the SDK should guard
mcp/server.gosetSchema(current code, v1.6.0 line ~436):internalSchemais checked forerr, never for nil. IfForTypereturns(nil, nil)(or a tree containing a nil sub-schema), the next call panics insidejsonschema-go.Suggested fix
In
setSchema(and the analogous*sfield/providedSchemabranches further down):For added safety in
toolForErr/AddTool, wrap the schema-related calls in adefer recover()that converts a panic to an error mentioning the tool name and the Go type, so a single bad tag in one tool does not take the whole server down at startup. Something like:That alone would have transformed our outage into a clear actionable error pointing at the tool name and input type.
Trigger
Reproducible by adding a tool whose input struct uses
jsonschema:"..."descriptions containing combinations of commas,=,>,<, em/en dashes, parens, brackets, single quotes, or non-ASCII characters. The underlyingjsonschema-gobug is reported separately at:The SDK fix is independent: regardless of whether
jsonschema-goever produces a nil schema, the SDK should not segfault as a result.Workaround
Restrict every
jsonschema:"..."tag description in tool input/output structs to ASCII letters and spaces only. No commas, no,requireddirective, no parens, brackets, quotes, equals, angle brackets, em/en dashes, or non-ASCII characters.Impact
This is a denial-of-service-grade bug for any server that registers tools dynamically based on user-authored Go types. A single malformed tag anywhere in the registered toolset prevents the server from starting and produces no actionable error message — just a stack trace pointing into a third-party library. Adding a nil-check and a panic-to-error conversion turns an outage into a clear, fixable error.