Aident AI

MCP Output Schema Error? Align structuredContent With outputSchema
If an MCP client says a tool "has an output schema but did not return structured content" or that structured content does not match the output schema, do not retry the same call. The server declared one result contract and returned another. Capture the advertised outputSchema, capture the raw tools/call result, validate structuredContent against that exact schema, and fix the producer or adapter that changed the shape.
For a successful tool call, the invariant is simple:
The human-readable content field is useful, but it does not satisfy a declared structured result contract by itself.
Identify Which Output Contract Failed
The error wording usually points to one of three boundaries.
Symptom | What it means | First check |
|---|---|---|
| The successful result omitted | Raw |
| A key, type, required field, enum, or nesting level does not match | Validator error path plus the advertised schema |
The original tool error is replaced by an output-schema validation failure | An SDK or adapter validated an error result as if it were success |
|
These are response-contract failures. They are different from invalid tool arguments, authentication failures, timeouts, and provider errors. A generic retry loop cannot repair them.
Know What MCP Requires
The current MCP tools specification separates three fields:
outputSchemais the JSON Schema advertised with the tool definition.structuredContentis the server-produced JSON value that machines consume.contentis the unstructured result shown to models and people.
When outputSchema exists, a successful server result must include structured data that conforms to it, and clients should validate that data. The current specification allows any JSON value, including an object, array, string, number, boolean, or null. If $schema is omitted, MCP uses JSON Schema 2020-12.
For compatibility, the specification also recommends returning serialized JSON in a text content block. That fallback helps clients that do not consume structured results, but it is not a substitute for structuredContent when an output schema is declared.
Capture the Definition and Result From the Same Run
Do not compare a cached schema with a new result. Tool lists can change after a server release, proxy transformation, or connection refresh.
Preserve these four artifacts from one failing session:
The negotiated MCP protocol version.
The exact tool definition from
tools/list, includingoutputSchema.The raw
tools/callresult before a model or UI transforms it.The full validation path, with secrets and personal data redacted.
Then answer two questions:
If either answer is no, the failure is already isolated to the server, an integration adapter, or a client-side transformation. It is not evidence that the provider operation itself should run again.
Build One Result Object, Then Render It Twice
The safest implementation creates one typed value and uses it as the source for both result surfaces.
This avoids two independently maintained shapes. It also makes contract tests straightforward: validate output, then verify the serialized fallback represents the same value.
Do not build structuredContent from one mapper and content from another. That duplication invites field renames, default handling, and nullability to drift between surfaces.
Read the Validator Path Literally
Most schema mismatch messages contain the fastest route to the bug.
Validator path or keyword | Typical cause | Repair |
|---|---|---|
| Serializer omitted a required field or applied a default late | Include the field or make the schema accurately optional |
| The handler returned an object wrapper or empty fallback | Return the array at the declared path |
| Number, boolean, or | Fix serialization or widen the schema only if contractually valid |
| Runtime value is outside the enum | Update the producer or version the contract deliberately |
| Provider added a field under a closed schema | Normalize once or intentionally expand the schema |
Do not weaken the schema until the error disappears. First decide whether the runtime value is wrong or the declared contract is stale. A permissive schema can hide a breaking provider change and pass malformed data downstream.
Keep Tool Errors Out of the Success Shape
An execution error is not a successful output with placeholder fields. Return a tool error through isError: true and place the actionable message in content.
The current official TypeScript SDK error guidance says output-schema validation is skipped when isError is true. Historical SDK defects did not always preserve that boundary. The closed TypeScript SDK issue 654 documents a server-side case where output validation hid the original error, while an open client consistency issue tracks differences in how clients consume content and structuredContent.
If a valid isError: true result is still replaced by a schema error, reproduce it with the newest compatible SDK before changing the tool contract. Record the client and server versions because that behavior can be version-specific.
Test the Contract at Three Layers
A unit test alone can prove the handler's object is valid while missing a transport or adapter bug. Test the same fixtures at three boundaries.
1. Handler contract
Validate representative success values directly against the schema. Include:
the smallest valid result;
a normal populated result;
empty arrays and empty strings when allowed;
every nullable field;
enum boundaries; and
an intentional missing-field fixture that must fail.
2. MCP wire contract
Call the tool through a real client and assert that:
the advertised
outputSchemais the expected version;the result contains
structuredContenton success;the value passes the same JSON Schema draft;
the text fallback is present; and
an intentional tool error arrives with
isError: trueinstead of a schema exception.
3. Host compatibility
Run the server in the clients you support. Some older clients ignore structured content, some validate it, and some display only text. A matching structured value plus a useful text fallback serves all three behaviors.
The Go SDK issue 386 is a useful example of why this final layer matters: a result shape that looked reasonable in server code still produced a client validation error because the returned value at sessions was not the declared array.
Inspect an Integration Contract Before Execution
If the mismatch appears in an integration-backed Action, inspect the current catalog contract before running the provider again.
Start with the canonical setup instruction:
Then keep the investigation read-only:
Search discovers the current Action, get exposes its contract, and preflight validates input and quotes cost without dispatching the provider. None of these steps proves that a live result matches outputSchema, but they establish which result contract the runtime currently advertises.
Do not execute a write merely to reproduce an output mismatch. Prefer a documented read-only fixture, a mocked provider response, or a previously captured redacted result.
Use a Release Gate for Every Output-Schema Change
Treat a changed output schema like an API change.
Before release:
Diff the old and new schemas.
Classify required-field, type, enum, nesting, and closed-object changes.
Test old clients against the new server when compatibility matters.
Test the error path separately from success.
Pin a raw
tools/listandtools/callfixture in CI.Roll out the schema and result producer together.
If consumers cannot upgrade atomically, expand the result contract first or version the tool. Do not deploy a required field before every producer can populate it.
Fix the Contract, Not the Retry Count
An MCP output-schema failure is deterministic evidence: the declared contract and returned value disagree. Save the exact schema and raw result, repair one source of truth, preserve a serialized text fallback, and test both success and isError paths through a real client.
Next, use the MCP Inspector testing guide to capture the wire contract. If discovery is wrong before execution, follow the empty TypeScript inputSchema guide or the stringified tool arguments guide instead.
Set up Aident Loadout and inspect one Action contract. Keep the run read-only until the advertised result schema and failure boundary are clear.
Sources
MCP TypeScript SDK issue 654: Missing or wrong structuredContent blocks tools from reporting error
MCP TypeScript SDK issue 911: Implement consistent structuredContent handling
MCP Go SDK issue 386: CallToolResult.StructuredContent usage
JetBrains issue IJPL-230494: Output schema is not valid for default properties
Refresh this article when MCP changes the tool result contract or default JSON Schema draft, the official SDKs change error-validation behavior, or supported clients converge on a different compatibility rule.



The one tool
for every tool
your agent needs.
Give any AI agent real capabilities in seconds. Connect 1,000+ tools once, skip the setup headache, and let your agents execute.
