All posts
10 min read

Meet the Beste UI MCP Server: Semantic Block Search and Page Composition for Your AI Agent

Beste UI ships a hosted MCP server. Connect it to Claude Code, Cursor, VS Code, or Windsurf with one URL and your AI agent can search 1,500+ shadcn/ui blocks by intent, pull sources with license-aware access, and compose entire pages from a single prompt. No API key, no local process.

mcpshadcnaiclaude-codecursor

Ask an AI agent to "add a pricing section" to your app and it will happily generate one: a plausible-looking grid with subtle spacing bugs, an accessibility hole or two, and a design language that matches nothing else on your page. The model is not bad at UI. It is bad at inventing your design system from scratch, on the spot, every time.

The fix is not a better prompt. It is giving the agent a catalog: real, tested components it can search and install instead of hallucinate. That is exactly what the Model Context Protocol is for, and as of today Beste UI speaks it natively. Our MCP server is live at https://ui.beste.co/api/mcp, and it gives any MCP-capable agent four abilities: search the entire catalog by intent, inspect and preview any block, install it through the shadcn CLI, and compose whole pages from a one-line brief.

This post is the full tour. If you just want the config, the MCP docs page has copy-paste setup; everything below is what you can do once it is connected.

Sixty-second setup

The server is hosted and speaks the standard Streamable HTTP transport, so there is nothing to run locally. Point your client at one URL:

json
{
  "mcpServers": {
    "beste-ui": {
      "url": "https://ui.beste.co/api/mcp"
    }
  }
}

That file is .mcp.json for Claude Code, .cursor/mcp.json for Cursor, and .vscode/mcp.json for VS Code (under a servers key); Windsurf uses the same shape in its MCP settings. In Claude Code it is a single command:

bash
claude mcp add --transport http beste-ui https://ui.beste.co/api/mcp

No account, no API key

Search, previews, metadata, install commands, and every free block source work without signing up for anything. A license only comes into play when your agent pulls Pro block sources.

Because the transport is plain HTTP, the failure modes of local stdio servers do not exist here. There is no npx cache to go stale, no Node version mismatch, no orphaned process. One URL either answers or it does not.

What your agent can do now

The server exposes six tools. The short version:

ToolWhat it doesAccess
search_blocksSemantic + keyword search across blocks, pieces, and componentsOpen
get_blockMetadata for one asset: description, category, preview URL, install commandOpen
get_block_sourceThe full .tsx source of any assetFree items open, Pro needs a license
list_categoriesEvery category with title and descriptionOpen
install_blockThe exact shadcn CLI command for an assetOpen
compose_pageAn ordered set of blocks for a page intentOpen

Individually these are small. Together they close a loop: your agent can go from "I need a hero with a waitlist" to found, previewed, and installed without you alt-tabbing to a browser once. The rest of this post walks that loop.

Search that understands intent, not keywords

Most registry search, including the official shadcn MCP's, is name and keyword matching. That works when you know what a thing is called. It fails at the way people actually describe UI, which is by intent: "something with a screenshot and a waitlist form."

search_blocks is a hybrid: lexical matching plus a semantic index over the whole catalog. Here is a real call and its real response:

json
// search_blocks({ query: "hero with a product screenshot and a waitlist form", type: "block", limit: 3 })
[
  {
    "type": "block",
    "name": "hero126",
    "title": "Waitlist Email Hero",
    "category": "Hero",
    "isPro": true,
    "url": "https://ui.beste.co/block/hero126"
  },
  {
    "type": "block",
    "name": "hero98",
    "title": "Cinematic Hero with Social Proof",
    "category": "Hero",
    "isPro": true,
    "url": "https://ui.beste.co/block/hero98"
  },
  {
    "type": "block",
    "name": "hero72",
    "title": "Browser Screenshot Hero with Notifications",
    "category": "Hero",
    "isPro": true,
    "url": "https://ui.beste.co/block/hero72"
  }
]

No block in the catalog is named "product screenshot waitlist hero". The query still resolves, because matching happens on meaning. This is the top result, live:

Waitlist Email Hero
View block
Loading preview…
hero126, the first hit for 'hero with a product screenshot and a waitlist form'. Your agent found this without knowing its name.

The architecture note that makes this unusual: the semantic index is built with a small open embedding model that runs locally on our server, at build time and at query time. There is no OpenAI or third-party AI API in the path, no per-query cost, and nothing about your prompts leaves the request. Search understands intent because the catalog was embedded, not because your query was shipped to a model provider.

Every result also carries a placement rule that tells the agent how the asset may be used: a block is a full page section, a piece belongs inside a media slot, a component is a primitive. That one field prevents the classic agent mistake of stretching a card asset into a page section.

compose_page: one prompt, a whole page

Installing blocks one at a time is already a good workflow. But the question an agent actually gets is rarely "find me a hero." It is "build me a landing page." compose_page answers at that altitude: give it an intent, get an ordered, thematically coherent set of blocks with install commands attached.

Real call, real response, trimmed to fit:

json
// compose_page({ intent: "landing page for a developer CLI tool", limit: 8 })
{
  "intent": "landing page for a developer CLI tool",
  "blocks": [
    { "name": "feature167", "title": "Terminal CLI Feature Showcase", "category": "Feature",
      "install": "npx shadcn add https://ui.beste.co/r-base/feature167" },
    { "name": "terminal2", "title": "Code Snippet with Marketing Split", "category": "Terminal",
      "install": "npx shadcn add https://ui.beste.co/r-base/terminal2" },
    { "name": "stats34", "title": "Terminal Output Stats", "category": "Stats",
      "install": "npx shadcn add https://ui.beste.co/r-base/stats34" },
    { "name": "faq25", "title": "Terminal Window", "category": "FAQ",
      "install": "npx shadcn add https://ui.beste.co/r-base/faq25" },
    { "name": "auth29", "title": "Terminal Sign In", "category": "Auth",
      "install": "npx shadcn add https://ui.beste.co/r-base/auth29" }
  ]
}

Look at what it selected. For a CLI tool it did not return generic sections; it found the terminal-styled corner of the catalog across five different categories: a feature showcase framed as a terminal, stats rendered as command output, an FAQ in a terminal window, even a terminal-themed sign-in. The semantic index is doing the aesthetic curation that normally costs you an afternoon of browsing.

The agent stays in charge of the final arrangement: compose_page proposes candidates in a sensible order, and your agent (or you) curates, reorders, and fills gaps with follow-up searches. We wrote about assembling a landing page in ten blocks by hand; this tool is that editorial process compressed into a single call.

It works for app UI too, not just marketing pages. A search for "admin dashboard with KPI cards and an orders table" returns dashboard1, the admin shell from our dashboard set, and install_block hands the agent the exact command to drop it in.

License-aware sources, same rules as the site

Here is the part no other registry MCP story covers properly: what happens with paid content.

Everything metadata-shaped is open: search, previews, categories, install commands, and the full source of every free asset. When an agent requests the source of a Pro block without a license, it gets a structured, honest answer instead of an error dump:

json
// get_block_source({ name: "dashboard1" }) with no license
{
  "code": "LICENSE_REQUIRED",
  "message": "This is a Pro block. Pass ?email=&license_key= on the MCP endpoint URL (or as tool args) to retrieve its source.",
  "upgrade": "https://ui.beste.co/pricing"
}

If you have a license, you thread it into the endpoint URL once and every tool becomes fully unlocked:

json
{
  "mcpServers": {
    "beste-ui": {
      "url": "https://ui.beste.co/api/mcp?email=you@example.com&license_key=YOUR_LICENSE_KEY"
    }
  }
}

Your email and key are on your account page. The validation is the identical code path the website and the CLI registry use, so there is exactly one set of rules: free is free everywhere, Pro is Pro everywhere, and your agent never sees content your browser would not.

Already using the official shadcn MCP? Keep it

shadcn ships its own MCP server (npx shadcn@latest mcp init --client claude), and it is genuinely good: it browses, searches, and installs from every registry declared in your components.json, including namespaced third-party registries. Beste UI works with it out of the box, because every asset here is a standard registry item; add @beste-ui to your registries per our installation guide and the official server can install our blocks today.

So why run ours next to it?

Official shadcn MCPBeste UI MCP
ScopeEvery registry in your components.jsonThe Beste UI catalog
RunsLocally via npx (stdio)Hosted (Streamable HTTP)
SearchName and keywordSemantic + keyword hybrid
Page compositionNot availablecompose_page
Paid contentAuth headers you configure per registryLicense in the URL, structured LICENSE_REQUIRED responses
Setupmcp init per client, npx on each startOne URL

They are not mutually exclusive, and this is not a knock on the official server; its multi-registry scope is something a single-vendor server cannot replicate. The honest guidance: use the official MCP as your general-purpose registry plumbing, and add the Beste UI server for what the generic path cannot do, which is understanding "a calm, editorial testimonial section" and knowing which of 1,500+ assets that is.

If a local MCP server shows 'No tools or prompts'

That is the well-known stale npx cache issue with stdio servers, and the official docs describe the cache-clearing fix. It is also a good illustration of why we made ours hosted: an HTTP endpoint has no local cache to go stale. If the Beste UI server ever misbehaves, restarting your MCP client is the only moving part on your side.

Try asking

Copy any of these into an MCP-connected agent and watch the loop run:

The prompts are deliberately vague about component names. That is the point: names are the registry's concern now, not yours.

Questions

Which AI tools does this work with? Any MCP-capable client: Claude Code, Cursor, Windsurf, VS Code, and anything else that speaks Streamable HTTP per the MCP spec. Client-side setup details live in each tool's docs, for example Cursor's MCP guide, and on our docs page.

Is this the same thing as an AI component generator? No, and deliberately so. Generators produce novel code per prompt; you review novel bugs per prompt. This server retrieves components that were designed, tested, and maintained by hand, which is the whole thesis behind copy-paste beating the library. The AI does the finding; humans did the building.

Does semantic search send my queries to an AI provider? No. Queries are embedded on our server with a local open model, the same one that indexed the catalog at build time. No third-party AI service is in the request path.

What does it cost? The server is free to use, with no account. A Pro license is only required for one thing: pulling the source of Pro blocks through get_block_source, under the same rules as the website and CLI.

Give your agent the full catalog

A Pro license unlocks every block source through the MCP server, the CLI, and the site, all with the same key. Connect once and let your agent build with the whole library.

Get Pro
Markdown version