Purpose of this document: A comprehensive specification to be used as a prompt for an AI system to build the
helixirOSS package from scratch, drawing on the Helix enterprise web component platform as its source of proven, battle-tested code.
helixir is a single, self-contained MCP (Model Context Protocol) server that gives AI coding assistants (Claude, Cursor, Copilot, etc.) full situational awareness of any web component library. When connected, the AI can:
- Discover what components exist in the library
- Read full component APIs (properties, events, slots, CSS parts, CSS custom properties)
- Check component health across 17 quality dimensions
- Detect breaking API changes between branches
- Get usage examples and correct import patterns
- Query design tokens
- Get TypeScript diagnostics for component source files
Tagline: Give Claude eyes into your design system.
npm package name: helixir
MCP server name (in .mcp.json): helixir
This is Priority #2 in the Clarity House / ProtoLabs OSS strategy, with a projected 2,000–6,000 GitHub stars in 12 months.
- Category: MCP Servers (the hottest OSS category in 2025/2026 — 79K stars on the reference repo)
- Niche: Web component tooling for AI assistants — currently unoccupied
- Closest competitor:
design-systems-mcp(~100 stars) — a static knowledge base, not an analyzer - Promotion targets: awesome-mcp-servers (7,260+ servers), MCP Market leaderboard, Lit Discord, Web Components Community Group
- Dev effort: 2–3 weeks (most logic ports directly from Helix)
The Helix repository (/Volumes/Development/helix) is an enterprise healthcare web component platform built on Lit 3.x. It contains 3 production MCP servers that serve as the foundation for this OSS project. All 3 servers plus the shared utilities package should be ported/adapted.
helix/
├── apps/
│ └── mcp-servers/
│ ├── cem-analyzer/ # CEM parsing + breaking change detection
│ │ └── src/
│ │ ├── index.ts # Server entry point
│ │ ├── tools.ts # Tool registration (MCP protocol handlers)
│ │ └── handlers.ts # Business logic (parseCem, diffCem, validateCompleteness, listAllComponents)
│ ├── health-scorer/ # 17-dimension component health scoring
│ │ └── src/
│ │ ├── index.ts
│ │ ├── tools.ts # Tool registration
│ │ └── handlers.ts # scoreComponent, scoreAllComponents, getHealthTrend, getHealthDiff
│ ├── typescript-diagnostics/ # TypeScript compiler integration
│ │ └── src/
│ │ ├── index.ts
│ │ ├── tools.ts
│ │ └── handlers.ts
│ └── shared/ # CRITICAL — shared utilities used by all servers
│ └── src/
│ ├── index.ts # Exports everything below
│ ├── git.ts # GitOperations class (withBranch, stash/unstash)
│ ├── file-ops.ts # SafeFileOperations class (readJSON w/ Zod, fileExists)
│ ├── error-handling.ts # MCPError, ErrorCategory enum, handleToolError
│ ├── mcp-helpers.ts # createSuccessResponse, createErrorResponse
│ └── validation.ts # TagNameSchema, BranchNameSchema, FilePathSchema, DaysSchema
├── packages/
│ ├── hx-library/
│ │ ├── custom-elements.json # The CEM — generated by `@custom-elements-manifest/analyzer`
│ │ └── src/components/ # 14 Lit components: hx-alert, hx-badge, hx-button, hx-card,
│ │ # hx-checkbox, hx-container, hx-form, hx-prose, hx-radio-group,
│ │ # hx-select, hx-switch, hx-text-input, hx-textarea
│ └── hx-tokens/
│ ├── src/tokens.json # W3C DTCG-compliant design tokens
│ ├── src/index.ts # TypeScript token exports
│ └── sd.config.mjs # Style Dictionary config (generates CSS + Lit CSSResult)
| Helix File | Target File | Changes Required |
|---|---|---|
shared/src/git.ts |
src/shared/git.ts |
None — copy verbatim |
shared/src/file-ops.ts |
src/shared/file-ops.ts |
None — copy verbatim |
shared/src/error-handling.ts |
src/shared/error-handling.ts |
None — copy verbatim |
shared/src/mcp-helpers.ts |
src/shared/mcp-helpers.ts |
None — copy verbatim |
shared/src/validation.ts |
src/shared/validation.ts |
Remove TagNameSchema regex (was ^hx-); replace with configurable prefix check |
cem-analyzer/src/handlers.ts |
src/handlers/cem.ts |
Make CEM_PATH configurable via config object, not hardcoded |
health-scorer/src/handlers.ts |
src/handlers/health.ts |
Make HEALTH_HISTORY_DIR configurable, make project root configurable |
typescript-diagnostics/src/handlers.ts |
src/handlers/typescript.ts |
Make tsconfig path configurable |
The Helix servers have these hardcoded assumptions that must become configuration:
- Tag name prefix
hx-— Helix'sTagNameSchemaenforces^hx-[a-z0-9-]+$. The OSS version must accept any prefix (e.g.,my-,ui-,sl-, or none at all). - CEM path
packages/hx-library/custom-elements.json— Must be configurable. - Health history dir
.claude/health-history— Must be configurable (default to.mcp-wc/health/). - Project root
resolve(process.cwd(), '../..')— Helix resolves relative to the MCP server's location inside the monorepo. OSS version should default toprocess.cwd()and be overridable. - TypeScript project
tsconfig.base.json— Must be configurable.
Unlike Helix (which has 3 separate servers), the OSS version consolidates into one server with 16 tools organized into logical groups. This is better for:
- Simpler installation (one entry in
.mcp.json) - Better MCP Market discoverability
- Shared config context across all tools
The server reads configuration from (in priority order):
- Environment variables (set in
.mcp.jsonenvblock) mcpwc.config.jsonat project root- Defaults
interface McpWcConfig {
// Required (or must be in env)
cemPath: string; // Path to custom-elements.json, relative to projectRoot
// Default: "custom-elements.json"
// Optional
projectRoot?: string; // Absolute path to project root. Default: process.cwd()
componentPrefix?: string; // Tag name prefix (e.g. "hx-", "my-", ""). Default: "" (any)
healthHistoryDir?: string; // Where to read/write health history. Default: ".mcp-wc/health"
tokensPath?: string; // Path to tokens.json. Default: null (token tools disabled)
tsconfigPath?: string; // Path to tsconfig. Default: "tsconfig.json"
}Environment variable names:
MCP_WC_CEM_PATHMCP_WC_PROJECT_ROOTMCP_WC_COMPONENT_PREFIXMCP_WC_HEALTH_HISTORY_DIRMCP_WC_TOKENS_PATHMCP_WC_TSCONFIG_PATH
src/
├── index.ts # Entry point — creates server, loads config, registers all tools
├── config.ts # Config loading: env vars → mcpwc.config.json → defaults
├── shared/
│ ├── git.ts # GitOperations (ported from Helix)
│ ├── file-ops.ts # SafeFileOperations (ported from Helix)
│ ├── error-handling.ts # MCPError, ErrorCategory (ported from Helix)
│ ├── mcp-helpers.ts # createSuccessResponse/createErrorResponse (ported from Helix)
│ └── validation.ts # Zod schemas — configurable prefix, not hardcoded (adapted from Helix)
├── handlers/
│ ├── cem.ts # CEM parsing and validation logic (adapted from Helix cem-analyzer)
│ ├── health.ts # Health scoring logic (adapted from Helix health-scorer)
│ ├── typescript.ts # TypeScript diagnostics (adapted from Helix typescript-diagnostics)
│ ├── tokens.ts # NEW — design token querying (not in Helix)
│ └── suggest.ts # NEW — usage suggestions, semantic component search (not in Helix)
└── tools/
├── discovery.ts # Tool registrations: list_components, find_component, get_library_summary
├── component.ts # Tool registrations: get_component, validate_cem, suggest_usage, generate_import
├── health.ts # Tool registrations: score_component, score_all_components, get_health_trend, get_health_diff
├── safety.ts # Tool registrations: diff_cem, check_breaking_changes
├── tokens.ts # Tool registrations: get_design_tokens, find_token
└── typescript.ts # Tool registrations: get_file_diagnostics, get_project_diagnostics
List all components registered in the CEM.
// Input
{
}
// Output (text)
// "Found 14 components: hx-alert, hx-badge, hx-button, ..."Ported from: listAllComponents() in Helix cem-analyzer/handlers.ts
Semantic search — describe what you need, get the right component.
// Input
{
query: string; // e.g., "a dropdown selector", "toggle switch", "text field with label"
}
// Output (text)
// Matches components by tag name, description, and member names.
// Returns ranked list: "Best match: hx-select — A dropdown selector component..."New — not in Helix. Uses fuzzy text matching across CEM descriptions and member names. No external ML dependency — pure string scoring.
High-level overview of the component library.
// Input
{
}
// Output (text)
// "Library summary:
// - 14 components
// - Average health score: 82/100 (Grade B)
// - 3 components at Grade A, 8 at Grade B, 3 at Grade C
// - 0 components with breaking changes vs main
// - Last health check: 2026-02-27T14:30:00Z"New — not in Helix. Aggregates across CEM + health history.
Full CEM metadata for a component.
// Input
{
tagName: string // e.g., "hx-button" or "my-button"
}
// Output (JSON stringified)
{
tagName: string,
name: string,
description: string,
members: Array<{ name, kind, type, description }>,
events: Array<{ name, type, description }>,
slots: Array<{ name, description }>,
cssProperties: Array<{ name, description }>,
cssParts: Array<{ name, description }>
}Ported from: parseCem() in Helix cem-analyzer/handlers.ts
Check whether a component's CEM entry is complete — all properties documented, descriptions present.
// Input
{
tagName: string;
}
// Output (text)
// "✅ CEM is valid for hx-button. Score: 100%"
// OR
// "❌ CEM validation issues: 2 properties missing descriptions: size, loading"Ported from: validateCompleteness() in Helix cem-analyzer/handlers.ts
Generate a practical usage example for a component based on its CEM.
// Input
{
tagName: string,
useCase?: string // optional context: "form submit button", "danger action", etc.
}
// Output (text)
// Returns an HTML snippet showing the component with its key attributes populated,
// plus a brief explanation of required vs optional attributes.
// Example:
// "<!-- hx-button: Primary action button -->
// <hx-button variant="primary" size="md" @click="${handleClick}">
// Submit
// </hx-button>
//
// Required: none
// Key attributes: variant (primary|secondary|ghost), size (sm|md|lg), disabled"New — not in Helix. Synthesizes from CEM member list. No LLM dependency — pure CEM analysis.
Generate the correct import statement for a component.
// Input
{
tagName: string,
packageName?: string // npm package name if known. Falls back to inferring from CEM path.
}
// Output (text)
// "import '@scope/library/components/hx-button';"
// OR (if side-effect import pattern)
// "import '@scope/library';"New — not in Helix. Reads from CEM exports field and package.json.
Get comprehensive health score for a component across 17 dimensions.
// Input
{
tagName: string
}
// Output (JSON stringified)
{
tagName: string,
score: number, // 0–100
grade: "A"|"B"|"C"|"D"|"F",
dimensions: Record<string, number>, // 17 named dimensions, each 0–100
issues: string[],
timestamp: string
}Ported from: scoreComponent() in Helix health-scorer/handlers.ts
Note on the 17 dimensions: The health scorer in Helix reads pre-computed scores from .claude/health-history/*.json files. The OSS version should read from the configurable healthHistoryDir. If no history exists, it should fall back to computing a subset of dimensions directly from the CEM (documentation coverage, event typing, slot documentation, CSS parts documentation). The full 17-dimension score requires the separate health-scorer build step.
Score every component in the library at once.
// Input
{}
// Output (JSON stringified)
ComponentHealth[] // same shape as score_component outputPorted from: scoreAllComponents() in Helix health-scorer/handlers.ts
Historical health trend for a component over N days.
// Input
{
tagName: string,
days?: number // default 7, max 365
}
// Output (JSON stringified)
{
tagName: string,
days: number,
dataPoints: Array<{ date: string, score: number, grade: string }>,
trend: "improving"|"declining"|"stable",
changePercent: number
}Ported from: getHealthTrend() in Helix health-scorer/handlers.ts
Compare component health between current branch and a base branch.
// Input
{
tagName: string,
baseBranch?: string // default "main"
}
// Output (text + JSON)
// "✅ HEALTH IMPROVED: 87 (↑ from 82)\n{...full diff JSON}"Ported from: getHealthDiff() in Helix health-scorer/handlers.ts
Compare a component's CEM between current branch and base branch. Flags breaking changes.
// Input
{
tagName: string,
baseBranch?: string // default "main"
}
// Output (text)
// "⚠️ BREAKING CHANGES DETECTED:\n{ breaking: ["Property removed: size"] }"
// OR "✅ No breaking changes. 2 additions, 0 removals"Ported from: diffCem() in Helix cem-analyzer/handlers.ts
Check ALL components in the library for breaking API changes vs the base branch.
// Input
{
baseBranch?: string // default "main"
}
// Output (text)
// "Breaking change summary vs main:
// ⚠️ hx-button: 1 breaking change (Property removed: size)
// ✅ hx-card: No breaking changes
// ✅ hx-select: No breaking changes (2 additions)"New — not in Helix. Runs diffCem across all components and aggregates.
List all design tokens, optionally filtered by category.
// Input
{
category?: string // e.g., "color", "spacing", "typography", "border-radius"
}
// Output (JSON stringified)
// Array of { name, value, category, description }New — not in Helix as MCP tool (Helix has a token package but no MCP tool for it). Reads from tokens.json (W3C DTCG format).
Find design tokens by value or name pattern.
// Input
{
query: string; // e.g., "primary", "blue", "16px", "border"
}
// Output (JSON stringified)
// Matching tokens with their valuesNew — not in Helix as MCP tool.
Get TypeScript compiler errors for a specific file.
// Input
{
filePath: string; // relative to projectRoot
}
// Output (JSON stringified)
// Array of { file, line, column, message, severity }Ported from: get-diagnostics in Helix typescript-diagnostics
Get all TypeScript errors across the project.
// Input
{
}
// Output (JSON stringified)
// { errorCount, warningCount, errors: [...], warnings: [...] }Ported from: get-project-diagnostics in Helix typescript-diagnostics
helixir/
├── src/
│ ├── index.ts # Server entry point
│ ├── config.ts # Config loading
│ ├── shared/
│ │ ├── git.ts
│ │ ├── file-ops.ts
│ │ ├── error-handling.ts
│ │ ├── mcp-helpers.ts
│ │ └── validation.ts
│ ├── handlers/
│ │ ├── cem.ts
│ │ ├── health.ts
│ │ ├── typescript.ts
│ │ ├── tokens.ts
│ │ └── suggest.ts
│ └── tools/
│ ├── discovery.ts
│ ├── component.ts
│ ├── health.ts
│ ├── safety.ts
│ ├── tokens.ts
│ └── typescript.ts
├── tests/
│ ├── __fixtures__/
│ │ ├── custom-elements.json # Sample CEM for testing (use hx-button, hx-card subset)
│ │ └── tokens.json # Sample tokens.json
│ ├── handlers/
│ │ ├── cem.test.ts
│ │ ├── health.test.ts
│ │ ├── tokens.test.ts
│ │ └── suggest.test.ts
│ └── tools/
│ ├── discovery.test.ts
│ └── component.test.ts
├── package.json
├── tsconfig.json
├── vitest.config.ts
├── README.md
├── CHANGELOG.md
├── .mcp.json.example # Example configuration
└── mcpwc.config.json.example # Example project config
| Concern | Choice | Rationale |
|---|---|---|
| Language | TypeScript 5.7 | Matches Helix; strict mode |
| Runtime | Node.js ≥20 | LTS; ESM native |
| MCP SDK | @modelcontextprotocol/sdk ^1.26.0 |
Same version as Helix |
| Validation | zod ^3.22.0 |
Same version as Helix; type-safe input parsing |
| Build | tsc only |
Simple; no bundler needed for a CLI server |
| Testing | vitest ^3 |
Same as Helix; fast, ESM-native |
| Module format | "type": "module" (ESM) |
Same as Helix |
| Publish | npm |
Broad reach; npx install story |
No additional runtime dependencies. Keep the dependency surface minimal — this is a developer tool that will be installed everywhere.
{
"name": "helixir",
"version": "0.1.0",
"description": "MCP server that gives AI agents full situational awareness of any web component library",
"type": "module",
"bin": {
"helixir": "./build/index.js"
},
"main": "./build/index.js",
"files": ["build", "README.md", "CHANGELOG.md"],
"scripts": {
"build": "tsc && chmod 755 build/index.js",
"dev": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest",
"type-check": "tsc --noEmit",
"prepublishOnly": "npm run build && npm test"
},
"keywords": [
"mcp",
"model-context-protocol",
"web-components",
"custom-elements",
"lit",
"design-system",
"claude",
"ai",
"developer-tools"
],
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.26.0",
"zod": "^3.22.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@vitest/coverage-v8": "^3.0.0",
"typescript": "^5.7.2",
"vitest": "^3.0.0"
},
"engines": {
"node": ">=20.0.0"
}
}{
"mcpServers": {
"helixir": {
"command": "npx",
"args": ["helixir"],
"env": {
"MCP_WC_CEM_PATH": "packages/my-library/custom-elements.json",
"MCP_WC_COMPONENT_PREFIX": "my-",
"MCP_WC_TOKENS_PATH": "packages/my-tokens/tokens.json"
}
}
}
}{
"cemPath": "packages/my-library/custom-elements.json",
"componentPrefix": "my-",
"healthHistoryDir": ".mcp-wc/health",
"tokensPath": "packages/my-tokens/tokens.json",
"tsconfigPath": "tsconfig.json"
}#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { loadConfig } from './config.js';
import { registerDiscoveryTools } from './tools/discovery.js';
import { registerComponentTools } from './tools/component.js';
import { registerHealthTools } from './tools/health.js';
import { registerSafetyTools } from './tools/safety.js';
import { registerTokenTools } from './tools/tokens.js';
import { registerTypescriptTools } from './tools/typescript.js';
async function main() {
const config = loadConfig();
const server = new Server({ name: 'helixir', version: '0.1.0' }, { capabilities: { tools: {} } });
registerDiscoveryTools(server, config);
registerComponentTools(server, config);
registerHealthTools(server, config);
registerSafetyTools(server, config);
if (config.tokensPath) {
registerTokenTools(server, config);
}
registerTypescriptTools(server, config);
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((err) => {
process.stderr.write(`Fatal: ${err.message}\n`);
process.exit(1);
});Each tools/*.ts file receives the Server instance and config object. It calls server.setRequestHandler twice — once for ListToolsRequestSchema (returning tool definitions) and once for CallToolRequestSchema (executing the tool). See Helix apps/mcp-servers/cem-analyzer/src/tools.ts for the exact pattern. This pattern should be preserved exactly.
Important: Multiple tool files all call server.setRequestHandler(ListToolsRequestSchema, ...) — in the Helix pattern, only one handler can be registered per request type. The OSS version must use a single consolidated ListToolsRequestSchema handler that returns ALL tools from ALL groups, and a single CallToolRequestSchema handler that dispatches to the correct group handler. This is a key refactor vs the Helix approach of separate servers.
The Helix health scorer depends on pre-computed JSON files in .claude/health-history/. For the OSS version, when no history directory exists, score_component and score_all_components should fall back to a CEM-derived score (documentation dimensions only):
- Description present: +15 points
- All properties have descriptions: +25 points
- All events have type annotations: +20 points
- All events have descriptions: +15 points
- CSS parts documented: +15 points
- Slots documented: +10 points
This gives a useful "documentation health" score even without the full 17-dimension analysis.
The find_component tool uses simple token-overlap scoring:
- Tokenize the query (split on spaces, lowercase)
- For each component, score = (matching tokens in tagName × 3) + (matching tokens in description × 2) + (matching tokens in member names × 1)
- Return top 3 by score with scores above 0
check_breaking_changes must handle the case where a component doesn't exist in the base branch (it's new). That's not a breaking change — it should be flagged as "NEW" not "BREAKING".
The SafeFileOperations.readJSON method and FilePathSchema from Helix already block path traversal (..) and absolute paths. Port these exactly. Do not accept any file path input without going through FilePathSchema validation.
Use the Helix cem-analyzer/__fixtures__/ and health-scorer/__fixtures__/ directories as a starting point. The fixtures contain sample CEM JSON and health history JSON that drive all tests without needing a real component library present.
handlers/cem.ts: 100% (this is the core, critical path)handlers/health.ts: 90%+ (health trend calculation edge cases)handlers/suggest.ts: 80%+ (new logic, needs fuzzy match tests)handlers/tokens.ts: 80%+tools/*.ts: Integration tests for each tool, not just unit tests
Use @vitest/coverage-v8. Set coverage thresholds: statements 80%, branches 75%, functions 90%, lines 80%.
The README is a key marketing asset. Structure:
- Hero section — Tagline, 1-sentence description, badge row (npm version
helixir, license, MCP compatible) - Why helixir? — 3 bullet points on the value prop
- Quick Start — 3 commands to get running:
npx helixir, configure.mcp.json, reload Claude - Tools Reference — Table with all 16 tools, 1-line description each
- Configuration — Full config reference table
- Framework support — Lit ✅, FAST ✅, Stencil ✅, any CEM-generating library ✅
- Contributing — How to add new tools, PR guidelines
- License — MIT
Build in this sequence to have something testable at each step:
- Scaffold —
package.json,tsconfig.json,vitest.config.ts, entry point stub - Port shared utilities —
src/shared/(git, file-ops, error-handling, mcp-helpers, validation) - Config system —
src/config.tswith env var + file loading - CEM handler —
src/handlers/cem.ts+ tests (this unlocks 6 tools) - Discovery tools —
list_components,find_component,get_library_summary - Component tools —
get_component,validate_cem,suggest_usage,generate_import - Safety tools —
diff_cem,check_breaking_changes - Health handler —
src/handlers/health.ts+ tests - Health tools —
score_component,score_all_components,get_health_trend,get_health_diff - Token handler + tools —
src/handlers/tokens.ts+src/tools/tokens.ts - TypeScript handler + tools —
src/handlers/typescript.ts+src/tools/typescript.ts - Integration test pass — End-to-end with real CEM fixture
- README + CHANGELOG — Polish for OSS release
- npm publish dry run —
npm pack+ inspect tarball
- No web UI or dashboard (that's
component-vitals, priority #3) - No Storybook integration
- No CI/CD GitHub Action (potential future package)
- No A11y shadow-DOM testing (that's
lit-a11y, priority #4) - No design token pipeline (that's
tokenflow, priority #5) - No built-in LLM calls — all analysis is deterministic, no API keys required
- No persistent database — file system only
- No HTTP transport — stdio only (standard MCP pattern)
Document version: 1.0 | Created: 2026-02-27 | Source: Clarity House Press / ProtoLabs OSS Strategy