diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index d3d6a1dfcc10..da7a24bff670 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -705,8 +705,13 @@ export const toModelMessagesEffect = Effect.fnUntraced(function* ( type: "text", text: part.text, }) - // text/plain and directory files are converted into text parts, ignore them - if (part.type === "file" && part.mime !== "text/plain" && part.mime !== "application/x-directory") { + // text/plain, directory files, and MCP resources are converted into text parts, ignore them + if ( + part.type === "file" && + part.mime !== "text/plain" && + part.mime !== "application/x-directory" && + part.source?.type !== "resource" + ) { if (options?.stripMedia && isMedia(part.mime)) { userMessage.parts.push({ type: "text", diff --git a/packages/opencode/test/session/message-v2.test.ts b/packages/opencode/test/session/message-v2.test.ts index 82bed0e9cc6f..99fe9a196288 100644 --- a/packages/opencode/test/session/message-v2.test.ts +++ b/packages/opencode/test/session/message-v2.test.ts @@ -316,6 +316,53 @@ describe("session.message-v2.toModelMessage", () => { ]) }) + test("does not forward resolved MCP resource parts as file downloads", async () => { + const messageID = "m-user" + + const input: MessageV2.WithParts[] = [ + { + info: userInfo(messageID), + parts: [ + { + ...basePart(messageID, "p1"), + type: "text", + text: "Reading MCP resource: status (status://info)", + synthetic: true, + }, + { + ...basePart(messageID, "p2"), + type: "text", + text: '{"ok":true}', + synthetic: true, + }, + { + ...basePart(messageID, "p3"), + type: "file", + mime: "application/json", + filename: "status", + url: "status://info", + source: { + type: "resource", + clientName: "status-server", + uri: "status://info", + text: { value: "@status", start: 0, end: 7 }, + }, + }, + ] as MessageV2.Part[], + }, + ] + + expect(await MessageV2.toModelMessages(input, model)).toStrictEqual([ + { + role: "user", + content: [ + { type: "text", text: "Reading MCP resource: status (status://info)" }, + { type: "text", text: '{"ok":true}' }, + ], + }, + ]) + }) + test("converts assistant tool completion into tool-call + tool-result messages with attachments", async () => { const userID = "m-user" const assistantID = "m-assistant"