Skip to content

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

@fstienhans-lg

Description

@fstienhans-lg

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions