svg-mcp
A FastMCP server providing structured SVG authoring tools for LLMs, enabling create, edit, and render SVG graphics through hierarchical primitives, gradients, paths, and reusable resources.
README
<p align="center"> <img src="./logo.png" alt="svg-mcp" width="460"> </p>
svg-mcp
A FastMCP server that exposes structured, hierarchical SVG authoring to an LLM — all primitives, gradients/patterns, paths, text-on-path, embedded raster, reusable styles — built around an inkex-backed document model the AI can navigate, query, and manipulate as groups, layers, transforms, and masks, with a fast construct → render → see → iterate loop.
The logo above was authored entirely through these tools — see Usage.
See DESIGN.md for the full architecture and the
INKEX_PRIMITIVES.md catalog for the inkex → tool mapping.
Quickstart (Claude)
-
Prerequisites — Python ≥ 3.12 and the resvg renderer:
brew install resvg # macOS (or: cargo install resvg) -
Install the server from a clone of this repo (pulls fastmcp, inkex, fontTools, …):
uv venv && uv pip install -e . # entrypoint lands at .venv/bin/svg-mcp # or: python -m venv .venv && .venv/bin/pip install -e . -
Connect Claude:
-
Claude Code — from the repo directory:
claude mcp add svg-mcp -- "$(pwd)/.venv/bin/svg-mcp"(Or just open the project — it ships a
.mcp.jsonyou can approve.) -
Claude Desktop — add to
claude_desktop_config.json(use the absolute path) and restart the app:{ "mcpServers": { "svg-mcp": { "command": "/ABSOLUTE/PATH/TO/svg-mcp/.venv/bin/svg-mcp" } } }
-
-
Try it — ask Claude:
Use svg-mcp to make a 320×120 badge that says "hello" on a blue gradient, then show me the render.
Claude creates a document, adds the shapes and text, and calls
render_documentto show you the image inline — then you iterate in plain language ("bigger text", "add a drop shadow", "outline the text to paths"). See Usage for the conventions and a worked example.
Status
The full inkex catalog is mapped through to 100 MCP tools (see
INKEX_PRIMITIVES.md), with ruff/mypy clean and the test suite green.
- Document model (inkex-backed, multi-document with an active-document default): shapes, paths (+ arc/star factories and path-data ops), text + tspan runs + text-on-path + flowed text, images (base64/file embed), groups (+ ungroup, z-order, duplicate, world-preserving reparent), layers (+ visible/locked/opacity), symbols/use, hyperlinks.
- Text → paths (
text_to_path): pure-Python glyph outlining (fontTools) — flattens tspans, honors bold/italic/per-run fill, and walks text along a curve (<textPath>).list_fontsenumerates installed families;measure_textreturns a run's width/height from font metrics so you can fit and center text without a render round-trip. - Reusable resources: named styles (CSS classes +
@namepaint refs), linear/radial/mesh gradients, patterns, markers, clip + mask, and filters (blur, drop-shadow, color-matrix/overlay, blend, morphology, component-transfer, turbulence, displacement, plus a raw filter-graph builder). - Transforms as primitives: translate, rotate-about-center, scale-about-anchor, skew, raw.
- Queries / context:
current_context,describe_node,list_resources,outline, bbox, computed style, transform/CTM, unit conversion, selectors (find/get_subtree), image extraction; metadata (title/desc/RDF); guides & pages. - Render-and-see loop: serialize-then-rasterize via the resvg CLI; the image is handed
back directly as base64 image content. Documents are also published as readable MCP
resources (
svg://documents,svg://{id}/svg,svg://{id}/render) with change notifications. - File export (
export_render): faithful raster (png/jpeg/webp via resvg) and true vector (pdf/ps/eps via librsvg). cairo is intentionally avoided — it silently drops SVG filters (a drop shadow renders blank), so it is not faithful to the document.
Architecture
<p align="center"> <img src="./docs/architecture.png" alt="svg-mcp architecture" width="900"> </p>
Authored entirely through the svg-mcp tools and rendered via the server's resvg backend (
docs/architecture.svgis the live serialized source).
Three layers: an interface & contract tier (the FastMCP server, pydantic input schemas, and
per-session document stores), a document-operations tier (inkex-facing construction/edit,
read-only introspection, and the document model), and a rendering & output tier (pure-Python
typesetting, the render/export backends, and SVG serialization). See DESIGN.md
for the full layering rationale.
Install
pip install -e ".[dev]"
inkex (the SVG DOM)
Depends on the released PyPI inkex>=1.4.1. Note its API shape (verified against the
install): inkex.colors is a flat module, and there is no Image.embed_image() — raster
embedding is done manually (base64 data URI). All element classes plus bounding_box,
composed_transform, specified_style, and Transform @ composition are present.
resvg (the default renderer)
The default backend shells out to the resvg binary — a deterministic, cross-platform static
renderer with native text-on-path and broad filter support, no system libs:
brew install resvg # macOS
# or: cargo install resvg
Override the path with SVG_MCP_RESVG_BINARY=/path/to/resvg. An optional in-process binding
is available via the resvg extra (pip install -e ".[resvg]").
Optional backends
cairoextra — secondary vector/raster backend (PDF/PS/SVG out) via cairocffi + pango; needs Homebrewcairo+pango. Stub today.- Headless Inkscape — reference renderer + heavy ops, driven via
--shell(no D-Bus on macOS). Stub today.
Configuration
All settings are env vars prefixed SVG_MCP_ (or a .env file):
| Var | Default | Meaning |
|---|---|---|
SVG_MCP_RENDERER |
resvg |
Default render backend (resvg/cairo/inkscape) |
SVG_MCP_RESVG_BINARY |
auto | Path to the resvg CLI |
SVG_MCP_INKSCAPE_BINARY |
auto | Path to the Inkscape CLI |
SVG_MCP_FEEDBACK_MAX_EDGE |
unset | Optional long-edge cap (px); unset = raw image handed back directly as base64 |
SVG_MCP_DEFAULT_BACKGROUND |
transparent | Default render background (CSS color) |
SVG_MCP_RENDER_TIMEOUT_S |
30 |
Per-render subprocess timeout |
SVG_MCP_TRANSPORT |
stdio |
Server transport: stdio or http |
SVG_MCP_HOST / SVG_MCP_PORT |
127.0.0.1 / 8000 |
Bind address for the http transport |
Develop
pytest # tests (resvg smoke test auto-skips if the binary is absent)
ruff check . # lint
mypy src # types (no Any / object — precise types only)
Run
svg-mcp # FastMCP server over stdio (default)
SVG_MCP_TRANSPORT=http svg-mcp # or over HTTP at 127.0.0.1:8000
Experiment with an LLM
Quickest sanity check (no LLM): render a sample poster to PNG.
.venv/bin/python scripts/demo.py # writes demo_output.png
Claude Code — this repo ships a project .mcp.json; open the project and
approve the svg-mcp server, or add it explicitly:
claude mcp add svg-mcp -- /Users/geohar/Development/svg-mcp/.venv/bin/svg-mcp
Claude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"svg-mcp": { "command": "/Users/geohar/Development/svg-mcp/.venv/bin/svg-mcp" }
}
}
MCP Inspector — interactively call tools and view rendered images in a browser:
uv run fastmcp dev src/svg_mcp/server.py:mcp
The model's loop is: create_document → add nodes / resources → render_document to see the
result inline → iterate → export_svg. The server's instructions describe the full workflow
and conventions; each tool carries its own description.
Usage
The tools are called by an LLM over MCP. The core loop is create → build → render-and-see → iterate → export. A minimal session (arguments shown as the JSON each tool receives):
create_document(width=320, height=120) # → {document_id:"doc1", active:true}
# define a reusable gradient, then paint with it by name (@name) or url(#id)
define_linear_gradient(x1=0, y1=0, x2=1, y2=0,
stops=[{offset:0, color:"#7dd3fc"}, {offset:1, color:"#1e3a8a"}], name="brand")
add_rect(x=0, y=0, width=320, height=120, rx=16, style={fill:"@brand"})
add_text(x=160, y=72, content="svg-mcp", name="title",
style={font_family:"Helvetica", font_size:"40px", font_weight:"bold",
text_anchor:"middle", fill:"#ffffff"})
apply_drop_shadow(target="title", dx=0, dy=2, blur=3, color="#000", opacity=0.4)
render_document(scale=2) # returns the rendered PNG inline — look, then adjust
export_svg() # final SVG source string
Conventions
- Active document.
create_documentreturns adocument_idand makes it active; you may omitdocument_idon later calls to target it. Pass it explicitly to switch, or useset_active_document. Callcurrent_context()to re-anchor (active id, open docs, outline). - Targets by id or name. Every
target/parent/contentarg takes a node's returned id or the friendlynameyou gave it. Name things you'll revisit; reason viafind(name=…)andoutline. - Coordinates. User units, origin top-left, y increases downward.
- Style. A structured object —
fill,stroke,stroke_width,opacity, plus typography (font_family,font_size,font_weight,font_style,text_anchor). Colors accept hex /rgb()/ CSS names /none, or a paint referenceurl(#id)/@nameto a defined gradient or pattern. - Resources follow create → define → reference/apply:
define_*returns an id you use as a fill (url(#id)/@name) or attach viaapply_*(clip/mask/marker/filter). Clip/mask/ symbol/pattern definitions move the listed content nodes into the resource, so build those shapes first.list_resources()shows what's defined. - Transforms compose:
translate_node,rotate_node(optional center),scale_node(optional anchor),skew_node, orapply_transform("rotate(45 100 100)"). - Text.
add_text+add_text_runfor multi-line/styled spans;add_text_on_pathto flow along a path. Judge text size withrender_document(geometry queries are empty for live text).text_to_pathoutlines text to glyph paths — font-independent, flattens tspans, and walks<textPath>along its curve;list_fontslists installable families.
Resources
Open documents are also exposed as readable MCP resources, so a host can surface live state:
svg://documents (index + which is active), svg://{id}/svg (source), svg://{id}/render
(PNG). Mutations emit resources/updated notifications.
Example: the logo
The header logo (logo.svg) was built with these tools — <mcp>svg</mcp> as a blue gradient
wordmark, an orange→white starfield clipped into the glyphs, the inner svg word under a
translucent white veil (so the <mcp> tags read as starry space and svg as frosted white), all
under one drop shadow — then text_to_path outlined the glyphs so the final file is
font-independent. scripts/demo.py shows a smaller end-to-end build you can run directly.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.