matchcn
A semantic index across shadcn-format component registries: find a
component by what it does, rather than what it is called.
Homepage: matchcn.dev
matchcn is an independent, unofficial project. It is not affiliated with,
endorsed by, or a partner of shadcn, any of the registries it indexes, or
classifier.dev/TypeSafe.
What it is
Hundreds of registries now publish components in the shadcn
registry.json format, and npx shadcn add can install from any of
them. No developer or coding agent can hold that many registries in
context, and existing directory tools only search component names, which
does not help when you know what you need but not what any given
registry decided to call it. matchcn tags every component across a fixed
set of properties (category, motion, visual density, interaction model,
and two others) using classifier.dev, then
matches a plain-language brief against those tags with deterministic
code, so the same brief always ranks the same way.
Status: early, read this before using it
This is a 2-3 day MVP covering 11 of the 372+ registries shadcn's own
public list currently has. It is not a comprehensive index. Specific,
known weak points, in order of how much they matter:
- shadcnblocks is not indexed, despite being tagged. A real per-item
availability check (does
npx shadcn add actually work unauthenticated,
not just what the index claims) found roughly half of its components
return 401/403 "License required" at their real install URL, something
invisible in the registry's own index data. shadcn-dashboard had the
same problem at a smaller scale (32.5%) and was fixed by filtering the
paywalled components out before shipping; shadcnblocks' own full-scale
check got contaminated by the vendor's rate limiter partway through
(see docs/ROADMAP.md for the full account) before a
clean filter could be produced, so it was pulled entirely rather than
ship it unfiltered or filter it against bad data. Its tagged data is
preserved, not lost, and it is expected back once a clean check runs.
visual_density is the weakest tagged dimension. 36% of pilot
answers on this dimension score under 0.6 confidence, the worst of the
six tagged dimensions, even after a rewrite. A confidence-weighted
matcher discounts weak tags automatically, but a visual_density-heavy
brief is still the most likely to disappoint. Full numbers:
docs/PILOT_REPORT.md, docs/DIMENSIONS.md.
- Catalog-wide, 24.4% of choice-dimension answers (
category, motion,
visual_density, interaction_model) score under 0.6 confidence,
across the 1,783 components shipped as of the first product-UI
expansion. Close to the original 23.9% baseline at the
1,069-component size; assistant-ui pulls this up, and shadcn-dashboard's
paywall filtering has no effect on tag confidence either way, see below.
This has not yet been recomputed for cnippet and uiable (added after
this measurement, bringing the catalog to 3,880 components); a 20-item
category-only sample taken during their pre-tagging recon scored 35%
under 0.6 confidence, higher than this baseline, not yet confirmed at
full scale.
- assistant-ui: 37.0% of its choice-dimension answers land under 0.6
confidence, worse than aceternity's 28.7% on every single dimension. It
is now the lowest-confidence registry in the catalog. Likely cause:
its content (agent and chat UI: tool timelines, trace waterfalls,
reasoning panels) is further from the schema's original
motion/marketing anchors than any other registry. In practice, this
means chat- and agent-UI briefs (tool timelines, reasoning panels) are
more likely to return
shortlist or no_match than a confident pick,
even when a relevant assistant-ui component exists and ranks near the
top by text similarity, because its underlying dimension tags carry low
confidence.
- Non-English briefs are known to be weaker, confirmed on 3 Polish
test briefs, not fixed. A Polish pricing brief and its English
equivalent were compared directly: English found a real, well-tagged
pricing component at 0.98 confidence, Polish returned no match at all.
A zero-cost fix was tried (an explicit "interpret by meaning, any
language" line added to the tagging/matching criteria) and tested
properly with a before/after comparison on 3 Polish briefs: it did not
help the pricing brief, made a cursor brief measurably worse (a
confident shortlist of 3 real candidates became no match), and left a
bento grid brief unchanged. It was reverted rather than kept for no
measured benefit. Non-English input should not be assumed to work.
Details: docs/DECISIONS.md #17.
- aceternity's 170
registry:block demo pages are tagged from short
index descriptions only, not enriched from source. Their per-item
source endpoint returns HTTP 401 for anonymous requests; aceternity's
112 registry:ui primitives do not have this problem and are enriched
from real source. Details: docs/DECISIONS.md #12.
- cult-ui.com is not indexed at all. Its
registry.json sits behind
a Vercel bot challenge that a plain HTTP request cannot pass. Details:
docs/DECISIONS.md #6.
None of these cause a wrong forced answer: when confidence is genuinely
low, pick_component returns a shortlist or an explicit no-match, never
a single silent guess. See docs/DEMO_REPORT.md for
real examples of all three outcomes, including two briefs designed to
fail.
Install
npx matchcn
MCP setup
Add to your MCP client's config (for example Claude Desktop's
claude_desktop_config.json, or Claude Code's .mcp.json):
{
"mcpServers": {
"matchcn": {
"command": "npx",
"args": ["-y", "matchcn"]
}
}
}
This exposes one tool, pick_component(brief, registry?, maxResults?).
Worked example
$ pnpm demo
Brief: a dense bento grid for a landing page. This is real output from a
real run. classifier.dev does not guarantee identical answers across
calls, so the exact confidence number will vary between runs (0.79 to
0.98 observed across repeated runs during development); the outcome, the
chosen component, and the install command have been stable across every
run tried.
BRIEF: a dense bento grid for a landing page
----------------------------------------------------------------------
OUTCOME: CONFIDENT
Selected "bento-grid" from magicui.
-> bento-grid (magicui) confidence 0.85
install: npx shadcn@latest add https://magicui.design/r/bento-grid.json
matched: category, motion, visual_density, interaction_model, needs_external_data, decorative_only
not matched: (none)
resolve used: true decisions spent: 7
Every response includes a per-dimension reason (which of the six tagged
dimensions matched the brief and which did not), never just a name. Full
JSON shape and more examples, including a shortlist and two deliberate
no-match cases: docs/DEMO_REPORT.md.
How it works
Five stages: Ingest, Tag, Match, Resolve, Surface. Tagging runs ahead of
time and is committed as reviewable JSON (data/tags/); matching at
query time is deterministic code, not a model call, so the same brief
always ranks the same way. Full architecture, every design decision and
its rejected alternatives, and the exact tagging criteria used:
- docs/WHY.md: the problem, who this is for, what it explicitly is not
- docs/ARCHITECTURE.md: the five stages and the data flow
- docs/DIMENSIONS.md: the tag schema, every dimension's exact criteria
- docs/DECISIONS.md: numbered decision records, alternatives rejected and why
- docs/ROADMAP.md: what is shipped, what is deferred, what is parked
- docs/PILOT_REPORT.md, docs/FULL_RUN_REPORT.md, docs/DEMO_REPORT.md: real numbers from real runs
- RECON.md, docs/FILTER_REPORT.md: what was fetched and what was filtered out, with evidence
Registries indexed, and attribution
matchcn stores only derived tags (category, motion, density, and so on)
and a link back to each registry's own install command. It never copies,
stores, or redistributes any registry's component source code. Every
component you install still comes directly from its own registry via
npx shadcn add <url>.
3,880 components total, drawn from the eleven registries above (343 of
shadcn-dashboard's 508 tagged components ship; the other 165 were
filtered out as paywalled, not installable unauthenticated, see the
limitations section above). The first six are the motion/marketing
family from the original MVP; the rest are a product-UI expansion
(forms, tables, dashboards, data display) added after per-registry
filter verification, see
docs/REGISTRY_EXPANSION_STEP1_2.md.
Three other product-UI candidates (shadcn-ui-blocks, plate, react-aria)
were evaluated and excluded for specific, evidenced filter gaps;
shadcnblocks was tagged but is not currently shipped, both tracked
in docs/ROADMAP.md. cnippet and uiable were added in a
second product-UI expansion; two other candidates from that same recon,
shadcnuikit and shadcn-space, were evaluated and excluded after a real
per-item availability check found genuine paywalls on 40% and 33.3% of
a 30-item sample respectively. uiable's descriptions are largely
name-echo templates ("X component.") rather than hand-written text, a
known limitation for that registry, shipped as is rather than blocked
on. Tagging runs through classifier.dev, a
free, keyless classification endpoint backed
by TypeSafe's Jev decision model.
Development
pnpm install
pnpm ingest # fetch and normalize all registries
pnpm tag # tag all components via classifier.dev
pnpm demo # run 3 briefs end to end
pnpm test # determinism tests for the matcher
pnpm typecheck
License
MIT, see LICENSE.