WingoUI
ComponentsTemplatesDocsChangelogBlogAI agents
ThemePricing
  1. Home
  2. Blog
  3. Building UI with AI agents
  4. Claude Code shadcn Setup: MCP Server, Skill and CLAUDE.md
  1. Blog
  2. Building UI with AI agents
  3. Claude Code shadcn Setup: MCP Server, Skill and CLAUDE.md
Building UI with AI agents
Building UI with AI agents

Claude Code shadcn Setup: MCP Server, Skill and CLAUDE.md

SR
Serban Rusu · Founder of Wingo UI
Oct 9, 2026 · 12 min read

On this page

0%
  1. What does a Claude Code shadcn setup need?
  2. How do I add the shadcn MCP server to Claude Code?
    1. Which tools does the shadcn MCP server give Claude?
  3. How do I add a second registry, private or Pro?
    1. What happens when two registries both write components/ui/button.tsx?
  4. Should I install the Claude Code shadcn skill too?
  5. What should CLAUDE.md say?
  6. Which prompts get Claude to install the right component?
  7. How do I keep the installed components up to date?
  8. Where should I start?
  9. FAQ
    1. How do I install the shadcn MCP server in Claude Code?
    2. What is the difference between the shadcn skill and the shadcn MCP server?
    3. Does the shadcn MCP server install components by itself?
    4. Why does a shadcn MCP search fail with MISSING_ENV_VARS?
    5. Can I use shadcn/ui and another registry in the same project?

TL;DR

Run npx shadcn@latest mcp init --client claude to add the shadcn MCP server to .mcp.json, install the shadcn skill with npx skills add shadcn/ui, and list every extra registry in components.json. Then add a short block to CLAUDE.md that says which registry owns which folder and which CLI installs and updates it, and name the registry in every prompt. The server finds components, the skill and the rules steer Claude, and the CLI writes the files.

Published Oct 9, 2026

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:

PieceJobHow you add it
shadcn MCP serverSearches every registry in components.json, returns add commandsnpx shadcn@latest mcp init --client claude
shadcn skillProject config and shadcn/ui composition rulesnpx skills add shadcn/ui
components.jsonThe registries to search, with auth headersEdit by hand
CLAUDE.md blockWhich registry owns which folder and which CLI installs itSix lines by hand
The CLIWrites files, installs npm packagesnpx shadcn@latest add or a library's own

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:

bash
npx shadcn@latest mcp init --client claude

It writes this entry to .mcp.json, then installs shadcn as a dev dependency:

json
{
"mcpServers": {
"shadcn": {
"command": "npx",
"args": ["shadcn@latest", "mcp"]
}
}
}

.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 in components.json.
  • list_items_in_registries and search_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 like dialog-demo.
  • get_add_command_for_items: the npx shadcn@latest add command 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:

json
{
"registries": {
"@wingo-ui": "https://wingo-ui.com/r/{name}.json"
}
}

A shadcn private registry takes headers, with the secret in an environment variable:

json
{
"registries": {
"@wingo-ui": {
"url": "https://wingo-ui.com/r/{name}.json",
"headers": { "Authorization": "Bearer ${WINGO_UI_API_KEY}" }
}
}
}

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:

text
Found 173 items matching "login" in registries @shadcn, @wingo-ui:
Showing items 1-4 of 173:
- login-page (registry:block) - The sign-in screen of Northwind CRM: password, email link or code, Google / Apple / Microsoft, two-step codes and the whole password recovery. [@wingo-ui]
- login-01 (registry:block) - A simple login form. [@shadcn]
- login-05 (registry:block) - A simple email-only login page. [@shadcn]
- login-02 (registry:block) - A two column login page with a cover image. [@shadcn]

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:

json
{
"aliases": {
"ui": "@/components/wingo",
"blocks": "@/components/blocks",
"hooks": "@/hooks",
"lib": "@/lib",
"utils": "@/lib/utils"
}
}

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:

bash
npx skills add shadcn/ui --agent claude-code

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 dialog returned links to the shadcn/ui and Radix Dialog docs, and docs @wingo-ui/dialog answered that the component was not found in the shadcn registry.
  • shadcn/ui composition. Forms use FieldGroup and Field, icons in buttons take data-icon, and since shadcn's Button has no loading prop, Claude composes a Spinner and disabled by 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.

Click Save changes: the spinner takes the label's place, the width holds and the button ignores clicks until it settles
$ npx wingo-ui@latest add button
FreeButton docs

What 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:

md
## UI components
- Two registries. `components/ui` is shadcn/ui (`@shadcn` in components.json). `components/wingo` is Wingo UI (versions in wingo-ui.json); import it from `@/components/wingo/<name>`.
- Before writing UI, search: the `shadcn` MCP tools for @shadcn and other registries, the `wingo-ui` MCP tools for Wingo UI. Never hand-write a component a registry has.
- Install shadcn items with `npx shadcn@latest add @shadcn/<name>` and Wingo UI items with `npx wingo-ui@latest add <slug>`. Never install a Wingo UI item with the shadcn CLI.
- Read the API of the file you import. The shadcn skill's rules and `shadcn docs` apply to `components/ui` only; for Wingo UI items call `get_component` and configure them through their props.
- Semantic tokens only, no hex colors. Check 390px and 1440px wide, in light and dark.
- Never pass `--overwrite` without asking me. Before any update, show me the diff or the release notes.

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:

json
{
"permissions": {
"allow": [
"mcp__shadcn",
"mcp__wingo-ui",
"Bash(npx shadcn@latest search *)",
"Bash(npx shadcn@latest view *)",
"Bash(npx wingo-ui@latest outdated)"
]
}
}

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:

  1. 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.
  2. 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.
  3. 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 accept 123456.
  4. 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."
  5. 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:

tsx
// app/settings/profile-dialog.tsx
"use client";
import { useState } from "react";
import { Pencil } from "lucide-react";
// next to shadcn/ui these imports read @/components/wingo/...
import { Button } from "@/components/ui/button";
import { Dialog, DialogClose } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
type ProfileDialogProps = {
initialName: string;
onSave: (name: string) => Promise<void>;
};
export function ProfileDialog({ initialName, onSave }: ProfileDialogProps) {
const [open, setOpen] = useState(false);
const [name, setName] = useState(initialName);
const [saving, setSaving] = useState(false);
const [error, setError] = useState("");
function changeOpen(next: boolean) {
// start from the saved name every time it opens
if (next) {
setName(initialName);
setError("");
}
setOpen(next);
}
async function save() {
setSaving(true);
setError("");
try {
await onSave(name.trim());
setOpen(false);
} catch {
setError("Your name was not saved. Try again.");
} finally {
setSaving(false);
}
}
return (
<Dialog
open={open}
onOpenChange={changeOpen}
trigger={
<Button variant="soft" leftIcon={<Pencil />}>
Edit profile
</Button>
}
title="Edit profile"
description="Your name appears on invoices and in comments."
icon={<Pencil />}
footer={
<>
<DialogClose asChild>
<Button variant="soft">Cancel</Button>
</DialogClose>
<Button tone="brand" loading={saving} loadingText="Saving" onClick={save}>
Save
</Button>
</>
}
>
<Input
label="Full name"
name="name"
autoComplete="name"
value={name}
onValueChange={setName}
error={error}
/>
</Dialog>
);
}

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.

Click Edit, change the price and press Save: the button shows its loading state, then the dialog closes; on a phone the same dialog opens as a bottom sheet
$ npx wingo-ui@latest add dialog
FreeDialog docs

How 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:

bash
npx wingo-ui@latest outdated # installed items vs the latest release
npx wingo-ui@latest diff dialog # your file vs upstream
npx wingo-ui@latest update dialog # 3-way merge that keeps your edits

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.

Components in this post

  • Button

    The button every screen starts with: five variants, six tones, three sizes plus icon sizes, and a loading state that never jumps.

    Free
  • Dialog

    The modal for focused tasks: a spring-scaled card on desktop that becomes a bottom sheet on phones, with nesting and a scrolling body.

    Free
  • Login Page

    The sign-in screen of Northwind CRM: password, email link or code, Google / Apple / Microsoft, two-step codes and the whole password recovery.

    Pro
  • Input

    The single-line text field: icons and addons inside the box, a clear button, a loading spinner, a counter and floating labels.

    Free

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

Share

SR

About the author

Serban Rusu

Founder of Wingo UI

Serban Rusu is the founder of Wingo UI. He builds the component library, its CLI and its MCP server, and writes about React interfaces that work well on phones and with AI coding agents.

More from Serban
Keep reading

Related posts

All posts
Building UI with AI agents

AGENTS.md vs CLAUDE.md vs Cursor Rules for React

AGENTS.md vs CLAUDE.md vs Cursor rules for React and Tailwind: which agent reads which file in October 2026, and a rules file that keeps agents on your tokens.

SRSerban Rusu·Oct 9, 2026·14 min read
Building UI with AI agents

Component Library MCP: Make Agents Use Real Components

How a component library MCP server, rules files, llms.txt and a registry get Claude Code, Cursor and Codex to install real components instead of guessing.

SRSerban Rusu·Oct 9, 2026·25 min read
Component guides

How to Update shadcn Components Without Losing Edits

Update shadcn components without losing edits: what shadcn diff and add --diff show, why a 2-way diff hides whose change is whose, and how a 3-way merge works.

SRSerban Rusu·Oct 9, 2026·10 min read
Newsletter

Get new posts by email

New guides, tutorials and comparisons from the Wingo UI blog, sent when they are published.

No spam. Unsubscribe at any time.

WingoUI

Animated, configurable, mobile-first React components. Copy the source, make it yours, and let your coding agent build with it.

ComponentsTemplatesPricingBlogTheme

Component categories

  • Buttons & Actions
  • Inputs
  • Forms
  • Navigation
  • Overlays
  • Feedback
  • Data Display
  • Tables & Lists
  • Charts & Stats
  • Layout
  • Media
  • AI Kit
  • Text & Effects
  • Mobile
  • Commerce
  • Marketing Sections
  • Blocks
  • Hooks & Utilities
Wingo UI