{"id":47622374,"url":"https://github.com/arikusi/nakkas","last_synced_at":"2026-04-06T07:01:36.362Z","repository":{"id":346025277,"uuid":"1188092104","full_name":"arikusi/nakkas","owner":"arikusi","description":"MCP server that turns AI into an SVG artist","archived":false,"fork":false,"pushed_at":"2026-03-25T13:06:21.000Z","size":146,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-02T06:26:35.552Z","etag":null,"topics":["ai","animation","css-animation","generative-art","mcp","mcp-server","modelcontextprotocol","svg","typescript","vector"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arikusi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-21T15:56:29.000Z","updated_at":"2026-04-01T16:28:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arikusi/nakkas","commit_stats":null,"previous_names":["arikusi/nakkas"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/arikusi/nakkas","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arikusi%2Fnakkas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arikusi%2Fnakkas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arikusi%2Fnakkas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arikusi%2Fnakkas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arikusi","download_url":"https://codeload.github.com/arikusi/nakkas/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arikusi%2Fnakkas/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31463015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"online","status_checked_at":"2026-04-06T02:00:07.287Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","animation","css-animation","generative-art","mcp","mcp-server","modelcontextprotocol","svg","typescript","vector"],"created_at":"2026-04-01T22:22:01.850Z","updated_at":"2026-04-06T07:01:36.355Z","avatar_url":"https://github.com/arikusi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.svg\" alt=\"nakkas\" width=\"460\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eMCP server that turns AI into an SVG artist.\u003c/strong\u003e\u003cbr\u003e\n  One rendering engine. AI decides everything.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/nakkas\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/nakkas\" alt=\"npm version\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/nakkas\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/nakkas\" alt=\"downloads\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/arikusi/nakkas/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/nakkas\" alt=\"license\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/arikusi/arikusi-marketplace\"\u003e\u003cimg src=\"https://img.shields.io/badge/marketplace-arikusi-orange.svg\" alt=\"Marketplace\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Officially listed on the \u003ca href=\"https://registry.modelcontextprotocol.io/?q=io.github.arikusi/nakkas\"\u003e\u003cstrong\u003eMCP Registry\u003c/strong\u003e\u003c/a\u003e, \u003ca href=\"https://glama.ai/mcp/servers/arikusi/nakkas\"\u003e\u003cstrong\u003eGlama\u003c/strong\u003e\u003c/a\u003e, \u003ca href=\"https://lobehub.com/mcp/arikusi-nakkas\"\u003e\u003cstrong\u003eLobeHub\u003c/strong\u003e\u003c/a\u003e, and \u003ca href=\"https://www.pulsemcp.com/servers/arikusi-nakkas\"\u003e\u003cstrong\u003ePulseMCP\u003c/strong\u003e\u003c/a\u003e.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://registry.modelcontextprotocol.io/?q=io.github.arikusi/nakkas\"\u003e\u003cimg src=\"https://img.shields.io/badge/Official_MCP_Registry-active-brightgreen\" alt=\"Official MCP Registry\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://lobehub.com/mcp/arikusi-nakkas\"\u003e\u003cimg src=\"https://lobehub.com/badge/mcp/arikusi-nakkas\" alt=\"LobeHub\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://deepwiki.com/arikusi/nakkas\"\u003e\u003cimg src=\"https://deepwiki.com/badge.svg\" alt=\"Ask DeepWiki\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://glama.ai/mcp/servers/arikusi/nakkas\"\u003e\n    \u003cimg width=\"380\" height=\"200\" src=\"https://glama.ai/mcp/servers/arikusi/nakkas/badge\" alt=\"Glama Badge\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003e *nakkaş* means painter/artist in Turkish (old).\n\n```\n\"make a neon terminal logo with animated binary digits\"\n  → AI constructs JSON config\n  → nakkas renders to animated SVG\n  → clean animated SVG output\n```\n\n## Why\n\n- **One tool, infinite designs.** `render_svg` takes a JSON config. AI fills in everything.\n- **AI-native schema.** Every field has `.describe()` annotations so the model knows what to do.\n- **Pure declarative SVG.** CSS @keyframes + SMIL animations, no JavaScript.\n- **Zero external deps.** No cloud API, no API keys. Runs locally.\n\n## Install\n\n### Claude Desktop\n\nAdd to your config file:\n- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Linux: `~/.config/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"nakkas\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"nakkas@latest\"]\n    }\n  }\n}\n```\n\n### Claude Code (CLI)\n\n```bash\nclaude mcp add nakkas npx nakkas@latest\n```\n\n### Cursor / Zed / Other MCP clients\n\n```json\n{\n  \"mcpServers\": {\n    \"nakkas\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"nakkas@latest\"]\n    }\n  }\n}\n```\n\n### Local Development\n\n```bash\ngit clone https://github.com/arikusi/nakkas\ncd nakkas\nnpm install \u0026\u0026 npm run build\n# Use dist/index.js as the command\n```\n\n## Quick Start\n\nAsk your AI (with Nakkas connected):\n\n\u003e \"Make an animated SVG: dark terminal frame (800×200), glowing cyan text 'NAKKAS', neon glow filter, fade-in on load.\"\n\n\u003e \"Create a loading spinner: a circle with a draw-on stroke animation that loops every 1.5 seconds.\"\n\n\u003e \"Data visualization: animated bar chart, 5 bars, each fading in with a staggered delay, gradient fills.\"\n\n\u003e \"Profile badge (400×120): blue-to-purple gradient, white username text, drop shadow, subtle pulse animation.\"\n\n## Tools\n\nNakkas provides three tools:\n\n| Tool | Purpose |\n|---|---|\n| `render_svg` | Takes SVGConfig JSON, returns SVG string + design analysis warnings |\n| `preview` | Takes rendered content, returns a PNG image for visual inspection |\n| `save` | Takes rendered content, saves to disk as SVG (text) or PNG (raster) |\n\nThe intended workflow: render → preview → iterate → save. The `save` tool is separate from `render_svg` to encourage previewing and refining before saving.\n\n### The `save` Tool\n\n```json\n{ \"content\": \"\u003csvg ...\u003e...\u003c/svg\u003e\", \"outputPath\": \"./design.svg\", \"format\": \"auto\" }\n```\n\nFormats: `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.\n\n## The `render_svg` Tool\n\n**Input:** `SVGConfig` JSON object\n**Output:** Complete SVG XML string plus optional design analysis notes\n\nAfter rendering, the response may include design warnings about common issues such as too many concurrent animations, missing transformBox, or group-level scale transforms.\n\n### SVGConfig Structure\n\n```typescript\n{\n  canvas: {\n    width: number | string,   // e.g. 800 or \"100%\"\n    height: number | string,\n    viewBox?: string,          // \"0 0 800 400\"\n    background?: string        // hex \"#111111\" or \"transparent\"\n  },\n\n  defs?: {\n    gradients?: Gradient[],   // linearGradient | radialGradient\n    filters?: Filter[],        // preset or raw primitives\n    clipPaths?: ClipPath[],\n    masks?: Mask[],\n    symbols?: Symbol[],\n    paths?: { id, d }[]       // for textPath elements\n  },\n\n  elements: Element[],         // shapes, text, groups, use instances\n\n  animations?: CSSAnimation[]  // CSS @keyframes definitions\n}\n```\n\n### Element Types\n\n| Type | Required fields | Notes |\n|------|----------------|-------|\n| `rect` | `width`, `height` | `x`, `y` default 0; `rx`/`ry` for rounded corners |\n| `circle` | `r` | `cx`, `cy` default 0 |\n| `ellipse` | `rx`, `ry` | Independent horizontal/vertical radii |\n| `line` | `x1`, `y1`, `x2`, `y2` | |\n| `polyline` | `points` | Open path: `\"10,20 50,80 90,20\"` |\n| `polygon` | `points` | Auto-closed shape |\n| `path` | `d` | Full SVG path commands |\n| `image` | `href`, `width`, `height` | URL or `data:image/...` URI for embedded images |\n| `text` | `content` | String or `(string \\| Tspan)[]` array |\n| `textPath` | `pathId`, `text` | Text following a curve; path defined in `defs.paths` |\n| `group` | `children` | Shared attrs applied to all children (no nested groups) |\n| `use` | `href` | Instance a symbol or clone an element by `#id` |\n| `radial-group` | `cx`, `cy`, `count`, `radius`, `child` | Place N copies around a full circle |\n| `arc-group` | `cx`, `cy`, `radius`, `count`, `startAngle`, `endAngle`, `child` | Place N copies along a circular arc |\n| `grid-group` | `cols`, `rows`, `colSpacing`, `rowSpacing`, `child` | Place copies in an M by N grid |\n| `scatter-group` | `width`, `height`, `count`, `seed`, `child` | Scatter N copies at seeded random positions |\n| `path-group` | `waypoints`, `count`, `child` | Distribute N copies evenly along a polyline |\n| `parametric` | `fn` | Mathematical curve: `rose`, `heart`, `star`, `lissajous`, `spiral`, `superformula`, `epitrochoid`, `hypotrochoid`, `wave` |\n\n### All Visual Elements (Shared Fields)\n\n```typescript\n{\n  id?: string,             // required for filter/gradient/clip references\n  cssClass?: string,       // matches CSS animation names\n  fill?: string,           // \"#rrggbb\" | \"none\" | \"url(#gradId)\"\n  stroke?: string,\n  strokeWidth?: number,\n  strokeDasharray?: string, // \"10 5\", use for draw-on animation\n  strokeDashoffset?: number,\n  opacity?: number,        // 0–1\n  filter?: string,         // \"url(#filterId)\"\n  clipPath?: string,       // \"url(#clipId)\"\n  transform?: string,      // \"rotate(45)\" \"translate(100, 50)\"\n  transformBox?: \"fill-box\" | \"view-box\" | \"stroke-box\",  // set \"fill-box\" for CSS rotation\n  transformOrigin?: string, // \"center\", works with fill-box\n  smilAnimations?: SMILAnimation[]\n}\n```\n\n### Filter Presets\n\nReference as `filter: \"url(#myId)\"` on any element after defining in `defs.filters`:\n\n```json\n{ \"type\": \"preset\", \"id\": \"myGlow\", \"preset\": \"glow\", \"stdDeviation\": 8, \"color\": \"#ff00ff\" }\n```\n\n| Preset | Key params | Effect |\n|--------|-----------|--------|\n| `glow` | `stdDeviation`, `color` | Soft halo |\n| `neon` | `stdDeviation`, `color` | Intense bright glow |\n| `blur` | `stdDeviation` | Gaussian blur |\n| `drop-shadow` | `stdDeviation`, `offsetX`, `offsetY`, `color` | Drop shadow |\n| `glitch` | `stdDeviation` | Turbulence displacement (animated) |\n| `grayscale` | `value` (0–1) | Desaturate |\n| `sepia` | — | Warm sepia tone |\n| `invert` | — | Invert colors |\n| `saturate` | `value` | Boost/reduce saturation |\n| `hue-rotate` | `value` (degrees) | Shift hues |\n| `chromatic-aberration` | `value` (px offset, default 3) | RGB channel split for lens distortion look |\n| `noise` | `value` (opacity 0 to 1, default 0.25) | Film grain and texture overlay |\n| `outline` | `color`, `value` (thickness, default 2) | Colored outline around the element |\n| `inner-shadow` | `color`, `stdDeviation`, `value` (opacity, default 0.5) | Shadow inside the element |\n| `emboss` | `stdDeviation`, `value` (intensity, default 1.5) | 3D relief shading effect |\n\n### CSS Animations\n\n```json\n{\n  \"animations\": [{\n    \"name\": \"pulse\",\n    \"duration\": \"2s\",\n    \"iterationCount\": \"infinite\",\n    \"direction\": \"alternate\",\n    \"keyframes\": [\n      { \"offset\": \"from\", \"properties\": { \"opacity\": \"0.3\", \"transform\": \"scale(0.9)\" } },\n      { \"offset\": \"to\",   \"properties\": { \"opacity\": \"1\",   \"transform\": \"scale(1.1)\" } }\n    ]\n  }],\n  \"elements\": [{\n    \"type\": \"circle\",\n    \"cx\": 100, \"cy\": 100, \"r\": 40,\n    \"cssClass\": \"pulse\",\n    \"transformBox\": \"fill-box\",\n    \"transformOrigin\": \"center\"\n  }]\n}\n```\n\n**CSS property keys**: camelCase (`strokeDashoffset`) or kebab-case (`stroke-dashoffset`). Both work.\n\n**Animatable CSS properties**: `opacity`, `fill`, `stroke`, `transform`, `filter`, `clip-path`, `stroke-dasharray`, `stroke-dashoffset`, `font-size`, `letter-spacing` and more.\n\n### SMIL Animations\n\nThree SMIL types, defined inline on each element via `smilAnimations: []`:\n\n```json\n{ \"kind\": \"animate\",          \"attributeName\": \"d\",       \"from\": \"...\", \"to\": \"...\", \"dur\": \"2s\" }\n{ \"kind\": \"animateTransform\", \"type\": \"rotate\",            \"from\": \"0 100 100\", \"to\": \"360 100 100\", \"dur\": \"3s\" }\n{ \"kind\": \"animateMotion\",    \"path\": \"M 0 0 C ...\",      \"dur\": \"4s\", \"rotate\": \"auto\" }\n```\n\n**Path morphing** (`attributeName: \"d\"`): from/to paths must have identical command types and counts. Only coordinates can differ.\n\n### Fonts\n\nSystem fonts work everywhere without any loading: `Arial`, `Helvetica`, `Courier New`, `Georgia`, `Verdana`, `monospace`, `sans-serif`, `serif`.\n\nCustom font families are also accepted. They work when the font is available in the rendering environment (web page with loaded fonts, design tool, etc.).\n\n### Use Cases \u0026 Compatibility\n\n| Context | CSS @keyframes | SMIL | External fonts | Interactive (onclick) |\n|---------|---------------|------|---------------|----------------------|\n| GitHub README `\u003cimg\u003e` | ✅ | ✅ | ❌ | ❌ |\n| Web page `\u003cimg\u003e` | ✅ | ✅ | ❌ | ❌ |\n| Web page inline SVG | ✅ | ✅ | ✅ | ✅ |\n| Design tool export | ✅ | ✅ | ✅ | — |\n| Static file viewer | ✅ | ✅ | depends | depends |\n\n## Troubleshooting\n\n### \"MCP error -32602: Input validation error\"\n\nThis 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:\n\n* **Gradient type typo.** Use `\"linearGradient\"` or `\"radialGradient\"`, not `\"linear\"` or `\"radial\"`. This is the single most frequent mistake.\n* **Keyframe offset as string.** Write `0` or `100` (numbers) or `\"from\"` / `\"to\"`. Writing `\"0%\"` or `\"100%\"` will fail.\n* **Named colors.** Only hex values work: `\"#ff0000\"`, not `\"red\"`. No `rgb()` either.\n* **Missing `type` on elements.** Every element object needs a `type` field.\n\nIf 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](https://github.com/anthropics/claude-code/issues/29104) for context on known serialization quirks.\n\n### Preview shows a blank or unexpected image\n\nThe preview tool renders a static snapshot at t=0. Animations are not captured. What you see is the SVG's initial state before any CSS or SMIL animation starts.\n\nIf the image is completely blank:\n\n* Check that your elements have `fill` or `stroke` set. A shape without fill on a transparent canvas is invisible.\n* Check coordinates. An element at `x: 2000` on an `800px` wide canvas is simply off-screen.\n* If using `filter: \"url(#myFilter)\"`, make sure `myFilter` is actually defined in `defs.filters`.\n\n### Animations not working on GitHub\n\nGitHub READMEs render SVG through `\u003cimg\u003e` tags, which strips JavaScript but keeps CSS and SMIL. If your animation works locally but not on GitHub:\n\n* Avoid `\u003cscript\u003e` or event handlers (`onclick`, `onmouseover`). These are removed.\n* External fonts won't load. Stick to system fonts: `Arial`, `Courier New`, `Georgia`, `monospace`, `sans-serif`.\n* CSS `@import` for fonts is blocked. If you need a specific font, use inline `\u003ctext\u003e` with a system fallback.\n\n### Large SVG output\n\nIf `render_svg` returns a warning about file size (over 50kb), the parametric curves or pattern groups are probably generating too many elements. Reduce `steps` on parametric curves or `count` on pattern groups. A grid-group with `cols: 50, rows: 50` produces 2500 elements, which adds up fast.\n\n## Tech Stack\n\n* TypeScript + Node.js 18+\n* `@modelcontextprotocol/sdk` (MCP server)\n* `zod` (schema validation and AI type guidance)\n* No external SVG libraries, pure XML construction\n* Vitest (280 tests)\n\n## License\n\nMIT. Built by [arikusi](https://github.com/arikusi).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farikusi%2Fnakkas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farikusi%2Fnakkas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farikusi%2Fnakkas/lists"}