社内デザインシステムMCPとFigma MCPを活用したゼロから作らない開発

  • URLをコピーしました!

こんにちは。2025年の9月に中途で入社しましたフロントエンドエンジニアの白濱(@shiraha_maru)です。社内デザインシステムのMCP化、Figma MCPを活用して入社1ヶ月目からスムーズに開発を進められる体制作りを行いましたので紹介します。

目次

背景

現在PR TIMESではUI刷新のプロジェクトが進行中で、私も入社後にこのプロジェクトへ参画しました。このプロジェクトでは、Figmaのデザインをもとに社内デザインシステムのコンポーネントを使って実装を進めます。

しかし、入社して間もないため既存コードや文脈の理解が十分でなく、コンポーネントの使い方やデザイントークンの定義有無の確認、Figmaのデザイン確認などの実装前に確認すべき事項が多く、開発の初動で時間がかかってしまっていました。

このような問題を解決するために、AIを活用して確認と実装の負荷を下げて開発の初動を速くできるように社内デザインシステムMCPを開発しました。

方針

着手時点でスケジュールがタイトだったため、今回はプロンプトからUI生成の完全自動化ではなく、社内デザインシステム準拠の叩き台を短時間で生成し、開発の初動を速めることを目標にしました。

そのため、社内デザインシステムを MCP 化し、Figma MCP と組み合わせて、AIを活用した実装が進められる状態を作ります。

初めから完成度の高い出力を目指すのではなく、初期出力はAIに任せ、細部の調整や必要な修正(余計なネストの削除や適切なタグへの置換など)は手動で行います。

実装手段

今回実装したものはデザインシステムMCP、Claude CodeのCustom Slash Commandの2つです。

デザインシステムMCP

デザインシステム MCP は Model Context Protocol の TypeScript SDK を使って実装しています。

デザインシステム MCP では以下の 3 つのツールを実装しています。

  • コンポーネント検索: 利用可能なコンポーネントを検索するツール
  • コンポーネント使用例取得: 特定のコンポーネントの使用例や主要な props を取得するツール
  • デザイントークン取得: デザインシステムで定義されているデザイントークンと CSS 変数を取得するツール

コンポーネント検索・使用例の取得には Storybook のビルド成果物(index.json)を、デザイントークンの取得には global.css を使用しています。

MCPサーバーは@modelcontextprotocol/sdkを使って以下のように実装しています(細かい実装は省略しています)。

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

// MCPサーバーを作成
const server = new Server(
  { name: 'design-system-mcp', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

// ツール一覧を定義
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'search_components',
      description: 'デザインシステムコンポーネントを検索',
      inputSchema: { /* query, category, limit */ }
    },
    {
      name: 'get_component_usage',
      description: 'コンポーネントの詳細な使用例を取得',
      inputSchema: { /* componentName */ }
    },
    {
      name: 'get_design_tokens',
      description: 'デザイントークン(CSS変数)を取得',
      inputSchema: { /* category */ }
    }
  ]
}));

// ツール実行ハンドラー
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  const result = await executeToolByName(name, args);

  return {
    content: [{ type: 'text', text: JSON.stringify(result) }]
  };
});

const transport = new StdioServerTransport();
await server.connect(transport);

細かい実装は省略しますが、以下のような構成で、Storybook を中心に、不足している情報は対象ファイルを参照して補完しています。

src/
├── loaders/
│   ├── storybook-loader.ts        # Storybook の情報を取得
│   ├── design-tokens-loader.ts    # global.css からデザイントークンを取得
│   └── simple-props-extractor.ts  # コンポーネント定義から props 情報を補完
├── tools/
│   ├── search-components.ts       # コンポーネント検索
│   └── get-usage.ts               # 使用例取得
└── index.ts

Claude Code の Custom Slash Command

作成する画面やコンポーネントの情報は Figma MCP で取得します。Figma MCP を単体で使うと、細部のずれや社内デザインシステムのコンポーネントを採用しないケースがあり、意図どおりの出力に揃えるのが難しい状況でした。

そこで、Claude Code の Custom Slash Command を作成し、生成ガイドラインや出力形式、重要事項などのコンテキストを明示的に渡すことで、誰が実行しても一定水準の出力を担保できるようにしました。

以下のようなプロンプトを用意して、作成したい画面・コンポーネントの Figma URL と作成先ディレクトリパスを渡して実行します。

### 使用方法

/figma-to-design <Figma URL> [作成パス]

FigmaのURLを指定するだけで、デザインシステムコンポーネントを使用したReactコンポーネントが自動生成されます。

### 実行プロセス

このコマンドは、複数のMCPサーバーを連携させて動作します:

1. **引数の解析**: Figma URLからnode IDを抽出し、作成パスを決定
2. **Figmaデザイン情報の取得** (figma-mcp): デザインの構造、コンポーネント、スタイル情報を取得
3. **design-systemコンポーネントライブラリの調査** (design-system-mcp): 利用可能なコンポーネント一覧を取得し、Figmaデザインに必要なコンポーネントを特定
4. **コンポーネント仕様の動的取得** (design-system-mcp): 特定されたコンポーネントの実装例とPropsを取得
5. **ファイル生成**: 指定されたパスに以下のファイルを生成
   - `index.tsx`: メインコンポーネントファイル
   - `component-name.module.css`: CSS Modulesファイル
   - `component-name.stories.tsx`: Storybookストーリーファイル

### 生成ガイドライン

生成されるコードは、以下の原則に従います:

- **コンポーネント優先**: design-systemの既存コンポーネントを最大限活用
- **デザイントークン**: design-system-mcpで取得した実際のトークンのみを使用(推測での作成は禁止)
- **TypeScript**: 厳格な型定義
- **アクセシビリティ**: セマンティックHTMLとARIA属性を考慮
- **構造の最適化**: 余計なネストを避けて必要最小限の構造に

### 出力例

```typescript
// index.tsx
import { Button } from '@design-system/components/button';
import styles from './component-name.module.css';

type Props = {
  readonly title: string;
};

export function GeneratedComponent({ title }: Props) {
  return (
    <Button variant="primary" className={styles.button}>
      {title}
    </Button>
  );
}

/* component-name.module.css */
.button {
  /* 実際に存在するdesign-systemトークンのみを使用 */
  background: var(--design-bg-base);
  padding: 12px 24px; /* デザインシステムにない値はハードコーディング */
}

実践

実際に作成したCustom Slash Commandを使ってFigmaのデザインからコンポーネントを作成します。MCPクライアントとしてはClaude Codeを使います。

先ほど作成したCustom Slash Commandに、作成したいFigmaデザインのURLと作成先ディレクトリパスを渡して実行します。この時、すぐに実装を進めるのではなく、まずは実装計画を出してもらうと出力後の手戻りや修正を減らすことができます。

実装計画を確認し、修正があれば追加で指示を出した上で進めます。
実際に実装してもらった結果、以下の出力となりました。

Figmaのデザイン出力結果
figmaデザインAIの出力結果

コンポーネントの実装はこのようになっています。

import {Button} from '@design-system/components/button';
import {ExternalLink} from '@design-system/components/link';
import styles from './company-profile.module.css';

type ProfileField = {
  readonly label: string;
  readonly value: string;
};

type Props = {
  readonly fields: readonly ProfileField[];
  readonly onEditClick: () => void;
  readonly publicPageUrl?: string;
};

export function CompanyPage({fields, onEditClick, publicPageUrl}: Props) {
  return (
    <div className={styles.container}>
      <header className={styles.header}>
        <div className={styles.headerLeft}>
          <h1 className={styles.title}>企業プロフィール</h1>
          {publicPageUrl !== undefined && publicPageUrl !== '' ? (
            <div className={styles.publicPageLinkWrapper}>
              <ExternalLink
                href={publicPageUrl}
                text='公開済みページ'
                size='small'
              />
            </div>
          ) : null}
        </div>
        <Button
          variant='filled'
          color='primary'
          size='large'
          onClick={onEditClick}
        >
          情報を変更
        </Button>
      </header>

      <section className={styles.profileSection}>
        {fields.map((field, index) => (
          <div
            key={field.label}
            className={
              index === fields.length - 1
                ? styles.profileRowLast
                : styles.profileRow
            }
          >
            <div className={styles.fieldLabel}>{field.label}</div>
            <div className={styles.fieldValue}>{field.value}</div>
          </div>
        ))}
      </section>
    </div>
  );
}

見た目の部分だけであれば、ほとんど問題がない精度で完成しています。デザイン内のボタンやリンクについてもデザインシステムのコンポーネントが使われているため問題なさそうです。

ただ、コードについては下記のような修正点が存在しています。

  • ラベル-値のペアがdivタグで実装されているが、<dl>、<dt>、<dd>要素などのより適切なHTML要素で表現できる可能性がある
  • ExternalLinkに不要なラッパーが使われている
  • 条件分岐やCSSの判定が冗長

このような修正点があるとは言え、数分程度でここまでの完成度で出力できています。

成果と課題

この仕組みによって、Figma のデザインや社内コンポーネントの存在確認、トークンの定義有無の確認といった実装前の確認コストが減少しました。MCP 経由の参照でその場で答えにたどり着けるようになり、実装前の準備に費やす時間を短縮できました。

また、Custom Slash Command を実行すると、Figma の構造を踏まえつつ、社内コンポーネントとデザイントークンを適用した 70〜80 点の初期実装が出力されます。ゼロから組み立てるのではなく、具体物を起点に微調整していく流れに変わったことで、手を動かすスピードや Pull Request 作成までの到達が早まりました。

一方で課題もあり、Figma デザインのネストが深い場合に div が過多な構造になってしまうこと、Storybook のビルド成果物を手元で使用しているため、デザインシステム更新時に手動ビルドが必要なことなどの制約は残っています。とはいえ、今回の目的は完全自動化ではなく、まずは使える叩き台を素早く出して実装の初動を速くすることです。限られた時間の中で進めるための現実的な方針だったと考えています。

まとめ

今回の取り組みは、入社初期に生じがちな確認の往復を減らし、実装の初動を速くすることに焦点を当てました。社内デザインシステムを MCP で参照可能にし、Figma MCP と組み合わせたスラッシュコマンドから叩き台を生成することで、ゼロから組み立てる負担を下げ、実装の流れを短縮できました。結果として、デザインと実装の間にある細かな確認作業はAIに任せられるようになり、実装着手までの迷いが減りました。

一方で、Figma の深いネストに引きずられて div が過多になること、Storybook のビルド成果物を手元で使用しているためデザインシステム更新時に手動ビルドが必要なことなどの課題は残っています。引き続き運用しながら精度を高め、より活用しやすくなるように改善を続けていきます。

  • URLをコピーしました!

この記事を書いた人

株式会社PR TIMES 開発部 フロントエンドエンジニア

目次