wunderui-mcp 1.1.0 → 1.2.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 +1 -1
- package/data/DESIGN.md +6987 -248
- package/data/design.json +31317 -1262
- package/package.json +2 -2
- package/src/index.mjs +36 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wunderui-mcp",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"mcpName": "io.github.Kl-webmedia/wunderui-mcp",
|
|
5
5
|
"description": "MCP server that gives AI coding agents the WunderUI design system: tokens, component props, templates and starters. Works with Claude Code, Cursor, Windsurf, Cline and Codex.",
|
|
6
6
|
"keywords": [
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"smoke": "node ./test/smoke.mjs"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
50
50
|
"zod": "^4.6.5"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/index.mjs
CHANGED
|
@@ -218,13 +218,31 @@ server.registerTool(
|
|
|
218
218
|
{
|
|
219
219
|
title: "Get a WunderUI component",
|
|
220
220
|
description:
|
|
221
|
-
"Everything needed to use one component correctly: its exported names, the TypeScript prop signature straight from the build, the import line and a link to its documentation page.",
|
|
221
|
+
"Everything needed to use one component correctly: its exported names, the TypeScript prop signature straight from the build, a props table per export (type, allowed values, default, required, description), its anatomy (parts and data-slot names), best practices, the CSS variables it reads with their defaults, the import line and a link to its documentation page. Also answers for hooks (signature, parameters, return value, notes), providers and utilities (useToast, ThemeProvider …).",
|
|
222
222
|
inputSchema: {
|
|
223
223
|
name: z.string().describe('Export name ("DataGrid"), slug ("data-grid") or title ("Data Grid")'),
|
|
224
224
|
},
|
|
225
225
|
},
|
|
226
226
|
async ({ name }) => {
|
|
227
227
|
const component = findComponent(name)
|
|
228
|
+
const other = component ? null : (design.otherExports ?? []).find((entry) => normalize(entry.name) === normalize(name))
|
|
229
|
+
if (other) {
|
|
230
|
+
return text({
|
|
231
|
+
name: other.name,
|
|
232
|
+
kind: other.kind,
|
|
233
|
+
plan: other.tier ?? "pro",
|
|
234
|
+
description: other.summary ?? other.description ?? undefined,
|
|
235
|
+
guide: other.guide ?? undefined,
|
|
236
|
+
import: `import { ${other.name} } from "${design.name}"`,
|
|
237
|
+
signature: other.signature ?? undefined,
|
|
238
|
+
params: other.params?.length ? other.params : undefined,
|
|
239
|
+
returns: other.returns ?? undefined,
|
|
240
|
+
notes: other.notes ?? undefined,
|
|
241
|
+
examples: other.examples ?? undefined,
|
|
242
|
+
propsTable: other.props ? [{ name: other.name, props: other.props, inherits: other.inherits }] : undefined,
|
|
243
|
+
docs: other.docs ?? undefined,
|
|
244
|
+
})
|
|
245
|
+
}
|
|
228
246
|
if (!component) {
|
|
229
247
|
const suggestions = design.components
|
|
230
248
|
.filter((candidate) => matches(candidate, name))
|
|
@@ -249,6 +267,13 @@ server.registerTool(
|
|
|
249
267
|
exports: component.exports,
|
|
250
268
|
example: component.example ?? undefined,
|
|
251
269
|
props: component.props ?? "No generated signature — see the documentation page.",
|
|
270
|
+
// per export: name, type, values (literal unions), default, required, description; DOM attributes summed up in inherits
|
|
271
|
+
propsTable: component.api?.length ? component.api : undefined,
|
|
272
|
+
// the guide: parts (export, data-slot, required, a sentence), do/don't/when notes, and the CSS
|
|
273
|
+
// variables the component reads with their defaults; `source` "generated" = drafted from the code
|
|
274
|
+
anatomy: component.anatomy ?? undefined,
|
|
275
|
+
bestPractices: component.bestPractices ?? undefined,
|
|
276
|
+
themingVariables: component.themingVariables ?? undefined,
|
|
252
277
|
docs: component.docs,
|
|
253
278
|
})
|
|
254
279
|
}
|
|
@@ -377,7 +402,16 @@ server.registerTool(
|
|
|
377
402
|
|
|
378
403
|
const siteUrl = () => (process.env.WUNDERUI_SITE ?? design.site ?? "https://wunderui.com").replace(/\/$/, "")
|
|
379
404
|
const licenseKey = () => process.env.WUNDERUI_LICENSE_KEY?.trim() || null
|
|
380
|
-
|
|
405
|
+
/** The key goes to wunderui.com (or a local dev server) only, never to another WUNDERUI_SITE. */
|
|
406
|
+
const keyAllowed = () => {
|
|
407
|
+
try {
|
|
408
|
+
const { protocol, hostname } = new URL(siteUrl())
|
|
409
|
+
return (protocol === "https:" && (hostname === "wunderui.com" || hostname === "www.wunderui.com")) || ["localhost", "127.0.0.1", "[::1]"].includes(hostname)
|
|
410
|
+
} catch {
|
|
411
|
+
return false
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const authHeaders = () => (licenseKey() && keyAllowed() ? { authorization: `Bearer ${licenseKey()}` } : {})
|
|
381
415
|
let instructions = null
|
|
382
416
|
try {
|
|
383
417
|
instructions = readFileSync(resolve(dataDir, "INSTRUCTIONS.md"), "utf8")
|