UNPKG

@debugg-ai/debugg-ai-mcp

Version:

Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.

99 lines (98 loc) 3.54 kB
/** * Image utility helpers for MCP image content blocks */ import axios from 'axios'; /** * Fetch an image from a URL and return its base64-encoded data + MIME type. * Returns null on any error (image embedding is always best-effort). */ export async function fetchImageAsBase64(url) { try { const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 15_000, headers: { Accept: 'image/*' }, }); const buffer = Buffer.from(response.data); const base64 = buffer.toString('base64'); const rawContentType = response.headers['content-type'] ?? ''; const mimeType = rawContentType.split(';')[0].trim() || inferMimeFromUrl(url); return { data: base64, mimeType }; } catch { return null; } } function inferMimeFromUrl(url) { const path = url.split('?')[0].toLowerCase(); if (path.endsWith('.gif')) return 'image/gif'; if (path.endsWith('.jpg') || path.endsWith('.jpeg')) return 'image/jpeg'; if (path.endsWith('.webp')) return 'image/webp'; return 'image/png'; } /** * Build an MCP image content block from base64 data. */ export function imageContentBlock(data, mimeType) { return { type: 'image', data, mimeType }; } /** * Build an MCP resource_link content block (MCP 2025-06-18) pointing at an * (often presigned) artifact URL — leaner than inlining the bytes, and the URL * stays renewable/on-demand. Use for large non-vision artifacts (run-recording * GIFs, HAR, console logs) rather than base64-embedding them. */ export function resourceLinkBlock(uri, name, opts = {}) { const block = { type: 'resource_link', uri, name }; if (opts.mimeType) block.mimeType = opts.mimeType; if (opts.title) block.title = opts.title; if (opts.description) block.description = opts.description; return block; } const MIME_BY_EXT = { gif: 'image/gif', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp', mp4: 'video/mp4', webm: 'video/webm', har: 'application/json', json: 'application/json', txt: 'text/plain', log: 'text/plain', }; function titleize(key) { return key .replace(/[_-]+/g, ' ') .replace(/([a-z])([A-Z])/g, '$1 $2') .replace(/\bUrl\b|\bUri\b/gi, '') .replace(/\s+/g, ' ') .trim() .replace(/^\w/, (c) => c.toUpperCase()); } /** * Build resource_link blocks for every (presigned) artifact URL found one level * deep in `source` (e.g. an execution's browserSession: HAR, console log, run * recording). Defensive about exact field names — it links any https value and * skips tunnel/ngrok hosts. Returns [] for nullish/empty input. */ export function artifactResourceLinks(source) { if (!source || typeof source !== 'object') return []; const out = []; for (const [key, value] of Object.entries(source)) { if (typeof value !== 'string') continue; if (!/^https?:\/\//i.test(value)) continue; if (/ngrok|tunnel/i.test(value)) continue; const ext = (value.split('?')[0].match(/\.([a-z0-9]+)$/i)?.[1] ?? '').toLowerCase(); const name = ext ? `${key}.${ext}` : key; out.push(resourceLinkBlock(value, name, { mimeType: MIME_BY_EXT[ext], title: titleize(key), description: 'Execution artifact (presigned URL — open or fetch on demand).', })); } return out; }