Skip to content

Commit 2b30123

Browse files
olaservoclaude
andcommitted
Neutralize mirror_structured_content wording: no model-vs-app routing claims
The opt-out is a wire choice (emit the serialized content copy or not); which field a client forwards to a model is the client's decision, not the SDK's. Reword the docstring, parameter help, and docs to describe duplication rather than asserting content is "for the model." Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6cdf09e commit 2b30123

3 files changed

Lines changed: 17 additions & 16 deletions

File tree

docs/servers/structured-output.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,17 @@ There is one way to end up unstructured without asking for it: return a class th
236236

237237
## Skip the text copy
238238

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.
239+
By default a structured tool sends the value twice: as `structured_content`, and as a serialized copy of it in a `content` text block. That copy is the spec's recommendation (a SHOULD, not a MUST): it keeps the value available to a client that consumes `content`. When the payload is large it is pure duplication — the same data crosses the wire twice. Which field a client actually forwards to a model is the client's choice; this flag only controls whether the serialized copy is emitted.
240240

241-
Pass `mirror_structured_content=False` to return `structured_content` only, with empty `content`:
241+
Pass `mirror_structured_content=False` to send `structured_content` only, with empty `content`:
242242

243243
```python
244244
@mcp.tool(mirror_structured_content=False)
245245
def list_accounts(segment: str) -> list[Account]:
246246
return query_accounts(segment) # only structured_content is sent
247247
```
248248

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.
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* `content` rendering rather than none, build the `CallToolResult` yourself and set `content` to a summary.
250250

251251
## Recap
252252

src/mcp/server/mcpserver/server.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -594,9 +594,9 @@ def add_tool(
594594
- If True, creates a structured tool (return type annotation permitting)
595595
- If False, unconditionally creates an unstructured tool
596596
mirror_structured_content: Whether structured output is also serialised into a
597-
`content` text block (the spec's SHOULD, default True). Set False to return
598-
`structuredContent` only -- useful when the host routes structured content to
599-
the model itself and the serialised copy would double the payload.
597+
`content` text block alongside `structuredContent` (the spec's SHOULD,
598+
default True). Set False to send `structuredContent` only, without the
599+
duplicate serialised copy on the wire.
600600
"""
601601
self._tool_manager.add_tool(
602602
fn,
@@ -650,9 +650,9 @@ def tool(
650650
- If True, creates a structured tool (return type annotation permitting)
651651
- If False, unconditionally creates an unstructured tool
652652
mirror_structured_content: Whether structured output is also serialised into a
653-
`content` text block (the spec's SHOULD, default True). Set False to return
654-
`structuredContent` only -- useful when the host routes structured content to
655-
the model itself and the serialised copy would double the payload.
653+
`content` text block alongside `structuredContent` (the spec's SHOULD,
654+
default True). Set False to send `structuredContent` only, without the
655+
duplicate serialised copy on the wire.
656656
657657
Example:
658658
```python

src/mcp/server/mcpserver/utilities/func_metadata.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,15 @@ def convert_result(
116116
flow surfaces on the wire as `resultType: "input_required"` rather than
117117
being JSON-dumped into a text block.
118118
119-
For a tool with an output schema, structured output is returned as
119+
For a tool with an output schema, the structured output is returned as
120120
`structuredContent`. By default it is *also* serialised into a `content`
121-
text block, so a client that reads only `content` still sees the value --
122-
the spec's SHOULD. When ``mirror_structured_content`` is False the
123-
serialised copy is omitted and `structuredContent` becomes the sole
124-
representation; a host that routes `structuredContent` to the model itself
125-
then no longer receives the payload twice. Because the serialised-text
126-
guidance is a SHOULD (not a MUST), opting out stays conformant.
121+
text block -- the spec's SHOULD, which keeps the value available to a
122+
client that consumes `content`. When ``mirror_structured_content`` is
123+
False that serialised copy is omitted and `structuredContent` is the only
124+
representation on the wire, so the same payload is not sent twice. Because
125+
the serialised-text guidance is a SHOULD (not a MUST), opting out stays
126+
conformant. The SDK makes no assumption about which field a client routes
127+
to a model versus elsewhere; that is the client's choice.
127128
"""
128129
if isinstance(result, InputRequiredResult):
129130
return result

0 commit comments

Comments
 (0)