untitledui-mcp 0.1.3 → 0.1.5
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/README.md +113 -29
- package/dist/index.js +131 -6
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -10,9 +10,32 @@ AI tools can generate functional UI, but the result often lacks the polish and c
|
|
|
10
10
|
|
|
11
11
|
Instead of generating a modal from patterns it's seen, your AI fetches the actual UntitledUI modal with all its carefully crafted details intact.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Quick Start
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### 1. Start with a UntitledUI Starter Kit
|
|
16
|
+
|
|
17
|
+
UntitledUI components require specific Tailwind configuration, design tokens, and providers. The easiest way to get started is with an official starter kit:
|
|
18
|
+
|
|
19
|
+
**Next.js:**
|
|
20
|
+
```bash
|
|
21
|
+
git clone https://github.com/untitleduico/untitledui-nextjs-starter-kit my-app
|
|
22
|
+
cd my-app && npm install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Vite:**
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/untitleduico/untitledui-vite-starter-kit my-app
|
|
28
|
+
cd my-app && npm install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
These starter kits come pre-configured with:
|
|
32
|
+
- Tailwind CSS with all UntitledUI design tokens
|
|
33
|
+
- Theme provider for light/dark mode
|
|
34
|
+
- Toast notifications
|
|
35
|
+
- Routing setup
|
|
36
|
+
- All required dependencies
|
|
37
|
+
|
|
38
|
+
### 2. Add the MCP Server
|
|
16
39
|
|
|
17
40
|
**Claude Code:**
|
|
18
41
|
```bash
|
|
@@ -25,7 +48,7 @@ claude mcp add untitledui -- npx untitledui-mcp
|
|
|
25
48
|
"mcpServers": {
|
|
26
49
|
"untitledui": {
|
|
27
50
|
"command": "npx",
|
|
28
|
-
"args": ["untitledui-mcp"]
|
|
51
|
+
"args": ["-y", "untitledui-mcp"]
|
|
29
52
|
}
|
|
30
53
|
}
|
|
31
54
|
}
|
|
@@ -33,7 +56,7 @@ claude mcp add untitledui -- npx untitledui-mcp
|
|
|
33
56
|
|
|
34
57
|
This gives you access to **base components** (button, input, select, avatar, badge, etc.) without authentication.
|
|
35
58
|
|
|
36
|
-
###
|
|
59
|
+
### 3. Authenticate for Pro Components (optional)
|
|
37
60
|
|
|
38
61
|
To access application components (modals, sidebars, tables, dashboards) and marketing sections, authenticate with your UntitledUI Pro license:
|
|
39
62
|
|
|
@@ -49,7 +72,7 @@ Alternatively, set the key manually in your MCP config:
|
|
|
49
72
|
"mcpServers": {
|
|
50
73
|
"untitledui": {
|
|
51
74
|
"command": "npx",
|
|
52
|
-
"args": ["untitledui-mcp"],
|
|
75
|
+
"args": ["-y", "untitledui-mcp"],
|
|
53
76
|
"env": {
|
|
54
77
|
"UNTITLEDUI_LICENSE_KEY": "<your-key>"
|
|
55
78
|
}
|
|
@@ -58,13 +81,26 @@ Alternatively, set the key manually in your MCP config:
|
|
|
58
81
|
}
|
|
59
82
|
```
|
|
60
83
|
|
|
61
|
-
### Verify
|
|
84
|
+
### 4. Verify Setup
|
|
62
85
|
|
|
63
86
|
```bash
|
|
64
87
|
npx untitledui-mcp --test
|
|
65
88
|
```
|
|
66
89
|
|
|
67
|
-
##
|
|
90
|
+
## Recommended Workflow
|
|
91
|
+
|
|
92
|
+
1. **Start with a starter kit** — Don't try to add UntitledUI components to an existing project without the proper Tailwind setup. The starter kits handle all configuration.
|
|
93
|
+
|
|
94
|
+
2. **Ask your AI to fetch components** — Once your project is set up, ask your AI to add specific components:
|
|
95
|
+
- "Add a settings modal"
|
|
96
|
+
- "I need a sidebar navigation"
|
|
97
|
+
- "Add the user profile dropdown"
|
|
98
|
+
|
|
99
|
+
3. **Build complete pages from examples** — For larger features, start with a page template:
|
|
100
|
+
- "Show me available dashboard templates"
|
|
101
|
+
- "Fetch the landing page example"
|
|
102
|
+
|
|
103
|
+
## Available Tools
|
|
68
104
|
|
|
69
105
|
Your AI gets these tools:
|
|
70
106
|
|
|
@@ -74,44 +110,70 @@ Your AI gets these tools:
|
|
|
74
110
|
| `list_components` | Browse a category |
|
|
75
111
|
| `get_component_with_deps` | Fetch component + all dependencies |
|
|
76
112
|
| `get_component` | Fetch component only |
|
|
113
|
+
| `get_component_file` | Fetch a single file (for large components) |
|
|
77
114
|
| `list_examples` | Browse available page templates |
|
|
78
|
-
| `get_example` | Fetch a
|
|
115
|
+
| `get_example` | Fetch a complete page template |
|
|
79
116
|
|
|
80
|
-
|
|
117
|
+
## What You Can Do
|
|
118
|
+
|
|
119
|
+
### Recreate Any UI from a Screenshot
|
|
81
120
|
|
|
82
121
|
```
|
|
83
|
-
You: "
|
|
122
|
+
You: [paste screenshot] "Recreate this with UntitledUI components"
|
|
84
123
|
|
|
85
|
-
AI
|
|
86
|
-
AI fetches
|
|
87
|
-
AI
|
|
124
|
+
AI analyzes the design → identifies matching components
|
|
125
|
+
AI fetches sidebar, header, cards, tables → all with dependencies
|
|
126
|
+
AI assembles → production-ready page matching your screenshot
|
|
88
127
|
```
|
|
89
128
|
|
|
90
|
-
###
|
|
129
|
+
### Build Complete Pages in Seconds
|
|
91
130
|
|
|
92
131
|
```
|
|
93
|
-
You: "
|
|
132
|
+
You: "Build me a SaaS pricing page with 3 tiers and a FAQ section"
|
|
94
133
|
|
|
95
|
-
AI
|
|
96
|
-
|
|
134
|
+
AI fetches marketing/pricing-sections + marketing/faq-sections
|
|
135
|
+
AI combines components → complete pricing page with toggle for monthly/annual
|
|
136
|
+
Result: Professional pricing page with all interactions working
|
|
97
137
|
```
|
|
98
138
|
|
|
99
|
-
###
|
|
139
|
+
### Set Up Your Entire App Shell
|
|
100
140
|
|
|
101
141
|
```
|
|
102
|
-
You: "
|
|
142
|
+
You: "Set up the main app layout with collapsible sidebar and header with user dropdown"
|
|
103
143
|
|
|
104
|
-
AI
|
|
105
|
-
|
|
144
|
+
AI fetches application/sidebars + application/headers + base components
|
|
145
|
+
AI wires up navigation state, theme toggle, user menu
|
|
146
|
+
Result: Complete app shell ready for your content
|
|
147
|
+
```
|
|
106
148
|
|
|
107
|
-
|
|
108
|
-
→ Returns: dashboards-01, dashboards-02, settings-01, ...
|
|
149
|
+
### Clone a Page from UntitledUI's Templates
|
|
109
150
|
|
|
110
|
-
|
|
111
|
-
|
|
151
|
+
```
|
|
152
|
+
You: "I want a dashboard like the one in dashboards-01"
|
|
112
153
|
|
|
113
154
|
AI calls get_example { path: "application/dashboards-01/01" }
|
|
114
|
-
→ Returns
|
|
155
|
+
→ Returns 27 files: page layout, charts, tables, cards, all base components
|
|
156
|
+
→ Ready to customize with your data
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Mix and Match Components
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
You: "Create a settings page with sections for profile, notifications, billing, and team members"
|
|
163
|
+
|
|
164
|
+
AI searches → finds matching components for each section
|
|
165
|
+
AI fetches settings panels, forms, tables, modals
|
|
166
|
+
AI composes → cohesive settings page with consistent styling
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Rapid Feature Development
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
You: "Add a command palette like Linear/Notion with keyboard shortcut"
|
|
173
|
+
|
|
174
|
+
AI fetches application/command-menus
|
|
175
|
+
→ Complete command palette with search, keyboard navigation, sections
|
|
176
|
+
→ Just wire up your actions
|
|
115
177
|
```
|
|
116
178
|
|
|
117
179
|
## Response Format
|
|
@@ -127,22 +189,44 @@ AI calls get_example { path: "application/dashboards-01/01" }
|
|
|
127
189
|
{ "name": "button", "files": [...] },
|
|
128
190
|
{ "name": "input", "files": [...] }
|
|
129
191
|
],
|
|
130
|
-
"allDependencies": ["@headlessui/react", "clsx"]
|
|
192
|
+
"allDependencies": ["@headlessui/react", "clsx"],
|
|
193
|
+
"estimatedTokens": 12500,
|
|
194
|
+
"fileList": [
|
|
195
|
+
{ "path": "settings-modal.tsx", "tokens": 850 },
|
|
196
|
+
{ "path": "button/button.tsx", "tokens": 420 }
|
|
197
|
+
]
|
|
131
198
|
}
|
|
132
199
|
```
|
|
133
200
|
|
|
201
|
+
Responses include token estimates to help AI agents manage context limits. For very large responses (>25K tokens), the AI can use `get_component_file` to fetch individual files.
|
|
202
|
+
|
|
203
|
+
## Troubleshooting
|
|
204
|
+
|
|
205
|
+
### Components don't look right / missing styles
|
|
206
|
+
|
|
207
|
+
UntitledUI components use custom Tailwind classes like `bg-primary`, `text-display-md`, and design tokens that aren't part of standard Tailwind.
|
|
208
|
+
|
|
209
|
+
**Solution:** Use a [UntitledUI starter kit](#1-start-with-a-untitledui-starter-kit). The starter kits include all required Tailwind configuration and design tokens.
|
|
210
|
+
|
|
211
|
+
### Import errors for base components
|
|
212
|
+
|
|
213
|
+
Pro components depend on base components (button, input, etc.). When fetching a component with `get_component_with_deps`, all dependencies are included automatically.
|
|
214
|
+
|
|
215
|
+
**Solution:** Make sure you're using `get_component_with_deps` instead of `get_component` for Pro components.
|
|
216
|
+
|
|
134
217
|
## Requirements
|
|
135
218
|
|
|
136
219
|
- Node.js 18+
|
|
137
|
-
- UntitledUI Pro license (
|
|
220
|
+
- UntitledUI Pro license (for Pro components only)
|
|
138
221
|
|
|
139
222
|
## Credits
|
|
140
223
|
|
|
141
|
-
[UntitledUI](https://www.untitledui.com) is created by [Jordan Hughes](https://jordanhughes.co).
|
|
224
|
+
[UntitledUI](https://www.untitledui.com?utm_source=untitledui-mcp&utm_medium=github&utm_campaign=readme) is created by [Jordan Hughes](https://jordanhughes.co?utm_source=untitledui-mcp&utm_medium=github&utm_campaign=readme).
|
|
142
225
|
|
|
143
226
|
- [Twitter/X](https://x.com/jordanphughes)
|
|
144
227
|
- [Dribbble](https://dribbble.com/jordanhughes)
|
|
145
228
|
- [UntitledUI on X](https://x.com/UntitledUI)
|
|
229
|
+
- [Get UntitledUI Pro](https://www.untitledui.com/pricing?utm_source=untitledui-mcp&utm_medium=github&utm_campaign=readme)
|
|
146
230
|
|
|
147
231
|
## License
|
|
148
232
|
|
package/dist/index.js
CHANGED
|
@@ -378,6 +378,28 @@ function getBaseComponentNames(files) {
|
|
|
378
378
|
return Array.from(names);
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
+
// src/utils/tokens.ts
|
|
382
|
+
function estimateTokens(content) {
|
|
383
|
+
return Math.ceil(content.length / 4);
|
|
384
|
+
}
|
|
385
|
+
function estimateFileTokens(file) {
|
|
386
|
+
const content = file.code || file.content || "";
|
|
387
|
+
return estimateTokens(content);
|
|
388
|
+
}
|
|
389
|
+
function estimateComponentTokens(files) {
|
|
390
|
+
return files.reduce((sum, file) => sum + estimateFileTokens(file), 0);
|
|
391
|
+
}
|
|
392
|
+
function getFileTokenList(files) {
|
|
393
|
+
return files.map((file) => ({
|
|
394
|
+
path: file.path,
|
|
395
|
+
tokens: estimateFileTokens(file)
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
var CLAUDE_READ_TOKEN_LIMIT = 25e3;
|
|
399
|
+
function isLikelyTooLarge(estimatedTokens) {
|
|
400
|
+
return estimatedTokens > CLAUDE_READ_TOKEN_LIMIT;
|
|
401
|
+
}
|
|
402
|
+
|
|
381
403
|
// src/server.ts
|
|
382
404
|
function createServer(licenseKey) {
|
|
383
405
|
const client = new UntitledUIClient(licenseKey);
|
|
@@ -455,7 +477,7 @@ function createServer(licenseKey) {
|
|
|
455
477
|
},
|
|
456
478
|
{
|
|
457
479
|
name: "get_component",
|
|
458
|
-
description: "Get a single component's code.
|
|
480
|
+
description: "Get a single component's code with token estimates. Returns estimatedTokens and file list. If estimatedTokens > 25000, consider using get_component_file for specific files instead.",
|
|
459
481
|
inputSchema: {
|
|
460
482
|
type: "object",
|
|
461
483
|
properties: {
|
|
@@ -467,7 +489,7 @@ function createServer(licenseKey) {
|
|
|
467
489
|
},
|
|
468
490
|
{
|
|
469
491
|
name: "get_component_with_deps",
|
|
470
|
-
description: "Get a component with all
|
|
492
|
+
description: "Get a component with all base dependencies. Returns estimatedTokens. If response is too large (>25000 tokens), use get_component_file to fetch specific files individually.",
|
|
471
493
|
inputSchema: {
|
|
472
494
|
type: "object",
|
|
473
495
|
properties: {
|
|
@@ -477,6 +499,19 @@ function createServer(licenseKey) {
|
|
|
477
499
|
required: ["type", "name"]
|
|
478
500
|
}
|
|
479
501
|
},
|
|
502
|
+
{
|
|
503
|
+
name: "get_component_file",
|
|
504
|
+
description: "Get a single file from a component. Use this when get_component or get_component_with_deps returns a large response (>25000 tokens). First call get_component to see the file list, then fetch specific files as needed.",
|
|
505
|
+
inputSchema: {
|
|
506
|
+
type: "object",
|
|
507
|
+
properties: {
|
|
508
|
+
type: { type: "string", description: "Component type" },
|
|
509
|
+
name: { type: "string", description: "Component name" },
|
|
510
|
+
file: { type: "string", description: "File path within the component (e.g., 'Button.tsx' or 'variants/InputPhone.tsx')" }
|
|
511
|
+
},
|
|
512
|
+
required: ["type", "name", "file"]
|
|
513
|
+
}
|
|
514
|
+
},
|
|
480
515
|
{
|
|
481
516
|
name: "list_examples",
|
|
482
517
|
description: "Browse available page examples. Call without path to see categories, then drill down.",
|
|
@@ -575,7 +610,20 @@ function createServer(licenseKey) {
|
|
|
575
610
|
};
|
|
576
611
|
cache.set(cacheKey, component, CACHE_TTL.componentCode);
|
|
577
612
|
}
|
|
578
|
-
|
|
613
|
+
const estimatedTokens = estimateComponentTokens(component.files);
|
|
614
|
+
const fileTokens = getFileTokenList(component.files);
|
|
615
|
+
const tooLarge = isLikelyTooLarge(estimatedTokens);
|
|
616
|
+
const result = {
|
|
617
|
+
...component,
|
|
618
|
+
estimatedTokens,
|
|
619
|
+
fileCount: component.files.length,
|
|
620
|
+
fileList: fileTokens,
|
|
621
|
+
...tooLarge && {
|
|
622
|
+
warning: `Response is large (${estimatedTokens} tokens). Consider using get_component_file for specific files.`,
|
|
623
|
+
hint: "Use get_component_file with type, name, and file path to fetch individual files."
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
579
627
|
}
|
|
580
628
|
case "get_component_with_deps": {
|
|
581
629
|
const { type, name: componentName } = args;
|
|
@@ -602,12 +650,19 @@ function createServer(licenseKey) {
|
|
|
602
650
|
c.dependencies?.forEach((d) => allDeps.add(d));
|
|
603
651
|
c.devDependencies?.forEach((d) => allDevDeps.add(d));
|
|
604
652
|
});
|
|
653
|
+
const allFiles = [
|
|
654
|
+
...primary.files,
|
|
655
|
+
...baseComponents.flatMap((c) => c.files)
|
|
656
|
+
];
|
|
657
|
+
const estimatedTokens = estimateComponentTokens(allFiles);
|
|
658
|
+
const tooLarge = isLikelyTooLarge(estimatedTokens);
|
|
605
659
|
const result = {
|
|
606
660
|
primary: {
|
|
607
661
|
name: primary.name,
|
|
608
662
|
type,
|
|
609
663
|
description: generateDescription(primary.name, type),
|
|
610
664
|
files: primary.files,
|
|
665
|
+
fileList: getFileTokenList(primary.files),
|
|
611
666
|
dependencies: primary.dependencies || [],
|
|
612
667
|
devDependencies: primary.devDependencies || [],
|
|
613
668
|
baseComponents: baseComponentNames
|
|
@@ -617,15 +672,77 @@ function createServer(licenseKey) {
|
|
|
617
672
|
type: "base",
|
|
618
673
|
description: generateDescription(c.name, "base"),
|
|
619
674
|
files: c.files,
|
|
675
|
+
fileList: getFileTokenList(c.files),
|
|
620
676
|
dependencies: c.dependencies || [],
|
|
621
677
|
devDependencies: c.devDependencies || []
|
|
622
678
|
})),
|
|
623
679
|
totalFiles: primary.files.length + baseComponents.reduce((sum, c) => sum + c.files.length, 0),
|
|
680
|
+
estimatedTokens,
|
|
624
681
|
allDependencies: Array.from(allDeps),
|
|
625
|
-
allDevDependencies: Array.from(allDevDeps)
|
|
682
|
+
allDevDependencies: Array.from(allDevDeps),
|
|
683
|
+
...tooLarge && {
|
|
684
|
+
warning: `Response is large (${estimatedTokens} tokens, limit is ${CLAUDE_READ_TOKEN_LIMIT}). Consider using get_component_file for specific files.`,
|
|
685
|
+
hint: "To fetch individual files, use get_component_file with the component type, name, and file path from fileList."
|
|
686
|
+
}
|
|
626
687
|
};
|
|
627
688
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
628
689
|
}
|
|
690
|
+
case "get_component_file": {
|
|
691
|
+
const { type, name: componentName, file: filePath } = args;
|
|
692
|
+
const cacheKey = `component:${type}:${componentName}`;
|
|
693
|
+
let files;
|
|
694
|
+
const cached = cache.get(cacheKey);
|
|
695
|
+
if (cached) {
|
|
696
|
+
files = cached.files;
|
|
697
|
+
} else {
|
|
698
|
+
const fetched = await client.fetchComponent(type, componentName);
|
|
699
|
+
if (!fetched) {
|
|
700
|
+
const index = await buildSearchIndex();
|
|
701
|
+
const suggestions = fuzzySearch(componentName, index, 5).map((r) => r.fullPath);
|
|
702
|
+
return {
|
|
703
|
+
content: [{
|
|
704
|
+
type: "text",
|
|
705
|
+
text: JSON.stringify({
|
|
706
|
+
error: `Component "${componentName}" not found`,
|
|
707
|
+
code: "NOT_FOUND",
|
|
708
|
+
suggestions
|
|
709
|
+
}, null, 2)
|
|
710
|
+
}]
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
files = fetched.files;
|
|
714
|
+
}
|
|
715
|
+
const file = files.find(
|
|
716
|
+
(f) => f.path === filePath || f.path.endsWith(`/${filePath}`) || f.path.endsWith(filePath)
|
|
717
|
+
);
|
|
718
|
+
if (!file) {
|
|
719
|
+
const availableFiles = files.map((f) => f.path);
|
|
720
|
+
return {
|
|
721
|
+
content: [{
|
|
722
|
+
type: "text",
|
|
723
|
+
text: JSON.stringify({
|
|
724
|
+
error: `File "${filePath}" not found in ${type}/${componentName}`,
|
|
725
|
+
code: "FILE_NOT_FOUND",
|
|
726
|
+
availableFiles,
|
|
727
|
+
hint: "Use one of the file paths from availableFiles"
|
|
728
|
+
}, null, 2)
|
|
729
|
+
}]
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
content: [{
|
|
734
|
+
type: "text",
|
|
735
|
+
text: JSON.stringify({
|
|
736
|
+
component: `${type}/${componentName}`,
|
|
737
|
+
file: {
|
|
738
|
+
path: file.path,
|
|
739
|
+
code: file.code
|
|
740
|
+
},
|
|
741
|
+
estimatedTokens: estimateComponentTokens([file])
|
|
742
|
+
}, null, 2)
|
|
743
|
+
}]
|
|
744
|
+
};
|
|
745
|
+
}
|
|
629
746
|
case "list_examples": {
|
|
630
747
|
const { path: path2 = "" } = args;
|
|
631
748
|
const cacheKey = `examples:list:${path2}`;
|
|
@@ -675,17 +792,25 @@ function createServer(licenseKey) {
|
|
|
675
792
|
};
|
|
676
793
|
}
|
|
677
794
|
if (example.type === "json-file" && example.content) {
|
|
795
|
+
const files = example.content.files || [];
|
|
796
|
+
const estimatedTokens = estimateComponentTokens(files);
|
|
797
|
+
const tooLarge = isLikelyTooLarge(estimatedTokens);
|
|
678
798
|
return {
|
|
679
799
|
content: [{
|
|
680
800
|
type: "text",
|
|
681
801
|
text: JSON.stringify({
|
|
682
802
|
path: examplePath,
|
|
683
803
|
name: example.content.name,
|
|
684
|
-
files
|
|
804
|
+
files,
|
|
805
|
+
fileList: getFileTokenList(files),
|
|
685
806
|
dependencies: example.content.dependencies || [],
|
|
686
807
|
devDependencies: example.content.devDependencies || [],
|
|
687
808
|
components: example.content.components || [],
|
|
688
|
-
fileCount:
|
|
809
|
+
fileCount: files.length,
|
|
810
|
+
estimatedTokens,
|
|
811
|
+
...tooLarge && {
|
|
812
|
+
warning: `Response is large (${estimatedTokens} tokens). Consider fetching specific component files instead.`
|
|
813
|
+
}
|
|
689
814
|
}, null, 2)
|
|
690
815
|
}]
|
|
691
816
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "untitledui-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "MCP server for UntitledUI Pro components - browse, search, and retrieve UI components via Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,12 @@
|
|
|
25
25
|
"test:watch": "vitest",
|
|
26
26
|
"start": "node dist/index.js"
|
|
27
27
|
},
|
|
28
|
-
"keywords": [
|
|
28
|
+
"keywords": [
|
|
29
|
+
"mcp",
|
|
30
|
+
"untitledui",
|
|
31
|
+
"ui-components",
|
|
32
|
+
"claude"
|
|
33
|
+
],
|
|
29
34
|
"author": "Steffen Bilde",
|
|
30
35
|
"license": "MIT",
|
|
31
36
|
"dependencies": {
|