Why architecture matters here
The architectural reason structured output matters is that MCP's whole purpose is to let a model act through deterministic systems, and deterministic systems are built on types. When a tool returns only prose, every host that wants to do something with the result — display it, chart it, feed it onward — has to reverse-engineer structure out of a string, and it must do so for output that a language model or a template generated and might phrase differently next time. That reverse parsing is brittle, untestable, and a rich source of silent bugs. A declared schema moves the structure from an implicit convention into an explicit, validatable contract.
The second driver is composability. Agents chain tools: the output of a search feeds a fetch, the output of a fetch feeds a summarizer. If each hop emits prose, the model is the only component that can bridge them, and it does so by reading and rewriting text at every step — slow, lossy, and expensive in tokens. With structured output, the host orchestrator can wire a field from one tool's typed result directly into the next tool's typed input, using the schemas to check compatibility, and invoke the model only where genuine reasoning is required rather than for mechanical data shuffling.
Third, schemas improve the model's own behavior. An outputSchema advertised in the tool definition tells the model what a call will return before it makes the call, which helps it decide whether the tool is the right one and how to use the result. When the model then receives a result that matches the advertised shape, its planning is grounded in a known structure instead of hoping the prose contains the field it needs. Predictability on the output side is as valuable to a planner as it is on the input side.
Fourth, validation turns a class of runtime surprises into caught errors. If the tool handler has a bug and returns a temperature as a string where the schema promised a number, a validating client catches the mismatch at the boundary rather than letting a malformed value propagate into a UI or a downstream call where it fails obscurely. The schema is both documentation and a test that runs on every response — a contract the protocol can enforce mechanically.
Finally, structured output preserves the human-and-model readability that made MCP results useful in the first place, which is why the design keeps content rather than replacing it. A result that is only a JSON blob is worse for a model that reasons over language and useless to a legacy client. By carrying both a typed object and a prose mirror, the protocol avoids forcing a choice between machine-consumable and human-readable, and it lets the ecosystem adopt structured output incrementally without a flag day.
The architecture: every piece explained
The first piece is the outputSchema in the tool definition. When a server lists its tools, each tool advertises a name, a human-readable description, an inputSchema for its arguments, and now optionally an outputSchema — a JSON Schema, conventionally an object type, describing the fields a successful result will contain. Declaring the schema is a promise: the server is committing that its structuredContent will validate against it. Clients read this at discovery time and can use it both to inform the model and to prepare validation.
The second piece is structuredContent in the CallToolResult. On a successful call, the handler populates this field with exactly one JSON object matching the declared schema. This is the machine-consumable payload — the typed data the host and downstream tools will actually use. It is a single object, not an array of blocks, precisely because it is meant to be the deserialized result rather than a stream of presentational content.
The third piece is the mirrored content array. For compatibility and for model readability, the spec advises that a tool returning structuredContent also include a text representation of the same data in the content array — typically the JSON serialized as a text block. A client that does not understand structured output still receives something meaningful, and a model that reasons better over text still has prose to read. The two views are expected to be consistent; the structured field is authoritative for programs, the content field is the readable rendering.
The fourth piece is client-side validation. A well-behaved client validates the returned structuredContent against the tool's advertised outputSchema before trusting it. This is where the contract acquires teeth: a payload that fails validation is a protocol error the client can surface immediately, rather than a malformed value that silently corrupts downstream logic. Validation is the mechanism that converts the schema from mere documentation into an enforced boundary.
The fifth piece is the error and result-status handling that surrounds all of this. A tool result carries an isError flag distinguishing a tool-level failure (the operation failed, reported in-band so the model can react) from a protocol-level error. Structured output composes with this: an error result need not carry structured content, and a success result should. The diagram below shows how the tool definition's outputSchema, the dual content and structured channels of the result, and client validation fit together into a single typed contract.
End-to-end flow
Trace a weather tool. At initialization the client calls tools/list and the server returns a get_forecast tool whose definition includes an outputSchema: an object with a numeric temperature, an integer humidity, and a string conditions. The client records this schema and, when surfacing the tool to the model, can note the shape of what a call will return.
The model decides to call get_forecast with {"city": "Bengaluru"}. The client sends a tools/call request; the server's handler runs, queries its weather source, and assembles a CallToolResult. It sets structuredContent to {"temperature": 27.4, "humidity": 71, "conditions": "partly cloudy"} and also fills content with a text block serializing the same object, so both channels agree.
The result travels back to the client, which now performs validation: it checks the returned structuredContent against the get_forecast outputSchema recorded earlier. Temperature is a number, humidity is an integer, conditions is a string, all required fields present — the payload conforms, so the client accepts it. Had the handler returned temperature as the string "27.4C", validation would fail here and the client would flag a contract violation rather than pass a bad value on.
Now both consumers are served from the one result. The host application reads structuredContent.temperature directly to drive a UI gauge or to compare against a threshold in a downstream tool — no prose parsing, no dependence on phrasing. Simultaneously, the model receives the content text and can reason about the forecast in language, and because it knew the schema in advance, it can confidently chain the numeric temperature into a follow-up tool that, say, decides whether to water the garden.
The composability payoff shows when the model plans a multi-step task. Because get_forecast declared its output shape, the orchestrator can wire temperature into a should_irrigate tool whose inputSchema expects a number, checking the types line up before invoking. The end-to-end property is that a tool call has become a typed function call across the model boundary: declared inputs, declared outputs, validated on the wire, readable by the model and consumable by the host from a single response — exactly the contract deterministic software needs to build reliably on top of a probabilistic caller.