Claude Code shadcn Setup: MCP Server, Skill and CLAUDE.md
Claude Code already knows shadcn/ui from its training data, and that is the problem. It writes the API it remembers, which may not match the files in your components/ui, and it has never seen the registries you added to components.json last month. A Claude Code shadcn setup that holds up has five parts: the shadcn MCP server so Claude can search real registries, the shadcn skill for project context, the registry list in components.json, a short rules file that says which registry owns which folder, and a CLI that writes the files. This tutorial sets up all five, adds a second registry, and ends with prompts shaped to get the right component installed.
Everything below was run in October 2026 against shadcn 4.21.4, the skills CLI 1.7.1 and our own registry, Wingo UI 1.7.0. What we say about Claude Code itself comes from its docs as of the same month. For the wider picture, read our component library MCP guide first.
What does a Claude Code shadcn setup need?
A server that searches registries, instructions that say when to use it, a list of registries and a CLI that installs:
A library's own MCP server is an optional sixth piece: the shadcn server returned no props in our tests, and a library server can. Ours joins shadcn's in the same .mcp.json through npx wingo-ui@latest agents install claude.
How do I add the shadcn MCP server to Claude Code?
Run one command in the project root, restart Claude Code and check /mcp:
It writes this entry to .mcp.json, then installs shadcn as a dev dependency:
.mcp.json is Claude Code's project scope: commit it and the team gets the server, after each person approves it once. For a setup only you use, claude mcp add shadcn -- npx shadcn@latest mcp adds it in local scope, the default, instead.
Which tools does the shadcn MCP server give Claude?
Once the Claude Code shadcn MCP connection is up, Claude gets seven read-only tools. This is the list the server returned in 4.21.4:
get_project_registries: the registries incomponents.json.list_items_in_registriesandsearch_items_in_registries: browse or fuzzy-search, with paging.view_items_in_registries: name, description, type, file count and npm dependencies.get_item_examples_from_registries: demo code, found by names likedialog-demo.get_add_command_for_items: thenpx shadcn@latest addcommand for items.get_audit_checklist: a checklist for after generating code.
Three details shape how you prompt it. The server never writes files: it hands Claude an add command, and every install is a Bash call you can read first. The server sends no instructions, and Claude Code's tool search loads only tool names and server instructions when a session starts, so the skill and your rules file have to say when to use it. And view_items_in_registries returned neither props nor source for @shadcn/button or our dialog; examples exist only where a registry publishes -demo items, which shadcn/ui does and we do not yet. For a third-party item, Claude learns the API from the installed file or the library's own docs.
One bug in 4.21.4: search results print each add command as [object Promise]. get_add_command_for_items returns the real one, npx shadcn@latest add @shadcn/button @wingo-ui/dialog in our test.
How do I add a second registry, private or Pro?
Add it under registries in components.json. A public registry needs only its URL template:
A shadcn private registry takes headers, with the secret in an environment variable:
The shadcn CLI reads that variable from the environment or from .env.local; we confirmed both. When the key is set, agents install writes our own MCP entry in .mcp.json with the same ${WINGO_UI_API_KEY}, and Claude Code expands it from the environment it started in. Export the key in your shell profile and both servers see it.
Use the header form only when the variable is set. Without it, every call that touches @wingo-ui fails with MISSING_ENV_VARS: a search across @shadcn and @wingo-ui returned only that error, with no shadcn results either. Free Wingo UI items need no key, so keep the plain URL until you have one.
Then one search covers both registries. This is the real answer to search_items_in_registries for "login" with a limit of 4, the broken add-command lines removed:
173 matches for one word is fuzzy matching at work (@shadcn alone returned 7), so ask for a small limit.
What happens when two registries both write components/ui/button.tsx?
Neither CLI replaces the other's file silently, and either answer breaks something. shadcn/ui and many shadcn-style libraries install into components/ui with the same file names and different APIs. When we added our Dialog to a project that already had the shadcn button, our CLI reported a conflict on components/ui/button.tsx, kept the shadcn file and still wrote the dialog and drawer next to it; the shadcn CLI asks before it overwrites. Keeping the shadcn button breaks the type-check, because our dialog and drawer render their close buttons with radius="full", a prop shadcn's Button does not have. Replacing it breaks every variant="default", "secondary" or "destructive" in your app, because our Button variants are solid, soft, outline, ghost and link (destructive is tone="danger").
Give the second library its own folder. Our CLI reads its paths from wingo-ui.json, so after npx wingo-ui@latest init, change the ui alias and keep the rest:
npx wingo-ui@latest add dialog then wrote button.tsx, drawer.tsx and dialog.tsx into components/wingo beside the untouched shadcn button and rewrote their imports to @/components/wingo/.... init had already merged our design tokens into app/globals.css, and add appended the drawer's transitions. That is the second reason to install our items with our CLI: the shadcn-format JSON at /r/<slug>.json carries no tokens, so in a project without them, z-modal and shadow-modal on a shadcn-installed dialog resolve to nothing.
Should I install the Claude Code shadcn skill too?
Yes, if your project uses shadcn/ui components; if it also uses another library, scope the skill in your rules file. The skill is the official instruction set from the shadcn/ui repository:
The skills CLI wrote two skills into .claude/skills, shadcn and migrate-radix-to-base, and recorded both in skills-lock.json. Claude Code keeps only a skill's description in context until the skill is used, so the 19.5 KB SKILL.md costs little until a UI task starts. Its description matches any project with a components.json, so expect it on most UI work.
When it loads, it runs npx shadcn@latest info --json and puts your framework, Tailwind version, aliases, base library and installed components in front of Claude. Its allowed-tools line also lets Claude run any npx shadcn@latest command without asking for the rest of that turn, add --overwrite included. Three of its rules matter before you prompt:
- Name the registry. Ask for "a login block" without one and the skill tells Claude to ask you which registry to use.
- Docs first. Claude runs
npx shadcn@latest docs <component>before using one. That command only knows shadcn/ui:docs dialogreturned links to the shadcn/ui and Radix Dialog docs, anddocs @wingo-ui/dialoganswered that the component was not found in the shadcn registry. - shadcn/ui composition. Forms use
FieldGroupandField, icons in buttons takedata-icon, and since shadcn's Button has no loading prop, Claude composes aSpinneranddisabledby hand.
All three are right for shadcn/ui files, and the last two are wrong for other registries. Our Button has a loading prop that shows a spinner in place of the label, keeps the width, sets aria-busy and ignores clicks, so a hand-built spinner duplicates it. In the demo below, pressing "Save changes" swaps the label for a spinner and "Saving" while the button keeps its width.
$ npx wingo-ui@latest add buttonWhat should CLAUDE.md say?
Who owns which folder, which tools to search with, which CLI installs and updates, and where the shadcn skill's rules stop. Claude Code loads CLAUDE.md at the start of every session, so this block is always in context:
The first line answers what no tool can: which folder belongs to whom. It matters twice in this layout, because our own skill, which agents install writes, assumes the default folder and tells Claude that imports use @/components/ui/<name>. The fourth line keeps shadcn/ui patterns out of files with a different API. The last repeats the skill's own --overwrite rule, because the skill's permission grant covers that flag. On shadcn/ui alone, keep the search, install and overwrite lines. If Codex or Cursor work in the same repository, put these lines in AGENTS.md and import it from CLAUDE.md; our AGENTS.md vs CLAUDE.md comparison explains which file each agent reads.
Allow the read-only tools in .claude/settings.json and leave installs on ask:
Which prompts get Claude to install the right component?
The ones that name the registry, ask for a search before an install, and say what to check at the end. Five prompts built that way:
- Find before you build. "Search @shadcn and @wingo-ui for a modal that becomes a bottom sheet on phones. List the candidates with their registry and install command, and install nothing yet." Naming both registries satisfies the skill's rule, and "install nothing yet" keeps the first step read-only.
- Build on one component's real API. "Add the Wingo UI dialog and input with the Wingo CLI, read their props with get_component, then build a profile dialog in app/settings/profile-dialog.tsx with a name field, Cancel and Save, a loading state on Save and an error if saving fails." Naming the source of the API steers Claude away from the shadcn/ui Dialog it remembers.
- Start screens from a block. "Build /login from the Wingo UI login-page block. Wire onSignIn and onVerifyCode to our /api/auth routes, hide the social providers, and list every on* handler you left on its simulated default." The Login Page ships simulated handlers so its demo works, and each one you do not pass stays simulated: without
onVerifyCode, the code views accept123456. - Ask for updates as a report. "Check my Wingo UI components and my shadcn/ui button for upstream changes. Show me the notes and the diff, and change nothing."
- End with a check. "Run get_audit_checklist, then type-check and lint the files you touched." The checklist is six generic lines; the type-check is what catches a wrong prop, such as
variant="destructive"on our Button.
For prompt 2, this is the file a good run ends with, on the real APIs of the Dialog, the Button and the Input. We type-checked it against release 1.7.0:
There is no media query in that file. Below 768px the same Dialog opens as a bottom sheet you can drag down to close, with the actions stacked full width at 48px and the primary on top; our shadcn responsive dialog tutorial explains how that switch avoids a hydration flash. The demo below is a listing card for a city bike in Brooklyn at $420, and its Edit button opens the same kind of dialog.
$ npx wingo-ui@latest add dialogHow do I keep the installed components up to date?
Ask Claude for a report, then decide. The two CLIs treat edited files differently, which is why the rules file sends each folder to its own tool.
For shadcn/ui files, npx shadcn@latest add button --diff shows your file against the registry, and --overwrite replaces it, edits included. The skill tells Claude to merge by hand when a file has local changes. Our guide to updating shadcn components without losing edits has a git script that runs a real 3-way merge instead.
For Wingo UI files, the CLI recorded each item's version and kept a pristine copy at install, so it merges for you:
With our MCP server connected, check_updates returns the same list with release notes, so prompt 4 works inside the session.
Where should I start?
On shadcn/ui alone: add the MCP server, install the skill, write the three rules lines, and name @shadcn in every prompt. If you want a second registry built for phones, the Dialog, Button and Input from this post are free; npx wingo-ui@latest agents install claude adds our MCP server and skill next to shadcn's, and the MCP server docs cover the setup. The Login Page block is part of Wingo UI Pro, on the pricing page. More guides like this one live under Building UI with AI agents.
FAQ
How do I install the shadcn MCP server in Claude Code?
Run npx shadcn@latest mcp init --client claude in the project root. It writes a shadcn entry to .mcp.json and installs shadcn as a dev dependency; restart Claude Code and run /mcp to check that the server connected.
What is the difference between the shadcn skill and the shadcn MCP server?
The MCP server gives Claude tools to list, search and view registry items and to get their add commands. The skill adds no tools: it injects your project config from shadcn info --json and the shadcn/ui composition rules into the session when Claude works on UI.
Does the shadcn MCP server install components by itself?
No. As of shadcn 4.21.4 its seven tools are read-only: it returns the npx shadcn@latest add command and Claude runs it in the terminal. That Bash call normally asks for your approval, but during a turn where the shadcn skill is active, the skill's allowed-tools line lets it run without asking.
Why does a shadcn MCP search fail with MISSING_ENV_VARS?
A registry in components.json uses an environment variable in its headers that is not set. The shadcn CLI then fails the whole call, other registries included. Export the variable or put it in .env.local, or use the plain URL form for public registries.
Can I use shadcn/ui and another registry in the same project?
Yes, but many shadcn-style libraries install into components/ui with the same file names, starting with button.tsx. Give the second library its own folder (Wingo UI reads it from aliases.ui in wingo-ui.json) so neither CLI has to overwrite the other's files.
- Claude Code
- shadcn/ui
- MCP
- Skills
- AI agents