# Let Claude Code install UI components with an MCP server

When I ask Claude Code to "add a hero with a phone number and rotating service words" in a project without any component context, it writes one from scratch. The result is usually fine and always different from the last one. The animations are invented on the spot, the accessibility is whatever the model remembered that day, and the next page gets a slightly different hero. An MCP server fixes this by giving the agent a catalog to pull from instead of a blank file. This is how the Facade MCP server works with Claude Code, what the agent gets back, and where it still needs a human.

## What MCP is, in one paragraph

The Model Context Protocol is a small JSON-RPC protocol that lets an AI agent call tools that live outside the model. A server publishes a list of tools with names, descriptions and input schemas. The agent reads the list, decides when a tool fits the task, calls it with arguments and gets text back. Claude Code, Cursor and Codex all speak it. The Facade server runs as a plain HTTP endpoint on the same Next.js app that serves the docs, so there is nothing to install locally.

## Adding Facade to Claude Code

One command registers the server:

```bash
claude mcp add --transport http facade https://facadeui.com/api/mcp
```

That gives the agent the 33 free items. With a Pro license, pass the key as a header so the agent can also read Pro sources:

```bash
claude mcp add --transport http facade https://facadeui.com/api/mcp \
  --header "Authorization: Bearer $FACADE_LICENSE_KEY"
```

The server exposes four tools:

- `list_components` searches the catalog by free text, kind (component or block), category or tier.
- `get_component` returns the docs for one item: description, props table, install command, usage example and, where allowed, the full TypeScript source.
- `get_install_command` returns a single `npx shadcn@latest add` line for several items.
- `suggest_page` returns an ordered block list for a business type.

Nothing in the server writes to your project. The agent reads from it and then runs the shadcn CLI in your repo like any other shell command, so the files that land in `components/facade/` are the same ones a developer would install by hand.

## Asking the agent to build a plumber page

Here is a prompt I use as a test, in a fresh Next.js project with shadcn initialized:

"Build the home page for a plumber in Austin using Facade blocks. Live rating in the hero, a service area map with a ZIP checker, and a mobile call bar. Phone is 512 555 0147."

What happens, step by step:

1. The agent calls `suggest_page` with `"plumber"` and gets a numbered block order back: hero, logo cloud, features, process steps, service area, testimonials, pricing, FAQ, contact, footer, each with its first line of usage.
2. It calls `get_component` for the blocks it decided to use, reads the props, and notices that `hero-local-business` and `sticky-contact-bar` are free while `service-area` is Pro.
3. It calls `get_install_command` with the three names and runs the returned command.
4. It writes `app/page.tsx`, passing real values from my prompt into real props.

The page it produced on my last run, lightly trimmed:

```tsx
import { HeroLocalBusiness } from "@/components/facade/hero-local-business";
import { ServiceArea } from "@/components/facade/service-area";
import { StickyContactBar } from "@/components/facade/sticky-contact-bar";

export default function Home() {
  return (
    <>
      <HeroLocalBusiness
        rating={4.9}
        reviewCount="640+"
        headlinePrefix="Austin plumbers for"
        rotatingWords={["burst pipes", "water heaters", "slab leaks", "remodels"]}
        description="Licensed, insured and on the way in under an hour across Travis County."
        primaryCta={{ label: "Book a visit", href: "/book" }}
        phone={{ label: "(512) 555-0147", href: "tel:+15125550147" }}
        trustItems={["Licensed and insured", "Same day service", "Upfront pricing"]}
      />
      <ServiceArea
        heading="Where we work"
        checkerLabel="Check your ZIP code"
        successMessage={(zip, zone) => `Yes, we serve ${zip}. ${zone.name} is a ${zone.eta ?? "same day"} call.`}
        failureMessage={(zip) => `We do not cover ${zip} yet. Join the waitlist and we will let you know.`}
        ctaLabel="Request a visit"
        ctaHref="/book"
      />
      <StickyContactBar phone="512 555 0147" address="2200 S Lamar Blvd, Austin, TX" bookingUrl="/book" />
    </>
  );
}
```

Every prop in that file exists. `rotatingWords` is a real prop on the [Hero, local business](/components/hero-local-business) block, `successMessage` on the [Service area](/components/service-area) block really receives the matched zone, and the [Sticky contact bar](/components/sticky-contact-bar) really builds `tel:` and `sms:` links from the phone string and a Google Maps directions link from the address. The agent did not guess any of them, because `get_component` had shown it the props table a minute earlier.

## What the agent gets back

The tool responses are plain text, formatted for a model rather than for a browser. `list_components` returns one line per item with the slug, kind, category, tier and description. `get_component` returns the same Markdown page a human can open by adding `.md` to any component URL. For free items that includes the full source, which matters: the agent can read how the hero rotates words or how the bar hides on scroll and adapt the page around it instead of treating the component as a black box.

For Pro items without a license header the source is withheld and the response says so, with a link to the setup docs. The agent still gets the props and the install command, so it can write correct usage; it just cannot read or modify the internals until the key is in place.

## Limits, and prompts that work

The server is a catalog, not a designer. `suggest_page` knows three page shapes: real estate, agency and local business. It will not invent a block that does not exist, which is the point, but it also means that if your project needs a restaurant menu with reservations, the agent should combine the service menu and the booking picker rather than wait for a "restaurant" suggestion.

Three habits make the results better:

- Name the business and the city. "A dentist in Portland" gives the agent the data for the hero copy and the service area; "a landing page" does not.
- Give real facts. Phone number, address, hours and rating in the prompt turn into props instead of placeholders the client has to find later.
- Ask for the install, then review the page before asking for the next section. Agents are good at wiring props and less good at knowing which block the client will actually want third.

Prompts that have worked for me: "Build a landing page for a dental clinic in Miami using Facade blocks." "Replace our contact form with the Facade multi step lead form and keep our fields." "Add the Facade availability table for units A to D, data from units.json."

The block order the agent follows for home services is the same one documented on the [plumber page](/for/plumber). If you want to skip the agent entirely, that page has the one line install for the whole set.

## FAQ

### Does this work in Cursor or Codex too?

Yes. Any client that supports HTTP MCP servers can connect. In Cursor, add `{ "mcpServers": { "facade": { "url": "https://facadeui.com/api/mcp" } } }` to `.cursor/mcp.json`. The tools and responses are identical.

### Does the MCP server change files in my project?

No. It only answers questions. The shadcn CLI, run by the agent in your repository, writes the files. You can inspect every command before it runs, as with any other shell command Claude Code proposes.

### Can I run the server locally or self host it?

The server is part of the Facade site, and the free catalog is also published as `llms.txt` and per component Markdown pages, which agents can read without MCP at all. If you need an offline copy, `llms-full.txt` contains the full source of every free item in one file.
