Preview SVG
previewRender SVG content to PNG images for visual inspection. Use after rendering to review and iterate on SVG designs until the result matches intent.
Instructions
Render SVG content to a PNG image so the AI can visually inspect the output.
When to use:
After render_svg, call preview to see what was generated
Use in a revision loop: render → preview → critique → revise → preview again
Stop when the visual result matches the intent
Behavior:
Returns a PNG image (base64) rendered from the SVG string
Background is transparent by default
CSS animations and SMIL are rendered as a static snapshot (t=0) — motion is not captured
Format is auto-detected from content; pass format: "svg" explicitly if needed
Width:
Omit width to use the SVG's own declared width/viewBox
Pass width to scale the output (useful for small SVGs that need a larger preview)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| content | Yes | SVG string to render as PNG | |
| format | No | Content format; auto-detected from content if omitted | |
| width | No | Render width in pixels; defaults to SVG's own declared width |
Implementation Reference
- src/index.ts:154-208 (registration)Registration of the 'preview' tool on the MCP server with input schema (content, format, width) and async handler.
server.registerTool( "preview", { title: "Preview SVG", description: ` Render SVG content to a PNG image so the AI can visually inspect the output. **When to use:** - After render_svg, call preview to see what was generated - Use in a revision loop: render → preview → critique → revise → preview again - Stop when the visual result matches the intent **Behavior:** - Returns a PNG image (base64) rendered from the SVG string - Background is transparent by default - CSS animations and SMIL are rendered as a static snapshot (t=0) — motion is not captured - Format is auto-detected from content; pass format: "svg" explicitly if needed **Width:** - Omit width to use the SVG's own declared width/viewBox - Pass width to scale the output (useful for small SVGs that need a larger preview) `.trim(), inputSchema: z.object({ content: z.string().describe("SVG string to render as PNG"), format: z .enum(["svg", "html"]) .optional() .describe("Content format; auto-detected from content if omitted"), width: z .number() .positive() .optional() .describe("Render width in pixels; defaults to SVG's own declared width"), }), }, async ({ content, format, width }) => { const start = Date.now(); try { const pngBuffer = renderPreview(content, format, width); const base64 = pngBuffer.toString("base64"); const elapsed = Date.now() - start; console.error(`[nakkas] preview OK — ${pngBuffer.length} bytes, ${elapsed}ms`); return { content: [{ type: "image", data: base64, mimeType: "image/png" }], }; } catch (err) { const message = err instanceof Error ? err.message : String(err); console.error(`[nakkas] preview ERROR — ${message}`); return { content: [{ type: "text", text: `Error rendering preview: ${message}` }], isError: true, }; } } ); - src/index.ts:189-208 (handler)Handler function for the 'preview' tool: calls renderPreview(), converts result to base64 PNG, and returns it as an image response.
async ({ content, format, width }) => { const start = Date.now(); try { const pngBuffer = renderPreview(content, format, width); const base64 = pngBuffer.toString("base64"); const elapsed = Date.now() - start; console.error(`[nakkas] preview OK — ${pngBuffer.length} bytes, ${elapsed}ms`); return { content: [{ type: "image", data: base64, mimeType: "image/png" }], }; } catch (err) { const message = err instanceof Error ? err.message : String(err); console.error(`[nakkas] preview ERROR — ${message}`); return { content: [{ type: "text", text: `Error rendering preview: ${message}` }], isError: true, }; } } ); - src/preview.ts:53-74 (helper)Public API function that auto-detects format and dispatches to the appropriate renderer (currently SVG only, HTML throws).
export function renderPreview( content: string, format?: "svg" | "html", width?: number ): Buffer { const detected = format ?? autoDetectFormat(content); if (detected === "svg") { return svgToPng(content, width); } if (detected === "html") { throw new Error( "HTML preview is not yet supported. Provide SVG content, or check back for a future update." ); } throw new Error( "Could not detect content format. Ensure content starts with <svg or <html, " + "or explicitly pass format: 'svg'." ); } - src/preview.ts:23-28 (helper)Detects content format (svg/html/unknown) by inspecting the leading tag of the content string.
export function autoDetectFormat(content: string): PreviewFormat { const trimmed = content.trimStart(); if (/^<svg[\s>]/i.test(trimmed)) return "svg"; if (/^<!doctype html|^<html[\s>]/i.test(trimmed)) return "html"; return "unknown"; } - src/preview.ts:35-40 (helper)Renders an SVG string to a transparent PNG buffer using resvg-js, optionally fitting to a given width.
export function svgToPng(svgString: string, width?: number): Buffer { const opts = width ? { fitTo: { mode: "width" as const, value: width } } : {}; const resvg = new Resvg(svgString, opts); const pngData = resvg.render(); return Buffer.from(pngData.asPng()); }