Files
FEDEO/backend/src/mcp/result.ts
florianfederspiel 544d607088 feat(mcp): Vorschau von Ausgangsbelegen mit Seitenbildern und Umbruchprüfung
- accounting.outgoing_documents.preview rendert den Beleg wie der Editor und liefert Seiten als PNG-Bilder im MCP-Ergebnis
- Layout je Seite in mm mit Warnungen zu übervollen Seiten, allein stehenden Überschriften, leeren Seiten und vom Überweisungs-QR-Code überdeckten Beträgen samt Vorschlag für den Seitenumbruch
- rows testet geänderte Positionen samt Seitenumbrüchen, ohne zu speichern
- Belegdaten für das PDF als Backend-Port von getDocumentData (Titelsummen, Überträge, Abschläge, Optional-/Alternativpositionen)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 08:29:17 +01:00

71 lines
2.3 KiB
TypeScript

import { McpToolResult } from "./types"
const OMIT_ARCHIVED = Symbol("omit-archived")
/** Attach images to a tool payload; they become MCP image content next to the JSON text. */
export const MCP_IMAGES = Symbol("mcp-images")
export type McpImage = { data: string, mimeType: string }
export const withImages = <T extends Record<string, unknown>>(payload: T, images: McpImage[]) =>
Object.assign(payload, { [MCP_IMAGES]: images })
function omitArchivedRecords(value: unknown): unknown | typeof OMIT_ARCHIVED {
if (Array.isArray(value)) {
return value
.map(omitArchivedRecords)
.filter((item) => item !== OMIT_ARCHIVED)
}
if (!value || typeof value !== "object") return value
const prototype = Object.getPrototypeOf(value)
if (prototype !== Object.prototype && prototype !== null) return value
const record = value as Record<string, unknown>
if (record.archived === true) return OMIT_ARCHIVED
return Object.fromEntries(
Object.entries(record)
.map(([key, item]) => [key, omitArchivedRecords(item)] as const)
.filter(([, item]) => item !== OMIT_ARCHIVED),
)
}
export function asToolResult(payload: unknown): McpToolResult {
const sanitizedPayload = omitArchivedRecords(payload)
const resultPayload = sanitizedPayload === OMIT_ARCHIVED ? {} : sanitizedPayload
const structuredContent =
resultPayload && typeof resultPayload === "object" && !Array.isArray(resultPayload)
? resultPayload as Record<string, unknown>
: { result: resultPayload }
const images: McpImage[] = (payload as any)?.[MCP_IMAGES] || []
return {
content: [
{
type: "text",
text: JSON.stringify(resultPayload, null, 2),
},
...images.map((image) => ({ type: "image" as const, data: image.data, mimeType: image.mimeType })),
],
structuredContent,
}
}
export function asToolError(error: unknown): McpToolResult {
const message = error instanceof Error ? error.message : "Unbekannter Fehler"
return {
content: [
{
type: "text",
text: message,
},
],
isError: true,
structuredContent: {
error: message,
},
}
}