@achmadalimin/ui-kit-mcp 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Achmad Alimin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @achmadalimin/ui-kit-mcp
2
+
3
+ Make the [@achmadalimin/ui-kit](https://achmadalimin.com/design-system) design
4
+ system AI-native. This MCP server gives Claude, Cursor, and Windsurf the **real**
5
+ component markup and design tokens — so when you ask your AI to build UI "using
6
+ Achmad's design system," it uses the actual `ui-kit` components instead of
7
+ inventing generic markup.
8
+
9
+ Free and open. No license key, no account, no network calls — the component
10
+ catalog ships inside the package.
11
+
12
+ ## Tools
13
+
14
+ | Tool | What it does |
15
+ |---|---|
16
+ | `list_components` | Lists every component with a one-line description |
17
+ | `get_component` | Returns the canonical HTML, classes, and any required script for one component |
18
+ | `get_tokens` | Returns the design tokens (CSS custom properties) + theming notes |
19
+ | `get_setup` | Returns install/import instructions so the stylesheet is actually loaded |
20
+
21
+ ## Requirements
22
+
23
+ - Node.js 18+
24
+ - Claude Desktop, Cursor, or Windsurf
25
+
26
+ ## Setup
27
+
28
+ ### Claude Desktop
29
+
30
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or
31
+ `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "achmad-ui-kit": {
37
+ "command": "npx",
38
+ "args": ["@achmadalimin/ui-kit-mcp"]
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Restart Claude Desktop.
45
+
46
+ ### Cursor
47
+
48
+ Edit `~/.cursor/mcp.json`:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "achmad-ui-kit": {
54
+ "command": "npx",
55
+ "args": ["@achmadalimin/ui-kit-mcp"]
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### Windsurf
62
+
63
+ Edit `~/.codeium/windsurf/mcp_config.json` with the same `mcpServers` block.
64
+
65
+ ## Usage
66
+
67
+ Once connected, prompt your AI naturally:
68
+
69
+ > "Build a settings page using Achmad's ui-kit — a card with a couple of inputs and a save button."
70
+
71
+ The AI calls `get_setup`, `list_components`, and `get_component` to pull the real
72
+ markup, classes, and the stylesheet link.
73
+
74
+ ## The design system
75
+
76
+ `@achmadalimin/ui-kit` is plain HTML + CSS — no framework, no runtime. Install it
77
+ with `npm install @achmadalimin/ui-kit` or grab the standalone stylesheet:
78
+
79
+ ```bash
80
+ curl -O https://achmadalimin.com/assets/css/ui-kit.css
81
+ ```
82
+
83
+ Full docs and live previews: **[achmadalimin.com/design-system](https://achmadalimin.com/design-system)**
84
+
85
+ ## License
86
+
87
+ MIT © Achmad Alimin
@@ -0,0 +1,98 @@
1
+ {
2
+ "meta": {
3
+ "name": "@achmadalimin/ui-kit",
4
+ "stylesUrl": "https://achmadalimin.com/assets/css/ui-kit.css",
5
+ "npm": "npm install @achmadalimin/ui-kit",
6
+ "import": "import \"@achmadalimin/ui-kit/styles.css\";",
7
+ "docs": "https://achmadalimin.com/design-system",
8
+ "theming": "Dark mode is the default. Set data-theme=\"light\" on <html> for light mode. Every color/spacing/radius is a CSS custom property (prefixed --color-* / --space-* / --radius-*) you can override.",
9
+ "conventions": "Component classes are prefixed with ui- (e.g. .ui-btn, .ui-modal). Modifiers use -- (e.g. .ui-btn--primary)."
10
+ },
11
+ "components": [
12
+ {
13
+ "name": "accordion",
14
+ "title": "Accordion",
15
+ "description": "Expand/collapse sections for FAQs or grouped content. Requires a small script to toggle aria-expanded and the .open class.",
16
+ "classes": ["ui-accordion", "ui-accordion-item", "ui-accordion-trigger", "ui-accordion-content", "ui-accordion-chevron"],
17
+ "html": "<div class=\"ui-accordion\">\n <div class=\"ui-accordion-item\">\n <button class=\"ui-accordion-trigger\" aria-expanded=\"false\">\n Accordion title\n <svg class=\"ui-accordion-chevron\" width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 6l4 4 4-4\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n <div class=\"ui-accordion-content\">\n Accordion content goes here.\n </div>\n </div>\n</div>",
18
+ "script": "document.querySelectorAll(\".ui-accordion-trigger\").forEach(t => {\n t.addEventListener(\"click\", () => {\n const open = t.getAttribute(\"aria-expanded\") === \"true\";\n t.setAttribute(\"aria-expanded\", !open);\n t.nextElementSibling.classList.toggle(\"open\", !open);\n });\n});"
19
+ },
20
+ {
21
+ "name": "badge",
22
+ "title": "Badge",
23
+ "description": "Inline pill for status, counts, or labels.",
24
+ "classes": ["ui-badge", "ui-badge--default", "ui-badge--success", "ui-badge--warning", "ui-badge--error", "ui-badge--outline"],
25
+ "html": "<span class=\"ui-badge ui-badge--default\">Default</span>\n<span class=\"ui-badge ui-badge--success\">Success</span>\n<span class=\"ui-badge ui-badge--warning\">Warning</span>\n<span class=\"ui-badge ui-badge--error\">Error</span>\n<span class=\"ui-badge ui-badge--outline\">Outline</span>"
26
+ },
27
+ {
28
+ "name": "banner",
29
+ "title": "Banner",
30
+ "description": "Full-width inline notification. Variants: --info, --success, --warning, --error. Add a .ui-banner-close button to make it dismissible.",
31
+ "classes": ["ui-banner", "ui-banner--info", "ui-banner--success", "ui-banner--warning", "ui-banner--error", "ui-banner-body", "ui-banner-close"],
32
+ "html": "<div class=\"ui-banner ui-banner--info\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n <path d=\"M8 5v4M8 11v.5\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"ui-banner-body\">Info message here.</span>\n</div>\n\n<!-- Variants: ui-banner--info | ui-banner--success | ui-banner--warning | ui-banner--error -->"
33
+ },
34
+ {
35
+ "name": "button",
36
+ "title": "Button",
37
+ "description": "Variants: --primary, --secondary, --outline, --ghost. Sizes: --sm, --lg, --icon. Add the disabled attribute for the disabled state. The same class works on <a> or <button>.",
38
+ "classes": ["ui-btn", "ui-btn--primary", "ui-btn--secondary", "ui-btn--outline", "ui-btn--ghost", "ui-btn--sm", "ui-btn--lg", "ui-btn--icon"],
39
+ "html": "<button class=\"ui-btn ui-btn--primary\">Primary</button>\n<button class=\"ui-btn ui-btn--secondary\">Secondary</button>\n<button class=\"ui-btn ui-btn--outline\">Outline</button>\n<button class=\"ui-btn ui-btn--ghost\">Ghost</button>\n\n<!-- Sizes -->\n<button class=\"ui-btn ui-btn--primary ui-btn--sm\">Small</button>\n<button class=\"ui-btn ui-btn--primary ui-btn--lg\">Large</button>\n\n<!-- Icon-only (pass aria-label) -->\n<button class=\"ui-btn ui-btn--primary ui-btn--icon\" aria-label=\"Share\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><!-- icon --></svg>\n</button>\n\n<!-- Disabled -->\n<button class=\"ui-btn ui-btn--primary\" disabled>Primary</button>"
40
+ },
41
+ {
42
+ "name": "card",
43
+ "title": "Card",
44
+ "description": "Container with optional header (title + subtitle), body, and footer. All regions are optional — compose freely.",
45
+ "classes": ["ui-card", "ui-card-header", "ui-card-title", "ui-card-subtitle", "ui-card-body", "ui-card-footer"],
46
+ "html": "<div class=\"ui-card\">\n <div class=\"ui-card-header\">\n <p class=\"ui-card-title\">Card title</p>\n <p class=\"ui-card-subtitle\">Supporting description text</p>\n </div>\n <div class=\"ui-card-body\">\n Card body content goes here.\n </div>\n <div class=\"ui-card-footer\">\n <button class=\"ui-btn ui-btn--primary\">Save changes</button>\n <button class=\"ui-btn ui-btn--ghost\">Cancel</button>\n </div>\n</div>"
47
+ },
48
+ {
49
+ "name": "input",
50
+ "title": "Input",
51
+ "description": "Text field. Pair with .ui-label and .ui-hint. Add .ui-input--error for the error state.",
52
+ "classes": ["ui-input", "ui-input--error", "ui-label", "ui-hint", "ui-hint--error"],
53
+ "html": "<label class=\"ui-label\" for=\"name\">Name</label>\n<input class=\"ui-input\" id=\"name\" type=\"text\" placeholder=\"Placeholder text\" />\n<span class=\"ui-hint\">Helper text below the field.</span>\n\n<!-- Error state -->\n<input class=\"ui-input ui-input--error\" type=\"text\" />\n<span class=\"ui-hint ui-hint--error\">This field is required.</span>"
54
+ },
55
+ {
56
+ "name": "modal",
57
+ "title": "Modal",
58
+ "description": "Overlay dialog. Add .open to .ui-modal-overlay to show it. Closes on backdrop click or Escape (wire your own handlers, or use the inline onclick shown).",
59
+ "classes": ["ui-modal-overlay", "ui-modal-overlay.open", "ui-modal", "ui-modal-title", "ui-modal-body", "ui-modal-actions", "ui-modal-btn", "ui-modal-btn--cancel", "ui-modal-btn--confirm"],
60
+ "html": "<!-- Trigger -->\n<button class=\"ui-btn ui-btn--primary\" onclick=\"document.getElementById('myModal').classList.add('open')\">\n Open modal\n</button>\n\n<!-- Modal -->\n<div class=\"ui-modal-overlay\" id=\"myModal\">\n <div class=\"ui-modal\" role=\"dialog\" aria-modal=\"true\">\n <p class=\"ui-modal-title\">Modal title</p>\n <p class=\"ui-modal-body\">Modal content goes here.</p>\n <div class=\"ui-modal-actions\">\n <button class=\"ui-modal-btn ui-modal-btn--cancel\" onclick=\"document.getElementById('myModal').classList.remove('open')\">Cancel</button>\n <button class=\"ui-modal-btn ui-modal-btn--confirm\">Confirm</button>\n </div>\n </div>\n</div>"
61
+ },
62
+ {
63
+ "name": "tabs",
64
+ "title": "Tabs",
65
+ "description": "Horizontal tab navigation with an animated slider indicator. Requires a script to switch the active trigger/panel and move the slider.",
66
+ "classes": ["ui-tabs", "ui-tabs--sm", "ui-tabs-list", "ui-tabs-slider", "ui-tab-trigger", "ui-tab-panel"],
67
+ "html": "<div class=\"ui-tabs\">\n <div class=\"ui-tabs-list\" role=\"tablist\">\n <div class=\"ui-tabs-slider\"></div>\n <button class=\"ui-tab-trigger active\" aria-selected=\"true\" role=\"tab\">Overview</button>\n <button class=\"ui-tab-trigger\" aria-selected=\"false\" role=\"tab\">Details</button>\n <button class=\"ui-tab-trigger\" aria-selected=\"false\" role=\"tab\">Settings</button>\n </div>\n <div class=\"ui-tab-panel active\">Overview content</div>\n <div class=\"ui-tab-panel\">Details content</div>\n <div class=\"ui-tab-panel\">Settings content</div>\n</div>",
68
+ "script": "document.querySelectorAll(\".ui-tabs\").forEach(tabs => {\n const list = tabs.querySelector(\".ui-tabs-list\");\n const slider = list.querySelector(\".ui-tabs-slider\");\n const triggers = list.querySelectorAll(\".ui-tab-trigger\");\n const panels = tabs.querySelectorAll(\".ui-tab-panel\");\n function moveSlider(btn) {\n slider.style.width = btn.offsetWidth + \"px\";\n slider.style.transform = \"translateX(\" + (btn.offsetLeft - 2) + \"px)\";\n }\n slider.style.transition = \"none\";\n const initial = list.querySelector(\".ui-tab-trigger.active\");\n if (initial) requestAnimationFrame(() => { moveSlider(initial); requestAnimationFrame(() => { slider.style.transition = \"\"; }); });\n triggers.forEach((t, i) => t.addEventListener(\"click\", () => {\n triggers.forEach(x => { x.classList.remove(\"active\"); x.setAttribute(\"aria-selected\", \"false\"); });\n panels.forEach(p => p.classList.remove(\"active\"));\n t.classList.add(\"active\"); t.setAttribute(\"aria-selected\", \"true\");\n panels[i].classList.add(\"active\");\n moveSlider(t);\n }));\n});"
69
+ },
70
+ {
71
+ "name": "tooltip",
72
+ "title": "Tooltip",
73
+ "description": "Hover tooltip on a wrapped element. Default placement is top; add .ui-tooltip--bottom for bottom. CSS-only — no script needed.",
74
+ "classes": ["ui-tooltip-wrap", "ui-tooltip", "ui-tooltip--bottom", "ui-tooltip--right"],
75
+ "html": "<!-- Top (default) -->\n<div class=\"ui-tooltip-wrap\">\n <button class=\"ui-btn ui-btn--secondary\">Hover me</button>\n <div class=\"ui-tooltip\">Tooltip text</div>\n</div>\n\n<!-- Bottom -->\n<div class=\"ui-tooltip-wrap\">\n <button class=\"ui-btn ui-btn--secondary\">Hover me</button>\n <div class=\"ui-tooltip ui-tooltip--bottom\">Tooltip text</div>\n</div>"
76
+ }
77
+ ],
78
+ "tokens": {
79
+ "color": {
80
+ "--color-bg": "#0f0f0f",
81
+ "--color-surface": "#2a2a2a",
82
+ "--color-surface-hover": "#222222",
83
+ "--color-border": "#2e2e2e",
84
+ "--color-border-subtle": "#1f1f1f",
85
+ "--color-text": "#f0f0f0",
86
+ "--color-text-muted": "#777777",
87
+ "--color-text-faint": "#3d3d3d",
88
+ "--color-accent": "#e8e8e8",
89
+ "--color-accent-hover": "#ffffff",
90
+ "--color-link": "#999999",
91
+ "--color-link-hover": "#f0f0f0",
92
+ "--color-success": "#4ade80",
93
+ "--color-warning": "#fbbf24",
94
+ "--color-error": "#f87171"
95
+ },
96
+ "note": "These are the dark-mode (default) values. Light mode overrides them under html[data-theme=\"light\"]. The full token set (spacing --space-*, radius --radius-*, typography, shadows) lives in the bundled stylesheet. Override any token in your own CSS to retheme."
97
+ }
98
+ }
package/index.js ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import {
5
+ CallToolRequestSchema,
6
+ ListToolsRequestSchema,
7
+ } from "@modelcontextprotocol/sdk/types.js";
8
+ import { readFileSync } from "node:fs";
9
+ import { dirname, resolve } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ // Catalog is bundled with the package — no network, no API, no license key.
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const CATALOG = JSON.parse(
15
+ readFileSync(resolve(__dirname, "data", "components.json"), "utf8"),
16
+ );
17
+
18
+ const TOOLS = [
19
+ {
20
+ name: "list_components",
21
+ description:
22
+ "List every component available in the @achmadalimin/ui-kit design system, with a one-line description of each. Call this first to see what you can use.",
23
+ inputSchema: { type: "object", properties: {} },
24
+ },
25
+ {
26
+ name: "get_component",
27
+ description:
28
+ "Get the canonical HTML markup, class list, and any required script for a single ui-kit component. Use the exact markup returned instead of inventing your own — it matches Achmad Alimin's design system.",
29
+ inputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ name: {
33
+ type: "string",
34
+ description:
35
+ "Component name, e.g. button, card, modal, tabs, accordion, badge, banner, input, tooltip",
36
+ },
37
+ },
38
+ required: ["name"],
39
+ },
40
+ },
41
+ {
42
+ name: "get_tokens",
43
+ description:
44
+ "Get the design tokens (CSS custom properties — colors, etc.) for the ui-kit design system, plus theming notes.",
45
+ inputSchema: { type: "object", properties: {} },
46
+ },
47
+ {
48
+ name: "get_setup",
49
+ description:
50
+ "Get install and setup instructions for @achmadalimin/ui-kit: the npm command, the stylesheet import/link, theming, and naming conventions. Call this before generating ui-kit markup so the stylesheet is actually loaded.",
51
+ inputSchema: { type: "object", properties: {} },
52
+ },
53
+ ];
54
+
55
+ function listComponents() {
56
+ const lines = CATALOG.components.map(
57
+ (c) => `- ${c.name} (${c.title}): ${c.description}`,
58
+ );
59
+ return (
60
+ `@achmadalimin/ui-kit components (${CATALOG.components.length}):\n\n` +
61
+ lines.join("\n") +
62
+ `\n\nCall get_component with a name to get its markup. Call get_setup for install instructions.`
63
+ );
64
+ }
65
+
66
+ function getComponent(name) {
67
+ const key = String(name || "").trim().toLowerCase();
68
+ const c = CATALOG.components.find((x) => x.name === key);
69
+ if (!c) {
70
+ const names = CATALOG.components.map((x) => x.name).join(", ");
71
+ return `Unknown component "${name}". Available: ${names}.`;
72
+ }
73
+ let out = `${c.title} (.${c.classes[0]})\n${c.description}\n\nClasses: ${c.classes.join(", ")}\n\nHTML:\n${c.html}`;
74
+ if (c.script) {
75
+ out += `\n\nRequired script:\n${c.script}`;
76
+ }
77
+ out += `\n\nRemember: include the stylesheet (see get_setup) or these classes won't be styled.`;
78
+ return out;
79
+ }
80
+
81
+ function getTokens() {
82
+ const t = CATALOG.tokens;
83
+ const colorLines = Object.entries(t.color).map(
84
+ ([k, v]) => ` ${k}: ${v};`,
85
+ );
86
+ return (
87
+ `Design tokens — colors (dark mode default):\n\n:root {\n${colorLines.join("\n")}\n}\n\n${t.note}`
88
+ );
89
+ }
90
+
91
+ function getSetup() {
92
+ const m = CATALOG.meta;
93
+ return (
94
+ `Setup — ${m.name}\n\n` +
95
+ `Option A — npm:\n ${m.npm}\n ${m.import}\n\n` +
96
+ `Option B — plain HTML (no build step):\n curl -O ${m.stylesUrl}\n <link rel="stylesheet" href="ui-kit.css">\n\n` +
97
+ `Theming: ${m.theming}\n\n` +
98
+ `Conventions: ${m.conventions}\n\n` +
99
+ `Docs: ${m.docs}`
100
+ );
101
+ }
102
+
103
+ const server = new Server(
104
+ { name: "ui-kit-mcp", version: "1.0.0" },
105
+ { capabilities: { tools: {} } },
106
+ );
107
+
108
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
109
+
110
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
111
+ const { name, arguments: args } = request.params;
112
+ try {
113
+ let text;
114
+ switch (name) {
115
+ case "list_components":
116
+ text = listComponents();
117
+ break;
118
+ case "get_component":
119
+ text = getComponent(args && args.name);
120
+ break;
121
+ case "get_tokens":
122
+ text = getTokens();
123
+ break;
124
+ case "get_setup":
125
+ text = getSetup();
126
+ break;
127
+ default:
128
+ return {
129
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
130
+ isError: true,
131
+ };
132
+ }
133
+ return { content: [{ type: "text", text }] };
134
+ } catch (err) {
135
+ return {
136
+ content: [
137
+ { type: "text", text: `Error running ${name}: ${err.message}` },
138
+ ],
139
+ isError: true,
140
+ };
141
+ }
142
+ });
143
+
144
+ const transport = new StdioServerTransport();
145
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@achmadalimin/ui-kit-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Make Achmad Alimin's design system (@achmadalimin/ui-kit) AI-native. Gives Claude, Cursor, and Windsurf the real component markup and tokens via the Model Context Protocol.",
5
+ "type": "module",
6
+ "bin": {
7
+ "ui-kit-mcp": "index.js"
8
+ },
9
+ "files": [
10
+ "index.js",
11
+ "data/components.json",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^1.12.0"
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "license": "MIT",
22
+ "author": "Achmad Alimin <contact@achmadalimin.com> (https://achmadalimin.com)",
23
+ "homepage": "https://achmadalimin.com/design-system",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/merahburam/achmadalimin-ui-kit-mcp.git"
27
+ },
28
+ "keywords": [
29
+ "mcp",
30
+ "model-context-protocol",
31
+ "design-system",
32
+ "ui",
33
+ "components",
34
+ "css",
35
+ "ai"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }