
Security News
upm Launches as a Fast, Tiny Package Manager Written in TypeScript
upm uses Node.js to deliver fast npm installs in about 250 KB, with a JavaScript API and security defaults.
MCP server that lets AI coding assistants search the icon packages actually installed in your project.
TrueIcon is an MCP server that gives AI coding assistants exact, version-correct icon references. Your assistant searches the icon packages your project actually uses (lucide-react, react-icons, @heroicons/react, @phosphor-icons/react, @tabler/icons-react, iconoir-react, @fluentui/react-icons, @carbon/icons-react, @ant-design/icons) and gets back real icon names, import paths and a ready-to-paste import line.
AI assistants often guess icon names. The guess can be an icon that never existed, one renamed a few releases ago, or one from a different library, and you only find out when the build fails. TrueIcon closes that gap:
search_icons and gets results that are guaranteed to exist in that version, for example import { Trash2 } from 'lucide-react';.| Provider id | npm package | Icon naming |
|---|---|---|
lucide | lucide-react | Lucide's file names, e.g. trash-2 → Trash2 |
heroicons | @heroicons/react | <icon>-<size>-<style>, e.g. trash-24-outline → TrashIcon |
react-icons | react-icons | <set>-<icon>, e.g. fa6-beer-mug-empty → FaBeerMugEmpty |
phosphor | @phosphor-icons/react | <icon> for the regular weight, <icon>-<weight> otherwise, e.g. trash-bold → TrashIcon with weight="bold" |
tabler | @tabler/icons-react | Tabler's icon names, e.g. trash → IconTrash, trash-filled → IconTrashFilled |
iconoir | iconoir-react | <icon> for regular, <icon>-solid for solid, e.g. trash-solid → TrashSolid |
fluentui | @fluentui/react-icons | <icon>-<style> with style regular, filled or color, e.g. delete-regular → DeleteRegular. Only the scalable (1em) icons are indexed, not the size-specific variants |
carbon | @carbon/icons-react | Carbon's export names in kebab case, e.g. trash-can → TrashCan. Variants add -filled, -alt or -color, e.g. accessibility-filled → AccessibilityFilled |
antdesign | @ant-design/icons | <icon>-<theme> with theme outlined, filled or two-tone, e.g. delete-outlined → DeleteOutlined |
Tools accept either the provider id or the npm package name ("lucide" or "lucide-react"). Usage snippets are for React. Phosphor weights all share one component, so pass the record's style as the weight prop (e.g. <TrashIcon weight="bold" />); the usage snippet only shows the import.
TrueIcon needs Node.js 20 or newer.
# Run without installing (this is what the MCP configs below do)
npx -y trueicon
# Or install globally and run the `trueicon` binary
npm i -g trueicon
trueicon
trueicon is a stdio MCP server. Your MCP client starts it; running it by hand only prints trueicon: v0.2.0 running on stdio to stderr and waits for JSON-RPC on stdin.
Add a .iconmcp.json to your project root that lists your icon packages:
{
"providers": [
{ "package": "lucide-react" },
{ "package": "@heroicons/react", "version": "2.1.5" }
]
}
Register TrueIcon with your MCP client (Claude Code or Claude Desktop).
Ask your assistant for an icon. The first search for each package downloads and indexes it, which takes a few seconds. Later searches use the local cache.
.iconmcp.jsonTrueIcon looks for .iconmcp.json in the project directory. That is $TRUEICON_PROJECT_DIR if set, otherwise the server's working directory.
{
"providers": [
{ "package": "lucide-react" },
{ "package": "react-icons", "version": "5.3.0" },
{ "package": "@heroicons/react", "version": "^2.1.0" }
]
}
| Field | Type | Required | Meaning |
|---|---|---|---|
providers | array | yes | Icon packages the project uses. search_icons searches all of them by default. |
providers[].package | string | yes | npm package name: lucide-react, react-icons, @heroicons/react, @phosphor-icons/react, @tabler/icons-react, iconoir-react, @fluentui/react-icons, @carbon/icons-react or @ant-design/icons. |
providers[].version | string | no | Exact version or npm range. If omitted, it is read from package.json (see below). |
search_icons then only works when you pass provider explicitly, and get_icon still works.search_icons reports them as a warning.| Variable | Default | Purpose |
|---|---|---|
TRUEICON_PROJECT_DIR | working directory | Project root holding .iconmcp.json and package.json |
TRUEICON_CACHE | ~/.trueicon/cache | Where downloaded packages and indexes are stored |
A provider's version is resolved in this order:
version argument passed to the tool call, if any.version in .iconmcp.json.package.json, checking dependencies first and then devDependencies.If none of these is available, the tool asks you to pin the version or add the package to package.json. TrueIcon reads the declared range from package.json. It does not read node_modules or the lockfile. For a range, it indexes the range's base version: ^0.460.0 indexes lucide-react@0.460.0. For a || b ranges, only the first part counts. To match an exact installed version, pin it in .iconmcp.json.
Indexes are keyed by major.minor:
0.460.x. The index built from 0.460.0 answers requests for 0.460.3.lucide-react from 0.460 to 0.461 builds a fresh index on the next search, with no manual step.synonyms.json changes (detected by hash) or the index format changes.Add TrueIcon from your project directory:
claude mcp add trueicon -- npx -y trueicon
Or commit a .mcp.json at the project root to share it with your team:
{
"mcpServers": {
"trueicon": {
"command": "npx",
"args": ["-y", "trueicon"]
}
}
}
Claude Code starts the server in your project directory, so it finds .iconmcp.json and package.json there. If it runs from somewhere else, add "env": { "TRUEICON_PROJECT_DIR": "/absolute/path/to/project" }.
Claude Desktop doesn't start servers in your project directory, so set TRUEICON_PROJECT_DIR. Edit claude_desktop_config.json: ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows.
{
"mcpServers": {
"trueicon": {
"command": "npx",
"args": ["-y", "trueicon"],
"env": {
"TRUEICON_PROJECT_DIR": "/absolute/path/to/your/project"
}
}
}
}
Restart Claude Desktop after editing the file.
Use the install badges at the top of this README. They add TrueIcon with TRUEICON_PROJECT_DIR set to ${workspaceFolder}, so it searches the project you have open.
To add it by hand in VS Code, create .vscode/mcp.json in your project:
{
"servers": {
"trueicon": {
"command": "npx",
"args": ["-y", "trueicon"],
"env": {
"TRUEICON_PROJECT_DIR": "${workspaceFolder}"
}
}
}
}
In Cursor, use the same entry under "mcpServers" in .cursor/mcp.json.
Every tool returns a single JSON text block. On failure, the block is {"error": "..."} and the MCP result is flagged with isError: true.
search_iconsSearches the index and returns ranked matches with import statements.
| Argument | Type | Required | Description |
|---|---|---|---|
query | string | yes | What the icon should depict, e.g. "trash" |
provider | string | no | Provider id or package. Default: every provider in .iconmcp.json |
version | string | no | Version or range. Default: resolved as described in Versions |
style | string | no | Exact style filter, e.g. "outline", "solid", "filled" (tabler), "regular" (fluentui), "two-tone" (antdesign) or a phosphor weight such as "bold". Lucide icons are all outline; base carbon icons have no style |
set | string | no | Exact set filter, e.g. "fa6" or "md" for react-icons |
limit | integer | no | Maximum results, 1 to 50, default 10 |
Example call:
{ "query": "trash", "provider": "lucide", "limit": 3 }
Response:
{
"results": [
{ "name": "trash", "importName": "Trash", "importPath": "lucide-react", "package": "lucide-react",
"version": "0.460.0", "style": "outline", "set": "lucide",
"usage": "import { Trash } from 'lucide-react';", "score": 2.0e-14 },
{ "name": "trash-2", "importName": "Trash2", "importPath": "lucide-react", "package": "lucide-react",
"version": "0.460.0", "style": "outline", "set": "lucide",
"usage": "import { Trash2 } from 'lucide-react';", "score": 1.6e-6 },
{ "name": "delete", "importName": "Delete", "importPath": "lucide-react", "package": "lucide-react",
"version": "0.460.0", "style": "outline", "set": "lucide",
"usage": "import { Delete } from 'lucide-react';", "score": 1.2e-4 }
]
}
How search works:
provider, style and set are exact, case-insensitive filters. They are applied before ranking."detele" finds Delete.score runs from 0 (perfect) to 1, so lower is better. Results from several providers are merged and sorted by score."trash can" finds trash-can icons. Short keyword queries ("trash", "settings", "beer") still cast the widest net.warnings array explains why. The other providers still return results.list_providersTakes no arguments. Returns the providers configured in .iconmcp.json with their resolved versions, plus every provider TrueIcon supports.
{
"configured": [
{ "id": "lucide", "package": "lucide-react", "version": "^0.460.0", "source": "package.json" },
{ "id": "heroicons", "package": "@heroicons/react", "version": "2.1.5", "source": "iconmcp.json" }
],
"registry": [
{ "id": "react-icons", "package": "react-icons", "description": "Aggregated icon sets (Font Awesome, Material, Feather, and more) as React components" },
{ "id": "lucide", "package": "lucide-react", "description": "Lucide icons as React components" },
{ "id": "heroicons", "package": "@heroicons/react", "description": "Heroicons by the Tailwind CSS team as React components" },
{ "id": "phosphor", "package": "@phosphor-icons/react", "description": "Phosphor icons in six weights (thin, light, regular, bold, fill, duotone) as React components" },
{ "id": "tabler", "package": "@tabler/icons-react", "description": "Tabler icons (outline and filled) as React components" },
{ "id": "iconoir", "package": "iconoir-react", "description": "Iconoir icons (regular and solid) as React components" },
{ "id": "fluentui", "package": "@fluentui/react-icons", "description": "Microsoft Fluent UI System icons (regular, filled and color) as React components" },
{ "id": "carbon", "package": "@carbon/icons-react", "description": "IBM Carbon Design System icons as React components" },
{ "id": "antdesign", "package": "@ant-design/icons", "description": "Ant Design icons (outlined, filled and two-tone) as React components" }
]
}
source is "iconmcp.json" or "package.json". version and source are null when neither file provides a version. id is null for a configured package TrueIcon doesn't support.
get_iconGets the full record and import statement for an icon whose name the assistant already knows.
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | yes | Icon name ("trash-2") or import name ("Trash2"). Exact match first, then case-insensitive |
provider | string | yes | Provider id or package |
version | string | no | Version or range. Default: resolved as described in Versions |
Example call:
{ "name": "Trash2", "provider": "lucide" }
Response:
{
"id": "lucide-react@0.460:trash-2",
"name": "trash-2",
"importName": "Trash2",
"importPath": "lucide-react",
"provider": "lucide",
"package": "lucide-react",
"version": "0.460.0",
"style": "outline",
"set": "lucide",
"categories": [],
"tags": [],
"keywords": ["trash", "2", "delete", "remove", "bin", "garbage", "rubbish"],
"svg": "<path d=\"M3 6h18\"/><path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\"/>…",
"usage": "import { Trash2 } from 'lucide-react';"
}
svg is the icon's inner SVG markup, meaning the children of the root <svg> element. Heroicons uses the same import name in every size and style (TrashIcon). Pass the full variant name, such as "trash-24-outline", to get a specific one.
pingA health check that returns {"status":"ok","server":"trueicon"}.
The first time a tool needs package@major.minor, TrueIcon does the following:
https://registry.npmjs.org, verifies its sha512 integrity, and extracts it into the cache.index.json (one record per icon) and meta.json (exact version, synonyms hash, index format, build time).Later calls only read index.json. Package files are never touched at query time, and your node_modules is never read or modified. If several tool calls need the same index at once, they share one download.
The cache root is ~/.trueicon/cache, or $TRUEICON_CACHE if set:
~/.trueicon/cache/
├── lucide-react@0.460/ # extracted package + index.json + meta.json
├── react-icons@5.3/ # extracted package + index.json + meta.json
├── heroicons-react@2.1/ # extracted @heroicons/react package
└── @heroicons/react@2.1/ # index.json + meta.json for @heroicons/react
<package>@<major.minor>, where scoped names are flattened: @heroicons/react becomes heroicons-react. A .download-complete marker is written last, and a directory without it is treated as partial and replaced.<package>@<major.minor>/index.json and meta.json. For unscoped packages this is the same directory as the download.Each record's keywords combine the name parts, the tags, and synonym expansions from the bundled synonyms.json. The expansions are added at index time, so "bin" finds Trash2 without any extra work at query time.
git clone https://github.com/manikumarkv/trueicon.git
cd trueicon
npm ci
npm run build # compile to dist/
npm test # vitest
npm run lint # eslint
npm run typecheck # tsc --noEmit
CI runs lint, typecheck and tests on Node 20 and 22 for every push and pull request.
| Command | What it checks | Network |
|---|---|---|
npm test | Adapter parsing and every tool (search_icons, get_icon, list_providers) for all 9 providers, using the small fixtures in tests/fixtures/ | No |
npm run smoke | Every provider against the real npm packages: several pinned releases plus the current latest, checking the icon count and a few well-known icons | Yes |
The fixtures only change when someone edits them, so they can't catch a provider that changes its package format upstream. npm run smoke does. Run npm run smoke -- lucide tabler to check only some providers. CI runs it on pull requests that change src/providers/, src/indexer/ or src/cache/, and every Monday against the latest releases.
These scripts build the server and run it against playground/, a sample project that lists all 9 providers at their latest versions. They use a separate cache in .cache/dev/, so your real ~/.trueicon cache is untouched.
# Call one tool and print the result
npm run dev:call -- list_providers
npm run dev:call -- search_icons query="trash can" limit=5
npm run dev:call -- search_icons query=trash provider=lucide version=1.47.0
npm run dev:call -- get_icon name=Trash2 provider=lucide
# Open the MCP Inspector web UI on the local build
npm run dev:inspect
# Same, with the Node debugger on port 9229
npm run dev:debug
key=value pairs. Numbers and booleans are parsed, so limit=5 is sent as a number.TRUEICON_PROJECT_DIR to test against its package.json and .iconmcp.json, e.g. TRUEICON_PROJECT_DIR=~/code/my-app npm run dev:inspect.--fresh to delete the dev cache first, e.g. npm run dev:call -- --fresh search_icons query=trash. Use it after changing an adapter.npm run dev:debug, then in VS Code use Debug: Attach to Node Process, or open chrome://inspect in Chrome. Source maps are on, so breakpoints work in the .ts files under src/. Set them, then call a tool from the Inspector.console.error. It shows in the terminal for dev:call and in the Inspector's server log for dev:inspect.synonyms.jsonsrc/synonyms/synonyms.json maps a term to extra search terms:
{
"trash": ["delete", "remove", "bin", "garbage", "rubbish"],
"logout": ["sign-out", "signout", "exit", "leave"]
}
-) and its tags. trash-2 matches the key trash.keywords.bin should also find icons named delete, add both "trash": ["bin"] and "delete": ["bin"], or add a reverse entry.tests/synonyms.test.ts checks this.src/providers/registry.ts with a stable id, the npm package and a short description.src/providers/adapters/<provider>.ts that exports parseIcons(packageDir: string): RawIcon[] (see src/providers/adapter.ts). It gets the extracted package directory and returns one RawIcon per icon:
name: kebab-case and unique within the package, because it becomes part of the record id. Use toKebabCase from adapter.ts. If the package has variants with clashing component names, add the variant to the name, as the heroicons, react-icons, phosphor and iconoir adapters do.importName and importPath: the exact export and module specifier a user would import.svg: the inner SVG markup. LiteralCursor (src/providers/jsLiteral.ts) parses JS object and array literals without executing code. toSvgAttrs and renderSvg (src/providers/svg.ts) turn React props into SVG markup, and parseCreateElement reads compiled createElement(...) trees.style, set, categories and tags.src/providers/adapters/index.ts by adding it to ADAPTERS under the provider id.tests/fixtures/<provider>/ that mirrors the package layout, with a few real icon files plus any files the adapter must skip. Then add tests/adapters/<provider>.test.ts, covering name mapping, import paths, SVG output and buildIndex record ids like the existing adapter tests. tests/adapters/common.test.ts fails if a registered provider has no adapter.CASES in tests/providers-tools.test.ts: the fixture, a search query, one icon with its exact import line, and a deprecated alias if the package has them. The test fails if a registered provider has no case.TARGETS in scripts/smoke.mjs: a few well-known import names and a minimum icon count well below the real one. Then run npm run smoke -- <provider> to check it against the real published package.MIT © 2026 manikumarkv
FAQs
MCP server that lets AI coding assistants search the icon packages actually installed in your project.
The npm package trueicon receives a total of 0 weekly downloads. As such, trueicon popularity was classified as not popular.
We found that trueicon demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
upm uses Node.js to deliver fast npm installs in about 250 KB, with a JavaScript API and security defaults.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.