From c86500ec501cd9d5690d006546585dc322628b20 Mon Sep 17 00:00:00 2001 From: Sheng Kun Chang Date: Tue, 16 Jun 2026 00:09:12 +0800 Subject: [PATCH] fix(mcp): inline tool schemas so inputSchema is type:object deriveSchema used jsonschema.Reflect's default, which emits a top-level {"$ref":"#/$defs/..."} with the object schema nested in $defs. The MCP spec requires inputSchema/outputSchema to be an object schema (type:"object"), and clients reject the ENTIRE tools/list when any tool's top-level schema is a $ref. Setting DoNotReference inlines the struct schema as a concrete {"type":"object",...}. Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/core/mcp.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/core/mcp.go b/internal/core/mcp.go index 4057c87..3037243 100644 --- a/internal/core/mcp.go +++ b/internal/core/mcp.go @@ -104,7 +104,12 @@ func MCPResource[Out any](name, description string, handler func(ctx context.Con func deriveSchema[T any]() (json.RawMessage, error) { var zero T - return json.Marshal(jsonschema.Reflect(zero)) + // DoNotReference inlines the struct schema so the top level is a concrete + // {"type":"object",...} rather than invopop's default {"$ref":"#/$defs/..."}. + // The MCP spec requires inputSchema/outputSchema to be an object schema with + // type:"object"; a top-level $ref makes clients reject the whole tools/list. + r := &jsonschema.Reflector{DoNotReference: true} + return json.Marshal(r.Reflect(zero)) } // wrapMCPToolHandler adapts a typed handler into the type-erased registry form.