Figma Storybook Component Matching MCP Server
MCP-сервер для сопоставления Figma → Storybook
Цель
Создать удаленный MCP-сервер. Цель — принимать на вход узлы дизайна Figma, сопоставлять их с React-компонентами нашей команды (зарегистрированными в Storybook) и генерировать примеры кода использования.
LLM (Claude) должна иметь возможность обрабатывать следующие запросы через этот MCP:
"Проанализируй этот URL Figma"
"Подскажи, как реализовать этот узел Figma с помощью наших компонентов"
"Покажи 3 подходящих компонента"
Технологический стек
Среда выполнения: Cloudflare Workers
Язык: TypeScript (strict mode)
MCP: использование пакетов
@modelcontextprotocol/sdk+agentsТранспорт: Streamable HTTP, эндпоинт
/mcpВалидация: zod
Сборка/развертывание: wrangler
Целевой фреймворк: React (вывод JSX при генерации кода)
Аутентификация (Вариант A: Bearer-токен)
Все запросы MCP требуют заголовок
Authorization: Bearer <token>Сравнение с
env.MCP_AUTH_TOKEN, при несовпадении возвращается 401Ошибка аутентификации должна сопровождаться понятным сообщением об ошибке (
{"error": "invalid_token"})
Переменные окружения (определяются в wrangler)
FIGMA_TOKEN: Персональный токен доступа Figma (хранится на сервере)STORYBOOK_URL: Базовый URL Storybook (например,https://storybook.example.com)MCP_AUTH_TOKEN: Токен для аутентификации клиентаCOMPONENT_IMPORT_PREFIX: Путь импорта при генерации кода (по умолчанию@/components)
Для локальной разработки используйте .dev.vars, для продакшена — wrangler secret put. В wrangler.toml оставляйте только фиктивные плейсхолдеры.
Инструменты (Tools)
1. get_figma_node
Описание: Получает URL Figma и возвращает ключевую информацию об узле в очищенном виде
Входные данные (zod):
{
url: string // Figma 노드 URL (예: https://www.figma.com/file/XXX/...?node-id=1%3A2)
}Действия:
Парсинг
fileKeyиnodeIdиз URL (декодирование?node-id=1%3A2→1:2)Вызов API Figma:
GET https://api.figma.com/v1/files/{fileKey}/nodes?ids={nodeId}Заголовок:
X-Figma-Token: {env.FIGMA_TOKEN}
Извлечение только следующего (ответ Figma слишком подробный, поэтому очищаем):
Имя узла (
name)Тип узла (
type: FRAME, INSTANCE, TEXT и т.д.)Если это компонент, имя компонента (
componentId→componentName)Стили: цвет фона, границы, радиус скругления, отступы, режим компоновки (направление autolayout), gap
Если это текст,
charactersи информация о шрифтеСтруктура дочерних элементов: только имена/типы дочерних узлов на 1 уровень глубины (без рекурсии, чтобы не перегружать)
Информация о переменных/вариантах компонента (если есть)
Вывод: Очищенный JSON-объект с указанной информацией
Ошибки: Разделение ошибок парсинга URL, API Figma 4xx/5xx, истечения срока действия токена и т.д.
2. get_figma_subtree
Описание: Рекурсивное получение полного дерева узла (для анализа всей страницы/фрейма)
Входные данные:
{
url: string,
maxDepth?: number // 기본 3, 너무 깊으면 토큰 폭발
}Действия: Аналогично get_figma_node, но с рекурсией дочерних элементов до maxDepth. Каждый дочерний элемент также в очищенном формате.
3. list_stories
Описание: Возвращает список компонентов из нашего Storybook
Входные данные: Нет (или { filter?: string } для поиска)
Действия:
Fetch
${env.STORYBOOK_URL}/index.json(При ошибке) Попытка
${env.STORYBOOK_URL}/stories.jsonИзвлечение из объекта
entriesтолько элементов сtype: "story"(исключая страницы docs)Преобразование в формат:
{
id: string,
componentName: string, // title에서 마지막 "/" 뒤 부분 (예: "Forms/Button" → "Button")
storyName: string, // name 필드
fullTitle: string, // 원본 title
tags: string[]
}[]Кэширование: Кэширование ответа в памяти на 5 минут (KV не нужен, достаточно простой переменной). Workers живут недолго, поэтому не стоит делать кэш слишком долгим.
4. get_story_details
Описание: Подробная информация о конкретной истории (props, args)
Входные данные:
{ storyId: string }Действия:
Поиск ID в
index.jsonПо возможности извлечение argTypes из
${STORYBOOK_URL}/stories.jsonили метаданных на основе IDОчистка сигнатуры props:
{
id: string,
componentName: string,
description?: string,
props: {
name: string,
type: string,
required: boolean,
description?: string,
defaultValue?: any
}[]
}Если argTypes получить не удалось, props остается пустым массивом, добавляется note: "argTypes unavailable".
5. match_figma_to_components
Описание: Возвращает кандидатов в компоненты, соответствующие данным узла Figma, с оценкой (основной инструмент)
Входные данные:
{
figmaNode: <get_figma_node 출력 형식>,
topK?: number // 기본 3
}Действия:
Получение всех компонентов через
list_storiesРасчет оценки соответствия для каждого компонента:
Сходство имен (вес 0.5): Имя узла Figma vs
componentNameТочное совпадение: 1.0
Совпадение без учета регистра: 0.9
Вхождение: 0.6
На основе расстояния Левенштейна: 0~0.5
Соответствие структуры (вес 0.3): Вывод паттернов дочерних элементов
Дочерние элементы Figma только текст → кандидаты "Button", "Label" +
Иконка + текст → кандидаты "Button", "Tag", "Chip" +
Несколько карточек → кандидаты "List", "Grid" +
Соответствие тегов (вес 0.2): Включение ключевых слов имени узла Figma в теги истории Storybook
Возврат топ-K (по умолчанию 3):
{
storyId: string,
componentName: string,
score: number, // 0~1
reasons: string[] // 왜 매칭됐는지 사람이 읽을 수 있게
}[]Оценки ниже 0.3 отсеиваются (фильтрация бессмысленных совпадений).
6. generate_component_usage
Описание: Генерация примера кода React JSX на основе сопоставленного компонента и информации об узле Figma
Входные данные:
{
storyId: string,
figmaNode: <get_figma_node 출력 형식>
}Действия:
Получение сигнатуры props через
get_story_detailsПопытка сопоставить текст, стили и варианты узла Figma с props
Текст Figma → prop
childrenилиlabelИмя варианта Figma → значение соответствующего prop
Генерация строки кода JSX
Вывод:
{
code: string, // <Button variant="primary">Click me</Button>
importStatement: string, // import { Button } from "@/components/Button"
notes: string[] // 매핑 추측이나 빠진 정보 안내
}Путь импорта основан на переменной окружения env.COMPONENT_IMPORT_PREFIX (по умолчанию @/components).
Структура проекта
figma-storybook-mcp/
├── src/
│ ├── index.ts # Worker 진입점, 인증 미들웨어, MCP 라우팅
│ ├── mcp.ts # MyMCP 클래스 (도구 등록)
│ ├── auth.ts # Bearer 토큰 검증
│ ├── figma/
│ │ ├── client.ts # Figma REST API 호출
│ │ ├── url-parser.ts # URL → fileKey + nodeId
│ │ └── normalizer.ts # Figma 응답 → 정제된 형식
│ ├── storybook/
│ │ ├── client.ts # index.json fetch + 캐싱
│ │ └── types.ts
│ ├── matching/
│ │ ├── scorer.ts # 매칭 점수 계산
│ │ └── name-similarity.ts # Levenshtein 등
│ ├── codegen/
│ │ └── react.ts # JSX 코드 생성
│ └── types.ts # 공통 타입
├── tests/
│ ├── url-parser.test.ts
│ ├── normalizer.test.ts
│ └── scorer.test.ts
├── wrangler.toml
├── .dev.vars.example # 실제 .dev.vars는 gitignore
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── README.mdТребования к реализации
Типобезопасность: Все схемы zod для входных данных инструментов и типы выходных данных должны быть явно определены
Обработка ошибок:
Figma 401 → "Токен Figma истек/неверный"
Figma 404 → "Узел не найден"
Ошибка fetch Storybook → понятное сообщение
Все ошибки возвращаются в формате, понятном MCP
Логирование:
console.logдля начала/конца вызова инструмента, ошибки черезconsole.error. Видно в панели управления WorkersТестирование: Unit-тесты vitest для основной логики (парсинг URL, оценка соответствия, логика нормализации)
Обновление README:
Описание инструмента
Описание переменных окружения
Локальный запуск (
npm run dev)Развертывание (
npm run deploy)Как подключить к Claude Desktop / Claude.ai
Примеры ввода/вывода для каждого инструмента
Порядок работы (отчитываться по этапам)
Фаза 1: Настройка
Инициализация проекта, установка зависимостей
Создание
wrangler.toml,tsconfig.jsonПроверка, что пустой MCP-сервер отвечает на
/mcp(даже с 0 инструментов)
Фаза 2: Аутентификация
Middleware для проверки Bearer-токена
Проверка 401 при вызове с неверным токеном
Фаза 3: Инструменты Figma
figma/url-parser.ts+ unit-тестыfigma/client.ts(реальный вызов API)figma/normalizer.ts(очистка ответа)Регистрация инструмента
get_figma_nodeПроверка работы с реальным URL Figma
Фаза 4: Инструменты Storybook
storybook/client.ts(fetch index.json + кэширование)Регистрация
list_stories,get_story_details
Фаза 5: Сопоставление
matching/scorer.ts+ unit-тестыРегистрация
match_figma_to_components
Фаза 6: Генерация кода
codegen/react.tsРегистрация
generate_component_usage
Фаза 7: Завершение
Добавление
get_figma_subtreeНаписание README
Предоставление
.dev.vars.example
После каждой фазы кратко отчитываться: "Это сделал, приступаю к следующему".
Примечания
Cloudflare Workers поддерживают только часть API Node.js.
fs,child_processи т.д. недоступны. Писать на основеfetchИспользовать последнюю стабильную версию
@modelcontextprotocol/sdkСтандарт MCP быстро меняется, следовать последним паттернам пакета
agentsНе делать все сразу, проверять по фазам
Код должен быть понятным, комментарии только для бизнес-логики (например, оценки соответствия)
Начало
Начни с Фазы 1.
Related MCP Connectors
The Figma MCP server brings Figma design context directly into your AI workflow.
Serves your design system and coding standards to coding agents, so they stop guessing.
Build and manage your design system with AI: tokens, themes, components, icons, Figma and code.
Live React design-system APIs, patterns, and code validation so AI agents build real UI, not slop.