Skip to main content
Glama

Nakkas is an MCP (Model Context Protocol) server that lets AI assistants like Claude create animated SVG graphics from a declarative JSON config: logos, icons, loading spinners, GitHub README banners, badges, and generative art. It renders CSS @keyframes and SMIL animations with no JavaScript, so the output works inside GitHub READMEs and anywhere an <img> tag renders SVG. Every render comes back as a PNG preview plus a server-side artifact id, so the AI sees its own work immediately and iterates without the SVG text ever passing through its context window.

nakkaş means painter/artist in Turkish (old).

"make a neon terminal logo with animated binary digits"
  → AI constructs JSON config
  → nakkas renders to animated SVG
  → AI previews the PNG, critiques, revises
  → clean animated SVG output

Why

  • One tool, infinite designs. render_svg takes a JSON config. AI fills in everything.

  • The AI sees its own work. Every render returns a PNG preview, so the model critiques and revises instead of designing blind.

  • Token-cheap iteration. The SVG stays on the server as an artifact; preview and save address it by id, so revision loops don't pay for the SVG text.

  • Pure declarative SVG. CSS @keyframes + SMIL animations, no JavaScript. Survives GitHub's camo proxy.

  • Zero external deps. No cloud API, no API keys. Runs locally.

Related MCP server: inkscape_mcp

Install

Claude Desktop

Add to your config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Linux: ~/.config/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "nakkas": {
      "command": "npx",
      "args": ["-y", "nakkas@latest"]
    }
  }
}

Claude Code (CLI)

claude mcp add nakkas npx nakkas@latest

Cursor / Zed / Other MCP clients

{
  "mcpServers": {
    "nakkas": {
      "command": "npx",
      "args": ["-y", "nakkas@latest"]
    }
  }
}

Local Development

git clone https://github.com/arikusi/nakkas
cd nakkas
npm install && npm run build
# Use dist/index.js as the command

Quick Start

Ask your AI (with Nakkas connected):

"Make an animated SVG: dark terminal frame (800×200), glowing cyan text 'NAKKAS', neon glow filter, fade-in on load."

"Create a loading spinner: a circle with a draw-on stroke animation that loops every 1.5 seconds."

"Data visualization: animated bar chart, 5 bars, each fading in with a staggered delay, gradient fills."

"Profile badge (400×120): blue-to-purple gradient, white username text, drop shadow, subtle pulse animation."

Tools

Nakkas provides three tools:

Tool

Purpose

render_svg

Takes SVGConfig JSON, returns a PNG preview + artifact id (+ design analysis warnings)

preview

Re-renders a stored artifact (or raw SVG) to PNG at any width

save

Saves a stored artifact (or raw content) to disk as SVG (text) or PNG (raster)

The intended workflow: render → look at the returned preview → revise the config → render again → save. The rendered SVG stays on the server as an artifact: render_svg answers with the preview image and an id like art-1, and preview/save accept that id directly. The SVG text never has to travel back through the model's context window, which cuts the token cost of an iteration loop to a fraction of pasting SVG around. Artifacts live for the server process lifetime (capped at 32, oldest evicted).

The save Tool

{ "artifact": "art-1", "outputPath": "./design.svg", "format": "auto" }

Pass either artifact (id from render_svg, preferred) or content (raw string). Formats: auto (infers from extension), svg (text file), png (renders to raster first). If the file exists, a numeric counter is appended to prevent overwriting. The actual saved path is returned.

The render_svg Tool

Input: SVGConfig JSON object Output: PNG preview image + a summary naming the artifact id, plus optional design analysis notes

The response shape is controlled by an optional output block in the config:

{ "output": { "svg": false, "preview": true, "previewWidth": 800, "minify": false } }
  • svg: true includes the full SVG text in the response (off by default; the artifact id covers preview and save)

  • preview: false skips the PNG image

  • previewWidth scales the preview

  • minify: true collapses whitespace in the stored and saved SVG

  • frames: N (2 to 10) replaces the static preview with one filmstrip image sampling the CSS animations at N points in time — the way to verify motion, since a single preview only shows the starting state

With frames, nakkas evaluates the @keyframes math itself (duration, delay, iteration count, direction, fill mode, easing per segment) and bakes each sampled state into a static frame. Transform origins declared as transform-box: fill-box are resolved numerically from the element's geometry. SMIL animations are not sampled.

After rendering, the response may include design warnings about common issues such as too many concurrent animations, missing transformBox, group-level scale transforms, content extending past the viewport (measured from the real rendered bounding box, with the overflow in pixels), or low-contrast text against the canvas background (WCAG ratios). Text gets its own layout audit: every text element's ink bounding box is measured through an isolated render, so text escaping the viewport is named with its exact overflow, and two texts printed over each other come back as an overlap warning naming both.

SVGConfig Structure

{
  canvas: {
    width: number | string,   // e.g. 800 or "100%"
    height: number | string,
    viewBox?: string,          // "0 0 800 400"
    background?: string        // hex "#111111" or "transparent"
  },

  defs?: {
    gradients?: Gradient[],   // linearGradient | radialGradient
    filters?: Filter[],        // preset or raw primitives
    clipPaths?: ClipPath[],
    masks?: Mask[],
    symbols?: Symbol[],
    paths?: { id, d }[],      // for textPath elements
    patterns?: Pattern[],     // repeating tile fills
    markers?: Marker[]        // arrowheads: triangle | arrow | circle | square | diamond | bar
  },

  elements: Element[],         // shapes, text, groups, use instances

  animations?: CSSAnimation[]  // CSS @keyframes definitions
}

Element Types

Type

Required fields

Notes

rect

width, height

x, y default 0; rx/ry for rounded corners

circle

r

cx, cy default 0

ellipse

rx, ry

Independent horizontal/vertical radii

line

x1, y1, x2, y2

markerStart/markerEnd take a marker id from defs.markers

polyline

points

Open path: "10,20 50,80 90,20"; supports markerStart/markerMid/markerEnd

polygon

points

Auto-closed shape

path

d

Full SVG path commands

image

href, width, height

URL or data:image/... URI for embedded images

text

content

String or (string | Tspan)[] array

textPath

pathId, text

Text following a curve; path defined in defs.paths

group

children

Shared attrs applied to all children (no nested groups)

use

href

Instance a symbol or clone an element by #id

radial-group

cx, cy, count, radius, child

Place N copies around a full circle

arc-group

cx, cy, radius, count, startAngle, endAngle, child

Place N copies along a circular arc

grid-group

cols, rows, colSpacing, rowSpacing, child

Place copies in an M by N grid

scatter-group

width, height, count, seed, child

Scatter N copies at seeded random positions

path-group

waypoints, count, child

Distribute N copies evenly along a polyline

parametric

fn

Mathematical curve: rose, heart, star, lissajous, spiral, superformula, epitrochoid, hypotrochoid, wave

Two field names differ from raw SVG on purpose: the string of a text element goes in content (on textPath it is text), and validation errors will point you to the exact field if you mix them up. Pattern groups rotate each copy to face outward by default; set rotateChildren: false when the child is text or any shape that should stay upright.

All Visual Elements (Shared Fields)

{
  id?: string,             // required for filter/gradient/clip references
  cssClass?: string,       // matches CSS animation names
  fill?: string,           // "#rrggbb" | "none" | "url(#gradId)"
  stroke?: string,
  strokeWidth?: number,
  strokeDasharray?: string, // "10 5", use for draw-on animation
  strokeDashoffset?: number,
  opacity?: number,        // 0–1
  filter?: string,         // "url(#filterId)"
  clipPath?: string,       // "url(#clipId)"
  transform?: string,      // "rotate(45)" "translate(100, 50)"
  transformBox?: "fill-box" | "view-box" | "stroke-box",  // set "fill-box" for CSS rotation
  transformOrigin?: string, // "center", works with fill-box
  smilAnimations?: SMILAnimation[]
}

Filter Presets

Reference as filter: "url(#myId)" on any element after defining in defs.filters:

{ "type": "preset", "id": "myGlow", "preset": "glow", "stdDeviation": 8, "color": "#ff00ff" }

Preset

Key params

Effect

glow

stdDeviation, color

Soft halo

neon

stdDeviation, color

Intense bright glow

blur

stdDeviation

Gaussian blur

drop-shadow

stdDeviation, offsetX, offsetY, color

Drop shadow

glitch

stdDeviation

Turbulence displacement (animated)

grayscale

value (0–1)

Desaturate

sepia

Warm sepia tone

invert

Invert colors

saturate

value

Boost/reduce saturation

hue-rotate

value (degrees)

Shift hues

chromatic-aberration

value (px offset, default 3)

RGB channel split for lens distortion look

noise

value (opacity 0 to 1, default 0.25)

Film grain and texture overlay

outline

color, value (thickness, default 2)

Colored outline around the element

inner-shadow

color, stdDeviation, value (opacity, default 0.5)

Shadow inside the element

emboss

stdDeviation, value (intensity, default 1.5)

3D relief shading effect

CSS Animations

{
  "animations": [{
    "name": "pulse",
    "duration": "2s",
    "iterationCount": "infinite",
    "direction": "alternate",
    "keyframes": [
      { "offset": "from", "properties": { "opacity": "0.3", "transform": "scale(0.9)" } },
      { "offset": "to",   "properties": { "opacity": "1",   "transform": "scale(1.1)" } }
    ]
  }],
  "elements": [{
    "type": "circle",
    "cx": 100, "cy": 100, "r": 40,
    "cssClass": "pulse",
    "transformBox": "fill-box",
    "transformOrigin": "center"
  }]
}

CSS property keys: camelCase (strokeDashoffset) or kebab-case (stroke-dashoffset). Both work.

Animatable CSS properties: opacity, fill, stroke, transform, filter, clip-path, stroke-dasharray, stroke-dashoffset, font-size, letter-spacing and more.

SMIL Animations

Three SMIL types, defined inline on each element via smilAnimations: []:

{ "kind": "animate",          "attributeName": "d",       "from": "...", "to": "...", "dur": "2s" }
{ "kind": "animateTransform", "type": "rotate",            "from": "0 100 100", "to": "360 100 100", "dur": "3s" }
{ "kind": "animateMotion",    "path": "M 0 0 C ...",      "dur": "4s", "rotate": "auto" }

Path morphing (attributeName: "d"): from/to paths must have identical command types and counts. Only coordinates can differ.

Fonts

Prefer the CSS generic families: sans-serif, serif, monospace. They resolve to a real font on every platform, both in browsers and in nakkas previews. Named fonts like Arial or Helvetica only exist on some systems (not on most Linux machines), so a design that depends on them will render differently elsewhere. The safe pattern is a named font with a generic fallback: "Georgia, serif".

In preview and PNG save, generic families are resolved through the operating system's own font mapping (fontconfig on Linux), so what the AI sees matches what a browser on that machine would show. Custom font families are accepted and work when the font is available in the rendering environment.

Use Cases & Compatibility

Context

CSS @keyframes

SMIL

External fonts

Interactive (onclick)

GitHub README <img>

Web page <img>

Web page inline SVG

Design tool export

Static file viewer

depends

depends

Troubleshooting

"MCP error -32602: Input validation error"

This means the MCP SDK rejected the input before it reached the handler. It usually happens on the first attempt and works on retry. The most common triggers:

  • Gradient type typo. Use "linearGradient" or "radialGradient", not "linear" or "radial". This is the single most frequent mistake.

  • Keyframe offset as string. Write 0 or 100 (numbers) or "from" / "to". Writing "0%" or "100%" will fail.

  • Colors in gradients and filters. Gradient stop and filter colors must be hex: "#ff0000", not "red" or rgb(). Element fill/stroke accept any paint string ("#ff0000", "none", "url(#id)"), with hex being the safest choice across renderers.

  • Missing type on elements. Every element object needs a type field.

Validation errors that reach the handler name the exact failing field (for example elements.1.content: Required) and append a field reference for the failing element type, so a retry usually succeeds on the first correction. Numeric strings on number fields (letterSpacing: "6") are coerced automatically instead of failing.

The handler also checks reference integrity before rendering: a dangling url(#id) in fill/stroke/filter/clipPath/mask, a use.href pointing nowhere, a textPath.pathId missing from defs.paths, or a duplicate ID all come back as errors with the exact field path and the list of defined IDs, instead of rendering silently broken output.

If you're building an MCP client integration and seeing this consistently, the issue is likely in how your client serializes arguments. See anthropics/claude-code#29104 for context on known serialization quirks.

Preview shows a blank or unexpected image

A single preview renders a static snapshot at t=0, before any animation starts. To see the motion, render with output: { frames: 4 } (or up to 10): nakkas samples the CSS animations at N points in time and returns one labeled filmstrip image. SMIL animations are the exception; they are not sampled and always show their base state.

If the image is completely blank:

  • Check that your elements have fill or stroke set. A shape without fill on a transparent canvas is invisible.

  • Check coordinates. An element at x: 2000 on an 800px wide canvas is simply off-screen.

  • If using filter: "url(#myFilter)", make sure myFilter is actually defined in defs.filters.

Animations not working on GitHub

GitHub READMEs render SVG through <img> tags, which strips JavaScript but keeps CSS and SMIL. If your animation works locally but not on GitHub:

  • Avoid <script> or event handlers (onclick, onmouseover). These are removed.

  • External fonts won't load. Stick to generic families (monospace, sans-serif, serif) or named fonts with a generic fallback.

  • CSS @import for fonts is blocked. If you need a specific font, use inline <text> with a system fallback.

Large SVG output

If render_svg returns a warning about file size (over 50kb), the parametric curves are probably sampling too many points; reduce steps. Pattern groups are cheap: the child is defined once and instanced with <use>, so a grid-group with cols: 50, rows: 50 costs one child definition plus 2500 one-line <use> tags. For the smallest possible file, add output: { minify: true }.

How It's Tested

Every release ships only after a dogfood run: a design produced through the real MCP stdio layer against the freshly built server, iterated render → preview → critique → revise until it holds up. The test design is tailored to the change under test, so the run proves the new feature works under realistic use, not just that nothing broke. The full log, with prompts, iteration counts and the resulting assets, lives in dogfooding.md.

This ritual catches real bugs. The v0.3.0 easing dogfood found that CSS axis shorthands like translateX were baked verbatim into the SVG transform attribute, where they are invalid and silently freeze the element; the fix shipped in the same release. The animation frame sampler behind output.frames is verified against a real browser: randomized animations (seeded, including random cubic-bezier curves) are frozen in headless Chromium via the negative animation-delay trick and must match nakkas's sampled positions within a pixel. That cross-check runs in CI as tests/browser-truth.test.ts; the original manual harness remains at scripts/easing-browser-truth.sh.

Alongside the dogfood runs there are 397 unit and integration tests, including MCP stdio end-to-end coverage.

Tech Stack

  • TypeScript + Node.js 18+

  • @modelcontextprotocol/sdk (MCP server)

  • zod (schema validation and AI type guidance)

  • No external SVG libraries, pure XML construction

  • Vitest (397 tests)

License

MIT. Built by arikusi.

Available Tools

3 tools
previewPreview SVGA

Render SVG content to a PNG image so the AI can visually inspect the output.

When to use:

  • render_svg already returns a preview image by default; call this tool to re-preview a stored artifact at a different width, or to preview SVG that did not come from render_svg

  • Stop iterating when the visual result matches the intent

Input: pass EITHER artifact (id from render_svg, e.g. "art-1" — preferred, no SVG resend) OR content (raw SVG string).

Behavior:

  • Returns a PNG image (base64) rendered from the SVG

  • Background is transparent by default

  • CSS animations and SMIL are rendered as a static snapshot (t=0) — motion is not captured

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)

ParametersJSON Schema
NameRequiredDescriptionDefault
widthNoRender width in pixels; defaults to SVG's own declared width
formatNoContent format; auto-detected from content if omitted
contentNoSVG string to render as PNG. Only needed when no artifact id exists.
artifactNoArtifact id returned by render_svg (e.g. "art-1"). Preferred over content.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that the output is a PNG base64, background is transparent, animations are static snapshots, and width can be omitted or specified. It does not contradict any annotations (none provided). However, it does not explain the 'format' parameter's effect (e.g., when to use 'html') though schema covers it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with sections (When to use, Input, Behavior, Width), front-loaded with purpose, and every sentence adds value without unnecessary text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 4 optional parameters, no output schema, and no annotations, the description covers key behaviors and usage contexts. It does not explicitly state mutual exclusivity of artifact and content, but the 'pass EITHER' guidance implies it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so baseline is 3. The description adds meaning by explaining that 'artifact' is preferred over 'content', width can be left to default, and content is only needed when no artifact id exists.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states clearly 'Render SVG content to a PNG image so the AI can visually inspect the output.' It distinguishes from sibling tool render_svg by noting that render_svg already returns a preview and this tool is for re-previewing or previewing external SVG.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'When to use' section explicitly tells when to use this tool vs render_svg, including re-previewing artifacts or previewing SVG from other sources. It also advises to stop iterating when visual result matches intent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

render_svgRender SVGA

Render animated SVG from JSON config. AI controls all design parameters.

Workflow: render_svg returns a PNG preview of the result plus an artifact id — critique the image, revise the config, render again. Iterate at least 3 times before finalizing. The SVG text stays on the server: pass the artifact id to save (and to preview for a different width). Add output:{svg:true} only if you actually need the SVG text in the conversation.

output options (response shape, not content): {"svg":false,"preview":true,"previewWidth":800,"minify":false,"frames":4} — all optional. minify:true collapses whitespace in the stored/saved SVG. frames:N (2-10) replaces the static preview with one filmstrip image sampling the CSS animations at N times — use it to verify motion (rotation direction, timing, easing) since a single preview only shows t=0. SMIL is not sampled.

Element types: rect, circle, ellipse, line, polyline, polygon, path, image, text, textPath, group, use, radial-group, arc-group, grid-group, scatter-group, path-group, parametric

Pattern groups (use for repetitive designs): radial-group (circular: cx, cy, radius, count), arc-group (arc: cx, cy, radius, count, startAngle, endAngle), grid-group (matrix: cols, rows, colSpacing, rowSpacing), scatter-group (random: width, height, count, seed), path-group (along polyline: waypoints, count). Each takes ONE "child" element.

Parametric curves (fn field): rose, heart, lissajous, spiral, star, superformula, epitrochoid, hypotrochoid, wave. Size via "scale" field. Server computes coordinates.

defs: gradients (linear/radial, SMIL animated stops), filters (presets: glow, neon, blur, drop-shadow, glitch, chromatic-aberration, noise, outline, inner-shadow, emboss + 5 more), clipPaths, masks, patterns (tile fills).

Animations: CSS @keyframes via animations array. Set cssClass on element matching animation name. For transforms add transformBox="fill-box" transformOrigin="center". SMIL via smilAnimations on elements (animate, animateTransform, animateMotion).

Critical format rules:

  • Gradient type must be "linearGradient" or "radialGradient" (not "linear"/"radial"). Each needs id, stops (array with offset 0-1, color).

  • Filter type must be "preset" with a "preset" field: {"type":"preset","id":"myGlow","preset":"glow","stdDeviation":8,"color":"#ff00ff"}

  • Keyframe offset: use "from"/"to" or percentage number 0-100 (not "0%"/"100%").

  • Gradient stop and filter colors: hex only (#rrggbb or #rrggbbaa). Element fill/stroke accept '#rrggbb', 'none', or 'url(#id)' (hex is safest).

  • Every element needs "type" field. circle needs r, rect needs width+height, path needs d.

Field names that differ from raw SVG:

  • text: string goes in "content" (not "text"): {"type":"text","x":100,"y":50,"content":"Hello","fontSize":24,"textAnchor":"middle"}

  • textPath: {"type":"textPath","pathId":"idFromDefsPaths","text":"..."} — here the field IS "text".

  • group: {"type":"group","children":[...]} — children are shapes/text/use only, no nested groups.

  • Pattern groups take ONE "child" element drawn at local origin (child uses cx=0/cy=0); set rotateChildren:false to keep text upright.

Output: Pure SVG XML. No JavaScript. CSS @keyframes + SMIL only.

ParametersJSON Schema
NameRequiredDescriptionDefault
defsNo
canvasYes
outputNo
elementsYes
animationsNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It transparently discloses that SVG text stays on the server (access via artifact id), describes output format (PNG preview + artifact id), explains field name differences from raw SVG, critical format rules, and the behavior of pattern groups and parametric curves. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but well-structured with sections (Workflow, output options, element types, pattern groups, etc.). Every sentence adds necessary detail given the complexity of SVG rendering. Slightly verbose but justified; could be tightened without losing clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (5 params, nested objects, no output schema), the description covers all essential aspects: input structure, workflow, output format, edge cases (field name differences, format rules), and usage of defs and animations. It explains return values (PNG preview + artifact id) despite no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate fully. It provides extensive detail on each parameter group (canvas, elements, animations, output, defs) with examples, required fields, and format constraints (e.g., gradient type must be 'linearGradient', elements need 'type' field). This goes far beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description begins with 'Render animated SVG from JSON config', clearly stating the tool's core function. It distinguishes from siblings (preview, save) via workflow context, and the detailed enumeration of element types, animations, and output options reinforces the specific purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides an explicit iterative workflow ('critique, revise, render again, iterate at least 3 times'), explains when to use output options like 'svg:true', and when to preview for different widths. However, it doesn't explicitly state when not to use this tool relative to the sibling tools, though the context strongly implies it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

saveSave ContentA

Save rendered content to disk. Format-aware: can save as text or render to raster image.

IMPORTANT: Use this only AFTER iterating on the design with render_svg's preview images. Do not save on the first render. Preview and refine your work first.

Input: pass EITHER artifact (id from render_svg, e.g. "art-1" — preferred, no SVG resend) OR content (raw string).

Format detection:

  • 'auto' (default): infers format from file extension. .svg saves as text, .png renders to image.

  • 'svg': saves content as a UTF-8 text file

  • 'png': renders the content (assumed SVG) to a PNG image, then saves it

If the file already exists, a numeric counter is appended before the extension to prevent overwriting: design.svg becomes design-1.svg, then design-2.svg. The actual saved path is returned in the response.

ParametersJSON Schema
NameRequiredDescriptionDefault
widthNoFor raster formats (png): render width in pixels. Defaults to the source content's own declared dimensions.
formatNoOutput format. 'auto' infers from file extension (.svg saves as text, .png renders to image). 'svg' saves content as a UTF-8 text file. 'png' renders SVG content to a PNG image before saving.auto
contentNoRaw content to save. Only needed when the content did not come from render_svg.
artifactNoArtifact id returned by render_svg (e.g. "art-1"). Preferred over content.
outputPathYesFile path to save to. The directory must already exist. If the file already exists, a numeric counter is appended before the extension: design.svg becomes design-1.svg, then design-2.svg, and so on. The actual saved path is returned in the response.

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses behaviors: format detection (auto, svg, png), file overwrite prevention with numeric counter, and input options (artifact vs content). No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with sections, bullet points, and bolded keywords. Every sentence earns its place—no fluff. Efficiently communicates complex behavior in a few paragraphs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 5 params, no annotations, and no output schema, description covers all aspects: input selection, format handling, overwrite behavior, and return value. Complete enough for an agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds crucial context: width defaults to source dimensions, artifact is preferred over content, outputPath explains counter behavior, format enum values are elaborated. Adds significant value beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Save rendered content to disk' and distinguishes itself from siblings (preview, render_svg) by specifying it is for final saving after iterating on design.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this only AFTER iterating on the design with render_svg's preview images' and warns 'Do not save on the first render', providing clear usage context and when-not-to-use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.5/5.0
Disambiguation5/5

Each tool has a distinct purpose: render_svg creates SVGs from config, preview re-renders existing SVGs to PNG at different widths, and saves artifacts to disk. No overlap in functionality.

Naming Consistency3/5

Naming pattern is inconsistent: 'preview' and 'save' are single verbs, while 'render_svg' uses a verb_noun pattern with underscore. This mixed convention could confuse an agent predicting tool names.

Tool Count4/5

With only 3 tools, the surface is minimal but covers the core workflow of creating, previewing, and saving SVG content. It could benefit from a few more tools (e.g., list artifacts), but it's not excessive or insufficient.

Completeness4/5

The tool set provides a complete cycle for SVG creation and output, but lacks functionality like deleting saved files or listing existing artifacts. Minor gaps that agents can work around by creating new IDs.

Maintenance

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/arikusi/nakkas'

If you have feedback or need assistance with the MCP directory API, please join our Discord server