# Gemini CLI shadcn MCP Setup for Real React UI

> Gemini CLI shadcn setup, tested on 0.67.0: the settings.json entry or gemini mcp add, folder trust, a GEMINI.md block and prompts that build real React screens.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Nov 5, 2026
- Category: [Building UI with AI agents](https://wingo-ui.com/blog/category/ai-agents)
- Reading time: 11 min
- Canonical: https://wingo-ui.com/blog/gemini-cli-shadcn-mcp

## TL;DR

To connect Gemini CLI to shadcn or another component registry, add the server under mcpServers in .gemini/settings.json (or run gemini mcp add), trust the folder, and put a rules block in GEMINI.md that says to search before writing UI. For shadcn/ui use npx -y shadcn@latest mcp; for a library with props and blocks, npx wingo-ui@latest agents install gemini writes both files. Check with gemini mcp list: Disabled means an untrusted folder, Disconnected means a bad key or a failed start.

Ask Gemini CLI for a customers table and it writes one from memory: a plain `<table>`, a hand-rolled filter, a layout that falls apart on a phone. A Gemini CLI shadcn setup fixes that by giving the agent a registry to search and a rules file that tells it to search first. This tutorial connects Gemini CLI to the shadcn MCP server and to a component library with props and full-screen blocks (ours, Wingo UI), through `settings.json` or `gemini mcp add`, adds a `GEMINI.md` block, and ends with prompts that turn into a real data table and a sign-in screen.

We ran every config below on November 5, 2026 with Gemini CLI 0.67.0, shadcn 4.23.0 and our CLI 1.0.4, in a clean project with its own home folder, and checked each one with `gemini mcp list`. We did not grade model output: what Gemini writes depends on the model and your auth. The config syntax matches Google's [MCP server docs](https://geminicli.com/docs/tools/mcp-server/) and [configuration reference](https://geminicli.com/docs/reference/configuration) as of the same day. Why an agent needs a component server at all is in our [component library MCP guide](https://wingo-ui.com/blog/component-library-mcp-guide).

## Can you still use Gemini CLI in November 2026?

Yes, with the right account. Google's [May 19, 2026 announcement](https://developers.googleblog.com/en/an-important-update-transitioning-gemini-cli-to-antigravity-cli/) moved individual users to Antigravity CLI: Gemini CLI stopped serving Google AI Pro and Ultra subscribers and free Gemini Code Assist users on June 18, 2026. Access stays unchanged for Gemini Code Assist Standard and Enterprise, and the post says Gemini CLI remains available with paid Gemini and Gemini Enterprise Agent Platform API keys.

If you moved to Antigravity CLI, check its own docs for MCP: Google's post does not cover it, and everything below is for Gemini CLI.

## What does a Gemini CLI MCP setup need?

Two config files, a trusted folder and a restart:

| Piece | Where | Job |
| --- | --- | --- |
| MCP servers | `.gemini/settings.json` (project) or `~/.gemini/settings.json` (user), key `mcpServers` | Lets Gemini search registries and read component APIs |
| Rules | `GEMINI.md` in the project, or `~/.gemini/GEMINI.md` | Tells Gemini to search before it writes UI, and how to install |
| Folder trust | `~/.gemini/trustedFolders.json`, set through the trust prompt or `/permissions` | Untrusted folders skip project settings and MCP servers |
| A key (optional) | `WINGO_UI_API_KEY` in your shell | Source for free or Pro items from our server |

Gemini re-reads `settings.json` when it starts. Inside a session, `/mcp` shows each server's status and tools, and `/memory show` prints the `GEMINI.md` text it loaded.

## How do I add the shadcn MCP server to Gemini CLI?

Write the Gemini CLI shadcn MCP entry by hand. shadcn's [MCP docs](https://ui.shadcn.com/docs/mcp) list Claude Code, Cursor, VS Code, Codex and OpenCode for `npx shadcn@latest mcp init --client`, and Gemini CLI is not among them as of November 5, 2026. The JSON shape is the same one Claude Code and Cursor use, with one change:

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

The `-y` matters. With shadcn's documented args (`["shadcn@latest", "mcp"]`) in a project where shadcn was not installed, `gemini mcp list` showed `✗ shadcn ... Disconnected`: npx stops to ask before it downloads a package, and a server started by Gemini has nobody to answer. With `-y` it showed `Connected`. shadcn's own `mcp init` avoids the problem for its clients by installing shadcn as a dev dependency ([source](https://github.com/shadcn-ui/ui/blob/main/packages/shadcn/src/commands/mcp.ts), checked November 5, 2026), which you can also do.

The shadcn server searches the registries in your `components.json` and hands back `npx shadcn@latest add` commands. In our [Claude Code shadcn MCP setup](https://wingo-ui.com/blog/claude-code-shadcn-mcp-setup) it returned no props, so for components with a real API surface you want the library's own server next to it.

## How do I connect a component library through settings.json or gemini mcp add?

Our CLI writes both files in one step. In the project root:

```bash
npx wingo-ui@latest login                 # once per computer; a free account needs only an email
npx wingo-ui@latest agents install gemini
```

In our clean test project, without a key in the environment, it printed:

```text
Coding agents (Gemini CLI; MCP over the local stdio proxy)
  created    GEMINI.md
  created    .gemini/settings.json
```

For Gemini the default is the project scope (`--scope user` writes `~/.gemini/settings.json` and `~/.gemini/GEMINI.md` instead), and running it again changes nothing. This is the Gemini CLI settings.json MCP entry it writes, in each mode:

```json
{
  "mcpServers": {
    "wingo-ui": {
      "command": "npx",
      "args": ["-y", "wingo-ui@latest", "mcp"]
    }
  }
}
```

```json
{
  "mcpServers": {
    "wingo-ui": {
      "httpUrl": "https://wingo-ui.com/mcp",
      "headers": { "Authorization": "Bearer ${WINGO_UI_API_KEY}" }
    }
  }
}
```

The first runs a local proxy that uses the key saved by `login`. The second, which the CLI picks when `WINGO_UI_API_KEY` is set (or with `--mcp http`), calls the server directly. Gemini resolves `${WINGO_UI_API_KEY}` from your environment when it loads the settings, so the file holds only the variable name and is safe to commit.

### What does gemini mcp add write?

The same thing, through Gemini's own command. Scope defaults to `project`, transport to `stdio`:

```bash
# project scope, stdio proxy
gemini mcp add wingo-ui npx -y wingo-ui@latest mcp

# user scope, HTTP; single quotes keep the variable unexpanded in the file
gemini mcp add --scope user --transport http wingo-ui https://wingo-ui.com/mcp \
  -H 'Authorization: Bearer ${WINGO_UI_API_KEY}'
```

Two details from our run. The HTTP form wrote `"url"` with `"type": "http"` instead of `httpUrl`; both connected. And with double quotes, your shell expands the variable before Gemini sees it, which puts the key itself in `settings.json`.

## Why does gemini mcp list say Disabled?

Because the folder is not trusted. Our first `gemini mcp list` in the new project printed:

```text
Warning: MCP servers are configured but disabled because this folder is untrusted.
User-level servers are also suppressed in untrusted folders to prevent accidental side-effects.

Configured MCP servers:

○ wingo-ui: npx -y wingo-ui@latest mcp (stdio) - Disabled
```

Note the second line: even a server in `~/.gemini/settings.json` stays off in an untrusted folder. `gemini mcp add` in project scope refused outright with "Cannot modify settings in an untrusted workspace". Trust the folder when Gemini asks at startup, or run `/permissions` in a session; the choice is saved in `~/.gemini/trustedFolders.json`. After that:

```text
Configured MCP servers:

✓ wingo-ui: npx -y wingo-ui@latest mcp (stdio) - Connected
```

Then ask one question only the server can answer: "Call get_account_status on the wingo-ui server." Without a key it answers that you are anonymous: docs, props and search work, and no source is sent.

## What should GEMINI.md say?

When to search, how to install, and which tokens to use. This is the block `agents install` wrote, between markers that `npx wingo-ui@latest agents update` rewrites later:

```md
<!-- wingo-ui:start v=3 -->
## Wingo UI

This project builds its UI with Wingo UI (React 19 + Tailwind CSS v4 components installed as source, see `wingo-ui.json`).

- Before writing UI, use the `wingo-ui` MCP tools: `search_components` to find what exists (blocks for whole screens), `get_component` / `get_block` for props and usage, `get_design_rules` for tokens, sizes, motion and mobile rules. Never recreate a component the library has.
- Install with `npx wingo-ui@latest add <slug...>`. At the start of UI work run `check_updates` (or `npx wingo-ui@latest outdated`) and mention updates; update with `npx wingo-ui@latest update`.
- Semantic tokens only (`bg-background`, `bg-surface`, `text-muted-foreground`, `border-border`), configure components through props, keep `cn()`, `data-slot` and license headers in copied files. Check 390px and 1440px, light and dark.
- Never put a card, panel, tile or field on a background of the same or a very similar shade: step the fill (page, `bg-surface`, `bg-foreground/5`). Pages: `PageBackdrop` with a tinted `background` plus bands via `MarketingSection background="muted"`. Border-only designs are the allowed, not recommended exception.
- No MCP tools available? The CLI has `search`, `info`, `add`, `outdated`, `update`; the docs for models are at https://wingo-ui.com/llms.txt.
<!-- wingo-ui:end -->
```

Per Google's [GEMINI.md docs](https://geminicli.com/docs/cli/gemini-md/), Gemini joins `~/.gemini/GEMINI.md`, the files in your workspace and its parent folders, and files it finds next to code it reads, then sends them with every prompt. `/memory reload` rescans after an edit. If the repository already has an `AGENTS.md` for Codex, set `"context": { "fileName": ["AGENTS.md", "GEMINI.md"] }` in `settings.json` and keep one rules file; our [AGENTS.md vs CLAUDE.md comparison](https://wingo-ui.com/blog/agents-md-vs-claude-md-react) has a fuller template.

With both servers in one project, add a line that says which registry owns which folder: shadcn/ui and our library both install a `button.tsx` with different props, so Gemini has to know which API it is reading.

## Which prompts get Gemini CLI to build UI from real components?

For Gemini CLI UI design work, the prompts that work name the screen, the data, and what Gemini should report back. The `GEMINI.md` rules already send it to `search_components`, then `get_component` or `get_block`, then the install command; the prompt supplies what a rules file cannot: columns, routes and copy. Gemini asks before every MCP tool call by default, even though all 10 of our server's tools are read-only. Add `"trust": true` to the server entry to skip those prompts, and keep the shell installs on confirm.

### Prompt 1: a customers table

```text
Build app/customers with the Wingo UI data table. Search first and read its props with get_component.
Columns: name, email, status (Active, Trial, Past due) as a badge with a filter, plan, MRR with a total,
signup date. Multiple selection with an Export CSV bulk action, a Past due quick filter, and each row
opens /customers/<id>. Install it with the CLI and list anything you could not wire.
```

A search for "users table with filters" on our server lists the [Data Table](https://wingo-ui.com/components/data-table) in its top three, next to a members table and an audit log, and its props cover every line of that prompt. The client component we expect back, type-checked against the current release:

```tsx
// app/customers/customers-table.tsx
"use client";

import { useRouter } from "next/navigation";
import { DataTable, createColumns } from "@/components/ui/data-table";

export type Customer = {
  id: string;
  name: string;
  email: string;
  plan: "Free" | "Pro";
  status: "Active" | "Trial" | "Past due";
  mrr: number;
  signedUpAt: string;
};

const col = createColumns<Customer>();
const columns = col([
  col.accessor("name", { header: "Customer", mobile: "title" }),
  col.accessor("email", { header: "Email", mobile: "subtitle" }),
  col.accessor("status", {
    header: "Status",
    type: "badge",
    badge: { Active: { tone: "success" }, Trial: { tone: "info" }, "Past due": { tone: "danger" } },
    filter: "multi-select",
    mobile: "meta",
  }),
  col.accessor("plan", { header: "Plan", filter: "select" }),
  col.accessor("mrr", { header: "MRR", type: "currency", footer: "sum" }),
  col.accessor("signedUpAt", { header: "Signed up", type: "date" }),
]);

type Props = { customers: Customer[]; onExport: (ids: string[]) => Promise<unknown> };

export function CustomersTable({ customers, onExport }: Props) {
  const router = useRouter();
  return (
    <DataTable
      data={customers}
      columns={columns}
      caption="Customers"
      locale="en-US"
      selectionMode="multiple"
      bulkActions={(_rows, keys) => [{ label: "Export CSV", onSelect: () => onExport(keys) }]}
      quickFilters={[{ id: "past-due", label: "Past due", columnFilter: { id: "status", value: ["Past due"] } }]}
      onRowClick={(row) => router.push(`/customers/${row.id}`)}
    />
  );
}
```

The `mobile` roles are the part a model writing from memory skips: below 768px each row becomes a card with the name as its title, and selection starts with a long press.

> Live demo (Data Table): Tick two rows to see the bulk bar replace the toolbar, then narrow the window below 768px: rows turn into cards and a long press starts selecting. Try it at [Data Table](https://wingo-ui.com/components/data-table) and install it with `npx wingo-ui@latest add data-table`.

### Prompt 2: a sign-in screen

```text
Build /login from the Wingo UI login-page block, card layout, password and 6 digit email code, no
social buttons. Post to our /api/auth routes, go to /dashboard on success, and replace every
Northwind string in the labels with Harbor. List each handler you left on its sample default.
```

The [Login Page](https://wingo-ui.com/components/login-page) ships sample handlers so its demo runs, and its default labels name Northwind CRM. That is why the prompt asks for a report: without `onVerifyCode`, the code view accepts 123456 for anyone. In the card layout with two-factor off, `appName`, `signInDescription` and `copyright` are the only Northwind labels on screen; the split layout's hero adds `heroLabel`, `customersTitle` and the testimonial, and `twoFactor` adds `twoFactorDescription`. A good result:

```tsx
// app/login/login-screen.tsx
"use client";

import Link from "next/link";
import { useRouter } from "next/navigation";
import { LoginPage, type SignInValues, type VerifyCodeContext } from "@/components/blocks/login-page";

async function post(path: string, body: unknown) {
  const res = await fetch(path, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  // the page shows a rejected promise's message in its error alert
  if (!res.ok) throw new Error("That did not work. Check your details and try again.");
}

export function LoginScreen() {
  const router = useRouter();
  return (
    <LoginPage
      layout="card"
      methods={["password", "otp"]}
      showProviders={false}
      logo={<span className="text-lg font-semibold">Harbor</span>}
      labels={{
        appName: "Harbor",
        signInDescription: "Sign in to your Harbor account to continue.",
        copyright: "© 2026 Harbor Inc.",
      }}
      onSignIn={(values: SignInValues) => post("/api/auth/sign-in", values)}
      onSendCode={(email) => post("/api/auth/send-code", { email })}
      onVerifyCode={(code: string, context: VerifyCodeContext) => post("/api/auth/verify-code", { code, ...context })}
      onForgot={(email) => post("/api/auth/forgot", { email })}
      onReset={(password) => post("/api/auth/reset", { password })}
      onSuccess={() => router.push("/dashboard")}
      linkComponent={(props) => <Link {...props} />}
    />
  );
}
```

> Live demo (Login Page): Type any email and a password shorter than 8 characters to see the error alert, then open Forgot password: the email carries over. Try it at [Login Page](https://wingo-ui.com/components/login-page) and install it with `npx wingo-ui@latest add login-page`.

### Prompt 3: swap a hand-written spinner

```text
Find every button in app/account that renders its own spinner while saving and replace it with the
Wingo UI button. Read its props with get_component first and show me the diff.
```

Our [Button](https://wingo-ui.com/components/button) has a `loading` prop, so the spinner markup and the `disabled` juggling go away:

```tsx
import { Button } from "@/components/ui/button";

export function SaveButton({ saving, onSave }: { saving: boolean; onSave: () => void }) {
  return (
    <Button tone="brand" loading={saving} loadingText="Saving" onClick={onSave}>
      Save changes
    </Button>
  );
}
```

## What goes wrong, and how do I fix it?

- **Disabled in `gemini mcp list`.** Untrusted folder. Trust it, then list again.
- **Disconnected over HTTP.** With a made-up key in `WINGO_UI_API_KEY`, our server refused the connection and Gemini showed `✗ ... Disconnected`. Check the key on your account's API keys page, or switch to the proxy with `npx wingo-ui@latest agents install gemini --mcp stdio`.
- **Connected, but no source.** With the variable missing, Gemini sent an empty bearer token and connected anonymously. Export the key in the shell profile of the terminal that starts Gemini, then restart it.
- **Disconnected over stdio.** Usually npx waiting for an install answer: add `-y` to `args`, as above.
- **Pro blocks with a free key.** `get_block` still returns props and usage for `login-page`, and `npx wingo-ui@latest add login-page` stops because the block is part of Pro. The Button is free; the Data Table and Login Page are Pro ($8 a month, $80 a year or $150 once, on the [pricing page](https://wingo-ui.com/pricing)).
- **Server names with underscores.** Gemini prefixes tools as `mcp_<server>_<tool>` and its docs warn that underscores in a server name can break policy matching. Keep `wingo-ui` and `shadcn` as they are.

## Where should I start?

Create a free account, run `npx wingo-ui@latest agents install gemini` in your project, trust the folder and try prompt 3 on the free Button. The [MCP server docs](https://wingo-ui.com/docs/mcp) list every tool and the [coding agents docs](https://wingo-ui.com/docs/agents) cover the `--scope` and `--mcp` options. The same setup for other agents is in our [Codex MCP setup](https://wingo-ui.com/blog/codex-mcp-setup-react-ui), the [Copilot shadcn setup for VS Code](https://wingo-ui.com/blog/copilot-shadcn-mcp-setup) and the rest of [Building UI with AI agents](https://wingo-ui.com/blog/category/ai-agents).

## Components in this post

- [Button](https://wingo-ui.com/components/button) (Free): The React button component every screen starts with: five variants, six tones, all sizes, and a loading state that never jumps. Install: `npx wingo-ui@latest add button`
- [Data Table](https://wingo-ui.com/components/data-table) (Pro): A React data table on TanStack Table: search, filters, sorting, bulk actions, pinning, resizing, virtualization and cards on phones. Install: `npx wingo-ui@latest add data-table`
- [Login Page](https://wingo-ui.com/components/login-page) (Pro): A React login page with password, magic link or code sign-in, Google, Apple and Microsoft buttons, two-step codes and password recovery. Install: `npx wingo-ui@latest add login-page`

## FAQ

### Does the shadcn MCP server work with Gemini CLI?

Yes, through a manual entry in .gemini/settings.json. shadcn's mcp init command does not list Gemini CLI as a client (as of November 2026), so add the server under mcpServers with command npx and args -y, shadcn@latest, mcp.

### Where does Gemini CLI store MCP servers?

Under mcpServers in settings.json: ~/.gemini/settings.json for every project, or .gemini/settings.json in the project. gemini mcp add writes to the project file by default; --scope user picks the home file.

### Why does gemini mcp list show my server as Disabled?

The folder is not trusted. With folder trust on, Gemini CLI ignores the project settings and suppresses user-level servers too in an untrusted folder; trust it when Gemini asks or through /permissions, and the server connects.

### Can Gemini CLI read an API key from an environment variable in settings.json?

Yes. String values in settings.json can reference $VAR or ${VAR}, which Gemini resolves when it loads the settings, so a header like Authorization: Bearer ${WINGO_UI_API_KEY} keeps the key out of the file.

### Is Gemini CLI still available after the move to Antigravity CLI?

For some users. Google's May 19, 2026 post says it stopped serving Google AI Pro, Ultra and free Code Assist users on June 18, 2026, and stays available with Code Assist Standard or Enterprise and with paid Gemini API keys.

---

Source: https://wingo-ui.com/blog/gemini-cli-shadcn-mcp
