How it works
Whenoutput_schema is configured on an agent, the execution flow is:
Agentic loop runs normally
The agent processes the prompt, calls tools, and iterates until no more tool calls remain.
Extraction turn fires
An additional LLM call is made with no tools, temperature
0.0 for deterministic output, and a prompt asking for valid JSON matching the schema.Validation
The response is parsed as JSON and validated against the schema using the
jsonschema crate’s Validator.Schema types
OutputSchema
The primary schema type:
| Field | Type | Default | Description |
|---|---|---|---|
schema | MeerkatSchema | (required) | The JSON schema definition |
name | Option<String> | None | Optional schema name (used by some providers) |
strict | bool | false | Whether to enforce strict schema validation |
compat | SchemaCompat | Lossy | Compatibility mode for provider lowering |
format | SchemaFormat | MeerkatV1 | Schema format version |
Construction methods
Builder methods
Wrapper format
Wrapper format
OutputSchema supports a wrapper format for explicit configuration. If the JSON object contains a schema key and either a format: "meerkat_v1" marker or only wrapper keys (schema, name, strict, compat, format), it is parsed as a wrapper:MeerkatSchema
Newtype around serde_json::Value with normalization:
- Constructed via
MeerkatSchema::new(Value)which appliesnormalize_schema(). - Normalization: ensures all object-typed nodes have
propertiesandrequiredkeys (inserting empty defaults if missing). This prevents provider-specific compilation issues. - Returns
SchemaError::InvalidRootif the root is not a JSON object.
SchemaFormat
Schema format versions:
| Variant | Description |
|---|---|
MeerkatV1 | Current format version (default) |
SchemaCompat
Compatibility mode for provider-specific schema lowering:
| Variant | Description |
|---|---|
Lossy | Best-effort lowering; unsupported features are dropped with warnings (default) |
Strict | Reject schemas with unsupported features for the target provider |
SchemaWarning
Warnings emitted during schema compilation:
CompiledSchema
Provider-compiled schema output:
SchemaError
Schema errors:
| Variant | Description |
|---|---|
InvalidRoot | Schema must be a JSON object at the root |
UnsupportedFeatures { provider, warnings } | Schema uses features not supported by the target provider (only in Strict compat mode) |
Extraction turn details
The extraction turn logic:Attempt flow
- Max attempts =
structured_output_retries + 1(default: 2 + 1 = 3 attempts). - First attempt prompt:
"Based on our conversation, provide the final output as valid JSON matching the required schema. Output ONLY the JSON, no additional text or markdown formatting." - Retry prompt (on validation failure):
"The previous output was invalid: {error}. Please provide valid JSON matching the schema. Output ONLY the JSON, no additional text." - LLM is called with no tools and temperature 0.0.
- Response text is trimmed, then markdown code fences are stripped (handles
```jsonand```wrappers). - Parsed as JSON via
serde_json::from_str. - Validated against the compiled schema via
jsonschema::Validator.
On success
ReturnsRunResult with:
text: the last assistant text from the extraction turnstructured_output:Some(parsed_value)— the validated JSONschema_warnings: any warnings from schema compilationturns: includes extraction attempts in the count
On failure (exhausted retries)
ReturnsAgentError::StructuredOutputValidationFailed:
Provider schema compilation
TheAgentLlmClient trait includes a compile_schema() method:
- Anthropic: may add
additionalProperties: falseto object nodes - Gemini: may strip unsupported JSON Schema keywords
- OpenAI: may apply strict-mode transformations
RunResult.schema_warnings.
Configuration
Agent config fields
| Field | Type | Default | Description |
|---|---|---|---|
output_schema | Option<OutputSchema> | None | JSON schema for extraction |
structured_output_retries | u32 | 2 | Max validation retries |
Usage
- CLI
- JSON-RPC
- REST API
- MCP Server
--output-schema flag accepts either a file path or inline JSON. The CLI detects files by checking if the value is an existing path.Wire parameters
Structured output is passed per-request viaStructuredOutputParams:
RunResult fields
When structured output extraction succeeds, the RunResult contains:
structured_output field is Some(value) when extraction succeeds and None when no schema was configured. The text field contains the raw JSON string from the last extraction response.
