Bug Description
When an MCP tool returns type: "resource" in its content array, OpenClaw incorrectly converts it to a (see attached image) placeholder instead of extracting the resource.text field.
Reproduction Steps
- Configure QMD as the memory backend (
"memory": { "backend": "qmd" })
- Call the
qmd__get tool via OpenClaw:
{"name": "get", "arguments": {"file": "memory/2026-05-01.md"}}
- The MCP server returns a valid
EmbeddedResource:
{
"content": [
{
"type": "resource",
"resource": {
"uri": "qmd://memory-root-main/memory/2026-05-01.md",
"mimeType": "text/markdown",
"text": "# 2026-05-01\n\nSome markdown content here..."
}
}
]
}
- OpenClaw displays
(see attached image) in the tool output instead of the markdown text.
Expected vs Actual Behavior
| Aspect |
Expected |
Actual |
| Tool output |
The resource.text content (markdown text) |
(see attached image) |
| LLM context |
The text is passed to the model |
A placeholder is passed |
Root Cause Analysis (Source Code Level)
I traced the issue through the OpenClaw distribution bundle (2026.4.27). The problem is that the entire content pipeline lacks support for the MCP EmbeddedResource type.
1. contentToText ignores resource
File: dist/compaction-successor-transcript-BwDunTlM.js:581-585
function contentToText(content) {
// ...
return content.filter((part) => Boolean(part) && typeof part === "object")
.filter((part) => (part.type === "text" || part.type === "input_text" || part.type === "output_text") && typeof part.text === "string")
.map((part) => part.text).join("");
}
Only text / input_text / output_text are recognized. type: "resource" is silently filtered out, returning an empty string.
2. contentToOpenAIParts ignores resource
File: dist/compaction-successor-transcript-BwDunTlM.js:586-644
for (const part of content) {
if ((part.type === "text" || part.type === "input_text" || part.type === "output_text") && typeof part.text === "string") {
parts.push({ type: "input_text", text: part.text });
continue;
}
if (!includeImages) continue;
if (part.type === "image" && typeof part.data === "string") { /* ... */ }
if (part.type === "input_image" && part.source && ...) { /* ... */ }
}
Only text and image/input_image are handled. resource is skipped, producing an empty parts array.
3. OpenAI transport falls back to placeholder
File: dist/openai-transport-stream-DwlW8wDu.js:634-649
} else if (msg.role === "toolResult") {
const textResult = msg.content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
const hasImages = msg.content.some((item) => item.type === "image");
// ...
output: hasImages && model.input.includes("image")
? [...]
: sanitizeTransportPayloadText(textResult || "(see attached image)")
}
textResult is empty (resource is not text)
hasImages is false (resource is not image)
- Falls through to
sanitizeTransportPayloadText("" || "(see attached image)")
4. Anthropic transport has the same issue
File: dist/provider-stream-CQzDRxyR.js:104-126
function convertContentBlocks(content) {
if (!content.some((item) => item.type === "image"))
return sanitizeNonEmptyTransportPayloadText(content.map((item) => "text" in item ? item.text : "").join("\n"));
// ...
if (!blocks.some((block) => block.type === "text"))
blocks.unshift({ type: "text", text: "(see attached image)" });
}
Non-text content is treated as image, and when no text blocks exist, the placeholder is inserted.
Proposed Fix
Add resource type handling to the content pipeline:
// In contentToText
if (part.type === "resource" && part.resource && typeof part.resource.text === "string") {
return part.resource.text;
}
// In contentToOpenAIParts
if (part.type === "resource" && part.resource && typeof part.resource.text === "string") {
parts.push({ type: "input_text", text: part.resource.text });
continue;
}
Additionally, the transport layer should not blindly fall back to (see attached image) for unknown content types. It should either extract text from known types or pass through an error/unknown-type indicator.
Environment
- OpenClaw version: 2026.4.27
- Platform: macOS
- Memory backend: QMD
- API provider: Xiaomi MiMo (openai-completions)
- Node.js: v22.22.2
Bug Description
When an MCP tool returns
type: "resource"in itscontentarray, OpenClaw incorrectly converts it to a(see attached image)placeholder instead of extracting theresource.textfield.Reproduction Steps
"memory": { "backend": "qmd" })qmd__gettool via OpenClaw:{"name": "get", "arguments": {"file": "memory/2026-05-01.md"}}EmbeddedResource:{ "content": [ { "type": "resource", "resource": { "uri": "qmd://memory-root-main/memory/2026-05-01.md", "mimeType": "text/markdown", "text": "# 2026-05-01\n\nSome markdown content here..." } } ] }(see attached image)in the tool output instead of the markdown text.Expected vs Actual Behavior
resource.textcontent (markdown text)(see attached image)Root Cause Analysis (Source Code Level)
I traced the issue through the OpenClaw distribution bundle (
2026.4.27). The problem is that the entire content pipeline lacks support for the MCPEmbeddedResourcetype.1.
contentToTextignoresresourceFile:
dist/compaction-successor-transcript-BwDunTlM.js:581-585Only
text/input_text/output_textare recognized.type: "resource"is silently filtered out, returning an empty string.2.
contentToOpenAIPartsignoresresourceFile:
dist/compaction-successor-transcript-BwDunTlM.js:586-644Only
textandimage/input_imageare handled.resourceis skipped, producing an emptypartsarray.3. OpenAI transport falls back to placeholder
File:
dist/openai-transport-stream-DwlW8wDu.js:634-649textResultis empty (resource is not text)hasImagesis false (resource is not image)sanitizeTransportPayloadText("" || "(see attached image)")4. Anthropic transport has the same issue
File:
dist/provider-stream-CQzDRxyR.js:104-126Non-text content is treated as image, and when no text blocks exist, the placeholder is inserted.
Proposed Fix
Add
resourcetype handling to the content pipeline:Additionally, the transport layer should not blindly fall back to
(see attached image)for unknown content types. It should either extract text from known types or pass through an error/unknown-type indicator.Environment