Repository navigation
mcp.AddTool crashes the server with SIGSEGV when input/output struct tags trigger a nil schema from jsonschema-go â missing nil-guard in setSchema #916
Description
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-sdkv1.4.0 and v1.6.0 (same code path)github.com/google/jsonschema-gov0.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 = internalSchemaFor 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.
Activity
jasondillingham commentedon May 3, 2026
Reproducer (no malformed
jsonschematag needed):Panics with
runtime error: invalid memory address or nil pointer dereferenceâ no tool name, no field, no actionable info. Mechanism: typed nil inanyis not equal to untyped nil, sot.InputSchema == nilis false. The(*jsonschema.Schema)type assertion succeeds with a nil pointer, thenResolvepanics. Same shape on the auto-generation path ifForTypeever returned(nil, nil)â defensive only since today it doesn't.Related:
Server.AddTool(the method, line 250) hits the same class of bug âs.Typederef on a typed-nil*jsonschema.Schemabefore the schema even reachessetSchema. Same fix shape.Local fix branch ready:
Server.AddToolInput/Output schemas,setSchemapost-ForType(defensive),setSchemapost-type-assertion (the reproducible path). Each panics/errors with a message naming the tool and field.TestAddToolNilSchemaPanicMessageswith 3 sub-cases. Asserts the panic message contains the tool name â not just that it panics. Fails onmainwith the generic runtime panic; passes with the guards.go test ./...green,go vet ./...clean.Deliberately scoped out: the
defer recover()wrapper intoolForErr. The nil-guards cover the reproducible cases; the recover would only catch deeper panics from inside jsonschema-go that we can't predict at the SDK boundary. Happy to send as a separate PR if useful â figured a smaller defensive-fix PR was easier to review.I'll open the PR if you confirm â happy to refine the scope based on your read.
guglielmo-san commentedon May 4, 2026
@jasondillingham thank you for the contribution! I added a comment on the PR that was already open on this issue
fix(mcp): prevent SIGSEGV in AddTool when schema is nil (modelcontextâĶ