dist-mcp / mcp / toolResults.test.js
dist-mcp / mcp / toolResults.test.js
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { buildGenericToolResult, buildMultiResultText, buildSingleResultText, buildUnslothToolResult } from "./toolResults.js";
async function withScratchpad(run) {
const scratchpadPath = await fs.mkdtemp(path.join(os.tmpdir(), "process-image-mcp-toolresults-"));
try {
await run(scratchpadPath);
}
finally {
await fs.rm(scratchpadPath, { recursive: true, force: true });
}
}
function makeResult(scratchpadPath, overrides = {}) {
return {
index: 1,
notation: "i1",
filename: "crop-a-i1.png",
absolutePath: path.join(scratchpadPath, "crop-a-i1.png"),
...overrides,
};
}
test("buildGenericToolResult includes a base64 image block per result when a preview file exists", async () => {
await withScratchpad(async (scratchpadPath) => {
await fs.writeFile(path.join(scratchpadPath, "preview-crop-a-i1.jpg"), "jpeg-bytes");
const result = makeResult(scratchpadPath, { previewFilename: "preview-crop-a-i1.jpg" });
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const imageBlock = content.find((c) => c.type === "image");
assert.ok(imageBlock, "expected an image content block");
assert.equal(imageBlock.data, Buffer.from("jpeg-bytes").toString("base64"));
});
});
test("buildGenericToolResult always includes a file:// original link, labeled with the iN notation", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const fileLink = content.find((c) => c.type === "text" && String(c.text).startsWith("Original i1: file://"));
assert.ok(fileLink, "expected a file:// original link");
assert.match(fileLink.text, /crop-a-i1\.png$/);
});
});
test("buildGenericToolResult omits the http:// original link when no local image server is reachable", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
assert.ok(!content.some((c) => c.type === "text" && String(c.text).includes("http://")));
});
});
test("buildGenericToolResult's closing hint reflects tier (process vs. final)", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const processResult = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const finalResult = await buildGenericToolResult("zoom-in", "final", scratchpadPath, "123", [result], undefined);
const processHint = processResult.content[processResult.content.length - 1];
const finalHint = finalResult.content[finalResult.content.length - 1];
assert.match(processHint.text, /intermediate\/working file\(s\)/);
assert.match(finalHint.text, /final result file\(s\)/);
assert.doesNotMatch(processHint.text, /open_url_in_app_browser|view_images|attach_file|bionic/i);
});
});
test("buildGenericToolResult forwards the summary text verbatim", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const summaryText = JSON.stringify({ originalWidth: 1024, originalHeight: 768 });
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], summaryText);
assert.ok(content.some((c) => c.type === "text" && c.text === summaryText));
});
});
test("buildGenericToolResult always mentions the display method (generic clients can't auto-display)", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const hint = content[content.length - 1];
assert.match(hint.text, /Use an appopriate method to show/);
});
});
test("buildUnslothToolResult never mentions the display method, and orders paragraphs image/produced/link/guidance/refer/metadata", async () => {
await withScratchpad(async (scratchpadPath) => {
await fs.writeFile(path.join(scratchpadPath, "preview-crop-a-i1.jpg"), "jpeg-bytes");
const result = makeResult(scratchpadPath, { previewFilename: "preview-crop-a-i1.jpg" });
const summaryText = JSON.stringify({ originalWidth: 1024, originalHeight: 768 });
const { content } = (await buildUnslothToolResult("crop", "process", scratchpadPath, "123", [result], summaryText));
assert.ok(!content.some((c) => typeof c.text === "string" && c.text.includes("Use an appopriate method to show")));
assert.equal(content[0].type, "image");
assert.equal(content[content.length - 1].text, summaryText);
assert.equal(content[content.length - 2].text, "Metadata for the generated file (from the tool call):");
});
});
// --- Bionic preset text builders (re-exported from made-for-bionic-core) ---
const bionicScratchpadPath = "/tmp/example-scratchpad";
const bionicReportPath = path.join(bionicScratchpadPath, "crop-req-1.html");
function makeBionicResult(overrides = {}) {
return {
index: 1,
notation: "i1",
filename: "crop-a-i1.png",
absolutePath: path.join(bionicScratchpadPath, "crop-a-i1.png"),
...overrides,
};
}
test("tier=process (crop/mask): always mentions open_url_in_app_browser, frames attach_file as optional only", () => {
const text = buildSingleResultText("crop", "process", makeBionicResult(), undefined);
assert.match(text, /open_url_in_app_browser/);
assert.match(text, /intermediate\/working result/);
});
test("tier=final (zoom-in/inpaint/outpaint): presents the image via attach_file guidance", () => {
const text = buildSingleResultText("zoom-in", "final", makeBionicResult(), undefined);
assert.match(text, /open_url_in_app_browser/);
assert.match(text, /If you choose to comment on how well the result matches your prompt/);
});
test("buildMultiResultText names the tool and follow-up notation for 2+ results", () => {
const text = buildMultiResultText("mask", "process", bionicReportPath, [makeBionicResult(), makeBionicResult({ index: 2, notation: "i2" })]);
assert.match(text, /^mask produced 2 file\(s\): i1, i2\./);
assert.match(text, /open_url_in_app_browser/);
assert.match(text, /`i1`/);
});
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { buildGenericToolResult, buildMultiResultText, buildSingleResultText, buildUnslothToolResult } from "./toolResults.js";
async function withScratchpad(run) {
const scratchpadPath = await fs.mkdtemp(path.join(os.tmpdir(), "process-image-mcp-toolresults-"));
try {
await run(scratchpadPath);
}
finally {
await fs.rm(scratchpadPath, { recursive: true, force: true });
}
}
function makeResult(scratchpadPath, overrides = {}) {
return {
index: 1,
notation: "i1",
filename: "crop-a-i1.png",
absolutePath: path.join(scratchpadPath, "crop-a-i1.png"),
...overrides,
};
}
test("buildGenericToolResult includes a base64 image block per result when a preview file exists", async () => {
await withScratchpad(async (scratchpadPath) => {
await fs.writeFile(path.join(scratchpadPath, "preview-crop-a-i1.jpg"), "jpeg-bytes");
const result = makeResult(scratchpadPath, { previewFilename: "preview-crop-a-i1.jpg" });
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const imageBlock = content.find((c) => c.type === "image");
assert.ok(imageBlock, "expected an image content block");
assert.equal(imageBlock.data, Buffer.from("jpeg-bytes").toString("base64"));
});
});
test("buildGenericToolResult always includes a file:// original link, labeled with the iN notation", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const fileLink = content.find((c) => c.type === "text" && String(c.text).startsWith("Original i1: file://"));
assert.ok(fileLink, "expected a file:// original link");
assert.match(fileLink.text, /crop-a-i1\.png$/);
});
});
test("buildGenericToolResult omits the http:// original link when no local image server is reachable", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
assert.ok(!content.some((c) => c.type === "text" && String(c.text).includes("http://")));
});
});
test("buildGenericToolResult's closing hint reflects tier (process vs. final)", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const processResult = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const finalResult = await buildGenericToolResult("zoom-in", "final", scratchpadPath, "123", [result], undefined);
const processHint = processResult.content[processResult.content.length - 1];
const finalHint = finalResult.content[finalResult.content.length - 1];
assert.match(processHint.text, /intermediate\/working file\(s\)/);
assert.match(finalHint.text, /final result file\(s\)/);
assert.doesNotMatch(processHint.text, /open_url_in_app_browser|view_images|attach_file|bionic/i);
});
});
test("buildGenericToolResult forwards the summary text verbatim", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const summaryText = JSON.stringify({ originalWidth: 1024, originalHeight: 768 });
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], summaryText);
assert.ok(content.some((c) => c.type === "text" && c.text === summaryText));
});
});
test("buildGenericToolResult always mentions the display method (generic clients can't auto-display)", async () => {
await withScratchpad(async (scratchpadPath) => {
const result = makeResult(scratchpadPath);
const { content } = await buildGenericToolResult("crop", "process", scratchpadPath, "123", [result], undefined);
const hint = content[content.length - 1];
assert.match(hint.text, /Use an appopriate method to show/);
});
});
test("buildUnslothToolResult never mentions the display method, and orders paragraphs image/produced/link/guidance/refer/metadata", async () => {
await withScratchpad(async (scratchpadPath) => {
await fs.writeFile(path.join(scratchpadPath, "preview-crop-a-i1.jpg"), "jpeg-bytes");
const result = makeResult(scratchpadPath, { previewFilename: "preview-crop-a-i1.jpg" });
const summaryText = JSON.stringify({ originalWidth: 1024, originalHeight: 768 });
const { content } = (await buildUnslothToolResult("crop", "process", scratchpadPath, "123", [result], summaryText));
assert.ok(!content.some((c) => typeof c.text === "string" && c.text.includes("Use an appopriate method to show")));
assert.equal(content[0].type, "image");
assert.equal(content[content.length - 1].text, summaryText);
assert.equal(content[content.length - 2].text, "Metadata for the generated file (from the tool call):");
});
});
// --- Bionic preset text builders (re-exported from made-for-bionic-core) ---
const bionicScratchpadPath = "/tmp/example-scratchpad";
const bionicReportPath = path.join(bionicScratchpadPath, "crop-req-1.html");
function makeBionicResult(overrides = {}) {
return {
index: 1,
notation: "i1",
filename: "crop-a-i1.png",
absolutePath: path.join(bionicScratchpadPath, "crop-a-i1.png"),
...overrides,
};
}
test("tier=process (crop/mask): always mentions open_url_in_app_browser, frames attach_file as optional only", () => {
const text = buildSingleResultText("crop", "process", makeBionicResult(), undefined);
assert.match(text, /open_url_in_app_browser/);
assert.match(text, /intermediate\/working result/);
});
test("tier=final (zoom-in/inpaint/outpaint): presents the image via attach_file guidance", () => {
const text = buildSingleResultText("zoom-in", "final", makeBionicResult(), undefined);
assert.match(text, /open_url_in_app_browser/);
assert.match(text, /If you choose to comment on how well the result matches your prompt/);
});
test("buildMultiResultText names the tool and follow-up notation for 2+ results", () => {
const text = buildMultiResultText("mask", "process", bionicReportPath, [makeBionicResult(), makeBionicResult({ index: 2, notation: "i2" })]);
assert.match(text, /^mask produced 2 file\(s\): i1, i2\./);
assert.match(text, /open_url_in_app_browser/);
assert.match(text, /`i1`/);
});