src / mcp / toolResults.ts
src / mcp / toolResults.ts
import path from "node:path";
import { pathToFileURL } from "node:url";
import type { MaterializedResult } from "./resultMaterializer.js";
function buildVideoNote(scratchpadPath: string, results: MaterializedResult[]): string {
const videoResults = results.filter((r) => r.isVideo && r.videoFilename);
if (videoResults.length === 0) return "";
return `\n\nVideo result(s) were generated: the preview above is a single still frame, not the video itself. To deliver the actual video to the user, call \`attach_file\` on the video file directly: ${videoResults
.map((r) => path.join(scratchpadPath, r.videoFilename as string))
.join(", ")}.`;
}
/**
* Bionic-guided result text, following the same report-open -> agent-verify -> user-display
* -> follow-up-handle pattern as find-images-mcp-bridge's findImageToolResult() (see
* planning/bionic-mcp-architecture.md, "Resultatgesteuerte Agentenführung").
*
* Used only for 2+ results — see generateSingleImageToolResult() for the single-result case,
* which skips the HTML report entirely.
*/
export function generateImageToolResult(tool: "generate_image" | "upscale", reportPath: string, results: MaterializedResult[]): string {
const scratchpadPath = path.dirname(reportPath);
const reportFileName = path.basename(reportPath);
const notations = results.map((r) => r.notation).join(", ");
const viewPaths = results.map((r) => r.absolutePath);
const viewPayload = JSON.stringify({ paths: viewPaths, quality: "medium" }, null, 2);
const openPayload = JSON.stringify({ opened: true, url: pathToFileURL(reportPath).href }, null, 2);
const primaryNotation = results[0]?.notation ?? "i1";
const videoNote = buildVideoNote(scratchpadPath, results);
return `${tool} produced ${results.length} file(s): ${notations}. ${reportFileName} provides an overview. Immediately call "open_url_in_app_browser" with the JSON below. Do not call any other tool and do not write a user-facing response before this call completes.\n\n${openPayload}\n\nAfter the HTML report is open, use "view_images" to inspect the result(s) yourself. This tool does not display images to the user:\n\n${viewPayload}\n\nThen present the result to the user in the chat. To show an image to the user, first call \`attach_file(path="${viewPaths[0] ?? ""}")\`, then write its Markdown link directly in the chat. Example: .${videoNote}\n\nRefer to a result in a subsequent generate_image or upscale call as its \`${primaryNotation}\` canvas/moodboard identifier.\n\nAll metadata for the generated file(s) is available in ${reportFileName}.`;
}
/**
* Single-result variant: no HTML report is written or referenced. The in-app browser opens the
* generated file's own preview directly, and metadata is inlined 1:1 from the tool call's own
* summary object (same as the LM Studio plugin path's raw JSON.stringify(summary) content item),
* instead of pointing at a report file.
*/
export function generateSingleImageToolResult(tool: "generate_image" | "upscale", result: MaterializedResult, summary: Record<string, unknown> | undefined): string {
const scratchpadPath = path.dirname(result.absolutePath);
const viewPayload = JSON.stringify({ paths: [result.absolutePath], quality: "medium" }, null, 2);
const openPayload = JSON.stringify({ opened: true, url: pathToFileURL(result.absolutePath).href }, null, 2);
const videoNote = buildVideoNote(scratchpadPath, [result]);
return `${tool} produced 1 file: ${result.notation}. Immediately call "open_url_in_app_browser" with the JSON below to show the preview to the user. Do not call any other tool and do not write a user-facing response before this call completes.\n\n${openPayload}\n\nAfter the preview is open, use "view_images" to inspect the result yourself. This tool does not display images to the user:\n\n${viewPayload}\n\nThen present the result to the user in the chat. To show an image to the user, first call \`attach_file(path="${result.absolutePath}")\`, then write its Markdown link directly in the chat. Example: .${videoNote}\n\nRefer to this result in a subsequent generate_image or upscale call as its \`${result.notation}\` canvas/moodboard identifier.\n\nMetadata for the generated file (from the tool call):\n\n${JSON.stringify(summary ?? {})}`;
}
import path from "node:path";
import { pathToFileURL } from "node:url";
import type { MaterializedResult } from "./resultMaterializer.js";
function buildVideoNote(scratchpadPath: string, results: MaterializedResult[]): string {
const videoResults = results.filter((r) => r.isVideo && r.videoFilename);
if (videoResults.length === 0) return "";
return `\n\nVideo result(s) were generated: the preview above is a single still frame, not the video itself. To deliver the actual video to the user, call \`attach_file\` on the video file directly: ${videoResults
.map((r) => path.join(scratchpadPath, r.videoFilename as string))
.join(", ")}.`;
}
/**
* Bionic-guided result text, following the same report-open -> agent-verify -> user-display
* -> follow-up-handle pattern as find-images-mcp-bridge's findImageToolResult() (see
* planning/bionic-mcp-architecture.md, "Resultatgesteuerte Agentenführung").
*
* Used only for 2+ results — see generateSingleImageToolResult() for the single-result case,
* which skips the HTML report entirely.
*/
export function generateImageToolResult(tool: "generate_image" | "upscale", reportPath: string, results: MaterializedResult[]): string {
const scratchpadPath = path.dirname(reportPath);
const reportFileName = path.basename(reportPath);
const notations = results.map((r) => r.notation).join(", ");
const viewPaths = results.map((r) => r.absolutePath);
const viewPayload = JSON.stringify({ paths: viewPaths, quality: "medium" }, null, 2);
const openPayload = JSON.stringify({ opened: true, url: pathToFileURL(reportPath).href }, null, 2);
const primaryNotation = results[0]?.notation ?? "i1";
const videoNote = buildVideoNote(scratchpadPath, results);
return `${tool} produced ${results.length} file(s): ${notations}. ${reportFileName} provides an overview. Immediately call "open_url_in_app_browser" with the JSON below. Do not call any other tool and do not write a user-facing response before this call completes.\n\n${openPayload}\n\nAfter the HTML report is open, use "view_images" to inspect the result(s) yourself. This tool does not display images to the user:\n\n${viewPayload}\n\nThen present the result to the user in the chat. To show an image to the user, first call \`attach_file(path="${viewPaths[0] ?? ""}")\`, then write its Markdown link directly in the chat. Example: .${videoNote}\n\nRefer to a result in a subsequent generate_image or upscale call as its \`${primaryNotation}\` canvas/moodboard identifier.\n\nAll metadata for the generated file(s) is available in ${reportFileName}.`;
}
/**
* Single-result variant: no HTML report is written or referenced. The in-app browser opens the
* generated file's own preview directly, and metadata is inlined 1:1 from the tool call's own
* summary object (same as the LM Studio plugin path's raw JSON.stringify(summary) content item),
* instead of pointing at a report file.
*/
export function generateSingleImageToolResult(tool: "generate_image" | "upscale", result: MaterializedResult, summary: Record<string, unknown> | undefined): string {
const scratchpadPath = path.dirname(result.absolutePath);
const viewPayload = JSON.stringify({ paths: [result.absolutePath], quality: "medium" }, null, 2);
const openPayload = JSON.stringify({ opened: true, url: pathToFileURL(result.absolutePath).href }, null, 2);
const videoNote = buildVideoNote(scratchpadPath, [result]);
return `${tool} produced 1 file: ${result.notation}. Immediately call "open_url_in_app_browser" with the JSON below to show the preview to the user. Do not call any other tool and do not write a user-facing response before this call completes.\n\n${openPayload}\n\nAfter the preview is open, use "view_images" to inspect the result yourself. This tool does not display images to the user:\n\n${viewPayload}\n\nThen present the result to the user in the chat. To show an image to the user, first call \`attach_file(path="${result.absolutePath}")\`, then write its Markdown link directly in the chat. Example: .${videoNote}\n\nRefer to this result in a subsequent generate_image or upscale call as its \`${result.notation}\` canvas/moodboard identifier.\n\nMetadata for the generated file (from the tool call):\n\n${JSON.stringify(summary ?? {})}`;
}