/** * Vendored from `@huggingface/mcp-client` * * https://github.com/huggingface/huggingface.js/blob/main/packages/mcp-client/src/ResultFormatter.ts */ import type { TextResourceContents, BlobResourceContents, CompatibilityCallToolResult, } from "@modelcontextprotocol/sdk/types"; /** * A utility class for formatting CallToolResult contents into human-readable text. * Processes different content types, extracts text, and summarizes binary data. */ export class McpResultFormatter { /** * Formats a CallToolResult's contents into a single string. * - Text content is included directly * - Binary content (images, audio, blobs) is summarized * * @param result The CallToolResult to format * @returns A human-readable string representation of the result contents */ static format(result: CompatibilityCallToolResult): string { if (!result.content || !Array.isArray(result.content) || result.content.length === 0) { return "[No content]"; } const formattedParts: string[] = []; for (const item of result.content) { switch (item.type) { case "text": // Extract text content directly formattedParts.push(item.text); break; case "image": { // Summarize image content const imageSize = this.getBase64Size(item.data); formattedParts.push( `[Binary Content: Image ${item.mimeType}, ${imageSize} bytes]\nThe task is complete and the content accessible to the User` ); break; } case "audio": { // Summarize audio content const audioSize = this.getBase64Size(item.data); formattedParts.push( `[Binary Content: Audio ${item.mimeType}, ${audioSize} bytes]\nThe task is complete and the content accessible to the User` ); break; } case "resource": // Handle embedded resources - explicitly type the resource if ("text" in item.resource) { // It's a text resource with a text property const textResource = item.resource as TextResourceContents; formattedParts.push(textResource.text); } else if ("blob" in item.resource) { // It's a binary resource with a blob property const blobResource = item.resource as BlobResourceContents; const blobSize = this.getBase64Size(blobResource.blob); const uri = blobResource.uri ? ` (${blobResource.uri})` : ""; const mimeType = blobResource.mimeType ? blobResource.mimeType : "unknown type"; formattedParts.push( `[Binary Content${uri}: ${mimeType}, ${blobSize} bytes]\nThe task is complete and the content accessible to the User` ); } break; } } return formattedParts.join("\n"); } /** * Calculates the approximate size in bytes of base64-encoded data */ private static getBase64Size(base64: string): number { // Remove base64 header if present (e.g., data:image/png;base64,) const cleanBase64 = base64.includes(",") ? base64.split(",")[1] : base64; // Calculate size: Base64 encodes 3 bytes into 4 characters const padding = cleanBase64.endsWith("==") ? 2 : cleanBase64.endsWith("=") ? 1 : 0; return Math.floor((cleanBase64.length * 3) / 4 - padding); } }