You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add mirror_structured_content opt-out for structured tool results
The spec's serialized-text mirror alongside structuredContent is a SHOULD,
not a MUST, but MCPServer forced it on every structured result. Add a
per-tool `mirror_structured_content` flag (default True, so existing wire
behavior is unchanged) that, when False, returns structuredContent only with
empty content -- useful for large payloads or hosts that route structured
content to the model themselves.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/servers/structured-output.md
+15-1Lines changed: 15 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -234,11 +234,25 @@ There is one way to end up unstructured without asking for it: return a class th
234
234
Need full control (building the `CallToolResult` yourself, or attaching `_meta` that the
235
235
application can see but the model can't)? That's **[The low-level Server](../advanced/low-level-server.md)**.
236
236
237
+
## Skip the text copy
238
+
239
+
By default a structured tool fills **both** channels: `structured_content` for the application, and a serialized copy in `content` for the model. That copy is the spec's recommendation (a SHOULD, not a MUST) so an old client that only reads `content` still sees the value. When the payload is large, or the host routes `structured_content` to the model itself, the copy is wasted — the same data crosses the wire twice.
240
+
241
+
Pass `mirror_structured_content=False` to return `structured_content` only, with empty `content`:
242
+
243
+
```python
244
+
@mcp.tool(mirror_structured_content=False)
245
+
deflist_accounts(segment: str) -> list[Account]:
246
+
return query_accounts(segment) # only structured_content is sent
247
+
```
248
+
249
+
The default is `True`, so nothing changes unless you opt out. A tool with no `output_schema` is unaffected — its `content` is the only representation and is always sent. If you want a *smaller* model-facing rendering rather than none, build the `CallToolResult` yourself and set `content` to a summary.
250
+
237
251
## Recap
238
252
239
253
* The **return type annotation** is the output schema. It's published in `tools/list` as `output_schema`.
240
254
* Scalars, lists, tuples and unions are wrapped in `{"result": ...}`. Models, `TypedDict`s, dataclasses, annotated classes and `dict[str, ...]` are objects already and stay as they are.
241
-
* Every result carries `content` (text, for the model) **and**`structured_content` (data, for the application).
255
+
* Every result carries `content` (text, for the model) **and**`structured_content` (data, for the application) — unless you pass `mirror_structured_content=False`, which sends `structured_content` only.
242
256
* What you return is validated against the schema. A mismatch is a tool error, not a corrupt result.
243
257
*`structured_output=False` opts a tool out. A class without type hints opts out silently; watch for it.
0 commit comments