@urbicon-ui/mcp-server 6.3.1 → 6.3.3

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": "@urbicon-ui/mcp-server",
3
- "version": "6.3.1",
3
+ "version": "6.3.3",
4
4
  "description": "Model Context Protocol server exposing the Urbicon UI component catalog, recipes and design intelligence to LLM agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,8 +32,8 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@modelcontextprotocol/sdk": "^1.29.0",
35
- "@urbicon-ui/design-content": "6.3.1",
36
- "@urbicon-ui/design-engine": "6.3.1",
35
+ "@urbicon-ui/design-content": "6.3.3",
36
+ "@urbicon-ui/design-engine": "6.3.3",
37
37
  "zod": "^4.3.6"
38
38
  },
39
39
  "devDependencies": {
@@ -2,7 +2,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { matchComponents } from '@urbicon-ui/design-engine/search';
3
3
  import { z } from 'zod';
4
4
  import { loadCatalog } from '../data/catalog-loader.js';
5
- import { formatCompactCatalog } from '../utils/format-catalog.js';
5
+ import { formatCompactCatalog, formatComponentLine } from '../utils/format-catalog.js';
6
6
 
7
7
  export function registerFindComponentsTool(server: McpServer): void {
8
8
  server.tool(
@@ -43,20 +43,11 @@ export function registerFindComponentsTool(server: McpServer): void {
43
43
  let md = `# Search Results for "${query}"\n\n`;
44
44
  md += `> ${results.length} matching components.\n\n`;
45
45
 
46
+ // Same line format as the browse view — including the origin-package tag for
47
+ // non-blocks components, so a match like `Table` (from @urbicon-ui/table) is
48
+ // never mistaken for a blocks export.
46
49
  for (const comp of results) {
47
- const variants = comp.variants
48
- .filter(
49
- (v) => !['true', 'false'].every((b) => v.values.includes(b) && v.values.length <= 2)
50
- )
51
- .map((v) => `${v.name}: ${v.values.join('/')}`)
52
- .join(' · ');
53
-
54
- md += `- **${comp.name}** — ${comp.description}`;
55
- if (variants) md += ` | ${variants}`;
56
- if (comp.relatedComponents.length > 0) {
57
- md += ` | Related: ${comp.relatedComponents.join(', ')}`;
58
- }
59
- md += '\n';
50
+ md += formatComponentLine(comp);
60
51
  }
61
52
 
62
53
  md += '\n> Use `get_component` with the component slug for full API docs.\n';
@@ -0,0 +1,77 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { OVERVIEW, SECTIONS } from './get-css-reference.js';
6
+
7
+ /**
8
+ * Drift guard for the hand-maintained CSS token reference.
9
+ *
10
+ * `get_css_reference` inlines its token tables as TS strings — the MCP server
11
+ * ships standalone (no blocks CSS at runtime), the same constraint that keeps
12
+ * design-engine's `VALID_TOKEN_CORES` inline. The hazard of any hand-copied list
13
+ * is silent drift: `--color-border-hairline` was added to the CSS and went
14
+ * unmirrored here for a while. When the blocks CSS is present (i.e. running
15
+ * in-repo) we re-derive the semantic surface / text / border token cores and
16
+ * assert each is documented, so a newly added token can no longer disappear.
17
+ *
18
+ * Scope: the three families `get_css_reference` enumerates exhaustively (one row
19
+ * per token). It deliberately does NOT require every intent scale step
20
+ * (`primary-50 … primary-950`, documented via shorthand), feedback/interactive,
21
+ * chart, or internal-only token (e.g. `skeleton-shimmer`, used by the Skeleton
22
+ * wave, never a consumer utility) to be spelled out. Whole-set token validity is
23
+ * already guarded by design-engine's `tokens.test.ts`.
24
+ */
25
+
26
+ const __dirname = dirname(fileURLToPath(import.meta.url));
27
+ const semantic = resolve(
28
+ __dirname,
29
+ '..',
30
+ '..',
31
+ '..',
32
+ 'blocks',
33
+ 'src',
34
+ 'lib',
35
+ 'style',
36
+ 'semantic.css'
37
+ );
38
+ const cssAvailable = existsSync(semantic);
39
+
40
+ const ALL_CONTENT = [OVERVIEW, ...Object.values(SECTIONS)].join('\n');
41
+
42
+ /** Families `get_css_reference` tables exhaustively, with the prose count to verify. */
43
+ const TABLED_FAMILIES = [
44
+ { family: 'surface', section: SECTIONS.surfaces! },
45
+ { family: 'text', section: SECTIONS.text! },
46
+ { family: 'border', section: SECTIONS.borders! }
47
+ ] as const;
48
+
49
+ /** Unique semantic `--color-<family>-*` cores in the CSS (scoped re-declarations collapse). */
50
+ function deriveSemanticCores(family: string): string[] {
51
+ const css = readFileSync(semantic, 'utf-8');
52
+ const re = new RegExp(`--color-(${family}-[a-z-]+)\\s*:`, 'g');
53
+ const cores = new Set<string>();
54
+ for (const m of css.matchAll(re)) cores.add(m[1]!);
55
+ return [...cores].sort();
56
+ }
57
+
58
+ describe.skipIf(!cssAvailable)('get_css_reference token drift guard', () => {
59
+ for (const { family } of TABLED_FAMILIES) {
60
+ it(`documents every semantic \`${family}-*\` token defined in the CSS`, () => {
61
+ const missing = deriveSemanticCores(family).filter((c) => !ALL_CONTENT.includes(c));
62
+ expect(
63
+ missing,
64
+ `Semantic ${family} tokens in the CSS but absent from get_css_reference: ${missing.join(', ')}`
65
+ ).toEqual([]);
66
+ });
67
+ }
68
+
69
+ for (const { family, section } of TABLED_FAMILIES) {
70
+ it(`states the correct ${family}-token count`, () => {
71
+ const stated = Number(section.match(/(\d+)\s+tokens for/)?.[1]);
72
+ expect(stated, 'prose count drifted from the real token count').toBe(
73
+ deriveSemanticCores(family).length
74
+ );
75
+ });
76
+ }
77
+ });
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
3
 
4
- const OVERVIEW = `# Urbicon UI — CSS Design Tokens
4
+ export const OVERVIEW = `# Urbicon UI — CSS Design Tokens
5
5
 
6
6
  ## Architecture
7
7
  Three CSS layers, imported in order:
@@ -49,7 +49,7 @@ The \`@theme\` block sets the Tailwind utility value. The \`:root\` rule overrid
49
49
  ## Available Sections
50
50
  → \`get_css_reference(section="surfaces")\` — 11 surface background tokens
51
51
  → \`get_css_reference(section="text")\` — 9 text color tokens
52
- → \`get_css_reference(section="borders")\` — 4 border color tokens
52
+ → \`get_css_reference(section="borders")\` — 5 border color tokens
53
53
  → \`get_css_reference(section="intents")\` — 6 intent palettes (primary, success, danger, etc.)
54
54
  → \`get_css_reference(section="shadows")\` — 5 shadow tokens + z-index scale
55
55
  → \`get_css_reference(section="theming")\` — How to create custom themes, available presets
@@ -114,16 +114,18 @@ Light → Dark mapping:
114
114
 
115
115
  const BORDERS = `# Border Tokens
116
116
 
117
- 4 tokens for border colors. All auto-switch in dark mode.
117
+ 5 tokens for border colors. All auto-switch in dark mode.
118
118
 
119
119
  | CSS Variable | Tailwind Utility | Purpose |
120
120
  |---|---|---|
121
+ | \`--color-border-hairline\` | \`border-border-hairline\` | Faintest divider — translucent (alpha), not a neutral step |
121
122
  | \`--color-border-subtle\` | \`border-border-subtle\` | Gentle grouping |
122
123
  | \`--color-border-default\` | \`border-border-default\` | Standard borders |
123
124
  | \`--color-border-emphasis\` | \`border-border-emphasis\` | Emphasized borders |
124
125
  | \`--color-border-strong\` | \`border-border-strong\` | High-contrast borders |
125
126
 
126
127
  Light → Dark mapping:
128
+ - \`border-hairline\`: black 8% → white 6% (translucent, blends onto any surface)
127
129
  - \`border-subtle\`: neutral-200 → neutral-700
128
130
  - \`border-default\`: neutral-300 → neutral-600
129
131
  - \`border-emphasis\`: neutral-400 → neutral-500
@@ -416,7 +418,7 @@ So the only requirement is to import \`index.css\` (your app owns the Tailwind i
416
418
  **Do NOT add manual \`@source\` directives, and do NOT import the \`foundation\`/\`semantic\`/\`interaction\` subfiles instead of \`index.css\`** — the subfiles omit the \`@source\` directives (and global classes), which is the usual cause of "responsive layouts break in production".
417
419
  `;
418
420
 
419
- const SECTIONS: Record<string, string> = {
421
+ export const SECTIONS: Record<string, string> = {
420
422
  surfaces: SURFACES,
421
423
  text: TEXT,
422
424
  borders: BORDERS,
@@ -10,7 +10,7 @@ export function registerGetRecipeTool(server: McpServer): void {
10
10
  scenario: z
11
11
  .string()
12
12
  .describe(
13
- 'Recipe id: login, settings, dashboard, pricing, profile-card, data-table, command-palette'
13
+ 'Recipe id — e.g. login, settings, dashboard, pricing, profile-card, onboarding-flow, wizard, notification-center, or an auth flow (auth-invitation-register, auth-passkey-login, auth-password-reset). Pass any unrecognised id to get the full, current list.'
14
14
  )
15
15
  },
16
16
  { readOnlyHint: true },
@@ -110,7 +110,7 @@ function isBooleanVariant(values: string[]): boolean {
110
110
  );
111
111
  }
112
112
 
113
- function formatComponentLine(comp: ComponentCatalogEntry): string {
113
+ export function formatComponentLine(comp: ComponentCatalogEntry): string {
114
114
  const variants = comp.variants
115
115
  .filter((v) => !isBooleanVariant(v.values))
116
116
  .map((v) => `${v.name}: ${v.values.join('/')}`)