Get Report
get_reportGenerate or retrieve a detailed markdown prediction report for a completed simulation. Use force_regenerate to rebuild cached reports.
Instructions
Generate and retrieve the prediction report for a completed simulation. If the report hasn't been generated yet, triggers generation (may take 1-3 minutes). Returns a detailed markdown analysis ready to display as an artifact in the side panel. Pass force_regenerate=true to rebuild an already-cached report.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| simulation_id | Yes | The simulation ID to generate/fetch a report for | |
| force_regenerate | No | If true, invalidates any cached report and runs a fresh ReportAgent pass. Useful after backend prompt or validator changes. Off by default — reports are cached once generated, so repeat calls are free. |
Implementation Reference
- mcp-server/src/tools/get-report.ts:21-99 (handler)registerGetReport function – the main handler that calls the client's getOrGenerateReport, checks status, extracts summary, and returns metadata + full markdown content.
export function registerGetReport(server: McpServer, client: MirofishClient): void { server.registerTool( "get_report", { title: "Get Report", description: "Generate and retrieve the prediction report for a completed simulation. " + "If the report hasn't been generated yet, triggers generation (may take 1-3 minutes). " + "Returns a detailed markdown analysis ready to display as an artifact in the side panel. " + "Pass force_regenerate=true to rebuild an already-cached report.", inputSchema, annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true }, }, async (args) => { try { const report = await client.getOrGenerateReport( args.simulation_id, args.force_regenerate ?? false, ); // Still generating or failed — return status only if (report.status !== "completed" || !report.markdown_content) { const statusMsg = report.status === "generating" ? "Report is still being generated. Call get_report again in a minute." : `Report status: ${report.status}`; return { content: [ { type: "text" as const, text: JSON.stringify( { report_id: report.report_id, simulation_id: report.simulation_id, status: report.status, message: statusMsg, }, null, 2, ), }, ], }; } // Extract summary from the report (first paragraph after title) const firstBlockquote = report.markdown_content.match(/^>\s*(.+?)$/m); const summary = firstBlockquote?.[1] ?? "Prediction report ready."; // Return metadata + full markdown as separate content items. // Claude Desktop and Claude Code will render the markdown block as an // artifact in the side panel when the LLM reproduces it in its response. const metadata = { report_id: report.report_id, simulation_id: report.simulation_id, status: "completed", summary, display_instructions: "The full prediction report is included below as markdown. " + "Output the markdown directly to the user — Claude Desktop will render " + "it as an artifact in the side panel. Do not summarize or truncate.", markdown_content: report.markdown_content, }; return { content: [ { type: "text" as const, text: JSON.stringify(metadata, null, 2), }, ], }; } catch (err) { throw toMcpError(err); } }, ); } - Input schema using Zod: simulation_id (required string) and force_regenerate (optional boolean).
const inputSchema = { simulation_id: z.string().describe("The simulation ID to generate/fetch a report for"), force_regenerate: z.coerce .boolean() .optional() .describe( "If true, invalidates any cached report and runs a fresh ReportAgent pass. " + "Useful after backend prompt or validator changes. Off by default — reports " + "are cached once generated, so repeat calls are free.", ), }; - mcp-server/src/tools/index.ts:8-26 (registration)Import and call of registerGetReport in registerAllTools to register the tool with the MCP server.
import { registerGetReport } from "./get-report.js"; import { registerInterviewAgent } from "./interview-agent.js"; import { registerListSimulations } from "./list-simulations.js"; import { registerSearchSimulations } from "./search-simulations.js"; import { registerUploadDocument } from "./upload-document.js"; import { registerSimulationData } from "./simulation-data.js"; import { registerCancelSimulation } from "./cancel-simulation.js"; export function registerAllTools(server: McpServer, client: MirofishClient): void { registerCreateSimulation(server, client); registerSimulationStatus(server, client); registerGetReport(server, client); registerInterviewAgent(server, client); registerListSimulations(server, client); registerSearchSimulations(server, client); registerUploadDocument(server, client); registerSimulationData(server, client); registerCancelSimulation(server, client); } - getOrGenerateReport method: checks for cached report via GET /api/report/by-simulation/:id, or POSTs to /api/report/generate to trigger generation, then polls up to 10 minutes for completion.
async getOrGenerateReport( simulationId: string, forceRegenerate = false, ): Promise<Report> { // First check if a cached report exists. if (!forceRegenerate) { try { const existing = await this.http.get<MirofishApiResponse<Report>>( `/api/report/by-simulation/${simulationId}`, ); if (existing.data?.data?.status === "completed") { return existing.data.data; } } catch { // Not cached — fall through to generate. } } // Kick off generation (returns task_id + report_id) const genResp = await this.http.post<MirofishApiResponse<{ report_id: string; task_id: string }>>( "/api/report/generate", { simulation_id: simulationId, force_regenerate: forceRegenerate }, ); const data = genResp.data?.data; if (!data) { throw new MirofishBackendError("Report generation didn't return task_id", 500); } // Poll for completion (up to 10 min) for (let i = 0; i < 300; i++) { await new Promise((r) => setTimeout(r, 2000)); try { const r = await this.http.get<MirofishApiResponse<Report>>( `/api/report/by-simulation/${simulationId}`, ); const rep = r.data?.data; if (rep?.status === "completed") return rep; if (rep?.status === "failed") { throw new MirofishBackendError("Report generation failed", 500); } } catch (err: unknown) { if (axios.isAxiosError(err) && err.response?.status === 404) continue; throw err; } } throw new MirofishBackendError("Report generation timed out", 504); } - Type definitions: ReportStatus (generating|completed|failed), Report interface (report_id, simulation_id, status, markdown_content, etc.).
export type ReportStatus = "generating" | "completed" | "failed"; export interface Report { report_id: string; simulation_id: string; status: ReportStatus; outline?: { title: string; summary: string; sections: ReportSection[] }; markdown_content?: string; created_at: string; completed_at?: string; }