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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wunderui-mcp",
3
- "version": "1.1.0",
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.30.0",
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
- const authHeaders = () => (licenseKey() ? { authorization: `Bearer ${licenseKey()}` } : {})
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")