search_items

Case-insensitive title search across every surface on shadcn.io. Returns lightweight rows so the agent can browse cheaply before committing to a component.

search_items is the default entry point for discovery. When a user says "find me a pricing block", the agent calls this first, reads the results, then drills into the winners with get_item / get_item_source.

When to use

  • The agent has keywords ("pricing", "chat", "sidebar") but doesn't know slugs.
  • You want to see matches across multiple surfaces at once (e.g. a chat block and an AI chat component).
  • You want ranked-by-position-in-index results. This tool does not apply fuzzy ranking — it's a substring filter in index order.

If the agent already knows the surface and wants the full contents, prefer list_items. If it wants block-specific drilling, use list_block_categories + list_blocks_in_category.

How it works

  1. Loads public/search-index.json (1.1 MB, generated by scripts/scripts/generate-search-index.ts at build time). Cached in memory on first call for the life of the Node process.
  2. Lowercases query once.
  3. Iterates the index in file order, keeping rows where:
    • tag === type (if type was passed)
    • title.toLowerCase().includes(query)
  4. Early-breaks once filtered.length >= limit.

Source: lib/mcp/content-loader.ts#filterItems and lib/mcp/tools.ts tool registration.

Each search-index row has the shape { id, title, url, tag }, where tag is the first non-route-group path segment under content/ (e.g. blocks, ai, components, hooks).

Arguments

NameTypeRequiredNotes
querystring (min length 1)Substring matched against each item's title
typesurface enum (see below)Restrict to one surface
limitnumber (1–200)Default 50

Valid type values: blocks, ai, components, examples, hooks, charts, button, background, shaders, text, themes, theme, ui, tools, prompts.

Response

{
  "count": 3,
  "items": [
    {
      "id": "/blocks/pricing-grouped-comparison-table",
      "title": "React Grouped Comparison Table Pricing Block",
      "url": "/blocks/pricing-grouped-comparison-table",
      "tag": "blocks"
    },
    {
      "id": "/blocks/pricing-metric-hero-tiles",
      "title": "React Metric Hero Tiles Pricing Block",
      "url": "/blocks/pricing-metric-hero-tiles",
      "tag": "blocks"
    },
    {
      "id": "/blocks/pricing-split-highlight",
      "title": "React Split Highlight Pricing Block",
      "url": "/blocks/pricing-split-highlight",
      "tag": "blocks"
    }
  ]
}

The slug to pass to downstream tools is the last path segment of url — here, pricing-grouped-comparison-table.

Example prompts

use shadcnio and search for pricing blocks with a comparison table
use shadcnio to search for anything related to chat across all surfaces
use shadcnio to find the five best hero blocks with "announcement" in the title

Manual invocation

curl -s -X POST "https://www.shadcn.io/api/mcp?token=YOUR_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "search_items",
      "arguments": {
        "query": "pricing",
        "type": "blocks",
        "limit": 5
      }
    }
  }' | jq

Wrap with | jq '.result.content[0].text | fromjson' to unwrap the JSON-stringified payload.

Tips + gotchas

  • Substring match, not fuzzy — "pric" matches "pricing" but "pricng" (typo) matches nothing. Sanitize user input upstream if needed.
  • No cross-surface ranking — results come out in whatever order the index has them. If a block and an AI component both match, the block appears first because content/blocks/ is scanned first by the generator.
  • Default limit is small (50) — raise limit up to 200 when the agent is cataloguing rather than picking.
  • Build-time index — newly added MDX doesn't show up until you re-run npx tsx scripts/generate-search-index.ts (or rebuild). In dev, the cache lives for the process lifetime.

Was this page helpful?

Sign in to leave feedback.