@epilot/volt-ui-mcp 0.1.3 → 0.3.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/index.js +13 -335
- package/lib.d.ts +40 -0
- package/lib.js +499 -0
- package/package.json +12 -2
- package/registry.json +5100 -4413
- package/tools.js +226 -0
package/tools.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for MCP tool metadata (name, description, params).
|
|
3
|
+
*
|
|
4
|
+
* Both transports — the stdio bin (index.js) and the remote HTTP route
|
|
5
|
+
* (docs/app/api/[transport]/route.ts) — build their tool schemas from these
|
|
6
|
+
* specs, so descriptions and parameter docs can't drift between them. Handler
|
|
7
|
+
* wiring (which query function each tool calls) stays in each transport.
|
|
8
|
+
*
|
|
9
|
+
* Descriptions are the ONLY thing an agent uses to pick a tool, and they load
|
|
10
|
+
* into context on every turn — so they lead with WHEN to use each tool,
|
|
11
|
+
* disambiguate siblings, and teach the drill-down workflow (browse → narrow →
|
|
12
|
+
* resolve). Keep them high-signal and lean.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// One server version, used by BOTH transports' serverInfo so they can't diverge.
|
|
16
|
+
export const SERVER_VERSION = "0.3.0"
|
|
17
|
+
|
|
18
|
+
// Every tool is a pure, side-effect-free registry lookup — declare it so clients
|
|
19
|
+
// can treat calls as safe/cacheable.
|
|
20
|
+
export const READ_ONLY_ANNOTATIONS = {
|
|
21
|
+
readOnlyHint: true,
|
|
22
|
+
idempotentHint: true,
|
|
23
|
+
openWorldHint: false,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TOOL_SPECS = [
|
|
27
|
+
{
|
|
28
|
+
name: "list_components",
|
|
29
|
+
description:
|
|
30
|
+
"List Volt UI components (names + summaries). Use to browse what exists; use search_components to find by keyword/intent, then get_component for full details.",
|
|
31
|
+
params: [
|
|
32
|
+
{
|
|
33
|
+
name: "query",
|
|
34
|
+
type: "string",
|
|
35
|
+
optional: true,
|
|
36
|
+
description: "Optional keyword filter (component name or description).",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "get_component",
|
|
42
|
+
description:
|
|
43
|
+
'Get a Volt UI component: props, composition requirements (required provider/ancestors/parts) and runtime constraints where applicable, and — for most components — one fully-wired example with the correct import. Pass include="examples,tokens" for the full example set and recommended token families.',
|
|
44
|
+
params: [
|
|
45
|
+
{
|
|
46
|
+
name: "name",
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "Component name (e.g. Button, Accordion).",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "include",
|
|
52
|
+
type: "string",
|
|
53
|
+
optional: true,
|
|
54
|
+
description:
|
|
55
|
+
'Optional CSV of extra detail: "examples", "tokens", or "all". Defaults to a lean response.',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "search_components",
|
|
61
|
+
description:
|
|
62
|
+
'Search components by name, description, prop names, or intent aliases (e.g. "info banner" → Callout, "side panel" → Drawer). Returns summaries; drill in with get_component.',
|
|
63
|
+
params: [
|
|
64
|
+
{
|
|
65
|
+
name: "query",
|
|
66
|
+
type: "string",
|
|
67
|
+
description:
|
|
68
|
+
'Search term — keyword or intent phrase (e.g. "info banner").',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "list_tokens",
|
|
74
|
+
description:
|
|
75
|
+
'Browse the design-token taxonomy. With NO arguments, returns the category index (groups: spacing, palette, semantic, utility, other) with counts — cheap, no token dump. Pass group= to drill into one category\'s tokens (e.g. group="spacing"), or use search_tokens / get_token to narrow large color groups. Note: theme (light/dark) applies to palette tokens; other groups are global.',
|
|
76
|
+
params: [
|
|
77
|
+
{
|
|
78
|
+
name: "query",
|
|
79
|
+
type: "string",
|
|
80
|
+
optional: true,
|
|
81
|
+
description:
|
|
82
|
+
"Optional keyword/intent filter; when set, returns matching tokens instead of the category index.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "theme",
|
|
86
|
+
type: "string",
|
|
87
|
+
optional: true,
|
|
88
|
+
description:
|
|
89
|
+
"Optional theme filter; applies to palette tokens (light/dark). Other groups are global, so theme+non-palette group returns empty.",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "group",
|
|
93
|
+
type: "string",
|
|
94
|
+
optional: true,
|
|
95
|
+
description:
|
|
96
|
+
"Category to drill into — one of: spacing, palette, semantic, utility, other. Returns that category's tokens.",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "limit",
|
|
100
|
+
type: "number",
|
|
101
|
+
optional: true,
|
|
102
|
+
description: "Max tokens per page (default 200, max 500).",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "offset",
|
|
106
|
+
type: "number",
|
|
107
|
+
optional: true,
|
|
108
|
+
description: "Pagination offset (default 0).",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "get_token",
|
|
114
|
+
description:
|
|
115
|
+
"Resolve one design token by exact name (e.g. --volt-accent-9 or --spacing-group-2) to its value(s) across light/dark/global themes, plus intent metadata (utility + usage) for non-color tokens.",
|
|
116
|
+
params: [
|
|
117
|
+
{
|
|
118
|
+
name: "name",
|
|
119
|
+
type: "string",
|
|
120
|
+
description:
|
|
121
|
+
"Exact token name (e.g. --volt-accent-a3 or --spacing-group-2).",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "search_tokens",
|
|
127
|
+
description:
|
|
128
|
+
'Find design tokens by name, value, or intent (e.g. "gap", "spacing", "accent"). Use when you know what you want; call list_tokens with no args to browse categories first.',
|
|
129
|
+
params: [
|
|
130
|
+
{
|
|
131
|
+
name: "query",
|
|
132
|
+
type: "string",
|
|
133
|
+
description:
|
|
134
|
+
'Search term — name, value, or intent (e.g. "gap", "accent").',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "list_guidelines",
|
|
140
|
+
description:
|
|
141
|
+
"List Volt UI design guidelines (color/token usage, spacing, theming, ux-writing/tone, typography). Returns an index; use get_guideline for the full text.",
|
|
142
|
+
params: [
|
|
143
|
+
{
|
|
144
|
+
name: "query",
|
|
145
|
+
type: "string",
|
|
146
|
+
optional: true,
|
|
147
|
+
description: "Optional filter for guideline slug, title, or headings.",
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "get_guideline",
|
|
153
|
+
description:
|
|
154
|
+
"Get the full text of a Volt UI design guideline by slug (e.g. color, spacing, theming, ux-writing, font).",
|
|
155
|
+
params: [
|
|
156
|
+
{
|
|
157
|
+
name: "slug",
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Guideline slug (e.g. color, ux-writing).",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
/** Builds a JSON-Schema tool definition (stdio bin / ListTools) from a spec. */
|
|
166
|
+
export function toJsonSchema(spec) {
|
|
167
|
+
const properties = {}
|
|
168
|
+
const required = []
|
|
169
|
+
for (const param of spec.params) {
|
|
170
|
+
properties[param.name] = {
|
|
171
|
+
type: param.type,
|
|
172
|
+
description: param.description,
|
|
173
|
+
}
|
|
174
|
+
if (!param.optional) {
|
|
175
|
+
required.push(param.name)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const inputSchema = {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties,
|
|
181
|
+
additionalProperties: false,
|
|
182
|
+
}
|
|
183
|
+
if (required.length > 0) {
|
|
184
|
+
inputSchema.required = required
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
name: spec.name,
|
|
188
|
+
description: spec.description,
|
|
189
|
+
inputSchema,
|
|
190
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Single dispatch shared by BOTH transports so the handler wiring can't drift
|
|
195
|
+
// (a one-sided tool addition would otherwise crash one transport silently).
|
|
196
|
+
export function callTool(queries, name, args) {
|
|
197
|
+
const a = args || {}
|
|
198
|
+
const str = (v) => (typeof v === "string" ? v : "")
|
|
199
|
+
const num = (v) => (typeof v === "number" ? v : undefined)
|
|
200
|
+
switch (name) {
|
|
201
|
+
case "list_components":
|
|
202
|
+
return queries.listComponents(str(a.query))
|
|
203
|
+
case "get_component":
|
|
204
|
+
return queries.getComponent(str(a.name), str(a.include))
|
|
205
|
+
case "search_components":
|
|
206
|
+
return queries.searchComponents(str(a.query))
|
|
207
|
+
case "list_tokens":
|
|
208
|
+
return queries.listTokens({
|
|
209
|
+
query: str(a.query),
|
|
210
|
+
theme: str(a.theme),
|
|
211
|
+
group: str(a.group),
|
|
212
|
+
limit: num(a.limit),
|
|
213
|
+
offset: num(a.offset),
|
|
214
|
+
})
|
|
215
|
+
case "get_token":
|
|
216
|
+
return queries.getToken(str(a.name))
|
|
217
|
+
case "search_tokens":
|
|
218
|
+
return queries.searchTokens(str(a.query))
|
|
219
|
+
case "list_guidelines":
|
|
220
|
+
return queries.listGuidelines(str(a.query))
|
|
221
|
+
case "get_guideline":
|
|
222
|
+
return queries.getGuideline(str(a.slug))
|
|
223
|
+
default:
|
|
224
|
+
return { error: `Unknown tool: ${name}` }
|
|
225
|
+
}
|
|
226
|
+
}
|