@molano/mcp-server
v1.0.0
Published
Molano Design System — MCP Server for AI agents. Provides tools for component lookup, token queries, code validation, accessibility checks, and design governance.
Maintainers
Readme
@molano/mcp-server
MCP (Model Context Protocol) server for the Molano Design System. Exposes design system knowledge — components, tokens, recipes, validation rules, code generation, and governance — to AI agents like GitHub Copilot, Claude, Cursor, and Windsurf.
35 tools · 172+ smoke tests · structured logging · telemetry · rate limiting · auth support
Quick Start
# 1. Install monorepo deps
npm install
# 2. Build
npm run build --workspace=packages/mcp-server
# 3. Run (via MCP client — VS Code, Claude Desktop, Cursor, etc.)
node packages/mcp-server/dist/index.jsTools (35)
Core (10 tools) — Query the design system
| Tool | Description |
|------|-------------|
| list_components | List all 91 DS components. Filter by category (forms, layout, overlays…) or status (stable, beta, experimental, deprecated) |
| get_component | Get full component detail: props, sub-components, variants, usage do/don't, code examples, a11y notes |
| list_tokens | List all design tokens (121 primitive + 28 semantic). Filter by category (color, typography, spacing…) |
| get_token | Get a token by name (exact or fuzzy). Returns CSS var, value, and light/dark for semantic tokens |
| list_recipes | List 25 pre-composed recipes (FormCard, EmptyState, StatCard…) with composedFrom info |
| get_recipe | Get recipe detail with usage guidance and code examples |
| list_layouts | List 4 layout components (AppShell, PageLayout, AuthLayout, DashboardLayout) with sub-components and hooks |
| search_ds | Full-text search across components + tokens. Returns top matches ranked by relevance |
| get_version | Current DS version, total components, and lifecycle breakdown by status |
| get_changelog | Get changelog entries. Optionally filter by version. Returns changes, breaking changes, and migration notes |
Validation (7 tools) — Validate agent-generated code
| Tool | Description |
|------|-------------|
| validate_code | Validate code against 7 DS rules: hardcoded colors, native elements, external libs, asChild, forwardRef, window dialogs, physical CSS |
| validate_tokens_usage | Check for hex colors in className, inline styles, hardcoded color values |
| check_a11y | Accessibility checks: img alt, icon button labels, div onClick, dialog/sheet titles |
| check_contrast | WCAG contrast ratio between two hex colors — returns ratio, AA, AAA, and large text compliance |
| validate_composition | Verify recipes compose DS primitives (Card uses CardHeader, not manual styles) |
| validate_responsive | Check for hardcoded media queries, fixed pixel widths, missing responsive breakpoints |
| validate_i18n | RTL readiness: detects physical CSS properties (ml, mr, pl, pr → ms, me, ps, pe) |
Generation (7 tools) — Generate code that follows DS conventions
| Tool | Description |
|------|-------------|
| generate_story | Generate Storybook story with all variants, auto-imports, and sub-component rendering |
| generate_test | Generate vitest + testing-library tests: render, variants, data-slot, a11y |
| generate_page | Generate full page: layout + search + table + empty state + form, with proper DS imports |
| scaffold_component | Scaffold new component: cva variants, data-slot, displayName, sub-components |
| generate_migration | Generate migration guide between versions with breaking changes and steps |
| generate_docs | Generate knowledge doc (.md) with YAML frontmatter: whenToUse, do/don't, a11y, code examples |
| generate_rfc | Generate RFC document for proposing new components: API design, a11y, checklist |
Governance (9 tools) — Monitor DS health and adoption
| Tool | Description |
|------|-------------|
| component_health | List components with health issues: missing docs, deprecated, experimental |
| get_anti_patterns | List 12 DS anti-patterns with bad/good code examples and fix reasons |
| audit_coverage | Cross-reference metadata vs knowledge docs — reports missing/orphaned docs |
| token_coverage | Analyze token usage: which --ds-* vars are used in globals.css vs defined |
| bundle_report | Report bundle sizes (files, total KB, budget compliance). Pass budgetKB to configure |
| breaking_changes | List all breaking changes since a given version. Helps plan migrations |
| ds_audit | Full DS health scorecard: components, tokens, bundle, theme bridge. Returns score with pass/fail |
| adoption_report | Analyze code for DS adoption: counts imports, detects violations (native elements, hardcoded colors, external libs) |
| deprecation_candidates | List components that are experimental or missing documentation |
Infrastructure (2 tools) — Monitor the MCP server itself
| Tool | Description |
|------|-------------|
| ds_health | Health check: server status, uptime, version, data counts, and per-tool usage telemetry |
| ds_server_info | Server metadata: protocol version, supported DS versions, tool categories, capabilities |
Usage Examples
In VS Code (GitHub Copilot)
The server is pre-configured in .vscode/mcp.json. Just build and restart VS Code.
Natural language queries:
"List all form components"
→ Calls list_components with category="forms" → 16 components
"How do I use Dialog?"
→ Calls get_component("Dialog") → usage do/don't, code example, a11y notes
"Validate this code for DS compliance"
→ Calls validate_code(code) → errors: asChild detected, use render prop instead
"Generate a Storybook story for Badge"
→ Calls generate_story("Badge") → complete story with all variants
"What's the health of the DS?"
→ Calls ds_audit() → scorecard: 14/15 (93%), 4 sections with pass/fail
"How many tokens am I actually using?"
→ Calls token_coverage() → 25/121 vars used in globals.css, orphan analysis
"Is this code using the DS correctly?"
→ Calls adoption_report(code) → 2 imports found, 3 violations detectedProgrammatic (JSON-RPC over stdio)
# Start server
node packages/mcp-server/dist/index.js
# Send MCP initialize request via stdin
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | node packages/mcp-server/dist/index.js// Call a tool
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "validate_code",
"arguments": {
"code": "<button onClick={handleClick}>Submit</button>"
}
}
}
// Response
{
"valid": false,
"errors": [
{
"rule": "no-native-elements",
"message": "Use <Button> instead of <button>",
"suggestion": "<Button onClick={handleClick}>Submit</Button>"
}
]
}Setup
VS Code (GitHub Copilot)
Pre-configured in .vscode/mcp.json:
{
"servers": {
"molano-ds": {
"type": "stdio",
"command": "node",
"args": ["packages/mcp-server/dist/index.js"],
"cwd": "${workspaceFolder}"
}
}
}Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"molano-ds": {
"command": "node",
"args": ["/absolute/path/to/Molano-DS/packages/mcp-server/dist/index.js"],
"cwd": "/absolute/path/to/Molano-DS"
}
}
}Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"molano-ds": {
"command": "node",
"args": ["packages/mcp-server/dist/index.js"],
"cwd": "."
}
}
}Docker
# Build image
docker build -t molano-mcp -f packages/mcp-server/Dockerfile .
# Run (stdio mode)
docker run -i molano-mcp
# With auth enabled
docker run -i -e MCP_AUTH_REQUIRED=true -e MCP_API_KEY=your-secret-key molano-mcpConfiguration
All configuration via environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| MCP_LOG_LEVEL | info | Log level: debug, info, warn, error |
| MCP_AUTH_REQUIRED | false | Set to true to require API key authentication |
| MCP_API_KEY | — | API key (required when MCP_AUTH_REQUIRED=true) |
| MCP_RATE_LIMIT | 200 | Max tool calls per minute |
Structured Logging
All logs are JSON-formatted and written to stderr (stdout is reserved for MCP JSON-RPC):
{
"timestamp": "2026-04-22T10:15:30.123Z",
"level": "info",
"correlationId": "a1b2c3d4",
"message": "tool_call_end",
"data": { "tool": "validate_code", "durationMs": 12 }
}Telemetry
Call ds_health to see real-time telemetry:
{
"status": "healthy",
"uptime": "2h 15m 30s",
"telemetry": {
"totalCalls": 142,
"totalErrors": 3,
"errorRate": "2.1%",
"toolStats": {
"validate_code": { "calls": 45, "errors": 1, "avgDurationMs": 8 },
"get_component": { "calls": 32, "errors": 0, "avgDurationMs": 3 }
}
}
}Error Handling
Every tool call is wrapped with:
- Correlation ID — unique per request for log tracing
- Graceful degradation — errors return structured JSON, never crash the server
- Rate limiting — 429-style response when limit exceeded
- Retry hints — error responses include
retryAfterMswhen rate-limited
Development
# Watch mode
npm run dev --workspace=packages/mcp-server
# Type check
npm run typecheck --workspace=packages/mcp-server
# Smoke tests (172 assertions)
npm test --workspace=packages/mcp-server
# Cross-system integration tests
node packages/mcp-server/scripts/cross-check.mjsArchitecture
packages/mcp-server/
├── src/
│ ├── index.ts # Server entry: 35 tool registrations + middleware (auth, rate limit, telemetry)
│ ├── data.ts # Data loading: tokens, components, version, changelog, directory helpers
│ ├── knowledge.ts # Auto-loads component knowledge from packages/ui/docs/*.md (YAML frontmatter)
│ ├── validators.ts # Code validation: DS rules, a11y, contrast, i18n, composition, responsive
│ ├── generators.ts # Code generation: stories, tests, pages, scaffolds, docs, migrations, RFCs
│ ├── governance.ts # Governance: token coverage, bundle report, audit, adoption, deprecation
│ ├── logger.ts # Structured JSON logger (stderr, correlation IDs, configurable level)
│ └── telemetry.ts # In-memory telemetry: tool call tracking, latency, error rates
├── scripts/
│ ├── smoke-test.mjs # 172-assertion end-to-end MCP server test via JSON-RPC
│ ├── cross-check.mjs # 22 cross-system integration tests
│ ├── generate-metadata.mjs # Auto-generates COMPONENT-METADATA.json from source
│ └── validate-metadata.mjs # Validates metadata sync with disk
├── Dockerfile # Multi-stage Docker build for shared deployment
├── dist/ # Built output (ESM bundle, ~87 KB)
├── package.json
├── tsconfig.json
└── tsup.config.tsHow it works
- Startup: Reads monorepo data — token JSONs, component metadata, knowledge docs, changelog
- Middleware: Every tool call gets correlation ID, structured logging, error handling, rate limiting, and telemetry
- 35 tools: Registered via MCP SDK, exposed over stdio for any MCP-compatible client
- Validation: Regex-based static analysis on code snippets — no AST parser needed
- Generation: Template-based code generation following DS conventions
- Governance: File system analysis for bundle sizes, token coverage, and adoption metrics
- Knowledge is auto-loaded from markdown files — no hardcoded data in the server.
Adding a new tool
// In src/index.ts
server.tool(
"tool_name",
"Description for AI agents",
{
param: z.string().describe("Parameter description"),
},
async ({ param }) => {
// Tool logic
return {
content: [
{ type: "text" as const, text: JSON.stringify(result, null, 2) },
],
};
},
);Adding component knowledge
Create a markdown file in packages/ui/docs/ named after the component (e.g., Button.md):
---
whenToUse: For primary actions, form submissions, CTAs.
whenNotToUse: For navigation use Link. For toggles use Switch.
do:
- Use semantic variants (destructive, outline, ghost)
- Add aria-label for icon-only buttons
dont:
- Use native <button> — always use DS Button
- Wrap in unnecessary divs
a11y: Icon-only buttons need aria-label for screen readers.
---
```tsx
<Button variant="destructive">Delete</Button>
The server auto-loads all `docs/*.md` files at startup — no code changes needed.
## Validation Rules
The server enforces these DS rules:
| Rule | Severity | Description |
|------|----------|-------------|
| `no-hardcoded-colors` | error | No Tailwind color classes (text-red-500) — use semantic tokens |
| `no-physical-properties` | warning | No physical CSS (ml-4) — use logical (ms-4) for RTL |
| `no-native-elements` | error | No native `<button>`, `<input>` — use DS components |
| `no-external-ui-libs` | error | No imports from Radix, Chakra, MUI, etc. |
| `no-window-dialogs` | error | No window.alert/confirm/prompt |
| `no-asChild` | error | No asChild prop — use render prop (base-ui, not Radix) |
| `no-forwardRef` | warning | Unnecessary in React 19 |
| `no-manual-card-styles` | error | No manual Card styles (`rounded-lg border bg-card...`) — compose CardHeader, CardContent, etc. |
| `no-manual-skeleton` | error | No manual skeleton divs (`animate-pulse rounded bg-muted`) — use `<Skeleton />` |
| `no-native-headings` | warning | No native `<h1>`..`<h6>`, `<p>` for page structure — use PageTitle, CardTitle, etc. |
| `no-hardcoded-spacing` | warning | No hardcoded pixel spacing in className — use DS spacing tokens |
| `no-hardcoded-hex` | error | No hex colors in className |
| `prefer-tokens-over-inline` | warning | Prefer Tailwind classes over inline styles |
| `img-alt` | error | Images must have alt text |
| `button-label` | error | Icon buttons need aria-label |
| `no-div-onclick` | error | No `<div onClick>` — use Button or ActionCard |
| `dialog-title` | warning | Dialogs need a title for screen readers |
## Testing
### Smoke test
Runs the server end-to-end: initializes, lists tools, calls key tools, validates responses.
```bash
# From packages/mcp-server/
npm test # builds + runs all tests
npm run test # same as aboveThe smoke test verifies:
- Server starts and completes MCP handshake
- All 18 tools are registered
- Components and tokens load correctly
- Code validation catches known bad patterns
- Good code passes validation
- Contrast checker returns valid ratios
- Accessibility checks detect issues
- Search returns relevant results
Metadata generation
Auto-generates COMPONENT-METADATA.json by scanning component source files:
npm run generate-metadataScans packages/ui/src/components/ui/*.tsx, extracts displayNames, categorizes components, and writes packages/ui/COMPONENT-METADATA.json.
Metadata sync validation
Checks that COMPONENT-METADATA.json matches the actual component files on disk.
npm run validate-metadataDetects:
- Files referenced in metadata that don't exist
- Component files on disk not tracked in metadata
- Duplicate component names
- Missing required fields
- Incorrect
totalComponentscount
MCP Inspector (interactive)
The official MCP debugging tool — sends requests interactively and inspects responses.
npx @modelcontextprotocol/inspector node dist/index.jsVS Code quick test
- Restart the MCP server:
Ctrl+Shift+P→ "MCP: List Servers" → restart - Open Copilot chat in Agent mode
- Ask: "Using the molano-ds MCP, list all available components"
- Verify it returns the component catalog
Keeping in sync
The MCP server reads data at runtime from the monorepo, not at build time:
- Tokens: loaded from
packages/tokens/*.jsonon every startup - Components: loaded from
packages/ui/COMPONENT-METADATA.jsonon startup - Knowledge: loaded from
packages/ui/docs/*.mdon startup (YAML frontmatter + code examples) - Version: loaded from
packages/ui/package.jsonon startup
This means tokens, knowledge, and version are always current — just restart the server after changes.
COMPONENT-METADATA.json is auto-generated via npm run generate-metadata. Run npm run validate-metadata to detect drift.
Recommended workflow
- Add a new component to
packages/ui/src/components/ui/ - Regenerate metadata:
npm run generate-metadata(frompackages/mcp-server/) - Add knowledge doc: create
packages/ui/docs/ComponentName.mdwith YAML frontmatter - Validate sync:
npm run validate-metadata - Audit coverage: use the
audit_coverageMCP tool or run smoke test - Restart the MCP server to pick up changes
