@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.
|
|
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.
|
|
36
|
-
"@urbicon-ui/design-engine": "6.3.
|
|
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
|
-
|
|
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")\` —
|
|
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
|
-
|
|
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,
|
package/src/tools/get-recipe.ts
CHANGED
|
@@ -10,7 +10,7 @@ export function registerGetRecipeTool(server: McpServer): void {
|
|
|
10
10
|
scenario: z
|
|
11
11
|
.string()
|
|
12
12
|
.describe(
|
|
13
|
-
'Recipe id
|
|
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('/')}`)
|