MCP `text/plain` `resource.blob` results reach the model as empty output
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- javascript, node.js
Direzione di ricerca
Start with nodejs/src/types.ts at convertMcpCallToolResult and compare how resource.text enters textResultForLlm with how resource.blob enters binaryResultsForLlm. Run the mcp-text-blob-repro.mjs server with the three commands to confirm the controls, then add coverage for direct text, resource.text, and text/plain resource.blob; done means the blob's decoded text is visible to the model without breaking binary handling.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
When an MCP tool returns a successful embedded resource containing valid base64-encoded
UTF-8 text in resource.blob with mimeType: "text/plain", Copilot CLI does not expose
that text to the model. The model receives empty or unusable tool output and cannot quote
the returned nonce.
Two control representations of the same kind of text work:
| MCP result representation | Result |
|---|---|
Direct { type: "text", text: ... } |
Model receives usable text and can quote the nonce |
Embedded resource.text |
Model receives usable text and can quote the nonce |
Embedded resource.blob with mimeType: "text/plain" |
Tool succeeds, but the model cannot read the nonce |
This is not specific to large output or to one model. It reproduces with a tiny randomized
UTF-8 string and with both GPT-6 Astra and GPT-5.6 Sol.
A fresh run on Copilot CLI 1.0.87-0 reproduced the failure on both models. The direct
text and resource.text controls remained usable.
Public SDK source shows the relevant representation boundary:
convertMcpCallToolResult
appends resource.text to textResultForLlm, while every resource.blob is placed in
binaryResultsForLlm as a resource, regardless of MIME type. This establishes that
resource.text and resource.blob enter different result channels before provider
serialization; the textual blob is not subsequently delivered as usable model input.
The MCP schema permits BlobResourceContents to carry the resource's optional MIME type
without restricting it to image formats:
ResourceContents / BlobResourceContents.
Affected version
GitHub Copilot CLI 1.0.87-0
Steps to reproduce the behavior
- Save the following dependency-free Node.js stdio MCP server as
mcp-text-blob-repro.mjs:
mcp-text-blob-repro.mjs
import { randomUUID } from "node:crypto";
import { createInterface } from "node:readline";
const tools = [
{
name: "direct_text",
description: "Return a unique UTF-8 nonce as ordinary MCP text content.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
},
{
name: "resource_text",
description: "Return a unique UTF-8 nonce in an embedded resource.text field.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
},
{
name: "resource_blob",
description:
"Return a unique UTF-8 nonce as base64 resource.blob with mimeType text/plain.",
inputSchema: { type: "object", properties: {}, additionalProperties: false },
},
];
function write(message) {
process.stdout.write(`${JSON.stringify(message)}\n`);
}
function toolResult(name) {
const nonce = process.env.REPRO_NONCE || randomUUID();
const expected = `${name}:${nonce}`;
process.stderr.write(`[repro] ${name} expected: ${expected}\n`);
if (name === "direct_text") {
return {
content: [{ type: "text", text: expected }],
isError: false,
};
}
if (name === "resource_text") {
return {
content: [{
type: "resource",
resource: {
uri: "memory://copilot-text-blob-repro/resource-text.txt",
mimeType: "text/plain",
text: expected,
},
}],
isError: false,
};
}
if (name === "resource_blob") {
return {
content: [{
type: "resource",
resource: {
uri: "memory://copilot-text-blob-repro/resource-blob.txt",
mimeType: "text/plain",
blob: Buffer.from(expected, "utf8").toString("base64"),
},
}],
isError: false,
};
}
throw new Error(`Unknown tool: ${name}`);
}
const input = createInterface({ input: process.stdin, crlfDelay: Infinity });
input.on("line", (line) => {
if (!line.trim()) return;
let request;
try {
request = JSON.parse(line);
} catch {
return;
}
if (request.id === undefined) return;
try {
let result;
switch (request.method) {
case "initialize":
result = {
protocolVersion: request.params?.protocolVersion ?? "2025-06-18",
capabilities: { tools: {} },
serverInfo: { name: "copilot-text-blob-repro", version: "1.0.0" },
};
break;
case "ping":
result = {};
break;
case "tools/list":
result = { tools };
break;
case "tools/call":
result = toolResult(request.params?.name);
break;
default:
write({
jsonrpc: "2.0",
id: request.id,
error: { code: -32601, message: `Method not found: ${request.method}` },
});
return;
}
write({ jsonrpc: "2.0", id: request.id, result });
} catch (error) {
write({
jsonrpc: "2.0",
id: request.id,
error: {
code: -32602,
message: error instanceof Error ? error.message : String(error),
},
});
}
});
- In the same directory, save this as
.mcp.json:
{
"mcpServers": {
"text-blob-repro": {
"type": "stdio",
"command": "node",
"args": ["./mcp-text-blob-repro.mjs"],
"env": {
"REPRO_NONCE": "6f76fba4-4639-4ee2-8e95-c6cdd2e5b80d"
},
"tools": ["*"]
}
}
}
- From that directory, run these three commands.
--available-toolsprevents the model
from reading.mcp.jsonor using another tool to discover the configured nonce:
copilot -p 'Call the direct_text tool exactly once. Return only the UUID suffix after the colon, with no explanation.' `
--model gpt-5.6-sol `
--additional-mcp-config '@.mcp.json' `
--disable-builtin-mcps `
--available-tools text-blob-repro-direct_text `
--allow-all-tools
copilot -p 'Call the resource_text tool exactly once. Return only the UUID suffix after the colon, with no explanation.' `
--model gpt-5.6-sol `
--additional-mcp-config '@.mcp.json' `
--disable-builtin-mcps `
--available-tools text-blob-repro-resource_text `
--allow-all-tools
copilot -p 'Call the resource_blob tool exactly once. Return only the UUID suffix after the colon, with no explanation. If no content is visible, reply exactly ATTACHMENT_UNAVAILABLE.' `
--model gpt-5.6-sol `
--additional-mcp-config '@.mcp.json' `
--disable-builtin-mcps `
--available-tools text-blob-repro-resource_blob `
--allow-all-tools
Observed:
direct_text:6f76fba4-4639-4ee2-8e95-c6cdd2e5b80dresource_text:6f76fba4-4639-4ee2-8e95-c6cdd2e5b80dresource_blob: tool execution succeeds, but the nonce is absent from model-visible
output and the model returnsATTACHMENT_UNAVAILABLE.
Replacing --model gpt-5.6-sol with --model gpt-6-astra reproduces the
resource_blob failure.
Expected behavior
For a successful embedded resource with an allowlisted textual MIME type such as
text/plain, Copilot CLI should make valid decoded UTF-8 content available to the model
through the normal text-result path (including the existing bounded large-output handling).
If the content cannot be safely decoded or supported, the tool result should fail
explicitly or contain a clear model-visible diagnostic. A successful tool execution should
not silently become empty model-visible output while usable text is present.
Additional context
- The failing resource contains valid base64 and valid UTF-8. Equivalent inline text and
resource.textcontrols succeed. - The failure occurs with a tiny nonce, so it is separate from large-output truncation.
- In the SDK-mediated path, the blob reaches the runtime's binary-result/session-asset
representation, but neither tested model receives usable text. The native MCP path fails
the same way, so this is not only an SDK forwarding issue. - Impact: MCP integrations that return file or document content as a textual blob can
report a successful read while the model receives no content. The agent may then
misdiagnose the result as a missing attachment, empty file, or permissions problem. - Related but distinct:
- github/copilot-cli#4536 covers MCP image content blocks.
- github/copilot-cli#4600 covers MCP images dropped by BYOK provider assembly.
- github/copilot-cli#1732 covered truncation of large MCP text before the existing
large-output-to-file mechanism; it was fixed in CLI 1.0.9. - github/copilot-sdk#1644 covered SDK language bindings failing to forward binary tool
results to the runtime; it is fixed, and the current reproduction reaches the next
runtime stage.
A possible fix direction, rather than a required implementation:
- Base64-decode only explicitly allowlisted textual MIME types.
- Enforce a decoded-size cap and strict UTF-8 validation.
- Route valid decoded text through
textResultForLlmor an equivalent supported
model-visible text/file path. - Keep binary attachment handling for MIME types and providers that support it.
- Apply the same normalization to native MCP and SDK/external-tool results.
- Add integration coverage for direct text,
resource.text, andtext/plain
resource.blobacross both provider/model paths.
- Lingua principale
- Shell
- Stelle
- 11.2k
- Fork
- 1.9k
- Merge medio
- 14h 16m
- PR unite (30g)
- 6
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di github/copilot-cli
-
triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
github/copilot-cli#4909 ·
-
triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
github/copilot-cli#4906 ·
-
triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
github/copilot-cli#4848 ·
-
area:agents area:mcp
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
github/copilot-cli#4729 ·
-
area:sessions
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
github/copilot-cli#4712 ·
Tutte le issue di github/copilot-cli
Issue simili
-
Issue-Enhancement Needs-Triage
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 86/100
PowerShell/PowerShell#28061 · 2 reazioni ·
-
Feature Request: Add ability to load custom environment variables in linux-exec-server-installer.sh Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
microsoft/vscode-remote-release#11867 ·
-
AuTest Bug Tests
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
apache/trafficserver#13714 ·
-
Update to NCCL 2.32 Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
conda-forge/nccl-feedstock#166 ·
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
vllm-project/agentic-api#358 · 1 commento ·