@withicons/mcp
v0.5.0
Published
MCP server for with icons: search 750 icons x 34 styles in plain English and get paste-ready SVG, React, Vue, Svelte, Angular, Solid, web-component or class code, every icon's colour palettes for the multi-colour styles, animation code (@withicons/motion)
Maintainers
Keywords
Readme
@withicons/mcp
An MCP server for with icons — 750 MIT icons, each drawn in 34 styles
(line, solid, duo, gloss, engrave, blueprint, sketch, glass, kawaii, sticker, pixel, retro, luxe, bauhaus, skeuo, anime, gothic, pastel, coquette, plush, clay, bento, suite, dock, liquid, chrome, soft3d, brutal, utsav, rangoli, halloween, christmas, lunar, valentine). Your AI assistant can search icons in plain English
("throw away", "settigns", "money", "cute heart", "8-bit star") and get paste-ready SVG, React, Vue, Svelte, Angular,
Solid, web-component, CSS-class or data-URI code, plus animation code for the optional @withicons/motion package
(750 icons have a tuned animation; every icon can use any preset), every icon's 20-30 hand-picked colour
palettes for the multi-colour styles, a style recommender (recommend_styles: "Diwali sale banner", "avatar picker for
a kids app", "AI landing page" -> the best styles with packages, snippets, presets and palettes), and files: SVG, PNG, PDF, PowerPoint, Word, Lottie, app assets and animated
GIF / APNG / SVG / PowerPoint for slides and docs (export_icon).
Self-contained: the search engine, the MCP SDK, the export formats and every icon are bundled — no network. PNG-based and
animated files use @resvg/resvg-js, an optional dependency installed with the server where your platform has a prebuilt binary.
Tools
| tool | arguments | returns |
|---|---|---|
| search_icons | query, limit? (10), style?, category?, format? (react, or none for names only) | ranked exact matches, why each matched (matched alias "bin", typo-tolerant), a ready snippet, a link. Every exact match carries a snippet in structuredContent and the text shows the first one only; format: "none" leaves the snippets out of both (name, title, category, confidence, reason, url), which is what to use for long lists and the large svg snippets. suggestions (names a typo away) appear only when a name really is close. When no icon carries the words: noExactMatch: true and up to 5 similar icons in additional (loosely related, no snippet), never padded into results |
| get_icon | name (name or alias), style? (line), format? (svg), size? (24), color?, flat?, palette?, colors?, theme? | the code: svg, react, vue, svelte, angular, solid, html-class, web-component, data-uri; for multi-colour styles the icon's colour variables (role + default), mainRole and roleAreas (measured on the render) and its palette ids; with palette / colors / theme, the code recoloured (every colour, not just one) plus the CSS, warnings for colours that change nothing in that style and notes for a colour it paints only as a see-through tone; a motion summary when the icon has a tuned animation |
| recommend_styles | for? (what you are making, plain words), use? (app · slides · saas · ai · brand · kids · print · festive), icon?, limit? (6), compact? | the best styles for the job, best first (several festivals named: by how many each style covers; "exact brand colours" or a hex: flat styles first, with exactColours on each entry and a note that gradient and shadow styles cannot match a hex), each with its group, what it is good for, the React import, <with-icon> and class snippets, the npm packages holding its raw files, its download-all zip, 3D motion support, holiday palettes (holiday styles) and the Duo presets (duo); festival icon categories, avatar advice (inclusive skin-tone palettes) and notes. compact: true returns only name, group, what it suits and the React / <with-icon> snippets (about a third of the size). Nothing recognised: every use and group |
| list_palettes | name, style?, tag?, limit? | the icon's 20-30 palettes (id, name, tags, ten role colours); with style, the exact --with-* variables and a CSS rule per palette, mainRole (the colour role that paints the largest part, measured on the render) and roleAreas (every role's share) |
| animate_icon | name, trigger? (loop · hover · once · inview · swap), preset?, to?, effect?, style?, format? (html), duration? | paste-ready animation code (html, react, vue, svelte, solid, angular, web-component, js), the icon's motion spec and install lines. inview and the draw preset come wired to the JS runtime (motion(el, name, options) in a mount hook / module script). In a 3D style (clay, glass, liquid, chrome, soft3d, luxe, skeuo, dock, plush) the motion plays in 3D: the code adds wm-3d (or style to motion()), and the result has motion3d and moves (the moves the icon offers in that style; soft3d 3-5: its own, then pop-up / press, then hop, turn, gleam …) |
| export_icon | name, style?, format? (svg; one or a comma list), size?, background?, matte?, palette?, colors?, color?, theme?, per_icon?, motion? (loop · hover · once · swap · none · a preset), to?, effect?, fps?, seconds?, loop?, all_styles?, out_dir?, inline? (local), cursor? (remote) | files: svg, svg-flat, pdf, eps, png, png-set, ico, favicon-pack, android, ios, pptx, pptx-sheet, docx, lottie, dotlottie, the code formats, and animated gif, apng, animated-svg, pptx-animated. Saved to out_dir when given; small files also come back inline (PNG / GIF as image content, text as text, the rest as base64 resources). The remote server makes PNG and every other still format, animated-svg and Lottie itself (files inline as base64 resources) and answers gif, apng, pptx-animated and pptx-sheet with the exact npx withicons export … command; large remote exports come in pages (next: { cursor, remaining }: call again with cursor), and plain svg-flat files then come back as urls[] to jsDelivr @withicons/static. colorReport: per icon and style, the colours that changed nothing there (ignored), where the theme colours went (exact: primary shows unmixed) and which donor colours a borrowed palette put on which part; warnings come first |
| preview_icons | names (1-24, name or alias), styles? (1-6, default line), size? (12-256, 64), background? (light · dark · #hex), labels?, columns? (one style), palette?, colors?, format? (png local, svg hosted), out_dir? (local) | one labelled picture to look at before choosing: rows are icons, columns are styles (one style: a gallery with the name under each icon), palette styles in their default colours (or a palette / colors brand preview), outlines drawn light on a dark page. Local server: a PNG as image content (and a file in out_dir); hosted server: one self-contained SVG sheet as text (no external references, gradient ids unique per sheet). A style below its minSize still renders, with a notes entry; unknown icons or styles come back with the nearest names |
| list_styles | — | the 34 styles with their group (Essentials, Product & brand, 3D & glass, Playful, Artistic, Holidays) and what they are good for, what they look like, minSize, onDark, the colour variables of palette styles, and colours for every style: the roles its icons really paint (read from the rendered icons: variables, solid / see-through tone / gradient, on how many icons) and exact (whether a brand hex shows unmixed); plus groups and uses (the "What are you making?" picks) |
| list_categories | category? | categories with counts, or every icon in one category (festivals: indian-festivals, christmas, lunar-new-year, valentines, halloween; avatars; ai …) |
| resolve_icon | name | resolved (+ via alias), ambiguous (+ candidates) or unknown (+ nearest) |
Colours and palettes
Every style except line, solid, duo, gloss, engrave, blueprint and sketch paints several colours (list_styles marks them palette: true), and duo (c1 as a see-through tone, --with-duo, plus an accent detail --with-duo-accent on some icons) and blueprint (c1, its construction lines) have colour roles too; list_styles gives each style's roles as its icons paint them. Every
colour is a CSS variable with a default (var(--with-retro-1, #F4B53F)); the outline follows currentColor. A palette gives a
colour to each of ten roles, and the server maps the roles onto the variables that icon actually uses in that style:
| role | paints |
|---|---|
| ink | outline / line work (sets color) |
| c1 · c2 · c3 · c4 | the main colour, then the 2nd-4th colours (sticker / kawaii parts, retro stripes) |
| tint · accent · shadow · shine · edge | glass front pane · blush and sparkles · drop shadows · highlights · sticker border |
get_icon({ name: 'heart', style: 'sticker', palette: 'neon-love' }): one of the icon's palettes (ids are incolors.suggestions).get_icon({ name: 'heart', style: 'retro', colors: { c1: '#22c55e', c2: '#facc15', ink: '#111827' } }): change any colour (merged overpalettewhen both are given;--with-*variable names work too).export_icon({ names: ['tooth', 'heart', 'camera'], style: 'clay', theme: { primary: '#0ea5e9', secondary: '#f59e0b' } }): brand colours on each icon's own parts, largest first, in any style (get_icontakesthemetoo);per_icon: { heart: '#e11d48' }colours single icons. Flat styles show the hex exactly; gradient, tone and shaded styles make it one tone of several (thethemedescription lists both).svgkeeps the variables and sets them on the root<svg style="…">(addflat: trueto bake them in, for<img>and design tools);data-uriis always baked;web-componentgets an inlinestyle; React, Vue, Svelte, Solid and Angular get a class plus the CSS rule (.icon-heart-neon-love { color: …; --with-sticker-bubblegum: …; }). CSS-class icons bake their palette into a data URI, so only the ink applies.
Choosing a style, festivals and avatars
recommend_styles({ for: 'Diwali sale banner', icon: 'diya' }) ranks rangoli and utsav first, with
<with-icon name="diya" variant="rangoli">, import { Diya as DiyaRangoli } from '@withicons/react/rangoli', the raw-file
packages (@withicons/holiday, @withicons/static-plus), the style's festival palettes (diwali, holi, navratri …:
CSS variable sets) and list_categories({ category: 'indian-festivals' }) for the festival icons. Avatars (category
avatars: people, animals, friendly monsters): people come with true-to-life palettes for many skin tones and hair colours
(list_palettes({ name: 'avatar-woman', tag: 'true-to-life' })); offer several. Every style also ships as one zip:
https://withicons.com/downloads/with-icons-<style>.zip (every SVG plus an offline searchable viewer) and
with-icons-all.zip.
Icons for slides and docs
recommend_styles({ use: 'slides' }), then the whole set in one call:
export_icon({ names: ['rocket', 'users', 'chart-line'], style: 'solid', format: 'png', size: 512, color: '#1d4ed8', out_dir: 'slides/icons' })
(transparent background; the hosted server returns the PNGs inline instead). Embed the PNGs, not SVG, with pptxgenjs
(slide.addImage({ path })) or python-pptx (add_picture): 512 px stays crisp from 0.5 to 3 inches on a slide. pptx is a
one-slide deck to copy an icon from, pptx-sheet one icon in every style; neither merges into your deck. Single-colour styles
(solid, line) match a brand hex exactly; gradient and shadow styles shade the colours you give.
Animated icons for slides and docs
export_icon({ name: 'bell', style: 'luxe', format: 'gif', background: '#ffffff', out_dir: 'slides/icons' }) writes
bell-luxe-ring.gif: the icon's own animation (the same keyframes as the website), looping forever. GIF plays in PowerPoint,
Keynote, Google Slides, Slack, email and Notion. GIF transparency is 1-bit, so pass the slide colour as background (or
keep it transparent with matte: '<slide colour>'); colours are baked in, so choose palette / colors before exporting.
format: 'pptx-animated' gives a ready 16:9 slide with the moving icon; apng has smooth see-through edges for web pages;
lottie / dotlottie stay vector for apps, After Effects and Canva. motion: 'swap', to: 'pause' records an icon turning
into another one.
Resources: icon://<style>/<name>.svg (e.g. icon://solid/home.svg) and icon://about.
Install in your client
All local configs run the same command: npx -y @withicons/mcp (Node 18+).
Claude Code
claude mcp add withicons -- npx -y @withicons/mcpClaude Desktop — Settings › Developer › Edit config (claude_desktop_config.json):
{
"mcpServers": {
"withicons": { "command": "npx", "args": ["-y", "@withicons/mcp"] }
}
}On Windows, if npx is not found, use "command": "cmd", "args": ["/c", "npx", "-y", "@withicons/mcp"].
Cursor — ~/.cursor/mcp.json (global) or .cursor/mcp.json (project):
{
"mcpServers": {
"withicons": { "command": "npx", "args": ["-y", "@withicons/mcp"] }
}
}VS Code (Copilot agent mode) — .vscode/mcp.json:
{
"servers": {
"withicons": { "type": "stdio", "command": "npx", "args": ["-y", "@withicons/mcp"] }
}
}Windsurf — ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"withicons": { "command": "npx", "args": ["-y", "@withicons/mcp"] }
}
}Remote (HTTP) instead of npx
The same server runs as a stateless Streamable-HTTP endpoint, hosted at https://withicons.com/mcp (no key, nothing to install; see withicons.com/ai):
claude mcp add --transport http withicons https://withicons.com/mcpCursor / Windsurf: { "mcpServers": { "withicons": { "url": "https://withicons.com/mcp" } } } (Windsurf: "serverUrl").
VS Code: { "servers": { "withicons": { "type": "http", "url": "https://withicons.com/mcp" } } }.
Try it
"Find me an icon for deleting things and give me the React code in the solid style."
The assistant calls search_icons({ query: 'deleting things' }) -> trash, then
get_icon({ name: 'trash', style: 'solid', format: 'react' }):
import { Trash as TrashSolid } from '@withicons/react/solid'
<TrashSolid />"Make the notification bell ring when I hover the button, in React."
animate_icon({ name: 'bell', trigger: 'hover', format: 'react' }):
import { Bell } from '@withicons/react'
import '@withicons/motion/motion.css'
import '@withicons/motion/icons.css'
<button type="button" className="wm-trigger" aria-label="Bell"><span className="wm wm-hover" data-wm="bell"><Bell /></span></button>"Give me the kawaii heart in Vue, but in a strawberry-milk pastel."
list_palettes({ name: 'heart', style: 'kawaii', tag: 'pastel' }) -> strawberry-milk, then
get_icon({ name: 'heart', style: 'kawaii', format: 'vue', palette: 'strawberry-milk' }):
<script setup>
import { Heart as HeartKawaii } from '@withicons/vue/kawaii'
</script>
<template>
<HeartKawaii class="icon-heart-strawberry-milk" />
</template>
<style>
.icon-heart-strawberry-milk { color: #5A2A3A; --with-kawaii-fill-1: #FFA3C0; --with-kawaii-blush: #FF6F91; --with-kawaii-shine: #FFE5ED; --with-kawaii-face: #5A2A3A; }
</style>HTTP API
The hosted endpoint at https://withicons.com also answers plain GET requests (JSON, CORS open). The remote MCP server's
export_icon makes PNG (rendered with @resvg/resvg-wasm, the same renderer as the local server, byte for byte), every other
still format, animated-svg and Lottie; for gif, apng, pptx-animated and pptx-sheet (dozens of frames, too slow for one
request) it answers with the npx withicons export … command to run locally. Large remote exports come in pages: a result with next: { cursor, remaining } has more to come, so call export_icon again with the same arguments plus cursor. Plain svg-flat files (no colour or size options) then come back as urls[] to the prebuilt @withicons/static SVGs on jsDelivr (colours baked in, ink follows currentColor, no <title>).
Routes:
| route | |
|---|---|
| POST /mcp | MCP JSON-RPC, stateless (no session id, JSON responses, no SSE). GET/DELETE /mcp -> 405. |
| GET /api/search?q=&limit=&style=&category=&format= | same as search_icons |
| GET /api/icon/<name>?style=&format=&size=&color=&palette= | same as get_icon (JSON). Custom colours: one parameter per role (&c1=22c55e&ink=111827, # optional). &raw=1 returns the bare code; /api/icon/<name>.svg returns image/svg+xml with the colours baked in (&flat=0 keeps the CSS variables) |
| GET /api/palettes/<name>?style=&tag=&limit= | same as list_palettes |
| GET /api/motion/<name>?trigger=&preset=&to=&effect=&style=&format=&duration= | same as animate_icon (JSON; &raw=1 for the bare code). GET /api/motion lists triggers, presets and effects |
| GET /api/recommend?for=&use=&icon=&limit= | same as recommend_styles |
| GET /api/resolve/<name> · GET /api/styles · GET /api/categories[/<category>] | catalogue |
| GET /api | status + version + endpoints |
GET responses carry Cache-Control: public, max-age=… (1 h for search, 24 h for icons). Example: https://withicons.com/api/icon/home.svg?style=duo.
The Lambda bundle behind it is not part of this npm package; it is built from the repository (node forge/build.mjs mcp, infra/).
Programmatic use
import { searchIcons, getIcon, resolveIcon, snippet, animateIcon, listPalettes, recommendStyles, stylePalette } from '@withicons/mcp/lib'
searchIcons({ query: 'trash can', limit: 3 })
getIcon({ name: 'home', style: 'solid', format: 'vue' })
getIcon({ name: 'heart', style: 'sticker', format: 'svg', palette: 'neon-love', flat: true })
listPalettes({ name: 'pizza', style: 'retro' })
animateIcon({ name: 'bell', trigger: 'hover', format: 'react' })
recommendStyles({ for: 'Christmas email' }) // -> christmas first, its palettes, notes
stylePalette('christmas', 'nordic').css // '--with-christmas-ink: #2E2620; --with-christmas-c1: #B5523B; …'TypeScript types ship in dist/lib.d.ts. Errors are IconErrors with a code (unknown_icon + nearest, unknown_palette + palettes, …).
The terminal CLI is the unscoped package withicons: npx withicons search "throw away".
MIT © with icons — powered by Evergrow · https://withicons.com
