IconServe
A free, agent-readable service that aggregates 10,000+ open-source icons โ Lucide,
Heroicons, Tabler, and Simple Icons โ
and serves them as SVG or PNG at predictable URLs, with keyword + semantic search, an llms.txt, and an MCP server.
Runs entirely within the Cloudflare free tier (Workers + R2 + Workers AI).
How an agent uses it
| Need | Do this |
|---|
| Embed an icon you know the name of | GET /i/{name}.svg (best match) or /icons/{set}/{name}.svg |
| Find the right icon | GET /api/search?q=shopping+cart โ names + ready URLs |
| Read the whole API in one shot | GET /llms.txt (or /llms-full.txt) |
| Native tool calls | MCP server at /mcp (search_icons, get_icon, list_sets) |
| OpenAPI | GET /openapi.json |
Discoverability surfaces (for crawling/browsing agents)
GET /icon/{set}/{name} โ crawlable HTML page per icon (preview, usage, JSON-LD ImageObject + breadcrumb). /icon/{name} 302-redirects to the best match.
GET /sitemap.xml โ every icon page (~10k URLs).
GET /robots.txt โ explicitly allows AI crawlers (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot, Google-Extended, โฆ) and points to the sitemap.
- Landing page carries
WebSite+SearchAction and WebAPI JSON-LD and Open Graph tags.
- IndexNow: key file served at
/{INDEXNOW_KEY}.txt; npm run indexnow pings Bing/Yandex to crawl new or changed URLs instantly (pass paths to submit a subset, e.g. npm run indexnow /icon/lucide/house).
color= hex / CSS color / currentColor โ e.g. ?color=%23e11d48
size= 1..2048 px
stroke= 0..20 (line icons)
style= e.g. solid (Heroicons), filled (Tabler)
format= svg | png
Example: /i/shopping-cart.svg?color=crimson&size=48&stroke=1.5
Architecture
Build (offline, on your machine โ no Cloudflare cost)
scripts/ingest.mjs โ data/pack/icons.json (id โ normalized SVG, currentColor-driven)
data/pack/catalog.json (names, tags, aliases, license, styles)
data/pack/corpus.json (search text per icon)
scripts/embed.mjs โ data/pack/embeddings.bin (10k ร 384, bge-small-en-v1.5)
data/pack/embed-index.json
Cloudflare
R2 (ICONS) holds the 4 packed artifacts; loaded once per isolate into memory
Workers AI embeds ONLY the query string at search time (same 384-dim model)
Worker routing, color/size/stroke transforms, PNG (resvg-wasm), search, MCP, docs
Assets public/index.html landing page
Semantic search = keyword ranking fused (reciprocal-rank fusion) with cosine similarity over the
in-memory embeddings. No Vectorize, no per-vector billing, no dimension cap. If embeddings.bin is
absent, search gracefully falls back to keyword-only.
Local development
npm install
npm run build:data
npm run upload:r2
npm run dev
In wrangler dev --local the Workers AI binding is offline, so search runs keyword-only locally.
Semantic search activates once deployed (or when running against the remote AI binding).
Deploy (Cloudflare free tier)
wrangler login
npm run deploy
npm run deploy runs scripts/deploy.mjs: it creates the iconserve-icons
bucket (idempotent), uploads the four artifacts to remote R2, then wrangler deploy.
Adding / updating icon sets
Edit scripts/lib/sets.mjs, then re-run npm run build:data && npm run upload:r2
(local) or npm run deploy (remote). Set priority for /i/{name} best-match is DEFAULT_SET_ORDER in
wrangler.toml.
Licensing
Icons are redistributed under their original permissive licenses: Lucide (ISC), Heroicons (MIT),
Tabler (MIT), Simple Icons (CC0). Brand marks from Simple Icons are trademarks of their respective
owners โ use them per each brand's guidelines.