siyuan-note/siyuan · error
root type must be "object"
Error message
root type must be "object"
What it means
Thrown by resolveToolSchema when requireObject is true (input schemas only) and the parsed jsonschema.Schema's Type field is not the literal "object". The MCP specification mandates that a tool's inputSchema be a JSON Schema of type object whose properties describe the named arguments; anything else is rejected before Resolve runs.
Source
Thrown at kernel/mcp/tools/validation.go:99
}
var raw any
if err = json.Unmarshal(data, &raw); err != nil {
return nil, err
}
if err = validateJSONComplexity(raw, maxToolSchemaDepth, maxToolSchemaNodes); err != nil {
return nil, err
}
if requireObject {
if err = validateParamHeaderAnnotations(raw); err != nil {
return nil, err
}
}
var parsed jsonschema.Schema
if err = json.Unmarshal(data, &parsed); err != nil {
return nil, err
}
if requireObject && parsed.Type != "object" {
return nil, fmt.Errorf(`root type must be "object"`)
}
return parsed.Resolve(nil)
}
func (validator *ToolValidator) ValidateInput(arguments map[string]any) error {
return validator.ValidateInputContext(context.Background(), arguments)
}
func (validator *ToolValidator) ValidateInputContext(ctx context.Context, arguments map[string]any) error {
if validator == nil || validator.input == nil {
return nil
}
value, err := prepareValidationValue(arguments)
if err != nil {
return err
}
return validateResolved(ctx, validator.validationSlots, validator.input, value)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Wrap the input schema as {"type":"object","properties":{...}} with the actual parameters under properties.
- If the tool takes no arguments, use {"type":"object","properties":{}} (an empty object schema) rather than null or an empty map.
- Verify you are assigning to InputSchema, not OutputSchema — output schemas allow non-object root types.
Example fix
// before
InputSchema: ToolSchema{Raw: map[string]any{"type": "array"}}
// after
InputSchema: ToolSchema{Raw: map[string]any{"type": "object", "properties": map[string]any{}}} Defensive patterns
Strategy: validation
Validate before calling
if t, _ := schema["type"].(string); t != "object" {
schema["type"] = "object"
if _, ok := schema["properties"]; !ok { schema["properties"] = map[string]any{} }
} Type guard
func isObjectSchema(schema map[string]any) bool {
t, _ := schema["type"].(string)
return t == "object"
} Prevention
- Always build input schemas from an object skeleton {"type":"object","properties":{...}}.
- Keep a shared helper that constructs a valid input-schema envelope for tools.
- Unit-test CompileToolValidator on each tool to catch type errors at registration time.
When it happens
Trigger: Registering a tool whose InputSchema has type "string", "array", "number", is missing a type, or is an empty/non-object schema. CompileToolValidator always passes requireObject=true for input schemas.
Common situations: A developer copies an output/result schema (which may be any type) into the input slot; a tool is registered with InputSchema set to a fragment like {"type":"array"}; the schema is built dynamically and the object wrapper is omitted.
Related errors
- schema exceeds %d bytes
- property %q: x-mcp-header must be a non-empty string
- property %q: x-mcp-header value %q is not a valid HTTP field
- property %q: duplicate x-mcp-header value %q
- invalid frontend capability [%s]: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/bd418b044c39d9e2.
Report an issue: GitHub.