@tabula-css/mcp
Tabula의 읽기 전용 MCP 서버: 생성된 .tabula/ 아티팩트에 대한 에이전트 대면 서피스입니다.
설치
npm install --save-dev @tabula-css/mcp개발 의존성입니다 — 여러분 앱의 런타임 의존성이 아니라, 코딩 에이전트의 MCP 클라이언트가 실행하는 로컬 도구입니다. @tabula-css/cli, @tabula-css/core, @tabula-css/eslint-plugin, @tabula-css/merge, @tabula-css/registry, @tabula-css/tokens를 번들링합니다. @typescript-eslint/parser는 일반 의존성으로 포함되며, eslint는 validate_source 도구에만 필요한 선택적 피어입니다.
개요
llms.txt/llms-full.txt가 에이전트에게 어휘의 정적인 지도를 제공하는 반면, MCP 서버는 동일한 .tabula/ 아티팩트에 대한 실시간의, 구조화된 접근을 제공합니다: 클래스 문자열이 실제로 무엇을 렌더링하는지 리졸브하고, cn() 병합을 작성하기 전에 미리 보고, 이름을 기억해내는 대신 의도로 클래스를 역방향 조회하고, 제안된 수정을 반영하기 전에 실제 레지스트리에 대해 검증합니다. 모든 도구는 읽기 전용입니다 — 여기서 어떤 파일도 기록되지 않으며, 두 개의 제안 도구는 에이전트 자신의 편집 경로가 적용할 패치를 반환하므로 사람이 diff를 검토합니다.
실행
npx tabula-mcp [--cwd <dir>]바이너리는 (bin.ts로부터의) tabula-mcp입니다. stdio(StdioServerTransport)를 통해 MCP를 말합니다 — 표준 출력은 오직 프로토콜만 전달하며, 그 밖의 어떤 것도 아닙니다; 모든 진단은 표준 오류로 가는데, 그렇지 않으면 프로토콜 프레임으로 파싱될 것이기 때문입니다. --cwd는 현재 디렉터리가 아닌 다른 프로젝트 루트를 가리킵니다. .tabula/가 없거나 형식이 잘못되었으면, 한 번도 읽어 본 적 없는 프로파일을 서비스하는 대신 시작을 거부합니다(종료 코드 2).
MCP 호환 클라이언트의 설정에서처럼, MCP 클라이언트가 이를 직접 가리키게 하십시오.
{
"mcpServers": {
"tabula": { "command": "npx", "args": ["tabula-mcp"] }
}
}모든 응답은 오래됨 봉투를 담고 있습니다
{ "profileVersion": "…", "sourceHash": "…", "stale": false }오래됨(staleness)은 매 호출마다 다시 계산됩니다 — 서버는 .tabula/와 토큰 소스를 다시 stat하고, 이들이 바뀌었으면 다시 로드하므로, 편집-후-재빌드 루프는 결코 재시작이 필요하지 않습니다. 모든 도구는 오래된 읽기를 다음과 같이 거부합니다.
{
"error": "STALE_REGISTRY",
"message": "The .tabula/ artifacts do not match the current inputs. …",
"command": ["tabula", "build"],
"reasons": ["…"],
"profileVersion": "…", "sourceHash": "…", "stale": true
}두 도구는 이 거부에서 면제됩니다(해당될 때는 여전히 stale: true를 보고하지만): 오래됨을 보고하는 것이 존재 이유 전부인 doctor, 그리고 @tabula-css/core의 고정된 카탈로그만 읽고 어떤 아티팩트도 건드리지 않는 explain입니다. 소스-드리프트 검사가 실행될 수 없었을 때(리졸브할 수 없는 패키지 버전, 또는 읽을 수 없는 프로젝트 소스), 봉투는 추가로 "staleCheck": "artifacts-only"를 담습니다 — 오래됨과 아티팩트 전용 검사를 참고하십시오.
도구
resolve_classes
"이 요소는 실제로 어떻게 생겼는가?" 클래스 문자열에 대한 완전한 로컬 스타일링 모델입니다: 기본 선언, 조건부 밴드, 앰비언트(ambient) 속성, 원자적·알려지지 않은 클래스, 선언된 그룹 의존성.
| 매개변수 | 타입 | 필수 |
|---|---|---|
classes | string | string[] | yes |
axes | Record<string, string> (예: { theme: "dark" }) | no |
// → resolve_classes({ classes: "bg-surface p-md" })
{ "profileVersion": "…", "sourceHash": "…", "stale": false, /* declarations, bands, … */ }preview_merge
cn(...)을 작성하기 전에 그것이 정확히 무엇을 산출할지 예측합니다: 병합된 문자열, 그리고 폐기된 모든 클래스, 무엇이 그것을 가렸는지, 어느 CSS 속성에서인지.
| 매개변수 | 타입 | 필수 |
|---|---|---|
fragments | string[] — 순서대로; 소비자의 className이 마지막 | yes |
find_class_for
의도에 의한 역방향 조회 — 이 집합에서 가장 가치 있는 도구입니다. 실제 텍스트(클래스 이름, 패밀리, 선언된 속성/값, 토큰 설명)만 매칭하며, 적중당 matchedOn을 보고합니다 — 동의어 테이블도, 퍼지 점수도 없습니다. 놓치면 그런 클래스가 존재하지 않는다는 뜻이지, "임의값을 추측하라"는 뜻이 아닙니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
intent | string | no |
property | string, 예: "padding-inline" | no |
value | string, 예: "#ffffff" | no |
family | string, 예: "bg" | no |
limit | number (기본값 25) | no |
// → find_class_for({ intent: "raised card background" })
{ "matches": [{ "class": "bg-surface-raised", "family": "bg", "matchedOn": ["intent"], "rank": 30091300002, … }], "total": 1 }get_tokens
모든 토큰(또는 네임스페이스/부분 문자열 슬라이스)을, tokens.resolved.json으로부터 바로 — 값을 고르기 전에 이것을 읽으십시오.
| 매개변수 | 타입 | 필수 |
|---|---|---|
namespace | string, 예: "color" | no |
query | string, 경로 + 설명에 대한 부분 문자열 매칭 | no |
get_vocabulary
전체 클래스 목록을, 페이지 단위로 — 결코 조용히 잘리지 않으며, 응답은 total/pages/hasMore를 보고합니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
family | string | no |
prefix | string | no |
page | number, 0부터 시작 | no |
pageSize | number (기본값 100, 최대 500) | no |
explain_ban
왜 클래스가 금지되었거나 등록되지 않았는지를, 그 대체제와 함께 — 결코 그냥 "찾을 수 없음"이 아닙니다. 확신이 서지 않는 클래스(space-x-4, dark:bg-black, pl-4)를 작성하기 전에 호출하십시오.
| 매개변수 | 타입 | 필수 |
|---|---|---|
class | string, 예: "space-x-4" | yes |
resolve_element
{ file, line, col }로 JSX 요소를 가리켜, 그 리졸브된 클래스, 동일 파일 내 조상으로부터 상속받는 텍스트 컨텍스트, 그리고 참조하는 각 group/peer가 그 파일 안에 마커를 가지고 있는지를 반환합니다. 동일 파일 분석만 수행합니다 — 컴포넌트 경계에서는 컴포넌트가 무엇을 렌더링하는지 추측하는 대신, 다음에 열어야 할 파일과 함께 status: "unknown"을 반환합니다. 또한 axisValues는 생략하는데, 요소가 어떤 축 조합 아래에서 렌더링되는지는 소스가 아니라 실행 중인 문서에 대한 사실이기 때문입니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
file | string, 프로젝트 루트 기준 상대 경로 | yes |
line | number, 1부터 시작 | yes |
col | number, 1부터 시작 | yes |
validate_source
소스 문자열을 실제 strict ESLint 설정으로 여러분의 레지스트리에 대해 린트합니다 — AST를 보기 때문에 check_classes가 구조적으로 할 수 없는 것을 잡아냅니다(런타임 클래스 구성, className 순서, group/peer 구조, 상속 경계, 예외 범위). 인라인 eslint-disable 주석은 무시되므로, 스니펫이 말로 둘러대어 깨끗한 판정을 받을 수 없습니다. eslint와 @typescript-eslint/parser가 설치되어 있어야 하며, 없으면 LINTER_UNAVAILABLE과 설치 명령을 반환합니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
code | string — 린트할 소스 텍스트 | yes |
filename | string, 예: "src/ui/card.tsx" — 결코 디스크에서 읽지 않음; 규칙과 예외 범위를 선택하는 데 사용 | yes |
propose_exception
토큰이 없는 값을 도입하는 유일한 합법적 방법입니다. 패치를 반환합니다 — 아무것도 기록하지 않습니다. 실제 토큰 검증기를 통해 검증하며, 기존 토큰이 약 5% 이내에 있으면 그쪽으로 유도하고, 패치가 발행할 클래스 이름을 반환합니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
name | string, kebab-case | yes |
$type | string (DTCG type) | yes |
value | string, 예: "347px" | yes |
families | string[], 예: ["w"] | yes |
reason | string, 40자 이상 | yes |
owner | string, 예: "@design-systems" | yes |
expires | string, YYYY-MM-DD, 12개월 이내(literal이면 90일 이내) | yes |
allowedIn | string[] (glob) | yes |
description | string | no |
ticket | string | no |
literal | boolean | no |
propose_token
새 디자인 토큰을 발행합니다 — 토큰은 영구적인 어휘이므로 propose_exception보다 이것을 우선하십시오. 패치를 반환합니다 — 아무것도 기록하지 않습니다. 실제 토큰 문서에 이어 붙여 검증됩니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
name | string, "<namespace>.<name>" | yes |
$type | string (DTCG type) | yes |
value | string — 단일 리터럴; cases와 상호 배타적 | no |
cases | object — 축 맵, 예: { $axis: "theme", light: "#fff", dark: "#0b0b0b" }; value와 상호 배타적 | no |
description | string | no |
find_group_marker
이름 있는 group/<name> 마커가 어디에 선언되어 있는지를 알려주어, 요소 간 의존성이 트리 순회가 아니라 조회가 되도록 합니다. "그런 마커가 없음"과 "이 빌드가 마커 색인을 만들어내지 않았음"을 구분합니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
name | string, 예: "card" | yes |
check_classes
사전 점검 — 클래스 속성을 작성하기 전에 호출하십시오. 알려지지 않은 클래스(혹시 이거였나요 제안 포함), 금지된 메커니즘, 동일 문자열 내 슬롯 충돌을 보고하고, 정규 병합 문자열을 반환합니다.
| 매개변수 | 타입 | 필수 |
|---|---|---|
classes | string[] | yes |
doctor
건강 검진입니다: 오래됨(이유와 수정 명령 포함), 손으로 수정된 아티팩트, 만료 예정 예외, 탈출 예산. 오래된 상태에서도 답합니다 — 오래됨을 보고하는 도구가 오래되었다는 이유로 거부한다면 무엇이 드리프트되었는지 결코 말할 수 없을 것이기 때문입니다.
매개변수 없음.
// → doctor()
{
"stale": false, "reasons": [], "checkedSource": true,
"drift": [], "expiringExceptions": [], "escapeBudget": { "used": 3, "max": 25 },
"profileVersion": "…", "sourceHash": "…", "manifestInputsHash": "…"
}explain
TAB-Exxx/TAB-Wxxx 코드의 원인과 해결책을, @tabula-css/core의 고정된 카탈로그로부터.
| 매개변수 | 타입 | 필수 |
|---|---|---|
code | string, 예: "TAB-E113" | yes |
함께 보기
@tabula-css/registry— 모든 도구가 읽는 아티팩트.@tabula-css/tokens—propose_token/propose_exception이 대조하여 검증하는 토큰 문서.@tabula-css/eslint-plugin—validate_source가 실행하는strict설정.- 에이전트 서피스 —
llms.txt, 실패-가시성 보장, 그리고 전체 오래됨 모델. @tabula-css/cli—tabula doctor와tabula explain, 이 서버의 CLI 대응물.