
Matchcn
io.github.francesco0242由 GitHub 帳號 francesco0242 發布v0.2.2更新於 Oct 24, 2026
Semantic index across shadcn-format component registries: find components by what they do.
概覽
針對 shadcn 格式元件註冊表的語意索引,讓助理用自然語言描述找到可安裝的 UI 元件。
- 可以做什麼
- 用自然語言描述 UI 需求,在預先標記的 shadcn 格式元件目錄中比對,回傳單一確定結果、排序候選清單或明確的「無相符」。
- 可限定單一註冊表、限制候選數量,或自行提供七個標記維度的提示以跳過伺服器端解析。
- 可選擇由伺服器端裁決相近候選,或由呼叫端自行挑選(回傳附摘要的排序清單,不發出模型呼叫)。
- 檢視每個維度的比對理由、信心分數、來源連結,以及可直接執行的安裝指令。
- 適用情境
- 適合程式編寫助理需要現成可安裝的 UI 元件、卻只知道功能而不知道註冊表裡叫什麼名字的情況。可避免逐一瀏覽註冊表或憑猜測元件名稱,也避免重複手寫元件。
- 執行需求
- 透過 npx 以 stdio 在本機執行,需要 Node.js。未宣告任何 API 金鑰、環境變數或請求標頭。需要網路存取註冊表以及免費的分類與翻譯端點。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Matchcn,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
[matchcn: describe your UI, find the right component]
matchcn
[npm version] [npm downloads] [GitHub stars] [CI status] [MIT license]
A semantic index across shadcn-format component registries.
Find a component by what it does, not what it is called.
matchcn.dev · Quick start · For AI agents · How it works · Limitations
[Install in Cursor] [Install in VS Code]
Claude Code: claude mcp add matchcn -- npx -y matchcn
Codex: codex mcp add matchcn -- npx -y matchcn
Grok CLI: grok mcp add matchcn -- npx -y matchcn
The problem
npx shadcn add can install a component from any registry that publishes
a registry.json. Hundreds of registries do. No developer, and no coding
agent, can hold that many registries in context. 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. "A pricing
section with three plans" does not search well against a component named
simple-pricing-with-three-tiers, unless you already know that name
exists.
matchcn tags every component it indexes across seven fixed properties (category, domain, motion, visual density, interaction model, and two others) using classifier.dev, then matches a plain-language brief against those tags with deterministic code. Same brief, same ranking, every time. No forced guesses: when nothing fits well, matchcn says so instead of returning the closest wrong answer.
Quick start
Add it to your MCP client's config:
Works with Claude Desktop (claude_desktop_config.json), Claude Code
(.mcp.json), Cursor, and any other MCP-compatible client. This exposes
one tool: pick_component(brief, registry?, maxResults?, hints?, resolve?).
More of a copy-paste person? Give your coding agent this prompt and let it set itself up:
Set up the matchcn MCP server using
npx -y matchcn. Configure it for my coding agent, then usepick_componentto find UI components that match my brief. Show me the match reasons and install command before adding a component.
Example
This is real output from a real run. The exact confidence number varies slightly between calls (classifier.dev does not guarantee identical answers across calls), but the outcome, the chosen component, and the install command have been stable across every run tried.
Every response includes a per-dimension reason: which of the seven
tagged properties matched the brief and which did not, both sides' actual
values, never just a pass or fail bit. That is what makes a no_match or
a shortlist result debuggable instead of a dead end.
For AI agents
If you are an LLM reading this to decide whether to use matchcn: this tool exists specifically for you. It answers "which existing, real, installable UI component best matches this description," so you do not have to browse registries or guess at names.
Tool: pick_component
Input:
Fastest path (local mode): write the brief in English, pass hints,
and set resolve: "caller". matchcn then makes no model calls at all:
ranking is local and deterministic (about 80 ms warm on the full
catalog), and on a tie you get the ranked shortlist back, each candidate
with a one-line summary of its tagged properties, and pick yourself.
Omit a hint you are unsure about rather than guessing; omitted fields are
simply not ranked on. Without hints and resolve, behavior is unchanged.
Output is always one of three shapes, never a fourth "best guess" shape:
outcome: "confident"— onechosencomponent:name,registry,installCommand(a ready-to-runnpx shadcn add <url>command),sourceUrl,confidence, andreasons(per-dimension match detail). If the component has language/styling variants, they are listed with their own install commands.outcome: "shortlist"— several candidates that all fit reasonably well with no clear single winner, ranked, each with the same per-dimension reasons, plusdifferentiators: which specific dimension(s) actually separate them, so you can decide on that axis instead of picking arbitrarily.outcome: "no_match"— nothing in the catalog is a real fit. The closest candidates are still listed for context but explicitly marked as rejected, not returned as an answer. Do not install one of these just because it was the closest; the catalog does not have what was asked for. Withresolve: "caller"this outcome is never returned, since only the server-side Resolve step reads candidates against the brief: a tie comes back as ashortlistand judging whether any candidate genuinely fits is up to you.
Every result also reports briefParsedBy (hints or classifier) and
resolvedBy (server or caller), and every candidate carries a
summary line.
Call this before hand-rolling a component or guessing a registry name. It is deterministic: the same brief against the same catalog version always ranks candidates the same way.
How it works
Five stages. Tagging runs ahead of time and is committed as data
(data/tags/); matching at query time is plain deterministic code, not
a model call, so results are reproducible.
- Ingest — fetch each registry's
registry.json, normalize into one shape. - Tag — one batched call per chunk of components to classifier.dev,
across seven dimensions:
category,domain,motion,visual_density,interaction_model,needs_external_data,decorative_only. Output is committed JSON, reviewable like code. - Match — parse the brief into the same seven dimensions with one classifier.dev call, then rank every tagged component against it in plain code. Each dimension's contribution to the ranking is weighted by its own confidence, so a weak tag pulls its weight down instead of polluting the result.
- Resolve — for close calls, one more call reviews the top candidates' real descriptions and picks a winner, or says none of them fit. Skipped when the top match is already clearly ahead, to save a round trip.
- Surface — the MCP server in this repo. One tool,
pick_component.
Registries indexed
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. Every
component you install still comes directly from its own registry via
npx shadcn add <url>.
4,578 components total. The first six are the original motion/marketing family; the middle three are a product-UI expansion (forms, tables, dashboards, data display) added after checking each registry's demo/duplicate conventions individually rather than assuming they match the original six; cnippet and uiable are a second product-UI expansion, added the same way; plate and react-aria are a third, added the same way after each got its own filter fix (dropping plate's documentation pages, collapsing react-aria's tailwind/css/hooks style-prefix duplicates down to one canonical component each). shadcn-ui-blocks is a fourth: its combined free+paid index was excluded outright (82% of it paywalled, see Limitations below), but the vendor's own maintainer pointed at a separate, curated free-only index, which a real per-item check found 100% installable unauthenticated, so only that free-only index is used here. aceternity's count already excludes 163 page-template components a real per-item availability check found paywalled at their actual install URL; shadcn-dashboard's count already excludes 165 components for the same reason; shadcnblocks was tagged but is not currently indexed, see Limitations below. Tagging runs through classifier.dev, a free, keyless classification endpoint backed by TypeSafe's Jev decision model.
matchcn is an independent, unofficial project. It is not affiliated with, endorsed by, or a partner of shadcn, any of the registries above, or classifier.dev/TypeSafe.
Limitations
Read this before relying on matchcn for something important.
- Some registries are partly or fully excluded for paywalled components,
found by a real per-item availability check (does
npx shadcn addactually work unauthenticated, not just what the index claims): aceternity (163 of 281 tagged components, mostly page-template demo pages) and shadcn-dashboard (165 of 508) had the gated share filtered out before shipping. shadcnblocks was tagged (4,171 components) but is held back entirely: its own filter check got contaminated by the vendor's rate limiter, so it ships once a clean check runs rather than on bad data. shadcnuikit and shadcn-space were evaluated and skipped outright for the same reason (40% and 33.3% paywalled). shadcn-ui-blocks's combined registry.json was evaluated the same way and skipped for the same reason, worse than either: 3,198 of its 3,903 real blocks (82%) are-pro-named and return 401 unauthenticated on their actual install URL. Its maintainer later pointed at a separate, curated free-only index the vendor publishes at a different URL; a fresh per-item check against that one found 626/626 (100%) installable unauthenticated, so only that free-only index is indexed here, not the combined one. cult-ui.com isn't indexed at all: its registry sits behind a bot challenge. - Tag confidence varies by registry and dimension, and the matcher
already accounts for it.
visual_densityused to be the clear weakest dimension (36-37% under 0.6 confidence, both a counting-based and a named-anchor wording were tried and both plateaued there). It was reformulated from a 3-way label choice to a continuous probability (a structural change, not another wording tweak), piloted against a real sample before any backfill, then backfilled across the full catalog: full-catalog result is now 21.4% under-threshold, in line with the rest of the schema (13.5-21.5% across all seven dimensions;motionis nominally the new low point, by less than a percentage point). assistant-ui (agent/chat UI content, far from the schema's motion/marketing anchors) still tags less confidently than the rest of the catalog. cnippet and uiable were measured at full scale after an earlier 20-item pre-tagging sample suggested they might be worse (35% under 0.6): they are not. Across their full 8,388 choice-dimension answers, 24.4% score under 0.6 confidence, matching the catalog-wide baseline almost exactly. A confidence-weighted matcher discounts all of this automatically, so a weak tag pulls its own weight down instead of producing a wrong confident answer, but a brief that leans heavily on assistant-ui-style content is the one most likely to get a shortlist or no-match instead of a clean pick. Separately, uiable's descriptions are still largely name-echoed templates ("Button component.") rather than hand-written text — a real data-quality gap, it just doesn't show up as measurably lower tag confidence. - Non-English briefs are auto-translated before matching, quality
depends on the source language and phrasing. A free wording-only fix
("judge by meaning, any language") was tried first and reverted for no
measured benefit. What actually closed the gap: local language
detection plus real translation to English before parsing (see
src/runtime/translate.ts). Verified directly on the exact case that first exposed this: a Polish pricing brief that previously returnedno_matchnow returns a real shortlist of pricing components once translated. Detection is local and free; translation calls a free, keyless third-party API (rate-limited per calling machine, not a shared pool this project could exhaust for everyone, since matchcn runs locally per user). Any detection or translation failure falls back to the original text silently, exactly as if this did not exist, so this can only help or be a no-op, never break a brief that already worked. Detection is restricted to the languages this project actually supports translating (found necessary after real briefs in Polish and Russian were confidently misdetected as unsupported languages when the detector was allowed to consider all ~180 it knows, silently skipping translation); detection on short, ambiguous phrases within the supported set can still occasionally misfire, and translation quality for the detected language is out of this project's control. - Ranking blends tagged-dimension distance with a text-relevance signal
over each candidate's name and title (TF-IDF-weighted cosine
similarity against the brief, not the tagged dimensions alone), because
the seven tagged dimensions cannot by themselves distinguish
near-identical variants (a login form, an OTP field, and a generic
form-input block all score as
form-input/auth/static). This closed a real, measured gap: a real-catalog eval found "a login form with email and password fields" losing to an OTP field by a wide margin despite eight genuine login-form components being indexed; after the fix, the real login form is a near-tie for the top rank, which Resolve then breaks using each candidate's real description. Text relevance can only be computed from a component'snameandtitle(the full description used at tagging time is not persisted for match-time use), so components with a sparse or generic title benefit less from this signal than ones with a specific, literal name. - 14 of 372+ shadcn-format registries are indexed. This is not a comprehensive index of the ecosystem.
None of the above produces a wrong forced answer: when confidence is
genuinely low, pick_component returns a shortlist or an explicit
no-match, never a single silent guess. That is the actual point of the
tagging and ranking design, not a disclaimer bolted on afterward.
Development
The ingest/tag pipeline that produces data/tags/ is also in this repo:
pnpm tag spends real classifier.dev decisions (free tier: 20,000/day,
3,000/min per IP, no API key needed). It checkpoints after every chunk, so
an interrupted run resumes without re-tagging anything already done.
Contributing
Issues and PRs are welcome, on a separate branch, never directly to
main. Only @whosfranki merges; opening
a PR does not mean it lands, but every one gets read.
Good first contributions:
- Fix a registry's filter.
src/pipeline/filter.tshas one function per registry (see open issues for known gaps, e.g. a non-componenttypethe current filter doesn't drop). Runpnpm ingest --registry=<name>against the affected registry and check the normalized output by hand before proposing a fix. - Add a registry. Add an entry to
src/runtime/registries.ts, verify its filter is correct (no demo/duplicate/non-component pollution, the same manual check every existing registry got, not an assumption), runpnpm check-availabilitybefore shipping (a registry.json's index gives no signal about which components are actually paywalled), thenpnpm tag. Do not add a registry and tag it in the same PR as an unrelated change. - Improve a tagging dimension's criteria.
src/runtime/dimensions.ts. Any wording change needs a before/after comparison on a real sample before it's proposed, not just a plausible-sounding rewrite; a prior attempt at a "make non-English briefs work" wording fix was tested this way and reverted for no measured benefit, which is the standard this project holds fixes to. - Runtime bugs in
src/runtime/match.ts,pick.ts,resolve.ts.
What a PR should include: what was measured before the change, what changed, what was measured after. "This should help" without a before/ after comparison on a real brief or component sample will get sent back for one.
License
MIT, see LICENSE.
來源:README.md,提交 e146f7a
工具
0版本歷史
1- v0.2.2最新Sep 23, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分