Text responses are for humans. Structured outputs are for agents. Before MCP 2025-06-18, every tool call returned a plain text blob — useful for a person to read, wasteful when another tool needs to parse it. Structured tool outputs change the contract: instead of asking an AI to summarize data into prose, your server now returns typed JSON that agents, clients, and downstream tools can consume directly without any interpretation overhead.
What Are MCP Structured Tool Outputs?
An MCP tool is a function your server exposes — something like list_users, query_database, or get_order_status. When Claude or another MCP client calls that tool, the server sends back a response. Before the June 2025 spec update, that response was always a content array of text or image blocks. The client had to read those text blocks and figure out what the data meant.
Structured tool outputs add a second field to the response: structuredContent. This field holds a JSON object whose shape is defined in advance by an outputSchema in the tool's definition. The client doesn't need to parse prose — it reads typed, predictable JSON directly.
Think of it like the difference between a waiter describing the bill verbally ("so that's two coffees and a sandwich, let me see, I think it comes to about twelve dollars") versus handing you a printed receipt with itemized line items. Both convey the same information. One is dramatically easier to process programmatically.
Before and After: A Real Comparison
Here's what a list_users tool response looked like before structured outputs:
// BEFORE: unstructured tool response
{
"content": [{
"type": "text",
"text": "Found 3 users: Alice (admin), Bob (editor), Charlie (viewer)"
}]
}
That text has to be parsed. If you want to know Alice's role, you need string manipulation or another LLM call to extract it. Every token spent describing data is a token that isn't doing useful work.
Here's the same response with structured outputs:
// AFTER: structured tool response (MCP 2025-06-18+)
{
"content": [{
"type": "text",
"text": "Found 3 users"
}],
"structuredContent": {
"users": [
{"name": "Alice", "role": "admin"},
{"name": "Bob", "role": "editor"},
{"name": "Charlie", "role": "viewer"}
]
}
}
The content block is now a minimal human-readable summary. The structuredContent field has the actual data, typed and ready to use. A client building a user management UI can render that array directly as a table. A subsequent agent tool can filter on role === "admin" without any parsing step.
Why Context Efficiency Matters
Context window size is one of the most important practical constraints for agent workflows. Every token in your context costs money and latency, and there's a hard ceiling on how much fits.
Consider a database query that returns 200 rows. As unstructured text, an LLM might produce something like: "The query returned 200 records. The first record shows user ID 1 with name John Smith and email john@example.com, created on January 3rd, 2024. The second record shows user ID 2..." That's hundreds of tokens just to describe data that could be expressed in a compact JSON array.
With structured outputs, the data stays as data. The JSON representation of 200 rows might use a fraction of the tokens that a prose description would. For long-running agent workflows that make many tool calls, this difference compounds. You can fit more steps, more history, and more reasoning into the same context window.
There's also a reliability angle. When an LLM describes structured data in prose, information can get dropped, rounded, or subtly reworded. A JSON object with a defined schema either has a field or it doesn't — there's no ambiguity.
Tool-to-Tool Chaining with Structured Outputs
One of the most powerful things structured outputs enable is clean tool chaining. In an agentic workflow, the output of one tool becomes the input to the next. Without structured outputs, that hand-off requires an LLM to interpret the text output and reformulate it as input parameters for the next tool — an error-prone step that burns tokens.
With structured outputs, a client or orchestrator can wire tools together directly. For example:
- Tool 1:
search_ordersreturns{"orders": [{"id": "A1", "status": "pending"}, ...]} - The client extracts the
idfields fromstructuredContent.orders - Tool 2:
update_orderis called with those IDs — no LLM interpretation needed
This pattern is what makes MCP viable for real automation workflows, not just conversational assistance. The server defines the contract; the client enforces it. Learn more about how this fits into the broader Model Context Protocol architecture.
How to Add Structured Outputs to Your MCP Server
Adding structured output support to a tool involves two things: declaring an outputSchema in the tool definition, and including structuredContent in the tool's response.
Step 1: Define the output schema in your tool definition
When you register a tool with your MCP server, you can now include an outputSchema field alongside the existing inputSchema. The schema uses JSON Schema syntax — the same standard you already use for inputs:
{
"name": "list_users",
"description": "Returns a list of users in the system",
"inputSchema": {
"type": "object",
"properties": {
"role": {
"type": "string",
"description": "Filter by role (optional)"
}
}
},
"outputSchema": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"role": {"type": "string"},
"id": {"type": "integer"}
},
"required": ["name", "role", "id"]
}
},
"total": {"type": "integer"}
},
"required": ["users", "total"]
}
}
Step 2: Return structuredContent in tool responses
When the tool executes, your handler returns both a human-readable content block and the structuredContent payload that matches your schema:
// Python example using the MCP SDK
async def handle_list_users(arguments):
users = await db.fetch_users(role=arguments.get("role"))
return {
"content": [{
"type": "text",
"text": f"Found {len(users)} user(s)"
}],
"structuredContent": {
"users": [
{"id": u.id, "name": u.name, "role": u.role}
for u in users
],
"total": len(users)
}
}
Always include the plain text content block even when returning structured data. Clients that predate the 2025-06-18 spec will fall back to reading the text content and ignore structuredContent entirely. This backward-compatibility is intentional — the spec was designed so new servers stay compatible with older clients.
Validating your output schema
A well-formed outputSchema lets clients validate responses at runtime. If your tool claims to return a list of users and instead returns an error object, a strict client can detect the mismatch immediately. This is a significant reliability improvement over untyped text responses where malformed output is invisible until something downstream breaks.
What Clients Do with Structured Data
What a client actually does with structuredContent depends on the client's capabilities and the context. Current behavior in clients that support the June 2025 spec includes:
- Table rendering: Clients like Claude Desktop can detect array-of-objects shapes and render them as formatted tables instead of raw JSON text.
- Tool chaining: Orchestrators extract fields from
structuredContentand pass them directly to the next tool's input parameters. - Conditional logic: Agents can branch based on structured values — for example, only escalating an order if
structuredContent.status === "failed". - Caching: Typed data is easier to cache and invalidate reliably compared to free-form text.
Over time, expect clients to build richer integrations — charting numeric arrays, diffing structured outputs between tool calls, or feeding data directly into UI components. The outputSchema is the contract that makes all of this possible. For an overview of where structured outputs fit in recent spec evolution, see the MCP March vs June 2025 comparison.
When to Use Structured vs Plain Text Outputs
Not every tool needs structured outputs. Here's a practical decision guide:
Use structured outputs when:
- The tool returns tabular data (lists of records, rows, items)
- Another tool in the workflow will consume this output as input
- The client needs to render or display the data in a specific format
- You want type-safe, validated responses in production workflows
- You're building agent pipelines where token efficiency matters
Stick with plain text when:
- The tool's purpose is to generate prose (summaries, emails, explanations)
- The output is only ever read by a human, not processed by code
- Your schema would be so open-ended it provides no real type safety
- You need to maintain compatibility with clients that can't handle structured outputs
A good rule of thumb: if you'd write a function signature for the output in a typed language like TypeScript or Python, it's a candidate for a structured output schema. If the output is essentially a string with variable content, keep it as text.
Structured outputs are one piece of MCP's growing feature set. To understand how they connect to other recent additions like elicitation, take a look at the MCP Elicitation guide and the broader MCP version history.
Frequently Asked Questions
Structured tool outputs were introduced in the MCP 2025-06-18 specification update. Before that release, all MCP tool responses were plain text content blocks with no typed data field. If you're using an older server or client, check the spec version it targets to know whether structured outputs are available.
Plain text responses are still completely valid and often the right choice. Structured outputs are opt-in — you only add them when downstream tools or clients need to parse and act on the data programmatically. There's no requirement to retrofit existing tools that work fine as text.
structuredContent is a JSON object whose shape must match the outputSchema declared in the tool's definition. The schema is expressed using JSON Schema — the same standard used for tool input schemas. Any valid JSON Schema construct can describe your output shape, including nested objects, arrays, enums, and required fields.
Older clients that predate the 2025-06-18 spec will ignore the structuredContent field entirely and only read the plain text content block. This is why best practice is to always include both: a human-readable text block and a structuredContent payload. Your tool stays backward-compatible while newer clients get the full benefit of typed data.