MCP tool results need a materialization boundary
MCP tool results are no longer just plain text. They can carry resource_link, resource, audio, images, and structured payloads. That is useful, but it also means an agent runtime needs a materialization boundary: one place that converts rich tool output into the exact shapes each model provider can safely accept.
Table of contents
- Why MCP tool results are getting harder to handle
- What OpenClaw changed in 2026.6.5-beta.2
- The reliability pattern: normalize before provider conversion
- What should happen to each block type
- How to test MCP result handling
- Where this fits in an agent architecture
- FAQ
Why MCP tool results are getting harder to handle
The Model Context Protocol lets clients call tools and receive a content array back from the server. The official tools spec describes text results, structured output, annotations, and richer content blocks such as resource_link.
That flexibility creates a boring but serious integration problem. Model providers do not all accept the same message shapes. A content block that is valid MCP can still be invalid for a provider adapter, invisible to a model, or malformed after a conversion layer tries to squeeze it into a narrower API.
Existing MCP security coverage often focuses on malicious servers and exposed tools. That is necessary. We have covered the MCP vulnerability side before in the MCP security crisis post and the Claude Code MCP vulnerability writeup. This post is about a quieter failure mode: valid or semi-valid tool output crossing runtime boundaries in the wrong shape.
What OpenClaw changed in 2026.6.5-beta.2
The OpenClaw v2026.6.5-beta.2 release notes call out a specific fix:
“MCP tool results now coerce
resource_link,resource,audio, malformed image, and future non-text/image blocks at the materialize boundary, preventing Anthropic 400s and poisoned session history after a tool returns richer MCP content.”
That wording matters because it names the boundary. The fix is not simply “handle more block types.” It says the runtime should coerce tool result blocks before they reach provider converters.
In practice, an OpenClaw agent run can treat MCP as a broad input surface while still giving each model provider a narrower, valid transcript. The runtime preserves useful content where it can, downgrades unsupported rich blocks into text where it must, and avoids storing malformed provider-facing messages in session history.
| Failure mode | Without a materialization boundary | With a materialization boundary |
|---|---|---|
resource_link block | Provider adapter may reject or drop it | Convert to readable link text or supported block |
resource block | Model may receive an unsupported embedded object | Summarize metadata and URI safely |
audio block | Non-audio provider path may fail | Preserve reference as text unless audio is supported |
| Malformed image | Provider may return a 400 | Validate, drop unsafe fields, or describe the failure |
| Future block type | Unknown shape leaks into transcript | Coerce to text and keep the run recoverable |
The reliability pattern: normalize before provider conversion
A materialization boundary is a normalizer. It sits after tool execution and before provider-specific message conversion.
The boundary should answer five questions for every tool result:
- Is this block type allowed by MCP?
- Is it supported by the selected provider and model?
- If unsupported, can it be represented as text without losing the important operator-facing meaning?
- If malformed, should the runtime repair it, drop it, or turn it into an explicit tool error?
- What exactly gets written into durable session history?
The last question is the one teams tend to miss. A one-time provider error is annoying. A bad message stored in history is worse because every retry can replay the same poison.
For self-hosted agents, this boundary is part of ownership. If you run OpenClaw because you care about inspectable agent infrastructure, the tool result path should be as inspectable as auth, memory, or channel delivery. That is also why the broader architecture pages, including how OpenClaw works and why OpenClaw, keep coming back to control surfaces instead of black-box automation.
What should happen to each block type
A clean MCP materialization layer does not treat every non-text block as danger. It treats every block as data that needs a safe provider-facing representation.
resource_link is useful because it lets a tool point at a resource without dumping the whole resource into the conversation. The MCP Inspector had a real bug in 2025 where a valid result with a resource_link content block was reported as an invalid tool result. The issue was closed after resource link support landed in the Inspector UI.
That history is a good reminder: support for richer MCP blocks is still uneven. A runtime should assume that some clients, model adapters, and transcript viewers lag the spec.
Embedded resources carry more payload. That can be useful for small text artifacts, but it gets risky when the resource is large, binary, or provider-incompatible. The materialization layer should preserve the resource identity, MIME type, and a short description. It should not blindly push large embedded blobs into a model request.
Audio is a channel capability, not a universal model capability. If the current provider path cannot accept audio in a tool result, the runtime should keep the run alive by recording a textual reference: file name, MIME type, duration if known, and where the operator can inspect it.
Images are common in browser, design, document, and computer-use workflows. They are also easy to malform: missing media type, bad base64, empty URL, or fields in a provider-specific shape. Validate image blocks before they enter the provider layer. If validation fails, record a concise error instead of letting the provider produce a less useful 400.
MCP will keep adding shapes. The safest default is not to reject the whole result. Coerce the unknown block into a text summary that includes the type, visible metadata, and a note that the block was not passed through as native provider content.
How to test MCP result handling
A good test suite for MCP tool results should include boring negative cases. Happy-path text tools are not enough.
Use fixtures like these:
- A result with
content: [{ type: "text" }]only. - A result with both
content[].textandstructuredContent. - A result with
resource_linkand a normal text summary. - A result with an embedded
resourcethat is too large to forward. - A result with an
audioblock on a text-only provider path. - A malformed image block with missing or invalid media data.
- An unknown block type that simulates a future MCP extension.
- A replay test where the first run fails and the second run resumes from session history.
That last one is important. The release note mentions poisoned session history for a reason. You do not know whether your materialization boundary works until you prove that bad tool output cannot corrupt durable history.
The Claude Code ecosystem has seen adjacent MCP result visibility issues too. One reported bug described content[].text being dropped when structuredContent was also present, causing the model to miss the human-readable list while another client displayed it correctly.
Where this fits in an agent architecture
MCP materialization belongs in the same mental bucket as auth profiles, approval gates, memory indexing, and channel delivery. It is not glamorous. It is plumbing. But it is exactly the plumbing that determines whether an agent can run for hours without getting stuck on a single odd tool result.
For teams comparing agent stacks, this is one reason to look beyond “supports MCP” as a checkbox. Ask what happens when an MCP server returns something the model provider cannot accept.
A practical checklist: can the runtime show both raw and materialized results, recover after provider rejection, keep malformed blocks out of durable history, and behave consistently across CLI, web chat, and channel delivery?
If you are evaluating OpenClaw against other agent stacks, add this to the checklist. The OpenClaw vs alternatives page covers the broader ownership argument.
FAQ
What are MCP tool results?
MCP tool results are the payloads returned after a client calls an MCP server tool. They usually include a content array and may also include structured data, resource links, embedded resources, images, audio, or other block types.
Why do MCP tool results cause provider errors?
A result can be valid MCP but invalid for a specific model provider API. If the runtime forwards unsupported blocks directly into a provider request, the provider may reject the message, drop content, or return an error such as an Anthropic 400.
What is a materialization boundary?
A materialization boundary is the layer that converts raw tool output into safe model-facing content. It validates rich blocks, preserves what matters, downgrades unsupported content into text, and keeps malformed provider messages out of session history.
Is this only an OpenClaw issue?
No. Any agent runtime that supports MCP and multiple model providers needs to solve this. The OpenClaw 2026.6.5-beta.2 release is useful because it names the boundary.
The short version
MCP makes tools portable, but portability stops at the provider boundary unless the runtime does the boring conversion work. OpenClaw’s 2026.6.5-beta.2 MCP fix points to the right pattern: normalize rich tool results once, before provider adapters and before durable history.
Sources: OpenClaw v2026.6.5-beta.2 release notes, Model Context Protocol tools specification, MCP Inspector resource_link issue #553, Claude Code MCP content and structuredContent issue #55677, Claude Code changelog