uxskill
Star on GitHub
Language
English 简体中文 繁體中文 日本語 한국어 हिन्दी Bahasa Indonesia Tiếng Việt ไทย العربية Español Français Deutsch Português Русский Türkçe Italiano
Blog · Technical · 2026-05-28

# MCP server for design intelligence — 14 tools any agent can call.

A slash command is shaped like a prompt template. A skill is shaped like a manifest the host model reads. Neither is composable with other agents. The Model Context Protocol is — and ux-skill v2.1 ships an MCP server that exposes the recommender, the 51-rule linter, and every queryable manifest as 14 tools any MCP-aware host can call. Claude Desktop, Cursor, Windsurf, Cline, Continue — same engine, same Pydantic schemas, same 1,243 entries.

By the ux-skill team · 9 min read · MIT-licensed open source

## Why MCP, not another skill

Claude Code skills are valuable: low-friction install, single-file manifests, model-friendly markdown. The ceiling is also low: a skill runs inside one host (Claude Code), it can't be invoked by another agent on the same machine, and it can't return structured data — only prose the model has to reparse.

MCP solves the ceiling. Once ux-skill speaks MCP:

- Any agent on the user's machine — Claude Desktop, Cursor's chat, Windsurf's Cascade, a custom Anthropic SDK script — calls the same tools.
- The schemas are typed. Pydantic models on the server, JSON Schema on the wire, validated structure on every call. No "ask the model nicely to format JSON."
- Tools compose. An agent can call `ux_recommend`, take the result, pass it to `ux_lint`, then write the output to disk with a different MCP file-server. The 14 tools are nodes in a larger graph.
- The catalog is queryable. Want a single component or palette from the manifest? Don't reload the whole skill — call `ux_components` or `ux_palettes`.

The asymmetric move against ui-ux-pro-max-skill (84,000 stars, Claude-Code-only skill) is not "another skill." It's the same engine, distributed via a protocol they don't yet speak.

## The 14 tools, verbatim

Pulled from the `TOOLS` registry in `engine/mcp/server.py`. Every tool has a Pydantic input model, a typed handler, and a one-line description that renders in the host's tool picker.

Tool | What it does | Returns
--- | --- | ---
ux_recommend | Run the 5-parallel-search recommender against a brief. | System object — style, palette, type, motion, components, brand.
ux_lint | Run the 51-rule regex linter over a path. | List of findings (id, severity, file, line, why, fix).
ux_styles | Query the 84-style manifest. | Style entries with tokens, voice, intent labels.
ux_palettes | Query the 176-palette manifest. | Palettes with hex tokens, intent, contrast ratios.
ux_type_pairs | Query the 70 typeface pairings. | Display + body + mono pairs with weights and reasoning.
ux_components | Query the 148-component catalog. | Component entries with anatomy, states, and motion bindings.
ux_industries | Query the 184 industry rule packs. | Per-industry tone, motion, and color biases.
ux_motion_presets | Query the 57 motion presets. | Presets with Framer Motion, GSAP, and CSS snippets.
ux_anti_patterns | Read the 51 lint rules as data, not as a runner. | Rule definitions with regex, scope, why, fix.
ux_brands | Read one of the 73 brand DESIGN.md specs. | Full brand spec — tokens, ramps, motion, surfaces.
ux_landing_patterns | Query landing-page layout patterns. | Pattern entries with sections, IA rhythm, and CTAs.
ux_persist_save | Persist a recommendation to `.ux/master.json`. | Path written, hash, timestamp.
ux_persist_load | Load a previously persisted system. | The previously-saved system object.
ux_stats | Health check + catalog counts. | Version, entry counts per manifest, build hash.

## Wiring into Claude Desktop

Claude Desktop reads its MCP server list from `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS (and the equivalent on Windows/Linux). Add ux-skill as a stdio server pointing at the installed CLI:

    // ~/Library/Application Support/Claude/claude_desktop_config.json
    {
      "mcpServers": {
        "uxskill": {
          "command": "uxskill",
          "args": ["mcp"],
          "env": {
            "UX_DATA_DIR": "~/.ux-skill/data"
          }
        }
      }
    }

Restart Claude Desktop. Open the tools pane — ux-skill's 14 tools appear under `uxskill`, each with its Pydantic-derived input schema. The agent picks them when relevant, calls them with structured arguments, receives structured responses.

## Wiring into Cursor

Cursor honors the same protocol. Edit `~/.cursor/mcp.json` (or the project-local `.cursor/mcp.json`):

    // .cursor/mcp.json
    {
      "mcpServers": {
        "uxskill": {
          "command": "uxskill",
          "args": ["mcp"]
        }
      }
    }

Restart Cursor. The agent panel now lists all 14 tools as MCP-provided. The full Cursor walkthrough covers the rest of the install paths.

## Wiring into Windsurf

Windsurf's Cascade reads its MCP server registry from `~/.windsurf/mcp_config.json`:

    // ~/.windsurf/mcp_config.json
    {
      "mcpServers": {
        "uxskill": {
          "command": "uxskill",
          "args": ["mcp"],
          "disabled": false,
          "autoApprove": [
            "ux_stats",
            "ux_styles",
            "ux_palettes",
            "ux_type_pairs",
            "ux_anti_patterns"
          ]
        }
      }
    }

The `autoApprove` list whitelists read-only tools so the agent doesn't prompt for confirmation on every catalog query. Writes (`ux_persist_save`) still require explicit approval. The full Windsurf walkthrough.

## An end-to-end agent call

Here's the actual JSON-RPC exchange the agent and the ux-skill server have when the user asks "recommend a design system for a freight TMS targeting fleet ops." The agent infers the brief, calls `ux_recommend`, gets a system back, and continues.

    // → request: tool call
    {
      "jsonrpc": "2.0",
      "id": 8,
      "method": "tools/call",
      "params": {
        "name": "ux_recommend",
        "arguments": {
          "brief": {
            "project_type": "b2b logistics dashboard",
            "industry": "freight / TMS / supply chain",
            "audience": ["fleet operators", "dispatch managers"],
            "tone": ["operational", "trustworthy", "dense"],
            "must_have": ["data density", "keyboard-first"],
            "forbidden": ["playful palette", "hero animations"]
          }
        }
      }
    }

    // ← response: typed system object
    {
      "jsonrpc": "2.0",
      "id": 8,
      "result": {
        "content": [{
          "type": "text",
          "text": "{...full system JSON...}"
        }],
        "structuredContent": {
          "style": { "id": "operational-density", "name": "Operational Density" },
          "palette": { "id": "dispatch-slate", "tokens": { "...": "..." } },
          "type_pair": { "id": "ibm-plex-sans-mono" },
          "motion": { "presets": ["state-flip", "row-highlight"] },
          "components": ["table-dense", "command-palette", "status-pill", "..."]
        }
      }
    }

The response is structured. The agent doesn't have to re-parse a prose recommendation; it gets a typed system object and continues to the next step (often `ux_lint` after generating a surface from that system).

## A composed call — recommend then lint

Where MCP earns its keep is composition. Here's a two-step the agent runs without re-prompting the user:

    // 1. Compose a system for the brief.
    const system = await mcp.call("ux_recommend", { brief });

    // 2. Generate surface code using that system (model call).
    const html = await model.generate({ system, surface: "hero" });
    await fs.write("src/Hero.tsx", html);

    // 3. Lint the generated file against the 51 rules.
    const findings = await mcp.call("ux_lint", {
      paths: ["src/Hero.tsx"],
      threshold: "high"
    });

    // 4. If anything fired, ask the model to fix and re-lint.
    if (findings.length > 0) {
      const fixed = await model.fix({ html, findings });
      await fs.write("src/Hero.tsx", fixed);
    }

Four steps, two MCP calls, no manual handoff. More on the linter loop.

## What's inside the server

The server module (`engine/mcp/server.py`) is roughly 500 lines. Three things matter about its shape:

The tool catalog is a dict, not a decorator forest. Every tool is registered in a single `TOOLS: Dict[str, ToolEntry]` map. `ToolEntry` is a triple of (handler function, Pydantic input model, description string). The MCP framework wrapper iterates the dict at startup and registers each entry. This means tests can call handlers directly without importing the `mcp` library.

The transport is optional. `import mcp.server` is wrapped in a try/except. If the `mcp` pip package isn't installed, the module still imports cleanly — the error only fires when you try to `run_server()`. This keeps `uxskill recommend` and `uxskill lint` usable as standalone CLI commands even without MCP installed.

Logging goes to stderr. stdout is reserved for the MCP JSON-RPC protocol stream — anything written there breaks the transport. The logger configuration enforces stderr-only at module import.

> The skill manifest is a contract with one host. The MCP server is a contract with every host that speaks the protocol.

## Discovering tools the agent doesn't know about

MCP hosts call `tools/list` on startup to enumerate what each server provides. The ux-skill server returns all 14 with their JSON Schema input definitions. The agent picks at runtime — the user doesn't have to remember a slash command, and the host doesn't have to ship a custom integration. That's the protocol working.

For agents that want to plan ahead — "what categories of design data does this server expose?" — call `ux_stats` first. It returns version, build hash, and entry counts per manifest:

    $ mcp call uxskill ux_stats
    {
      "version": "2.1.0-alpha.3",
      "build_hash": "a4c1f7e",
      "manifests": {
        "styles": 84,
        "palettes": 176,
        "type_pairs": 70,
        "components": 148,
        "industries": 184,
        "motion_presets": 57,
        "anti_patterns": 51,
        "brands": 73,
        "chart_types": 35,
        "tech_stacks": 25,
        "ux_guidelines": 112
      },
      "total_entries": 1015
    }

Honesty card

#### MCP is young. The protocol moves.

The Model Context Protocol shipped late 2024 and is still version-pinning aggressively. Anthropic's Python SDK is the canonical implementation; the spec stabilized into 1.0 in early 2025. Our server tracks the SDK closely but if you're running an older Claude Desktop build (anything before December 2024) the tool list may not appear — upgrade the host first.

The 14 tools are also still alpha-tier. Names are stable; argument shapes may grow new optional fields. We don't break the contract on existing arguments.

## The asymmetric move

ui-ux-pro-max-skill is the most-installed UX skill on GitHub (83,958 stars on 2026-05-28). Its distribution is single-host: Claude Code, via the marketplace, as a manifest. If a Cursor user wants the same recommender, there isn't one.

ux-skill's MCP server side-steps the popularity contest. We don't have to win Claude Code's UX-skill category — we can be present in every MCP-aware host simultaneously. Full side-by-side comparison with ui-ux-pro-max.

## Related reading

- MCP page — install configs for every host.
- Cursor design plugin — install paths.
- Windsurf design rules — Cascade install.
- Best Claude Code marketplace plugins 2026.
- ui-ux-pro-max alternative — side by side.
- Roadmap — what's next for the MCP surface.
- FAQ — when to use MCP vs the skill vs the CLI.

Wire it into your host

## One server. 14 tools. Every MCP host.

Same Python engine that powers the CLI, the skill, and the recommender — exposed over MCP for Claude Desktop, Cursor, Windsurf, and every other host that speaks the protocol. MIT, no telemetry, no account.

$ /plugin marketplace add Laith0003/ux-skill
$ /plugin install ux@ux-skill
— or —
$ pip install uxskill[mcp]
— or —
$ npx uxskill@alpha mcp

## Related reads

- Python design system generator
- Windsurf design rules
- Cursor design plugin
- Dogfooding ux-skill