Spaces:
Running
Running
File size: 3,149 Bytes
3d97d52 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
/**
* 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);
}
}
|