siyuan-note/siyuan · error

structured content is required when an output schema is defi

Error message

structured content is required when an output schema is defined

What it means

Thrown by ValidateOutputContext when a tool has a compiled output schema (validator.output != nil), the result is not an error result (result.IsError is false), but result.HasStructuredContent() returns false. The MCP contract requires that successful results of tools with an output schema carry structured content that conforms to that schema so consumers can rely on it.

Source

Thrown at kernel/mcp/tools/validation.go:128

		return nil
	}
	value, err := prepareValidationValue(arguments)
	if err != nil {
		return err
	}
	return validateResolved(ctx, validator.validationSlots, validator.input, value)
}

func (validator *ToolValidator) ValidateOutput(result CallToolResult) error {
	return validator.ValidateOutputContext(context.Background(), result)
}

func (validator *ToolValidator) ValidateOutputContext(ctx context.Context, result CallToolResult) error {
	if validator == nil || validator.output == nil || result.IsError {
		return nil
	}
	if !result.HasStructuredContent() {
		return fmt.Errorf("structured content is required when an output schema is defined")
	}
	value, err := prepareValidationValue(result.StructuredContent)
	if err != nil {
		return fmt.Errorf("prepare structured content: %w", err)
	}
	return validateResolved(ctx, validator.validationSlots, validator.output, value)
}

func prepareValidationValue(value any) (any, error) {
	if err := validateJSONComplexity(value, maxToolValueDepth, maxToolValueNodes); err != nil {
		return nil, err
	}
	data, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}
	if len(data) > maxToolValueBytes {
		return nil, fmt.Errorf("value exceeds %d bytes", maxToolValueBytes)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Populate result.StructuredContent with a value matching the declared OutputSchema on every success path.
  2. If the tool only ever returns unstructured text, remove the OutputSchema (set tool.OutputSchema to nil).
  3. When the operation genuinely fails, set result.IsError = true — error results are exempt from the structured-content requirement.

Example fix

// before
return CallToolResult{Content: []Content{{Type: "text", Text: "ok"}}
// after — tool has OutputSchema {type:object, properties:{ok:{type:boolean}}}
return CallToolResult{
  Content: []Content{{Type: "text", Text: "ok"}},
  StructuredContent: map[string]any{"ok": true},
}
Defensive patterns

Strategy: validation

Validate before calling

if tool.OutputSchema != nil && !result.IsError && !result.HasStructuredContent() {
    result.StructuredContent = map[string]any{} // or populate per schema
}

Type guard

func hasRequiredOutput(result CallToolResult, hasOutputSchema bool) bool {
    return !hasOutputSchema || result.IsError || result.HasStructuredContent()
}

Prevention

When it happens

Trigger: Calling a tool that defines OutputSchema, the handler returns a CallToolResult with only text content (or no content) and IsError=false, so HasStructuredContent() is false.

Common situations: A tool handler returns text-only output but the tool declared an output schema; a handler sets StructuredContent to nil on success paths; refactoring that adds an OutputSchema without updating the handler to populate StructuredContent.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/51b1bdabc89100c6. Report an issue: GitHub.