@epilot/volt-ui-mcp 0.1.0 → 0.1.1
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 +169 -7
- package/index.js +106 -38
- package/package.json +1 -1
- package/registry.json +14688 -30
package/README.md
CHANGED
|
@@ -1,24 +1,186 @@
|
|
|
1
1
|
# Volt UI MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An MCP (Model Context Protocol) server that exposes Volt UI components, props, documentation, and design tokens to AI assistants.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Component Discovery**: List and search all Volt UI components
|
|
8
|
+
- **Props & Documentation**: Get detailed component props, descriptions, and usage examples
|
|
9
|
+
- **Design Tokens**: Access CSS variables, semantic colors, and theming information
|
|
10
|
+
- **Theme Support**: Light and dark theme token values
|
|
11
|
+
|
|
12
|
+
## Available Tools
|
|
13
|
+
|
|
14
|
+
| Tool | Description |
|
|
15
|
+
|------|-------------|
|
|
16
|
+
| `list_components` | List all available Volt UI components |
|
|
17
|
+
| `get_component` | Get detailed information about a specific component |
|
|
18
|
+
| `search_components` | Search components by name, description, or props |
|
|
19
|
+
| `list_tokens` | List design tokens with optional filtering |
|
|
20
|
+
| `get_token` | Get details for a specific design token |
|
|
21
|
+
| `search_tokens` | Search tokens by name or value |
|
|
22
|
+
|
|
23
|
+
## Setup Instructions
|
|
24
|
+
|
|
25
|
+
### Claude Desktop
|
|
26
|
+
|
|
27
|
+
Add the following to your Claude Desktop configuration file:
|
|
28
|
+
|
|
29
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
30
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"volt-ui": {
|
|
36
|
+
"command": "npx",
|
|
37
|
+
"args": ["-y", "@epilot/volt-ui-mcp"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Claude Code (VS Code Extension)
|
|
44
|
+
|
|
45
|
+
Add to your VS Code settings or project's `.vscode/mcp.json`:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mcpServers": {
|
|
50
|
+
"volt-ui": {
|
|
51
|
+
"command": "npx",
|
|
52
|
+
"args": ["-y", "@epilot/volt-ui-mcp"]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or add to `~/.claude/settings.json` for global access:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"volt-ui": {
|
|
64
|
+
"command": "npx",
|
|
65
|
+
"args": ["-y", "@epilot/volt-ui-mcp"]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Cursor
|
|
72
|
+
|
|
73
|
+
Add to your Cursor MCP configuration (`.cursor/mcp.json` in your project or global settings):
|
|
6
74
|
|
|
7
75
|
```json
|
|
8
76
|
{
|
|
9
77
|
"mcpServers": {
|
|
10
|
-
"volt-ui
|
|
78
|
+
"volt-ui": {
|
|
11
79
|
"command": "npx",
|
|
12
|
-
"args": ["-y", "volt-ui-mcp"]
|
|
80
|
+
"args": ["-y", "@epilot/volt-ui-mcp"]
|
|
13
81
|
}
|
|
14
82
|
}
|
|
15
83
|
}
|
|
16
84
|
```
|
|
17
85
|
|
|
18
|
-
|
|
86
|
+
### Windsurf
|
|
19
87
|
|
|
20
|
-
|
|
88
|
+
Add to your Windsurf MCP configuration:
|
|
21
89
|
|
|
22
|
-
```
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"volt-ui": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["-y", "@epilot/volt-ui-mcp"]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### GitHub Copilot (VS Code)
|
|
102
|
+
|
|
103
|
+
Add to your VS Code `settings.json`:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"github.copilot.chat.mcpServers": {
|
|
108
|
+
"volt-ui": {
|
|
109
|
+
"command": "npx",
|
|
110
|
+
"args": ["-y", "@epilot/volt-ui-mcp"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Local Development (Testing)
|
|
117
|
+
|
|
118
|
+
For testing during development, point to the local path:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"volt-ui": {
|
|
124
|
+
"command": "node",
|
|
125
|
+
"args": ["/path/to/volt-ui/tools/volt-ui-mcp/index.js"]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Usage Examples
|
|
132
|
+
|
|
133
|
+
Once configured, you can ask your AI assistant questions like:
|
|
134
|
+
|
|
135
|
+
- "What props does the Button component accept?"
|
|
136
|
+
- "Show me how to use the Dialog component"
|
|
137
|
+
- "What are the available color tokens in Volt UI?"
|
|
138
|
+
- "How do I set up dark mode?"
|
|
139
|
+
- "Search for components with loading states"
|
|
140
|
+
|
|
141
|
+
## For Maintainers
|
|
142
|
+
|
|
143
|
+
### Building the Registry
|
|
144
|
+
|
|
145
|
+
The registry is automatically built during `npm publish` via the `prepack` script. To manually rebuild:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# From the volt-ui repo root
|
|
23
149
|
bun run build:mcp
|
|
24
150
|
```
|
|
151
|
+
|
|
152
|
+
This parses:
|
|
153
|
+
- Component exports from `src/index.ts`
|
|
154
|
+
- Documentation from `docs/content/docs/components/*.mdx`
|
|
155
|
+
- Design tokens from `src/styles/*.css`
|
|
156
|
+
|
|
157
|
+
### Publishing
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
cd tools/volt-ui-mcp
|
|
161
|
+
npm publish
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The `prepack` script automatically runs `build:mcp` to ensure the registry is up-to-date.
|
|
165
|
+
|
|
166
|
+
### Versioning
|
|
167
|
+
|
|
168
|
+
Update the version in `package.json` before publishing:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
cd tools/volt-ui-mcp
|
|
172
|
+
npm version patch # or minor, major
|
|
173
|
+
npm publish
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Resources
|
|
177
|
+
|
|
178
|
+
The server also exposes MCP resources:
|
|
179
|
+
|
|
180
|
+
| URI | Description |
|
|
181
|
+
|-----|-------------|
|
|
182
|
+
| `volt-ui://components` | List of all components |
|
|
183
|
+
| `volt-ui://tokens` | List of all design tokens |
|
|
184
|
+
| `volt-ui://components/{name}` | Specific component details |
|
|
185
|
+
| `volt-ui://tokens/{name}` | Specific token details |
|
|
186
|
+
|
package/index.js
CHANGED
|
@@ -37,9 +37,9 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
37
37
|
mimeType: "application/json",
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
|
-
uri: "volt-ui://
|
|
41
|
-
name: "Volt UI
|
|
42
|
-
description: "List of
|
|
40
|
+
uri: "volt-ui://tokens",
|
|
41
|
+
name: "Volt UI Tokens",
|
|
42
|
+
description: "List of Volt UI design tokens.",
|
|
43
43
|
mimeType: "application/json",
|
|
44
44
|
},
|
|
45
45
|
]
|
|
@@ -51,15 +51,18 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
51
51
|
mimeType: "application/json",
|
|
52
52
|
}))
|
|
53
53
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const tokenNames = Array.from(
|
|
55
|
+
new Set((registry.tokens || []).map((token) => token.name))
|
|
56
|
+
).sort((a, b) => a.localeCompare(b))
|
|
57
|
+
const tokenResources = tokenNames.map((name) => ({
|
|
58
|
+
uri: `volt-ui://tokens/${encodeURIComponent(name)}`,
|
|
59
|
+
name,
|
|
60
|
+
description: "Design token",
|
|
58
61
|
mimeType: "application/json",
|
|
59
62
|
}))
|
|
60
63
|
|
|
61
64
|
return {
|
|
62
|
-
resources: [...baseResources, ...componentResources, ...
|
|
65
|
+
resources: [...baseResources, ...componentResources, ...tokenResources],
|
|
63
66
|
}
|
|
64
67
|
})
|
|
65
68
|
|
|
@@ -126,29 +129,57 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
126
129
|
},
|
|
127
130
|
},
|
|
128
131
|
{
|
|
129
|
-
name: "
|
|
130
|
-
description: "List
|
|
132
|
+
name: "list_tokens",
|
|
133
|
+
description: "List Volt UI design tokens.",
|
|
131
134
|
inputSchema: {
|
|
132
135
|
type: "object",
|
|
133
|
-
properties: {
|
|
136
|
+
properties: {
|
|
137
|
+
query: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Optional text filter for token name or value.",
|
|
140
|
+
},
|
|
141
|
+
theme: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "Optional theme filter (light, dark, global).",
|
|
144
|
+
},
|
|
145
|
+
group: {
|
|
146
|
+
type: "string",
|
|
147
|
+
description: "Optional group filter (palette, semantic, utility).",
|
|
148
|
+
},
|
|
149
|
+
},
|
|
134
150
|
additionalProperties: false,
|
|
135
151
|
},
|
|
136
152
|
},
|
|
137
153
|
{
|
|
138
|
-
name: "
|
|
139
|
-
description: "Get a specific Volt UI
|
|
154
|
+
name: "get_token",
|
|
155
|
+
description: "Get details for a specific Volt UI token.",
|
|
140
156
|
inputSchema: {
|
|
141
157
|
type: "object",
|
|
142
158
|
properties: {
|
|
143
159
|
name: {
|
|
144
160
|
type: "string",
|
|
145
|
-
description: "
|
|
161
|
+
description: "Token name (e.g. --volt-blue-9).",
|
|
146
162
|
},
|
|
147
163
|
},
|
|
148
164
|
required: ["name"],
|
|
149
165
|
additionalProperties: false,
|
|
150
166
|
},
|
|
151
167
|
},
|
|
168
|
+
{
|
|
169
|
+
name: "search_tokens",
|
|
170
|
+
description: "Search tokens by name or value.",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
174
|
+
query: {
|
|
175
|
+
type: "string",
|
|
176
|
+
description: "Search term.",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
required: ["query"],
|
|
180
|
+
additionalProperties: false,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
152
183
|
],
|
|
153
184
|
}
|
|
154
185
|
})
|
|
@@ -169,12 +200,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
169
200
|
const query = typeof args?.query === "string" ? args.query : ""
|
|
170
201
|
return toolResult(searchComponents(query))
|
|
171
202
|
}
|
|
172
|
-
case "
|
|
173
|
-
|
|
203
|
+
case "list_tokens": {
|
|
204
|
+
const query = typeof args?.query === "string" ? args.query : ""
|
|
205
|
+
const theme = typeof args?.theme === "string" ? args.theme : ""
|
|
206
|
+
const group = typeof args?.group === "string" ? args.group : ""
|
|
207
|
+
return toolResult(listTokens({ query, theme, group }))
|
|
208
|
+
}
|
|
209
|
+
case "get_token": {
|
|
210
|
+
const tokenName = typeof args?.name === "string" ? args.name : ""
|
|
211
|
+
return toolResult(getToken(tokenName))
|
|
174
212
|
}
|
|
175
|
-
case "
|
|
176
|
-
const
|
|
177
|
-
return toolResult(
|
|
213
|
+
case "search_tokens": {
|
|
214
|
+
const query = typeof args?.query === "string" ? args.query : ""
|
|
215
|
+
return toolResult(searchTokens(query))
|
|
178
216
|
}
|
|
179
217
|
default:
|
|
180
218
|
return toolResult({ error: `Unknown tool: ${name}` })
|
|
@@ -192,7 +230,7 @@ function loadRegistry(filePath) {
|
|
|
192
230
|
return {
|
|
193
231
|
schemaVersion: 1,
|
|
194
232
|
components: [],
|
|
195
|
-
|
|
233
|
+
tokens: [],
|
|
196
234
|
error: String(error),
|
|
197
235
|
}
|
|
198
236
|
}
|
|
@@ -202,16 +240,16 @@ function resolveResource(uri) {
|
|
|
202
240
|
if (uri === "volt-ui://components") {
|
|
203
241
|
return listComponents("")
|
|
204
242
|
}
|
|
205
|
-
if (uri === "volt-ui://
|
|
206
|
-
return
|
|
243
|
+
if (uri === "volt-ui://tokens") {
|
|
244
|
+
return listTokens({})
|
|
207
245
|
}
|
|
208
246
|
if (uri.startsWith("volt-ui://components/")) {
|
|
209
247
|
const name = decodeURIComponent(uri.replace("volt-ui://components/", ""))
|
|
210
248
|
return getComponent(name)
|
|
211
249
|
}
|
|
212
|
-
if (uri.startsWith("volt-ui://
|
|
213
|
-
const name = decodeURIComponent(uri.replace("volt-ui://
|
|
214
|
-
return
|
|
250
|
+
if (uri.startsWith("volt-ui://tokens/")) {
|
|
251
|
+
const name = decodeURIComponent(uri.replace("volt-ui://tokens/", ""))
|
|
252
|
+
return getToken(name)
|
|
215
253
|
}
|
|
216
254
|
return { error: `Unknown resource: ${uri}` }
|
|
217
255
|
}
|
|
@@ -277,25 +315,55 @@ function searchComponents(query) {
|
|
|
277
315
|
}
|
|
278
316
|
}
|
|
279
317
|
|
|
280
|
-
function
|
|
318
|
+
function listTokens({ query = "", theme = "", group = "" }) {
|
|
319
|
+
const q = query.trim().toLowerCase()
|
|
320
|
+
const t = theme.trim().toLowerCase()
|
|
321
|
+
const g = group.trim().toLowerCase()
|
|
322
|
+
const tokens = (registry.tokens || []).filter((token) => {
|
|
323
|
+
if (t && token.theme.toLowerCase() !== t) {
|
|
324
|
+
return false
|
|
325
|
+
}
|
|
326
|
+
if (g && token.group.toLowerCase() !== g) {
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
if (!q) {
|
|
330
|
+
return true
|
|
331
|
+
}
|
|
332
|
+
return (
|
|
333
|
+
token.name.toLowerCase().includes(q) ||
|
|
334
|
+
token.value.toLowerCase().includes(q)
|
|
335
|
+
)
|
|
336
|
+
})
|
|
337
|
+
|
|
281
338
|
return {
|
|
282
|
-
count:
|
|
283
|
-
|
|
284
|
-
name: block.name,
|
|
285
|
-
title: block.title,
|
|
286
|
-
sourcePath: block.sourcePath,
|
|
287
|
-
})),
|
|
339
|
+
count: tokens.length,
|
|
340
|
+
tokens,
|
|
288
341
|
}
|
|
289
342
|
}
|
|
290
343
|
|
|
291
|
-
function
|
|
292
|
-
const
|
|
293
|
-
|
|
344
|
+
function getToken(name) {
|
|
345
|
+
const target = name.trim().toLowerCase()
|
|
346
|
+
if (!target) {
|
|
347
|
+
return { error: "Token name is required." }
|
|
348
|
+
}
|
|
349
|
+
const tokens = (registry.tokens || []).filter(
|
|
350
|
+
(token) => token.name.toLowerCase() === target
|
|
294
351
|
)
|
|
295
|
-
if (
|
|
296
|
-
return { error: `
|
|
352
|
+
if (tokens.length === 0) {
|
|
353
|
+
return { error: `Token not found: ${name}` }
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
name,
|
|
357
|
+
tokens,
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function searchTokens(query) {
|
|
362
|
+
const q = query.trim().toLowerCase()
|
|
363
|
+
if (!q) {
|
|
364
|
+
return { count: 0, tokens: [] }
|
|
297
365
|
}
|
|
298
|
-
return
|
|
366
|
+
return listTokens({ query: q })
|
|
299
367
|
}
|
|
300
368
|
|
|
301
369
|
function findComponent(name) {
|