eha-design-system-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.mcp.json +10 -0
  2. package/EHA_RULES.md +34 -0
  3. package/README.md +42 -0
  4. package/dist/chunk-43DPD5CQ.js +173 -0
  5. package/dist/chunk-43DPD5CQ.js.map +1 -0
  6. package/dist/chunk-BENZVDE6.js +176 -0
  7. package/dist/chunk-BENZVDE6.js.map +1 -0
  8. package/dist/chunk-TGIKADVU.js +97 -0
  9. package/dist/chunk-TGIKADVU.js.map +1 -0
  10. package/dist/chunk-TOWRWUJK.js +172 -0
  11. package/dist/chunk-TOWRWUJK.js.map +1 -0
  12. package/dist/index.cjs +578 -0
  13. package/dist/index.cjs.map +1 -0
  14. package/dist/index.d.cts +2 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +13 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/manifests/index.cjs +197 -0
  19. package/dist/manifests/index.cjs.map +1 -0
  20. package/dist/manifests/index.d.cts +19 -0
  21. package/dist/manifests/index.d.ts +19 -0
  22. package/dist/manifests/index.js +9 -0
  23. package/dist/manifests/index.js.map +1 -0
  24. package/dist/mcp/server.cjs +600 -0
  25. package/dist/mcp/server.cjs.map +1 -0
  26. package/dist/mcp/server.d.cts +6 -0
  27. package/dist/mcp/server.d.ts +6 -0
  28. package/dist/mcp/server.js +12 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/recipes/index.cjs +198 -0
  31. package/dist/recipes/index.cjs.map +1 -0
  32. package/dist/recipes/index.d.cts +13 -0
  33. package/dist/recipes/index.d.ts +13 -0
  34. package/dist/recipes/index.js +9 -0
  35. package/dist/recipes/index.js.map +1 -0
  36. package/dist/tokens/index.cjs +129 -0
  37. package/dist/tokens/index.cjs.map +1 -0
  38. package/dist/tokens/index.d.cts +82 -0
  39. package/dist/tokens/index.d.ts +82 -0
  40. package/dist/tokens/index.js +23 -0
  41. package/dist/tokens/index.js.map +1 -0
  42. package/package.json +26 -0
  43. package/scripts/sync-manifest.ts +34 -0
  44. package/src/index.ts +6 -0
  45. package/src/manifests/components.ts +185 -0
  46. package/src/mcp/server.ts +163 -0
  47. package/src/recipes/index.ts +178 -0
  48. package/src/tokens/index.ts +75 -0
  49. package/tsconfig.json +14 -0
  50. package/tsup.config.ts +17 -0
package/.mcp.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "mcpServers": {
3
+ "eha-design-system": {
4
+ "command": "npx",
5
+ "args": [
6
+ "eha-design-system-ai"
7
+ ]
8
+ }
9
+ }
10
+ }
package/EHA_RULES.md ADDED
@@ -0,0 +1,34 @@
1
+ # EHA Design System — AI Rules
2
+
3
+ These rules apply to any AI agent using the `eha-ai` MCP server.
4
+
5
+ ## Non-Negotiable
6
+
7
+ 1. All component imports from `eha-design-system` — never from `rsuite` directly
8
+ 2. Button always uses `variant` prop — `'primary' | 'secondary' | 'tertiary' | 'ghost'`
9
+ 3. `ThemeProvider` with correct `brand` prop always wraps the app root
10
+ 4. Missing components → output comment, never build custom: `// MISSING: [Name] — not in EHA DS`
11
+ 5. Never hardcode brand colours — resolve via `brands[brandId].colorPrimitives.*`
12
+ 6. Logo assets must be static files at `/public/brands/[brand]/logo.svg` — never remote URLs
13
+
14
+ ## Brand Token Access
15
+
16
+ ```ts
17
+ import { brands } from 'eha-design-system';
18
+ const brand = brands['eha']; // or 'reach', 'lomis', 'etrac'
19
+ ```
20
+
21
+ ## Token Access
22
+
23
+ ```ts
24
+ import { designTokens } from 'eha-design-system';
25
+ // designTokens.spacing.lg, designTokens.fontSize.sm, designTokens.colors.neutral20 etc.
26
+ ```
27
+
28
+ `getColor`, `getSpacing`, `getSemanticColor` are NOT exported — do not use them.
29
+
30
+ ## Figma Workflow
31
+
32
+ 1. Map every Figma element to a component using `get_component` tool
33
+ 2. Report MISSING items before generating code
34
+ 3. Generate code using only resolved components
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # eha-ai
2
+
3
+ MCP server for the EHA Design System. Exposes components, design tokens, and code recipes to AI agents via the Model Context Protocol.
4
+
5
+ ## Tools
6
+
7
+ | Tool | Description |
8
+ |------|-------------|
9
+ | `get_component` | Get full component spec by name or Figma node name |
10
+ | `list_components` | List all available components |
11
+ | `get_token` | Look up a design token by name |
12
+ | `get_recipe` | Get a code recipe for a common UI pattern |
13
+ | `get_brand_tokens` | Get all token values for a specific brand |
14
+ | `resolve_figma` | Map a Figma node name to a DS component |
15
+
16
+ ## Setup
17
+
18
+ ```bash
19
+ npm install
20
+ npm run build
21
+ ```
22
+
23
+ Add to your Claude Code config:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "eha-ai": {
29
+ "command": "node",
30
+ "args": ["/path/to/eha-ai/dist/index.js"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ## Sync manifest from DS
37
+
38
+ ```bash
39
+ npm run sync
40
+ ```
41
+
42
+ This pulls the latest component and token definitions from `eha-design-system`.
@@ -0,0 +1,173 @@
1
+ // src/recipes/index.ts
2
+ var recipes = [
3
+ {
4
+ name: "app-shell",
5
+ description: "Full app shell with ThemeProvider, Sidenav, and PageHeader",
6
+ tags: ["layout", "sidenav", "header", "brand"],
7
+ code: `import { ThemeProvider, Sidenav, PageHeader, brands } from 'eha-design-system';
8
+ import { useState } from 'react';
9
+
10
+ const brand = brands['reach'];
11
+
12
+ export function AppShell({ children }: { children: React.ReactNode }) {
13
+ const [expanded, setExpanded] = useState(true);
14
+
15
+ return (
16
+ <ThemeProvider brand="reach">
17
+ <div style={{ display: 'flex', height: '100vh' }}>
18
+ <Sidenav
19
+ expanded={expanded}
20
+ onToggle={setExpanded}
21
+ logo="/brands/reach/logo.svg"
22
+ collapsedLogo="/brands/reach/icon.svg"
23
+ style={{ backgroundColor: brand.colorPrimitives.primary50 }}
24
+ items={[]}
25
+ />
26
+ <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
27
+ <PageHeader title="Dashboard" />
28
+ <main style={{ flex: 1, padding: 24, overflow: 'auto' }}>
29
+ {children}
30
+ </main>
31
+ </div>
32
+ </div>
33
+ </ThemeProvider>
34
+ );
35
+ }`
36
+ },
37
+ {
38
+ name: "metric-grid",
39
+ description: "Responsive grid of MetricCard components",
40
+ tags: ["dashboard", "metrics", "grid"],
41
+ code: `import { MetricCard } from 'eha-design-system';
42
+ import AccountCircleOutlinedIcon from '@mui/icons-material/AccountCircleOutlined';
43
+ import TrendingUpOutlinedIcon from '@mui/icons-material/TrendingUpOutlined';
44
+
45
+ export function MetricGrid() {
46
+ return (
47
+ <div style={{
48
+ display: 'grid',
49
+ gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
50
+ gap: 20,
51
+ }}>
52
+ <MetricCard value="2,420" label="Total Users" icon={<AccountCircleOutlinedIcon />} />
53
+ <MetricCard value="12.5%" label="Growth Rate" icon={<TrendingUpOutlinedIcon />} />
54
+ <MetricCard value="98.2%" label="Uptime" />
55
+ </div>
56
+ );
57
+ }`
58
+ },
59
+ {
60
+ name: "data-table-with-pagination",
61
+ description: "Table with sorting and pagination",
62
+ tags: ["table", "data", "pagination"],
63
+ code: `import { Table, Button } from 'eha-design-system';
64
+ import { useState } from 'react';
65
+
66
+ export function DataTable({ data }: { data: Record<string, unknown>[] }) {
67
+ const [sortColumn, setSortColumn] = useState('');
68
+ const [sortType, setSortType] = useState<'asc' | 'desc'>('asc');
69
+
70
+ return (
71
+ <Table
72
+ autoHeight
73
+ data={data}
74
+ sortColumn={sortColumn}
75
+ sortType={sortType}
76
+ onSortColumn={(col, type) => { setSortColumn(col); setSortType(type); }}
77
+ >
78
+ <Table.Column flexGrow={1} sortable>
79
+ <Table.HeaderCell>Name</Table.HeaderCell>
80
+ <Table.Cell dataKey="name" />
81
+ </Table.Column>
82
+ <Table.Column width={120} align="center">
83
+ <Table.HeaderCell>Actions</Table.HeaderCell>
84
+ <Table.Cell>
85
+ {(rowData) => (
86
+ <Button variant="ghost" size="sm">Edit</Button>
87
+ )}
88
+ </Table.Cell>
89
+ </Table.Column>
90
+ </Table>
91
+ );
92
+ }`
93
+ },
94
+ {
95
+ name: "confirm-modal",
96
+ description: "Confirmation modal with primary and ghost actions",
97
+ tags: ["modal", "dialog", "confirm"],
98
+ code: `import { Modal, Button } from 'eha-design-system';
99
+ import { useState } from 'react';
100
+
101
+ export function ConfirmModal({
102
+ open,
103
+ onClose,
104
+ onConfirm,
105
+ title,
106
+ message,
107
+ }: {
108
+ open: boolean;
109
+ onClose: () => void;
110
+ onConfirm: () => void;
111
+ title: string;
112
+ message: string;
113
+ }) {
114
+ return (
115
+ <Modal open={open} onClose={onClose} size="xs" backdrop="static">
116
+ <Modal.Header>
117
+ <Modal.Title>{title}</Modal.Title>
118
+ </Modal.Header>
119
+ <Modal.Body>{message}</Modal.Body>
120
+ <Modal.Footer>
121
+ <Button variant="primary" onClick={onConfirm}>Confirm</Button>
122
+ <Button variant="ghost" onClick={onClose}>Cancel</Button>
123
+ </Modal.Footer>
124
+ </Modal>
125
+ );
126
+ }`
127
+ },
128
+ {
129
+ name: "filter-drawer",
130
+ description: "Filter drawer with clear and apply actions",
131
+ tags: ["drawer", "filter", "form"],
132
+ code: `import { Drawer, Button, SelectPicker } from 'eha-design-system';
133
+
134
+ export function FilterDrawer({
135
+ open,
136
+ onClose,
137
+ onApply,
138
+ }: {
139
+ open: boolean;
140
+ onClose: () => void;
141
+ onApply: () => void;
142
+ }) {
143
+ return (
144
+ <Drawer open={open} onClose={onClose} size="sm">
145
+ <Drawer.Header>
146
+ <Drawer.Title>Filter</Drawer.Title>
147
+ <Drawer.Actions>
148
+ <Button variant="ghost" onClick={onClose}>Clear</Button>
149
+ <Button variant="primary" onClick={onApply}>Apply</Button>
150
+ </Drawer.Actions>
151
+ </Drawer.Header>
152
+ <Drawer.Body>
153
+ <SelectPicker
154
+ data={[{ label: 'Active', value: 'active' }, { label: 'Inactive', value: 'inactive' }]}
155
+ label="Status"
156
+ placeholder="Select status"
157
+ block
158
+ />
159
+ </Drawer.Body>
160
+ </Drawer>
161
+ );
162
+ }`
163
+ }
164
+ ];
165
+ function getRecipe(name) {
166
+ return recipes.find((r) => r.name === name || r.tags.includes(name));
167
+ }
168
+
169
+ export {
170
+ recipes,
171
+ getRecipe
172
+ };
173
+ //# sourceMappingURL=chunk-43DPD5CQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/recipes/index.ts"],"sourcesContent":["/**\n * Code recipes for common EHA Design System UI patterns.\n */\n\nexport interface Recipe {\n name: string;\n description: string;\n tags: string[];\n code: string;\n}\n\nexport const recipes: Recipe[] = [\n {\n name: 'app-shell',\n description: 'Full app shell with ThemeProvider, Sidenav, and PageHeader',\n tags: ['layout', 'sidenav', 'header', 'brand'],\n code: `import { ThemeProvider, Sidenav, PageHeader, brands } from 'eha-design-system';\nimport { useState } from 'react';\n\nconst brand = brands['reach'];\n\nexport function AppShell({ children }: { children: React.ReactNode }) {\n const [expanded, setExpanded] = useState(true);\n\n return (\n <ThemeProvider brand=\"reach\">\n <div style={{ display: 'flex', height: '100vh' }}>\n <Sidenav\n expanded={expanded}\n onToggle={setExpanded}\n logo=\"/brands/reach/logo.svg\"\n collapsedLogo=\"/brands/reach/icon.svg\"\n style={{ backgroundColor: brand.colorPrimitives.primary50 }}\n items={[]}\n />\n <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>\n <PageHeader title=\"Dashboard\" />\n <main style={{ flex: 1, padding: 24, overflow: 'auto' }}>\n {children}\n </main>\n </div>\n </div>\n </ThemeProvider>\n );\n}`,\n },\n {\n name: 'metric-grid',\n description: 'Responsive grid of MetricCard components',\n tags: ['dashboard', 'metrics', 'grid'],\n code: `import { MetricCard } from 'eha-design-system';\nimport AccountCircleOutlinedIcon from '@mui/icons-material/AccountCircleOutlined';\nimport TrendingUpOutlinedIcon from '@mui/icons-material/TrendingUpOutlined';\n\nexport function MetricGrid() {\n return (\n <div style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',\n gap: 20,\n }}>\n <MetricCard value=\"2,420\" label=\"Total Users\" icon={<AccountCircleOutlinedIcon />} />\n <MetricCard value=\"12.5%\" label=\"Growth Rate\" icon={<TrendingUpOutlinedIcon />} />\n <MetricCard value=\"98.2%\" label=\"Uptime\" />\n </div>\n );\n}`,\n },\n {\n name: 'data-table-with-pagination',\n description: 'Table with sorting and pagination',\n tags: ['table', 'data', 'pagination'],\n code: `import { Table, Button } from 'eha-design-system';\nimport { useState } from 'react';\n\nexport function DataTable({ data }: { data: Record<string, unknown>[] }) {\n const [sortColumn, setSortColumn] = useState('');\n const [sortType, setSortType] = useState<'asc' | 'desc'>('asc');\n\n return (\n <Table\n autoHeight\n data={data}\n sortColumn={sortColumn}\n sortType={sortType}\n onSortColumn={(col, type) => { setSortColumn(col); setSortType(type); }}\n >\n <Table.Column flexGrow={1} sortable>\n <Table.HeaderCell>Name</Table.HeaderCell>\n <Table.Cell dataKey=\"name\" />\n </Table.Column>\n <Table.Column width={120} align=\"center\">\n <Table.HeaderCell>Actions</Table.HeaderCell>\n <Table.Cell>\n {(rowData) => (\n <Button variant=\"ghost\" size=\"sm\">Edit</Button>\n )}\n </Table.Cell>\n </Table.Column>\n </Table>\n );\n}`,\n },\n {\n name: 'confirm-modal',\n description: 'Confirmation modal with primary and ghost actions',\n tags: ['modal', 'dialog', 'confirm'],\n code: `import { Modal, Button } from 'eha-design-system';\nimport { useState } from 'react';\n\nexport function ConfirmModal({\n open,\n onClose,\n onConfirm,\n title,\n message,\n}: {\n open: boolean;\n onClose: () => void;\n onConfirm: () => void;\n title: string;\n message: string;\n}) {\n return (\n <Modal open={open} onClose={onClose} size=\"xs\" backdrop=\"static\">\n <Modal.Header>\n <Modal.Title>{title}</Modal.Title>\n </Modal.Header>\n <Modal.Body>{message}</Modal.Body>\n <Modal.Footer>\n <Button variant=\"primary\" onClick={onConfirm}>Confirm</Button>\n <Button variant=\"ghost\" onClick={onClose}>Cancel</Button>\n </Modal.Footer>\n </Modal>\n );\n}`,\n },\n {\n name: 'filter-drawer',\n description: 'Filter drawer with clear and apply actions',\n tags: ['drawer', 'filter', 'form'],\n code: `import { Drawer, Button, SelectPicker } from 'eha-design-system';\n\nexport function FilterDrawer({\n open,\n onClose,\n onApply,\n}: {\n open: boolean;\n onClose: () => void;\n onApply: () => void;\n}) {\n return (\n <Drawer open={open} onClose={onClose} size=\"sm\">\n <Drawer.Header>\n <Drawer.Title>Filter</Drawer.Title>\n <Drawer.Actions>\n <Button variant=\"ghost\" onClick={onClose}>Clear</Button>\n <Button variant=\"primary\" onClick={onApply}>Apply</Button>\n </Drawer.Actions>\n </Drawer.Header>\n <Drawer.Body>\n <SelectPicker\n data={[{ label: 'Active', value: 'active' }, { label: 'Inactive', value: 'inactive' }]}\n label=\"Status\"\n placeholder=\"Select status\"\n block\n />\n </Drawer.Body>\n </Drawer>\n );\n}`,\n },\n];\n\nexport function getRecipe(name: string): Recipe | undefined {\n return recipes.find((r) => r.name === name || r.tags.includes(name));\n}\n"],"mappings":";AAWO,IAAM,UAAoB;AAAA,EAC/B;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,UAAU,WAAW,UAAU,OAAO;AAAA,IAC7C,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BR;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,aAAa,WAAW,MAAM;AAAA,IACrC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBR;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,SAAS,QAAQ,YAAY;AAAA,IACpC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BR;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,SAAS,UAAU,SAAS;AAAA,IACnC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BR;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,MAAM,CAAC,UAAU,UAAU,MAAM;AAAA,IACjC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BR;AACF;AAEO,SAAS,UAAU,MAAkC;AAC1D,SAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,KAAK,SAAS,IAAI,CAAC;AACrE;","names":[]}
@@ -0,0 +1,176 @@
1
+ import {
2
+ components,
3
+ resolveComponent
4
+ } from "./chunk-TOWRWUJK.js";
5
+ import {
6
+ brandUiRoles,
7
+ cssVariables,
8
+ fontSize,
9
+ lookupToken,
10
+ spacing
11
+ } from "./chunk-TGIKADVU.js";
12
+ import {
13
+ getRecipe,
14
+ recipes
15
+ } from "./chunk-43DPD5CQ.js";
16
+
17
+ // src/mcp/server.ts
18
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
19
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
20
+ import { z } from "zod";
21
+ function createServer() {
22
+ const server = new McpServer({
23
+ name: "eha-ai",
24
+ version: "0.1.0"
25
+ });
26
+ server.tool(
27
+ "get_component",
28
+ "Get full spec for a DS component by name or Figma node name",
29
+ { query: z.string().describe("Component name or Figma node name") },
30
+ async ({ query }) => {
31
+ const component = resolveComponent(query);
32
+ if (!component) {
33
+ return {
34
+ content: [{
35
+ type: "text",
36
+ text: `No component found for "${query}". Use list_components to see all available components.`
37
+ }]
38
+ };
39
+ }
40
+ return {
41
+ content: [{ type: "text", text: JSON.stringify(component, null, 2) }]
42
+ };
43
+ }
44
+ );
45
+ server.tool(
46
+ "list_components",
47
+ "List all available EHA Design System components",
48
+ {},
49
+ async () => {
50
+ const list = components.map((c) => ({
51
+ name: c.name,
52
+ figmaNodeNames: c.figmaNodeNames,
53
+ cssClass: c.cssClass
54
+ }));
55
+ return {
56
+ content: [{ type: "text", text: JSON.stringify(list, null, 2) }]
57
+ };
58
+ }
59
+ );
60
+ server.tool(
61
+ "resolve_figma",
62
+ "Map a Figma node name to an EHA DS component",
63
+ { nodeName: z.string().describe("Figma node name from the design") },
64
+ async ({ nodeName }) => {
65
+ const component = resolveComponent(nodeName);
66
+ if (!component) {
67
+ return {
68
+ content: [{
69
+ type: "text",
70
+ text: `MISSING: "${nodeName}" \u2014 no match in EHA Design System. Do not build a custom implementation without user direction.`
71
+ }]
72
+ };
73
+ }
74
+ return {
75
+ content: [{
76
+ type: "text",
77
+ text: `"${nodeName}" \u2192 ${component.name}
78
+ Import: ${component.import}`
79
+ }]
80
+ };
81
+ }
82
+ );
83
+ server.tool(
84
+ "get_token",
85
+ "Look up a design token value by name (spacing, fontSize, borderRadius, shadows)",
86
+ { name: z.string().describe('Token name e.g. "lg", "sm", "md"') },
87
+ async ({ name }) => {
88
+ const value = lookupToken(name);
89
+ if (!value) {
90
+ return {
91
+ content: [{
92
+ type: "text",
93
+ text: `Token "${name}" not found. Available spacing: ${Object.keys(spacing).join(", ")}. Available fontSizes: ${Object.keys(fontSize).join(", ")}.`
94
+ }]
95
+ };
96
+ }
97
+ return {
98
+ content: [{ type: "text", text: `${name}: ${value}` }]
99
+ };
100
+ }
101
+ );
102
+ server.tool(
103
+ "get_brand_tokens",
104
+ "Get brand token access pattern and UI role mappings for a specific brand",
105
+ { brand: z.enum(["eha", "reach", "lomis", "etrac"]).describe("Brand identifier") },
106
+ async ({ brand }) => {
107
+ const result = {
108
+ brand,
109
+ access: `import { brands } from 'eha-design-system';
110
+ const brand = brands['${brand}'];`,
111
+ uiRoleMapping: brandUiRoles,
112
+ note: "Resolve brand once at app entry and re-export. Never hardcode hex values.",
113
+ logoAssets: {
114
+ logo: `/brands/${brand}/logo.svg`,
115
+ icon: `/brands/${brand}/icon.svg`
116
+ }
117
+ };
118
+ return {
119
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
120
+ };
121
+ }
122
+ );
123
+ server.tool(
124
+ "get_recipe",
125
+ "Get a ready-to-use code recipe for a common UI pattern",
126
+ { name: z.string().describe('Recipe name or tag e.g. "app-shell", "dashboard", "filter"') },
127
+ async ({ name }) => {
128
+ const recipe = getRecipe(name);
129
+ if (!recipe) {
130
+ const available = recipes.map((r) => `${r.name} (${r.tags.join(", ")})`).join("\n");
131
+ return {
132
+ content: [{
133
+ type: "text",
134
+ text: `Recipe "${name}" not found.
135
+
136
+ Available recipes:
137
+ ${available}`
138
+ }]
139
+ };
140
+ }
141
+ return {
142
+ content: [{
143
+ type: "text",
144
+ text: `# ${recipe.name}
145
+ ${recipe.description}
146
+
147
+ \`\`\`tsx
148
+ ${recipe.code}
149
+ \`\`\``
150
+ }]
151
+ };
152
+ }
153
+ );
154
+ server.tool(
155
+ "list_css_variables",
156
+ "List all --ds-* CSS custom properties exposed by ThemeProvider",
157
+ {},
158
+ async () => {
159
+ return {
160
+ content: [{ type: "text", text: JSON.stringify(cssVariables, null, 2) }]
161
+ };
162
+ }
163
+ );
164
+ return server;
165
+ }
166
+ async function startServer() {
167
+ const server = createServer();
168
+ const transport = new StdioServerTransport();
169
+ await server.connect(transport);
170
+ }
171
+
172
+ export {
173
+ createServer,
174
+ startServer
175
+ };
176
+ //# sourceMappingURL=chunk-BENZVDE6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/server.ts"],"sourcesContent":["import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {z} from 'zod';\nimport {components, resolveComponent} from '../manifests/components.js';\nimport {getRecipe, recipes} from '../recipes/index.js';\nimport {availableBrands, brandUiRoles, cssVariables, lookupToken, spacing, fontSize} from '../tokens/index.js';\n\nexport function createServer() {\n const server = new McpServer({\n name: 'eha-ai',\n version: '0.1.0',\n });\n\n // ── get_component ──────────────────────────────────────────────────────────\n server.tool(\n 'get_component',\n 'Get full spec for a DS component by name or Figma node name',\n {query: z.string().describe('Component name or Figma node name')},\n async ({query}) => {\n const component = resolveComponent(query);\n if (!component) {\n return {\n content: [{\n type: 'text',\n text: `No component found for \"${query}\". Use list_components to see all available components.`,\n }],\n };\n }\n return {\n content: [{type: 'text', text: JSON.stringify(component, null, 2)}],\n };\n },\n );\n\n // ── list_components ────────────────────────────────────────────────────────\n server.tool(\n 'list_components',\n 'List all available EHA Design System components',\n {},\n async () => {\n const list = components.map((c) => ({\n name: c.name,\n figmaNodeNames: c.figmaNodeNames,\n cssClass: c.cssClass,\n }));\n return {\n content: [{type: 'text', text: JSON.stringify(list, null, 2)}],\n };\n },\n );\n\n // ── resolve_figma ──────────────────────────────────────────────────────────\n server.tool(\n 'resolve_figma',\n 'Map a Figma node name to an EHA DS component',\n {nodeName: z.string().describe('Figma node name from the design')},\n async ({nodeName}) => {\n const component = resolveComponent(nodeName);\n if (!component) {\n return {\n content: [{\n type: 'text',\n text: `MISSING: \"${nodeName}\" — no match in EHA Design System. Do not build a custom implementation without user direction.`,\n }],\n };\n }\n return {\n content: [{\n type: 'text',\n text: `\"${nodeName}\" → ${component.name}\\nImport: ${component.import}`,\n }],\n };\n },\n );\n\n // ── get_token ──────────────────────────────────────────────────────────────\n server.tool(\n 'get_token',\n 'Look up a design token value by name (spacing, fontSize, borderRadius, shadows)',\n {name: z.string().describe('Token name e.g. \"lg\", \"sm\", \"md\"')},\n async ({name}) => {\n const value = lookupToken(name);\n if (!value) {\n return {\n content: [{\n type: 'text',\n text: `Token \"${name}\" not found. Available spacing: ${Object.keys(spacing).join(', ')}. Available fontSizes: ${Object.keys(fontSize).join(', ')}.`,\n }],\n };\n }\n return {\n content: [{type: 'text', text: `${name}: ${value}`}],\n };\n },\n );\n\n // ── get_brand_tokens ───────────────────────────────────────────────────────\n server.tool(\n 'get_brand_tokens',\n 'Get brand token access pattern and UI role mappings for a specific brand',\n {brand: z.enum(['eha', 'reach', 'lomis', 'etrac']).describe('Brand identifier')},\n async ({brand}) => {\n const result = {\n brand,\n access: `import { brands } from 'eha-design-system';\\nconst brand = brands['${brand}'];`,\n uiRoleMapping: brandUiRoles,\n note: 'Resolve brand once at app entry and re-export. Never hardcode hex values.',\n logoAssets: {\n logo: `/brands/${brand}/logo.svg`,\n icon: `/brands/${brand}/icon.svg`,\n },\n };\n return {\n content: [{type: 'text', text: JSON.stringify(result, null, 2)}],\n };\n },\n );\n\n // ── get_recipe ─────────────────────────────────────────────────────────────\n server.tool(\n 'get_recipe',\n 'Get a ready-to-use code recipe for a common UI pattern',\n {name: z.string().describe('Recipe name or tag e.g. \"app-shell\", \"dashboard\", \"filter\"')},\n async ({name}) => {\n const recipe = getRecipe(name);\n if (!recipe) {\n const available = recipes.map((r) => `${r.name} (${r.tags.join(', ')})`).join('\\n');\n return {\n content: [{\n type: 'text',\n text: `Recipe \"${name}\" not found.\\n\\nAvailable recipes:\\n${available}`,\n }],\n };\n }\n return {\n content: [{\n type: 'text',\n text: `# ${recipe.name}\\n${recipe.description}\\n\\n\\`\\`\\`tsx\\n${recipe.code}\\n\\`\\`\\``,\n }],\n };\n },\n );\n\n // ── list_css_variables ─────────────────────────────────────────────────────\n server.tool(\n 'list_css_variables',\n 'List all --ds-* CSS custom properties exposed by ThemeProvider',\n {},\n async () => {\n return {\n content: [{type: 'text', text: JSON.stringify(cssVariables, null, 2)}],\n };\n },\n );\n\n return server;\n}\n\nexport async function startServer() {\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,97 @@
1
+ // src/tokens/index.ts
2
+ var spacing = {
3
+ xs: "4px",
4
+ sm: "8px",
5
+ md: "12px",
6
+ lg: "16px",
7
+ xl: "20px",
8
+ "2xl": "24px",
9
+ "3xl": "32px",
10
+ "4xl": "40px",
11
+ "5xl": "48px",
12
+ "6xl": "64px"
13
+ };
14
+ var fontSize = {
15
+ xxs: "10px",
16
+ xs: "12px",
17
+ sm: "14px",
18
+ md: "16px",
19
+ lg: "18px",
20
+ xl: "20px",
21
+ "2xl": "24px",
22
+ "3xl": "32px",
23
+ "4xl": "36px",
24
+ "5xl": "48px"
25
+ };
26
+ var fontWeight = {
27
+ regular: 400,
28
+ medium: 500,
29
+ semibold: 600,
30
+ bold: 700
31
+ };
32
+ var borderRadius = {
33
+ none: "0",
34
+ xs: "2px",
35
+ sm: "4px",
36
+ md: "6px",
37
+ lg: "8px",
38
+ xl: "12px",
39
+ "2xl": "16px",
40
+ full: "9999px"
41
+ };
42
+ var shadows = {
43
+ none: "none",
44
+ sm: "0 1px 2px 0 rgba(0,0,0,0.05)",
45
+ md: "0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06)",
46
+ lg: "0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)"
47
+ };
48
+ var cssVariables = {
49
+ "color-primary-10": "--ds-color-primary-10",
50
+ "color-primary-20": "--ds-color-primary-20",
51
+ "color-primary-30": "--ds-color-primary-30",
52
+ "color-primary-40": "--ds-color-primary-40",
53
+ "color-primary-50": "--ds-color-primary-50",
54
+ "color-primary-60": "--ds-color-primary-60",
55
+ "text-primary": "--ds-text-primary",
56
+ "text-secondary": "--ds-text-secondary",
57
+ "bg-primary": "--ds-bg-primary",
58
+ "bg-secondary": "--ds-bg-secondary",
59
+ "border-default": "--ds-border-default",
60
+ "interactive-primary": "--ds-interactive-primary",
61
+ "feedback-error": "--ds-feedback-error",
62
+ "feedback-success": "--ds-feedback-success",
63
+ "feedback-warning": "--ds-feedback-warning"
64
+ };
65
+ var brandUiRoles = {
66
+ sidenavBackground: "colorPrimitives.primary50",
67
+ sidenavActiveAndHover: "colorPrimitives.primary60",
68
+ sidenavSubMenuBackground: "colorPrimitives.primary60",
69
+ metricCardIconCircleBackground: "colorPrimitives.primary10",
70
+ metricCardIconColor: "colorPrimitives.primary50",
71
+ loginTemplatePanelBackground: "colorPrimitives.primary50",
72
+ buttonPrimary: "semanticColors.interactive.primary",
73
+ buttonPrimaryHover: "semanticColors.interactive.primaryHover"
74
+ };
75
+ var availableBrands = ["eha", "reach", "lomis", "etrac"];
76
+ function lookupToken(name) {
77
+ const all = {
78
+ ...spacing,
79
+ ...fontSize,
80
+ ...borderRadius,
81
+ ...shadows
82
+ };
83
+ return all[name];
84
+ }
85
+
86
+ export {
87
+ spacing,
88
+ fontSize,
89
+ fontWeight,
90
+ borderRadius,
91
+ shadows,
92
+ cssVariables,
93
+ brandUiRoles,
94
+ availableBrands,
95
+ lookupToken
96
+ };
97
+ //# sourceMappingURL=chunk-TGIKADVU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens/index.ts"],"sourcesContent":["/**\n * Design token definitions for the EHA Design System.\n * Sourced from eha-design-system@0.2.2.\n */\n\nexport const spacing = {\n xs: '4px', sm: '8px', md: '12px', lg: '16px',\n xl: '20px', '2xl': '24px', '3xl': '32px', '4xl': '40px',\n '5xl': '48px', '6xl': '64px',\n} as const;\n\nexport const fontSize = {\n xxs: '10px', xs: '12px', sm: '14px', md: '16px',\n lg: '18px', xl: '20px', '2xl': '24px', '3xl': '32px',\n '4xl': '36px', '5xl': '48px',\n} as const;\n\nexport const fontWeight = {\n regular: 400, medium: 500, semibold: 600, bold: 700,\n} as const;\n\nexport const borderRadius = {\n none: '0', xs: '2px', sm: '4px', md: '6px',\n lg: '8px', xl: '12px', '2xl': '16px', full: '9999px',\n} as const;\n\nexport const shadows = {\n none: 'none',\n sm: '0 1px 2px 0 rgba(0,0,0,0.05)',\n md: '0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06)',\n lg: '0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)',\n} as const;\n\nexport const cssVariables = {\n 'color-primary-10': '--ds-color-primary-10',\n 'color-primary-20': '--ds-color-primary-20',\n 'color-primary-30': '--ds-color-primary-30',\n 'color-primary-40': '--ds-color-primary-40',\n 'color-primary-50': '--ds-color-primary-50',\n 'color-primary-60': '--ds-color-primary-60',\n 'text-primary': '--ds-text-primary',\n 'text-secondary': '--ds-text-secondary',\n 'bg-primary': '--ds-bg-primary',\n 'bg-secondary': '--ds-bg-secondary',\n 'border-default': '--ds-border-default',\n 'interactive-primary': '--ds-interactive-primary',\n 'feedback-error': '--ds-feedback-error',\n 'feedback-success': '--ds-feedback-success',\n 'feedback-warning': '--ds-feedback-warning',\n} as const;\n\nexport const brandUiRoles = {\n sidenavBackground: 'colorPrimitives.primary50',\n sidenavActiveAndHover: 'colorPrimitives.primary60',\n sidenavSubMenuBackground: 'colorPrimitives.primary60',\n metricCardIconCircleBackground: 'colorPrimitives.primary10',\n metricCardIconColor: 'colorPrimitives.primary50',\n loginTemplatePanelBackground: 'colorPrimitives.primary50',\n buttonPrimary: 'semanticColors.interactive.primary',\n buttonPrimaryHover: 'semanticColors.interactive.primaryHover',\n} as const;\n\nexport type BrandId = 'eha' | 'reach' | 'lomis' | 'etrac';\n\nexport const availableBrands: BrandId[] = ['eha', 'reach', 'lomis', 'etrac'];\n\nexport function lookupToken(name: string): string | undefined {\n const all: Record<string, string> = {\n ...spacing,\n ...fontSize,\n ...(borderRadius as Record<string, string>),\n ...(shadows as Record<string, string>),\n };\n return all[name];\n}\n"],"mappings":";AAKO,IAAM,UAAU;AAAA,EACrB,IAAI;AAAA,EAAO,IAAI;AAAA,EAAO,IAAI;AAAA,EAAQ,IAAI;AAAA,EACtC,IAAI;AAAA,EAAQ,OAAO;AAAA,EAAQ,OAAO;AAAA,EAAQ,OAAO;AAAA,EACjD,OAAO;AAAA,EAAQ,OAAO;AACxB;AAEO,IAAM,WAAW;AAAA,EACtB,KAAK;AAAA,EAAQ,IAAI;AAAA,EAAQ,IAAI;AAAA,EAAQ,IAAI;AAAA,EACzC,IAAI;AAAA,EAAQ,IAAI;AAAA,EAAQ,OAAO;AAAA,EAAQ,OAAO;AAAA,EAC9C,OAAO;AAAA,EAAQ,OAAO;AACxB;AAEO,IAAM,aAAa;AAAA,EACxB,SAAS;AAAA,EAAK,QAAQ;AAAA,EAAK,UAAU;AAAA,EAAK,MAAM;AAClD;AAEO,IAAM,eAAe;AAAA,EAC1B,MAAM;AAAA,EAAK,IAAI;AAAA,EAAO,IAAI;AAAA,EAAO,IAAI;AAAA,EACrC,IAAI;AAAA,EAAO,IAAI;AAAA,EAAQ,OAAO;AAAA,EAAQ,MAAM;AAC9C;AAEO,IAAM,UAAU;AAAA,EACrB,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEO,IAAM,eAAe;AAAA,EAC1B,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,oBAAoB;AACtB;AAEO,IAAM,eAAe;AAAA,EAC1B,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,0BAA0B;AAAA,EAC1B,gCAAgC;AAAA,EAChC,qBAAqB;AAAA,EACrB,8BAA8B;AAAA,EAC9B,eAAe;AAAA,EACf,oBAAoB;AACtB;AAIO,IAAM,kBAA6B,CAAC,OAAO,SAAS,SAAS,OAAO;AAEpE,SAAS,YAAY,MAAkC;AAC5D,QAAM,MAA8B;AAAA,IAClC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAI;AAAA,IACJ,GAAI;AAAA,EACN;AACA,SAAO,IAAI,IAAI;AACjB;","names":[]}