MCP tool results · field-visibility survey

Structured Content vs Content: What MCP clients actually do with tool results

The companion to the server survey. That one asks what servers put on the wire; this one asks what clients forward to the model. The MCP spec gives a tool result two places for data — unstructured content and a structuredContent object. For tools with an outputSchema, servers MUST populate structuredContent — and SHOULD also duplicate it into a content text block for backwards compatibility. What a client then forwards to the model is left open — hence this survey.

10 content only structuredContent never reaches the model
4 structuredContent first content text dropped when structured is present
4 forward both whole envelope serialized — payload lands in context twice
1 Fallback content, falling back to structuredContent

The same tool result, handled four ways

Take one tool that returns a dataset. Because it declares an outputSchema, it must populate structuredContent — and it also serializes the rows into content, so the payload sits in both fields. Here is the one response — then what each variant forwards to the model.

One server response

a dataset-returning tool · ~250 rows × 9 fields

{
  "content": [
    { "type": "text",
      "text": "[{\"id\":\"row-0001\",\"name\":\"Acme Corp\",\"category\":\"Enterprise\",
        \"revenue\":48200000,\"headcount\":1200,\"ageMonths\":31,\"score\":78,
        \"openTickets\":4,\"nps\":62}, … 249 more ]" }
  ],
  "structuredContent": { "rows": [ { … same ~250 × 9 … } ] }   // identical payload
}
Client Variant Acontent only

Model receives the content text — the full ~250-row string. structuredContent is never sent.

strandsagent-framework·pyclinecrewAI +toolsdeepagentsgemini-cligoose*LibreChatRoo-Codezed
Client Variant BstructuredContent first

Model receives JSON.stringify(structuredContent). The content text is dropped.

codex*fast-agentmastraVS Code*
Client Variant Cforward both

Model receives both content and structuredContent — the dataset lands twice.

adk-pythonagent-framework·netkilocodeopencode
Client Variant Dfallback

Both present → both forwarded; structuredContent substitutes only when content is empty. For this response, still a double dump.

hermes-agent

Nineteen clients, four variants

Each row was verified by reading the client's tool-result handling at a pinned SHA — each client name links to its repo at that commit. Open-source clients only: the conversion is checked against source, so closed-source agents are out of scope.

Last updated · commit history

19 rows across 18 projects — agent-framework is counted twice (Python → Variant A, .NET → Variant C).

ClientWhat reaches the modelClient VariantRelated links / PRs
strands content only structuredContent kept as a separate field on the result for hooks/programmatic access; not forwarded to the model (providers serialize only content) A #528
agent-framework Python content only at top level structuredContent read only for embedded blocks; code mode reuses the same parser A #3313 · #4763
cline content text/images only empty → "(No response)"; no structuredContent read A
crewAI + crewAI-tools content[0].text only crewAI-tools delegates the same content-only path via mcpadapt A
deepagents content only structuredContent → LangChain artifact (tooling, not the model) A
gemini-cli content only reverse shim backfills content from structuredContent; never sends sC to the model A #27045
goose conversational: content only code-mode TS sandbox prefers structuredContent — two paths, one client A B
LibreChat content only empty → "(No response)"; PTC code paths decided in external @librechat/agents A #8447
Roo-Code content text/images only empty → "(No response)" A
zed content only structuredContent deserialized then discarded on the model path A
codex structuredContent as whole body if present, else content code-mode runtime/TS sandbox hands the whole result (both) — a second path B #10334 · #2594
fast-agent compact JSON of structuredContent replaces text, else content B #703
mastra structuredContent alone if present, else whole envelope execute_typescript code mode inherits the same wrapper B #10430
VS Code + Copilot Chat JSON.stringify(structuredContent), dropping content text; else content a separate agentHost/Claude path forwards both; copilot-chat inherits, no own conversion B #290063
adk-python entire CallToolResult dict (model_dump) — both fields generic dump, no field-specific logic C #3893
agent-framework .NET whole result serialized — both fields delegated to the .NET MCP SDK; same project as the Variant-A Python binding C
kilocode raw CallToolResult → AI SDK — both fields delegated C
opencode whole CallToolResult JSON.stringify'd — both fields incidental, no field-specific logic C #27263 · #28567
hermes-agent both when both present; structuredContent if content empty; else content closest to a clean fallback — yet still double-dumps this response D #7043 · #7118