Skip to content

MCP Server

@csszyx/mcp-server exposes CSSzyx as an MCP (Model Context Protocol) server so AI assistants (Claude, Cursor, Copilot, etc.) can expand, validate, look up, and migrate sz props directly inside your editor.

Install the server next to the compiler, at the same version, so the lockfile pins the two together and the server answers with the rules your build uses:

Terminal window
npm install -D @csszyx/mcp-server

Requires Node.js >= 22.12.

Each setup below runs the installed binary, csszyx-mcp. npx --no-install runs it from node_modules/.bin without a registry request, and installs nothing: a project without the server gets an error, never a release it has not installed.

Add .mcp.json to the project root:

{
"mcpServers": {
"csszyx": {
"command": "npx",
"args": ["--no-install", "csszyx-mcp"]
}
}
}

or run claude mcp add csszyx -- npx --no-install csszyx-mcp in the project.

Add the same block to .cursor/mcp.json in the project root.

Claude Desktop starts servers outside any project, so give the binary’s absolute path in claude_desktop_config.json:

{
"mcpServers": {
"csszyx": {
"command": "/path/to/your-app/node_modules/.bin/csszyx-mcp"
}
}
}

For a client that cannot run a project’s binary, pin the version your compiler has and read the npm cache first: npx --prefer-offline -y @csszyx/mcp-server@<version>.

The server speaks MCP over stdio, so any MCP-compatible client runs the same command.

The server answers with the @csszyx/compiler it was installed with. Every tool reply names it in _meta.compilerVersion, beside the project’s in _meta.projectCompilerVersion; when the two differ, the reply opens with a line saying so, and the server’s instructions say it at startup.

Expand a single sz object into Tailwind CSS class strings.

Input: { p: 4, bg: 'blue-500', hover: { bg: 'blue-700' } }
Output: "p-4 bg-blue-500 hover:bg-blue-700"

Expand multiple sz objects in one call — more efficient than calling csszyx_expand repeatedly.

Input: [{ p: 4 }, { m: 2, bg: 'red-500' }]
Output: ["p-4", "m-2 bg-red-500"]

Convert a Tailwind class string back into an sz object.

Input: "p-4 bg-blue-500 hover:text-white"
Output: { p: 4, bg: 'blue-500', hover: { color: 'white' } }

A few classes depend on the project: a sign on a keyword (-z-auto) or a fraction on a spacing key (-m-1/2) is served only when the project’s Tailwind says so, for example through a --spacing-1\/2 theme step. For those, the tool asks the project’s Tailwind, from the directory the server runs in, as csszyx migrate does; with no Tailwind v4 there, the class stays in the unrecognized list. A prefix the project’s stylesheet sets (prefix(tw)) is read off each class the same way, and the round trip compiles the answer back with it.

When the project cannot answer in full, a note says so: its stylesheets set different prefixes, none compiled, or Tailwind could not be asked. A server started in a directory without a package.json reads no stylesheet, so start it from the project root.

Validate a sz object before using it. Reports unknown props, CSS property name mistakes (padding → p), and transform errors.

Input: { padding: 4 }
Output: error — "Unknown prop 'padding'. Use 'p' instead."

A problem the compiler reports comes as fields, the same ones csszyx check --json writes: code, message (the main sentence), help, note (each left out when there is none) and url.

Input: { p: 1.3 }
Output: warnings: [{ code: "off-scale-value",
message: "`p: 1.3` is not on Tailwind's spacing scale, so the class generates no CSS.",
help: "use a quarter step such as `1.25`, `1.5` or `1.75`, or a unit value such as `'1.3rem'`.",
url: "https://csszyx.com/e/off-scale-value" }]

Look up how a CSS property or keyword maps to an sz key. Accepts CSS property names (kebab-case or camelCase), sz keys, or free-text keywords.

Input: "background-color"
Output: { szKey: "bg", tailwindPrefix: "bg", examples: [...] }

Transform a JSX/TSX code snippet, replacing className attributes with sz props. Handles clsx calls, ternary expressions, and template literals.

// Input
<div className="p-4 bg-blue-500" />
// Output
<div sz={{ p: 4, bg: 'blue-500' }} />

Optional parameters:

  • customMap — pass a parsed .csszyx-todo.json map to resolve custom classes
  • injectTodos — insert {/* @sz-todo: ... */} comments above unresolved elements

The snippet is read as csszyx migrate reads a file of the project the server runs in: with the prefix its stylesheets set, and with its Tailwind answering for the classes that depend on the project, as for csszyx_reverse. A note says when it could not.

Parse @theme CSS blocks and categorize custom design tokens (colors, spacings, fonts, radii, shadows).

Compile a whole source module and report what csszyx made of it: the rewritten code, the classes, the diagnostics, and the runtime helpers left over.

Every other tool takes an sz object, which assumes the code around it already compiles. Reach for this one when the question is about that surrounding shape — whether a szv factory precompiles into a lookup, whether a value stays dynamic and falls back to a custom property, whether a key is unknown and ships a class that styles nothing.

{ "source": "export const A = () => <div sz={{ p: 4 }} />;" }

Pass filename when the parser matters — the extension selects it, so JSX needs a .tsx name. It defaults to preview.tsx.

The classes carry the prefix the project’s stylesheet sets. With @import "tailwindcss" prefix(tw) the project serves tw:p-4 and nothing for p-4, so the tool reads the prefix from the stylesheets of the project the server runs in, and classPrefixFrom says it did. Pass classPrefix when the project is elsewhere, or to compile under another prefix:

{ "source": "export const A = () => <div sz={{ p: 4 }} />;", "classPrefix": "tw" }

The result echoes the classPrefix it used. When none was given and none was read, a note says why, since the classes are then in the unprefixed vocabulary: no stylesheet sets one, or the project’s stylesheets set different prefixes or did not compile.

The preview does not read the project’s stylesheet, so it cannot tell which class covers which. A build can: it drops a Tailwind class that a later one on the same element fully covers, so { pb: 2, p: 4 } and className="pb-2" sz={{ p: 4 }} both build to p-4 while the preview lists both classes. Whenever one static sz object in the result holds two or more classes, or a static className sits beside a static sz, a mergeNote says so; two elements never merge with each other, so a file of one-key objects gets none.

The server also exposes read-only resources that AI agents can load for context:

URIContent
csszyx://referenceFull API reference (llms-full.txt)
csszyx://property-mapPROPERTY_MAP as JSON
csszyx://variantsAll known variant names as a JSON array

Two guided workflow prompts are available:

PromptDescription
migrate_componentPaste a Tailwind component and get it migrated to sz props
create_componentDescribe a UI component and get production-ready sz code