Figma Storybook Component Matching MCP Server
Allows fetching Figma design node information (structure, styles, text, variants) from a URL, enabling AI to analyze and match Figma designs to components.
Allows retrieving Storybook component lists, story details, and argTypes, enabling AI to match Figma designs to existing React components and generate usage examples.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Figma Storybook Component Matching MCP ServerMatch this Figma button to a component"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Figma → Storybook 컴포넌트 매칭 MCP 서버
목표
원격 MCP 서버를 만든다. Figma 디자인 노드를 입력받아 우리 팀의 React 컴포넌트(Storybook에 등록된)와 매칭시키고, 사용 코드 예시까지 생성하는 게 목적.
LLM(Claude)이 이 MCP를 통해 다음 같은 요청을 처리할 수 있어야 함:
"이 Figma URL 분석해줘"
"이 Figma 노드를 우리 컴포넌트로 어떻게 구현할지 알려줘"
"후보 컴포넌트 3개 보여줘"
기술 스택
런타임: Cloudflare Workers
언어: TypeScript (strict mode)
MCP:
@modelcontextprotocol/sdk+agents패키지 사용Transport: 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 Personal Access Token (서버가 보관)STORYBOOK_URL: Storybook 베이스 URL (예:https://storybook.example.com)MCP_AUTH_TOKEN: 클라이언트 인증용 토큰COMPONENT_IMPORT_PREFIX: 코드 생성 시 import 경로 (기본@/components)
로컬 개발용은 .dev.vars, 프로덕션은 wrangler secret put으로 관리. wrangler.toml에는 더미 placeholder만 두기.
노출할 도구(Tools)
1. get_figma_node
설명: Figma URL을 받아서 노드의 핵심 정보를 정제된 형태로 반환
입력 (zod):
{
url: string // Figma 노드 URL (예: https://www.figma.com/file/XXX/...?node-id=1%3A2)
}동작:
URL에서
fileKey와nodeId파싱 (?node-id=1%3A2→1:2로 디코드)Figma API 호출:
GET https://api.figma.com/v1/files/{fileKey}/nodes?ids={nodeId}헤더:
X-Figma-Token: {env.FIGMA_TOKEN}
응답에서 다음만 추출 (Figma 응답은 너무 verbose하니 정제):
노드 이름 (
name)노드 타입 (
type: FRAME, INSTANCE, TEXT, ...)컴포넌트면 컴포넌트 이름 (
componentId→componentName)스타일: 배경색, 테두리, 보더 반경, 패딩, 레이아웃 모드(autolayout 방향), gap
텍스트면
characters와 폰트 정보자식 구조: 자식 노드들의 이름/타입만 1단계 깊이로 (재귀 X, 너무 길어짐)
컴포넌트 변수/variants 정보(있으면)
출력: 위 정보를 담은 정제된 JSON 객체
에러: URL 파싱 실패, Figma API 4xx/5xx, 토큰 만료 등 구분해서 에러 메시지
2. get_figma_subtree
설명: 노드의 전체 트리를 재귀적으로 가져옴 (전체 페이지/프레임 분석용)
입력:
{
url: string,
maxDepth?: number // 기본 3, 너무 깊으면 토큰 폭발
}동작: get_figma_node와 비슷하지만 자식을 maxDepth까지 재귀. 각 자식도 정제된 형식.
3. list_stories
설명: 우리 Storybook의 컴포넌트 목록을 반환
입력: 없음 (또는 { filter?: string } 검색용)
동작:
${env.STORYBOOK_URL}/index.jsonfetch(실패 시 폴백)
${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분간 in-memory 캐싱 (KV 안 써도 됨, 단순 변수로). Workers는 인스턴스가 짧게 살아있으니 너무 길게 잡지 말 것.
4. get_story_details
설명: 특정 스토리의 상세 정보 (props, args)
입력:
{ storyId: string }동작:
index.json에서 해당 ID 찾음가능하면
${STORYBOOK_URL}/stories.json또는 ID 기반 메타에서 argTypes 추출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
Levenshtein 거리 기반: 0~0.5
구조 매칭 (가중치 0.3): 자식 패턴 추론
Figma 자식이 텍스트만 → "Button", "Label" 후보 +
아이콘 + 텍스트 → "Button", "Tag", "Chip" 후보 +
여러 카드 형태 자식 → "List", "Grid" 후보 +
태그 일치 (가중치 0.2): Storybook 스토리 태그에 Figma 노드 이름의 키워드 포함
상위 K개 반환 (기본 3):
{
storyId: string,
componentName: string,
score: number, // 0~1
reasons: string[] // 왜 매칭됐는지 사람이 읽을 수 있게
}[]매칭 점수 0.3 미만은 제외 (의미 없는 매칭 거름).
6. generate_component_usage
설명: 매칭된 컴포넌트 + Figma 노드 정보로 React JSX 코드 예시 생성
입력:
{
storyId: string,
figmaNode: <get_figma_node 출력 형식>
}동작:
get_story_details로 props 시그니처 가져옴Figma 노드의 텍스트, 스타일, 변형 정보를 props에 매핑 시도
Figma 텍스트 →
children또는labelpropFigma variant 이름 → matching prop value
JSX 코드 문자열 생성
출력:
{
code: string, // <Button variant="primary">Click me</Button>
importStatement: string, // import { Button } from "@/components/Button"
notes: string[] // 매핑 추측이나 빠진 정보 안내
}import path는 환경변수 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 → "노드를 찾을 수 없음"
Storybook fetch 실패 → 명확한 메시지
모든 에러는 MCP가 이해할 수 있는 형식으로 반환
로깅:
console.log로 도구 호출 시작/끝, 에러는console.error. Workers 대시보드에서 보임테스트: vitest로 핵심 로직 단위 테스트 (URL 파싱, 매칭 점수, 정제 로직)
README 갱신:
무엇을 하는 도구인지
환경변수 설명
로컬 실행 (
npm run dev)배포 (
npm run deploy)Claude Desktop / Claude.ai에 연결하는 방법
각 도구의 입출력 예시
작업 순서 (단계별로 보고하면서 진행)
Phase 1: 셋업
프로젝트 초기화, 의존성 설치
wrangler.toml,tsconfig.json작성빈 MCP 서버가
/mcp에서 응답하는지 확인 (도구 0개여도 OK)
Phase 2: 인증
Bearer 토큰 검증 미들웨어
잘못된 토큰으로 호출 시 401 확인
Phase 3: Figma 도구
figma/url-parser.ts+ 단위 테스트figma/client.ts(실제 API 호출)figma/normalizer.ts(응답 정제)get_figma_node도구 등록실제 Figma URL로 동작 확인
Phase 4: Storybook 도구
storybook/client.ts(index.json fetch + 캐싱)list_stories,get_story_details등록
Phase 5: 매칭
matching/scorer.ts+ 단위 테스트match_figma_to_components등록
Phase 6: 코드 생성
codegen/react.tsgenerate_component_usage등록
Phase 7: 마무리
get_figma_subtree추가README 작성
.dev.vars.example제공
각 Phase 끝나면 짧게 "이거 했고 다음 이거 할게" 보고하고 진행.
주의사항
Cloudflare Workers는 Node.js API 일부만 지원. fs, child_process 등 안 됨. fetch 기반으로 작성
@modelcontextprotocol/sdk최신 안정 버전 사용MCP 표준은 빠르게 변하니
agents패키지의 최신 패턴 따를 것한 번에 다 만들지 말고 Phase별로 검증하면서 진행
코드는 명확하게, 주석은 비즈니스 로직(매칭 점수 같은 거)에만
시작
Phase 1부터 시작해줘.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/martin-delivered/storybook-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server