---
title: "MCP server"
description: "Point the shadcn MCP server at the opsinjs registry so an assistant can browse and install real items instead of guessing at them."
url: "https://opsinjs.pensievelabs.org/agents/mcp-server"
source: "https://opsinjs.pensievelabs.org/agents/mcp-server.md"
section: "Agents & automation"
kind: "guide"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["mcp", "model context protocol", "claude code setup", "cursor setup"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="guide" />

## Overview [#overview]

opsinjs does not ship an MCP server of its own. It ships a registry that the
**shadcn** MCP server already understands, which means the setup is the standard
one and everything you learn transfers to any other shadcn-compatible registry.

What the connection buys you is inventory. Without it, an assistant asked for a
health component answers from its training data. With it, the assistant can list
what exists and read each item's metadata. Crucially, it can also discover
whether the thing it was about to write has source behind it, and at what
stability.

Budget five minutes. You need a project with a `components.json`.

## Configure the registry [#configure-the-registry]

The MCP server reads registries from your project's `components.json`. Add the
`@opsinjs` namespace:

```json title="components.json"
{
  "registries": {
    "@opsinjs": "https://opsinjs.pensievelabs.org/r/{name}.json"
  }
}
```

The `{name}` placeholder is required by the spec. The CLI substitutes the item
name into it. See [Namespaces](../registry/namespaces.mdx) for composing several
registries, and for why the namespace is `@opsinjs` rather than something
shorter.

No authentication is needed. The registry is public and read-only.

## Start the server [#start-the-server]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x shadcn@latest mcp init --client claude
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Substitute your client: `claude`, `cursor`, `vscode`, `codex` or `opencode`.
Restart the client afterwards.

If you prefer to write the configuration by hand, it is the same three lines
everywhere. Those lines name a command and its arguments:

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

Cursor uses `.cursor/mcp.json` with the same shape; VS Code uses
`.vscode/mcp.json` with a `servers` key instead of `mcpServers`; Codex uses TOML
in `~/.codex/config.toml` and the CLI cannot write that file for you.

## Ask it something [#ask-it-something]

Three prompts that exercise the connection and expose the honest state of the
system:

```txt
List every component in the opsinjs registry with its status.
```

```txt
What does the opsinjs registry say about range-bar? Do not write any code.
```

```txt
Is there an opsinjs component for scanning a barcode?
```

The third is the interesting one. There is no barcode scanner in the catalogue,
so a correct answer names that absence plainly, not a 404 and not an invented
component. If your assistant invents one, that is the failure mode the
[evals](./evals.mdx) exist to measure, and
[Rules for agents](./rules-for-agents.mdx) is the fix.

## What the server can and cannot do here [#what-the-server-can-and-cannot-do-here]

<Callout>
  **All sixty built items carry source.** `add`
  against a built id writes real files. Against a `planned` id it would resolve the
  item and find nothing to write. That is the
  correct behaviour and not a broken configuration. `meta.opsinjs.implemented` on
  the item is the discriminator, and it is worth checking before you conclude
  anything from an empty result.
</Callout>

| The server can                                                                 | The server cannot                                                                          |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| List every item with its status and `implemented` flag                         | Install an id the catalogue does not list                                                  |
| Install the source of all sixty built items, exported prop interfaces included | Promise that a shipped API survives the next release, or that anybody has reviewed it      |
| Install the theme preset, which is real CSS                                    | Fetch a documentation example on its own. The examples are not published as registry items |
| Search across opsinjs and your other registries                                | Enforce the clinical rules. That is the [skill](./agent-skill.mdx)                         |

The last row is the important division of labour. The MCP server answers *what
exists*. The skill answers *how to use it correctly*. Configure both.

## Verify it worked [#verify-it-worked]

<Steps>
  ### The client sees the server [#the-client-sees-the-server]

  In Claude Code, run `/mcp` and look for `shadcn` marked connected. Cursor shows a
  green dot in its MCP list; VS Code shows a Start control next to the entry in
  `.vscode/mcp.json`.

  ### The registry resolves [#the-registry-resolves]

  Ask the assistant to list opsinjs items. A non-empty list of names, each with a
  `status`, means the namespace resolved. An empty list means `components.json`
  was not read or the namespace is misspelled.
</Steps>

## Troubleshooting [#troubleshooting]

**No tools or prompts.** Clear the npx cache with `npx clear-npx-cache`, then
re-enable the server in your client. This is by far the most common cause.

**`@opsinjs` is not found.** The registry block is missing from
`components.json`, or the file is not at the project root the client opened.
The full-URL form is `npx shadcn@latest add https://opsinjs.pensievelabs.org/r/<name>.json`.
That form needs no configuration for an item that composes nothing, and is a
quick way to isolate the problem. It will not rescue an item whose
`registryDependencies` name other opsinjs items; those need the block whichever
form you use.

**It lists items but `add` writes nothing.** Check the id first. For one of the
sixty built ids it is a real fault: the item carries files, so an empty write means the
fetch failed, the CLI resolved a different registry, or the aliases in
`components.json` do not name a directory it can write to.

**The assistant still invents components.** The server supplies inventory, not
judgement. Install the [agent skill](./agent-skill.mdx) or paste
[Rules for agents](./rules-for-agents.mdx) into the system prompt.

**It works in one editor and not another.** Each client stores its own
configuration file. Configure each; there is no shared one.

## Next [#next]

* [Agent skill](./agent-skill.mdx) is the other half of the setup.
* [Registry overview](../registry/index.mdx) says what the server is talking to.
* [Machine-readable schemas](./machine-readable-schemas.mdx) covers the same
  data, fetched directly, when you are not using MCP at all.
