Read OSS

AIファースト設計によるコンポーネント探索:MCPサーバーとプログラマティックRegistry API

中級

前提知識

  • 第1回:アーキテクチャ概観
  • 第2回:registryシステムと依存関係の解決
  • Model Context Protocol(MCP)の基本概念への理解

AIファースト設計によるコンポーネント探索:MCPサーバーとプログラマティックRegistry API

shadcn/ui の CLI は、最初から人間だけを対象としたツールとして設計されたわけではありません。第1回で見たように、このパッケージは7つのサブパスエクスポートを通じて3種類のユーザーに対応しています。本シリーズの最終回では、その中でも最も先進的なサーフェスに焦点を当てます。AIコーディングアシスタントがコンポーネントを探索・検査・インストールできるようにするMCPサーバーと、ライブラリ作者が直接インポートして使えるプログラマティックregistry APIです。

MCPサーバーのアーキテクチャ

packages/shadcn/src/mcp/index.ts にあるMCPサーバーは @modelcontextprotocol/sdk をベースに構築されています。tools capabilityを持つ Server インスタンスを作成し、7つのツールを登録します。

graph TD
    MCP["MCP Server (shadcn)"]
    MCP --> T1["get_project_registries"]
    MCP --> T2["list_items_in_registries"]
    MCP --> T3["search_items_in_registries"]
    MCP --> T4["view_items_in_registries"]
    MCP --> T5["get_item_examples_from_registries"]
    MCP --> T6["get_add_command_for_items"]
    MCP --> T7["get_audit_checklist"]

各ツールはZodのinputスキーマを持ち、MCPプロトコル向けに zod-to-json-schema でJSON Schemaへ変換されます。サーバーはstdioプロセスとして動作し、shadcn mcp を実行すると StdioServerTransport 経由で接続を確立し、ツールの呼び出しを待ち受けます。

CallToolRequestSchema ハンドラー(413〜462行目)のエラーハンドリングは、第2回で紹介したregistryエラーシステムとMCPレスポンスをつなぐ橋渡し役を担っています。RegistryError インスタンスはエラーコード、メッセージ、提案、コンテキストとともにフォーマットされ、AIアシスタントが問題を診断して修正案を提示するのに十分な情報が提供されます。

7つのMCPツール:探索 → 検査 → インストール

各ツールは、自然なワークフローの流れに沿って設計されています。

1. get_project_registriescomponents.json を読み込み、設定済みのregistry名を返します。AIが利用可能なリソースを把握するための最初の呼び出しとなります。

2. list_items_in_registries — 指定したregistryの全アイテムをページネーション付きで一覧表示します。registries: string[]limitoffset を受け取ります。

3. search_items_in_registries — registry横断のファジー検索を行います。197〜240行目の実装は searchRegistries に委譲しており、内部では fuzzysort ライブラリを使用しています。

4. view_items_in_registries — ファイルの内容を含むアイテムの詳細情報を取得します。registryプレフィックス付きのアイテム名(例:["@shadcn/button"])を受け取ります。

5. get_item_examples_from_registries — デモ・サンプルアイテムを検索し、そのコードをまるごと返します。accordion-demobutton exampleexample-hero といったパターンに対応しています。

6. get_add_command_for_items — アイテムをインストールするCLIコマンド(例:npx shadcn@latest add @shadcn/button)を返します。

7. get_audit_checklist — インストール後の確認事項をまとめたチェックリストを返します。importの正確性、next.config.js の画像パターン、依存関係のインストール、linting、TypeScriptエラーといった一般的な問題をカバーしています。

flowchart LR
    A["1. get_project_registries"] --> B["2. list/search items"]
    B --> C["3. view item details"]
    C --> D["4. get examples"]
    D --> E["5. get add command"]
    E --> F["6. User runs command"]
    F --> G["7. audit checklist"]

Tips: MCPツールは意図的に shadcn add を直接実行せず、ユーザーが手動で実行するコマンド文字列を返す設計になっています。これはセーフティデザインの一環です。AIアシスタントはアクションを提案し、最終的な承認は人間が行います。

IDE別のクライアント設定

mcp コマンドには init サブコマンドが含まれており、5つのAIコーディング環境向けの設定ファイルを生成できます。それぞれ異なる設定フォーマットを持ちます。

クライアント 設定ファイルのパス フォーマット
Claude Code .mcp.json { mcpServers: { shadcn: { command, args } } }
Cursor .cursor/mcp.json { mcpServers: { shadcn: { command, args } } }
VS Code .vscode/mcp.json { servers: { shadcn: { command, args } } }
Codex .codex/config.toml TOMLフォーマット([mcp_servers.shadcn]
OpenCode opencode.json { mcp: { shadcn: { type, command, enabled } } }

22〜86行目のCLIENTS配列が、各クライアントの設定パスと構造を定義しています。runMcpInit 関数は既存の設定を読み込み、shadcnサーバーの設定をディープマージして書き戻します。設定ファイルに既存のMCPサーバーが定義されていても、その内容はそのまま保持されます。

Codexだけは特別扱いで、設定ファイルの場所がプロジェクトディレクトリではなくグローバルな ~/.codex/config.toml になります。そのため、ファイルへの自動書き込みは行わず、手動設定の手順が出力されます。

プログラマティックRegistry API

packages/shadcn/src/registry/index.tsshadcn/registry エクスポートは、公開APIサーフェスを再エクスポートしています。

export { getRegistries, getRegistryItems, resolveRegistryItems, 
         getRegistry, getRegistriesIndex } from "./api"
export { searchRegistries } from "./search"
export { RegistryError, RegistryNotFoundError, /* ... */ } from "./errors"

これはMCPサーバーが内部で使っているのと同じAPIです。ライブラリ作者はこれをインポートしてカスタムツールを構築できます。

import { searchRegistries, getRegistryItems } from "shadcn/registry"

const results = await searchRegistries(["@shadcn"], {
  query: "button",
  limit: 10,
})

api.ts モジュールは内部でbuilder、fetcher、resolverを組み合わせています。getRegistryItems は名前空間付き(@acme/button)とURL形式の両方のアイテムを扱い、認証ヘッダー用のregistryコンテキストをセットアップし、レスポンスをZodスキーマでバリデーションします。

検索サブシステム

searchRegistries 関数は、複数のregistryをまたいだ検索を次のように実装しています。

  1. 各registryに対して getRegistry でregistryデータ全体を取得する
  2. アイテムを addCommandArgument(例:@shadcn/button)付きの検索可能な形式にマッピングする
  3. クエリが指定されている場合、fuzzysort を使って namedescription フィールドに対してファジーマッチングを実行する
  4. offsetlimit でページネーションを適用する
  5. 結果を searchResultsSchema でバリデーションする
flowchart TD
    A["searchRegistries(['@shadcn', '@acme'], {query: 'button'})"]
    A --> B["Fetch @shadcn registry"]
    A --> C["Fetch @acme registry"]
    B --> D["Map to searchable items"]
    C --> E["Map to searchable items"]
    D --> F["Concatenate all items"]
    E --> F
    F --> G["fuzzysort.go(query, items)"]
    G --> H["Apply pagination"]
    H --> I["Return SearchResults"]

fuzzysort ライブラリは、設定可能な閾値(デフォルトは -10000 と非常に寛容な値)でタイポに強いマッチングを提供します。結果には addCommandArgument フィールドが含まれており、これが shadcn add に渡すべき正確な文字列になります。

トリプルサーフェス設計:CLI、ライブラリ、そしてAI

アーキテクチャ全体を俯瞰すると、shadcn/ui は意図的にトリプルサーフェスのシステムとして設計されていることがわかります。

graph TD
    subgraph "Shared Internals"
        Parser["parser.ts"]
        Builder["builder.ts"]
        Fetcher["fetcher.ts"]
        Resolver["resolver.ts"]
        Schema["schema.ts"]
        Context["context.ts"]
        Search["search.ts"]
        API["api.ts"]
    end

    subgraph "Surface 1: CLI"
        Init["init command"]
        Add["add command"]
        Build["build command"]
    end

    subgraph "Surface 2: Library"
        RegExport["shadcn/registry"]
        SchemaExport["shadcn/schema"]
    end

    subgraph "Surface 3: AI"
        MCP["shadcn/mcp"]
        Tools["7 MCP tools"]
    end

    Init --> API
    Add --> API
    Build --> Schema
    RegExport --> API
    SchemaExport --> Schema
    MCP --> API
    MCP --> Search
    Tools --> MCP

    API --> Parser
    API --> Builder
    API --> Fetcher
    API --> Resolver
    API --> Context
    Search --> API

このアーキテクチャの核心となる原則は、3つのサーフェスがすべて同じ内部モジュールを共有しているという点です。resolverに新機能(第2回で紹介したトポロジカルソートなど)が追加されると、CLIユーザー・プログラマティックな利用者・AIアシスタントの全員がその恩恵を即座に受けられます。アダプター層も個別実装も存在せず、同じコードへの入り口が異なるだけです。

package.json のサブパスエクスポートによって、境界が明確に保たれています。shadcn/registry をインポートしてもCLIのCommanderへの依存は引き込まれませんし、shadcn/schema をインポートすればランタイムコードなしにZodスキーマだけを取得できます。tsup.config.ts のツリーシェイキングにより、各エントリーポイントは必要なものだけをバンドルします。

これは偶然の産物ではありません。index.ts(CLIエントリーポイント)の末尾にある export * from "./registry/api" という一行は、CLIモジュールをプログラマティックなライブラリとしても機能させるための意図的な選択です。これは、最小限のAPIサーフェスで最大限の利用パターンを実現するアプローチです。

Tips: shadcn/ui 周辺のツール(VS Code拡張、CIチェック、ドキュメントジェネレーターなど)を構築する場合は、ルートエクスポートではなく shadcn/registry からインポートしましょう。バンドルサイズを抑えつつ、CLI固有の依存関係を引き込まずに済みます。

シリーズのまとめ

この6回のシリーズを通じて、shadcn/ui のアーキテクチャ全体をひとつひとつ追ってきました。

  1. アーキテクチャ:マルチサーフェスCLIパッケージとregistryプロトコルを持つmonorepo
  2. Registryプロトコル:名前空間のパース、URL構築、HTTPフェッチ、トポロジカルソート
  3. トランスフォーマー:ts-morphによるAST操作とPostCSSスタイルマップ
  4. ビルドパイプライン:ベース × スタイルのデカルト積による静的JSONの生成
  5. 初期化:フレームワーク検出、テンプレート、プリセット、monorepoルーティング
  6. AI統合:MCPサーバー、プログラマティックAPI、トリプルサーフェス設計

これらすべてをつなぐアーキテクチャ上の洞察は、cn-* 抽象化レイヤーにあります。抽象CSSクラスを通じてコンポーネントのロジックとビジュアルスタイリングを分離することで、shadcn/ui はめったに実現できないことを達成しています。複数のheadlessライブラリ、複数のビジュアルスタイル、複数のアイコンライブラリ、複数のプロジェクト設定を横断して動作する、単一のコンポーネント実装です。しかもすべての解決はランタイムではなく、インストール時に行われます。あなたのプロジェクトに生成されるコードには、抽象化のオーバーヘッドは一切ありません。あるのはただ、あなたが選んだスタイルで仕上げられたコンポーネントだけです。