@urbicon-ui/mcp-server 6.19.3 → 6.21.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 +4 -4
- package/src/data/design-system-loader.ts +3 -3
- package/src/tools/find-icons.ts +3 -2
- package/src/tools/get-css-reference.test.ts +28 -5
- package/src/tools/get-css-reference.ts +16 -9
- package/src/tools/suggest-implementation.ts +1 -1
- package/src/transports/http.ts +9 -6
- package/src/utils/format-catalog.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbicon-ui/mcp-server",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.21.1",
|
|
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,13 +32,13 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
-
"@urbicon-ui/design-content": "6.
|
|
36
|
-
"@urbicon-ui/design-engine": "6.
|
|
35
|
+
"@urbicon-ui/design-content": "6.21.1",
|
|
36
|
+
"@urbicon-ui/design-engine": "6.21.1",
|
|
37
37
|
"zod": "^4.3.6"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "^6.0.3",
|
|
41
|
-
"@types/node": "^25.9.
|
|
41
|
+
"@types/node": "^25.9.5",
|
|
42
42
|
"vitest": "^4.1.9"
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -88,8 +88,8 @@ function extractDescription(content: string): string {
|
|
|
88
88
|
if (titleIdx === -1) return '';
|
|
89
89
|
|
|
90
90
|
for (let i = titleIdx + 1; i < lines.length; i++) {
|
|
91
|
-
const line = lines[i]
|
|
92
|
-
if (line
|
|
91
|
+
const line = lines[i]?.trim();
|
|
92
|
+
if (!line) continue;
|
|
93
93
|
if (line.startsWith('#')) break;
|
|
94
94
|
return line;
|
|
95
95
|
}
|
|
@@ -115,7 +115,7 @@ export function extractPrincipleSection(content: string, topic: PrincipleTopic):
|
|
|
115
115
|
|
|
116
116
|
let endIdx = lines.length;
|
|
117
117
|
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
118
|
-
if (/^## /.test(lines[i]
|
|
118
|
+
if (/^## /.test(lines[i] ?? '')) {
|
|
119
119
|
endIdx = i;
|
|
120
120
|
break;
|
|
121
121
|
}
|
package/src/tools/find-icons.ts
CHANGED
|
@@ -36,8 +36,9 @@ export function registerFindIconsTool(server: McpServer): void {
|
|
|
36
36
|
const byCategory = new Map<string, typeof icons>();
|
|
37
37
|
for (const icon of icons) {
|
|
38
38
|
for (const cat of icon.categories) {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const bucket = byCategory.get(cat);
|
|
40
|
+
if (bucket) bucket.push(icon);
|
|
41
|
+
else byCategory.set(cat, [icon]);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -16,11 +16,15 @@ import { OVERVIEW, SECTIONS } from './get-css-reference.js';
|
|
|
16
16
|
* assert each is documented, so a newly added token can no longer disappear.
|
|
17
17
|
*
|
|
18
18
|
* Scope: the three families `get_css_reference` enumerates exhaustively (one row
|
|
19
|
-
* per token)
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* per token), plus a lighter check that every intent is at least *named* (its base
|
|
20
|
+
* `--color-<intent>` token or a `bg-<intent>` utility appears in the prose) — the
|
|
21
|
+
* F-B drift where a whole intent (`info`, a real ramp behind `bg-info` /
|
|
22
|
+
* `--color-feedback-info`) went undocumented while the six others were listed. It
|
|
23
|
+
* deliberately does NOT require every intent scale step (`primary-50 … primary-950`,
|
|
24
|
+
* documented via shorthand), feedback/interactive, chart, or internal-only token
|
|
25
|
+
* (e.g. `skeleton-shimmer`, used by the Skeleton wave, never a consumer utility) to
|
|
26
|
+
* be spelled out. Whole-set token validity is already guarded by design-engine's
|
|
27
|
+
* `tokens.test.ts`.
|
|
24
28
|
*/
|
|
25
29
|
|
|
26
30
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -55,6 +59,15 @@ function deriveSemanticCores(family: string): string[] {
|
|
|
55
59
|
return [...cores].sort();
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
/** Intent names from the `=== X INTENT ===` section markers in the CSS — robust to
|
|
63
|
+
* the multi-line `--color-neutral` definition a self-referential regex would miss. */
|
|
64
|
+
function deriveIntents(): string[] {
|
|
65
|
+
const css = readFileSync(semantic, 'utf-8');
|
|
66
|
+
const cores = new Set<string>();
|
|
67
|
+
for (const m of css.matchAll(/=== ([A-Z-]+) INTENT ===/g)) cores.add(m[1]!.toLowerCase());
|
|
68
|
+
return [...cores].sort();
|
|
69
|
+
}
|
|
70
|
+
|
|
58
71
|
describe.skipIf(!cssAvailable)('get_css_reference token drift guard', () => {
|
|
59
72
|
for (const { family } of TABLED_FAMILIES) {
|
|
60
73
|
it(`documents every semantic \`${family}-*\` token defined in the CSS`, () => {
|
|
@@ -74,4 +87,14 @@ describe.skipIf(!cssAvailable)('get_css_reference token drift guard', () => {
|
|
|
74
87
|
);
|
|
75
88
|
});
|
|
76
89
|
}
|
|
90
|
+
|
|
91
|
+
it('names every intent defined in the CSS (base token or utility)', () => {
|
|
92
|
+
const missing = deriveIntents().filter(
|
|
93
|
+
(c) => !ALL_CONTENT.includes(`--color-${c}`) && !ALL_CONTENT.includes(`bg-${c}`)
|
|
94
|
+
);
|
|
95
|
+
expect(
|
|
96
|
+
missing,
|
|
97
|
+
`Intents in the CSS but absent from get_css_reference: ${missing.join(', ')}`
|
|
98
|
+
).toEqual([]);
|
|
99
|
+
});
|
|
77
100
|
});
|
|
@@ -5,7 +5,7 @@ export const OVERVIEW = `# Urbicon UI — CSS Design Tokens
|
|
|
5
5
|
|
|
6
6
|
## Architecture
|
|
7
7
|
Three CSS layers, imported in order:
|
|
8
|
-
1. \`foundation.css\` — Raw OKLCH color scales (neutral, primary, secondary, success, warning, danger)
|
|
8
|
+
1. \`foundation.css\` — Raw OKLCH color scales (neutral, warm-neutral, primary, secondary, success, warning, danger, info)
|
|
9
9
|
2. \`semantic.css\` — Purpose-based tokens that reference foundation (\`--color-surface-base\`, \`--color-text-primary\`, etc.)
|
|
10
10
|
3. \`interaction.css\` — Animation timing, easing, shadows, focus rings
|
|
11
11
|
|
|
@@ -50,7 +50,7 @@ The \`@theme\` block sets the Tailwind utility value. The \`:root\` rule overrid
|
|
|
50
50
|
→ \`get_css_reference(section="surfaces")\` — 11 surface background tokens
|
|
51
51
|
→ \`get_css_reference(section="text")\` — 9 text color tokens
|
|
52
52
|
→ \`get_css_reference(section="borders")\` — 5 border color tokens
|
|
53
|
-
→ \`get_css_reference(section="intents")\` — 6
|
|
53
|
+
→ \`get_css_reference(section="intents")\` — 6 component intents + the \`info\` status colour, feedback + interactive tokens
|
|
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
|
|
56
56
|
`;
|
|
@@ -137,7 +137,7 @@ Also available for intent-colored borders:
|
|
|
137
137
|
|
|
138
138
|
const INTENTS = `# Intent Color System
|
|
139
139
|
|
|
140
|
-
6
|
|
140
|
+
6 component intents (primary, secondary, success, warning, danger, neutral) plus a status \`info\` colour — each a full palette with 5 semantic variants + 11 foundation steps.
|
|
141
141
|
|
|
142
142
|
## Semantic Intent Tokens (auto dark mode)
|
|
143
143
|
|
|
@@ -151,7 +151,9 @@ Each intent has these variants (example: \`primary\`):
|
|
|
151
151
|
| \`--color-primary-subtle\` | \`bg-primary-subtle\` | Soft background | primary-50 | primary-900 |
|
|
152
152
|
| \`--color-primary-emphasis\` | \`bg-primary-emphasis\` | Strong/dark variant | primary-900 | — |
|
|
153
153
|
|
|
154
|
-
Same pattern applies to: \`success-*\`, \`warning-*\`, \`danger-*\`, \`secondary-*\`, \`neutral
|
|
154
|
+
Same pattern applies to: \`success-*\`, \`warning-*\`, \`danger-*\`, \`secondary-*\`, \`neutral-*\`.
|
|
155
|
+
|
|
156
|
+
\`info-*\` has the identical shape (\`--color-info\`, \`-hover\`, \`-active\`, \`-subtle\`, \`-emphasis\` → \`bg-info\`, \`text-info\`, \`bg-info-subtle\`, …) — the status/feedback blue (hue 220) behind Alert/Toast/Tooltip's info state, \`--color-feedback-info\`, and \`--color-chart-5\`. It is NOT in the global \`ComponentIntent\` union above: the feedback components with a built-in info state (Alert, Toast, Tooltip) do accept \`intent="info"\`, but generic components (Button, Badge, …) take the six-value union, so on those reach for the \`bg-info\`/\`text-info\` utilities rather than \`intent="info"\`.
|
|
155
157
|
|
|
156
158
|
## Foundation Intent Scales
|
|
157
159
|
|
|
@@ -163,16 +165,21 @@ Each intent has 11 numbered steps (50–950) for granular control:
|
|
|
163
165
|
--color-danger-50 through --color-danger-950
|
|
164
166
|
--color-secondary-50 through --color-secondary-950
|
|
165
167
|
--color-neutral-50 through --color-neutral-950
|
|
168
|
+
--color-info-50 through --color-info-950
|
|
166
169
|
\`\`\`
|
|
167
170
|
|
|
171
|
+
Two ramps carry stops beyond the standard 50–950:
|
|
172
|
+
- \`neutral\` adds finer steps — \`--color-neutral-0\` (pure white), \`-25\`, \`-650\`, \`-750\`, \`-850\` — so \`bg-neutral-650\` and friends are real tokens (they drive the surface/border light↔dark mappings), not hallucinations.
|
|
173
|
+
- \`--color-warm-neutral-50\` through \`-950\` is a separate warm-tinted greyscale ramp; the \`neutral\` intent borrows its lightness profile (see the theming section) rather than exposing it as a surface.
|
|
174
|
+
|
|
168
175
|
Tailwind usage: \`bg-primary-500\`, \`text-danger-700\`, \`border-success-300\`, etc.
|
|
169
176
|
|
|
170
177
|
## Feedback Tokens (for status messages)
|
|
171
178
|
|
|
172
179
|
| CSS Variable | Tailwind Utility | Maps to |
|
|
173
180
|
|---|---|---|
|
|
174
|
-
| \`--color-feedback-info\` | \`bg-feedback-info\` / \`text-feedback-info\` |
|
|
175
|
-
| \`--color-feedback-info-subtle\` | \`bg-feedback-info-subtle\` |
|
|
181
|
+
| \`--color-feedback-info\` | \`bg-feedback-info\` / \`text-feedback-info\` | info-500 |
|
|
182
|
+
| \`--color-feedback-info-subtle\` | \`bg-feedback-info-subtle\` | info-50 |
|
|
176
183
|
| \`--color-feedback-success\` | \`bg-feedback-success\` / \`text-feedback-success\` | success-500 |
|
|
177
184
|
| \`--color-feedback-success-subtle\` | \`bg-feedback-success-subtle\` | success-50 |
|
|
178
185
|
| \`--color-feedback-warning\` | \`bg-feedback-warning\` / \`text-feedback-warning\` | warning-500 |
|
|
@@ -229,8 +236,8 @@ Shadows automatically increase opacity in dark mode (0.05 → 0.2 for xs, etc.).
|
|
|
229
236
|
| \`--z-hide\` | -1 | Hidden |
|
|
230
237
|
| \`--z-base\` | 0 | Default |
|
|
231
238
|
| \`--z-docked\` | 10 | Docked elements |
|
|
232
|
-
| \`--z-dropdown\` | 9999 | Menus, listboxes, floating panels |
|
|
233
239
|
| \`--z-sticky\` | 1100 | Sticky headers |
|
|
240
|
+
| \`--z-dropdown\` | 1150 | Menus, listboxes, floating panels (above sticky, below banner) |
|
|
234
241
|
| \`--z-banner\` | 1200 | Banners |
|
|
235
242
|
| \`--z-overlay\` | 1300 | Overlays |
|
|
236
243
|
| \`--z-sidebar\` | 1350 | Sidebars |
|
|
@@ -338,7 +345,7 @@ For a temperature-free, true grayscale chassis (content-focused UIs), set chroma
|
|
|
338
345
|
|
|
339
346
|
## Intent-hue collisions
|
|
340
347
|
|
|
341
|
-
If your accent hue lands near an intent hue, the two become hard to tell apart. The library intents sit at: success 140, warning 80, danger 25, info 220, secondary 280. A green brand (~140) collides with success; an amber brand (~80) collides with warning. When that happens, re-tune the colliding intent ramp away from the accent (push the hue ±15–25° and/or drop its lightness so it reads as "status", not "brand"). \`apps/docs/src/lib/style/
|
|
348
|
+
If your accent hue lands near an intent hue, the two become hard to tell apart. The library intents sit at: success 140, warning 80, danger 25, info 220, secondary 280. A green brand (~140) collides with success; an amber brand (~80) collides with warning. When that happens, re-tune the colliding intent ramp away from the accent (push the hue ±15–25° and/or drop its lightness so it reads as "status", not "brand"). \`apps/docs/src/lib/style/rooms-docs.css\` is a worked example (green brand → success pushed to 150 + darkened, warning pulled to amber 55).
|
|
342
349
|
|
|
343
350
|
## Overriding Semantic Tokens
|
|
344
351
|
|
|
@@ -383,7 +390,7 @@ Fix: inside the scoped block, re-declare the derived tokens too, so substitution
|
|
|
383
390
|
/* ...and the same for any neutral-derived surface/text/border tokens you rely on */
|
|
384
391
|
}
|
|
385
392
|
\`\`\`
|
|
386
|
-
A global \`@theme\` block (the built-in themes, the Theme Builder output) does NOT hit this — everything lands on \`:root\`, the same element where the derived tokens compute, so re-declaration is unnecessary. Prefer global themes unless you genuinely need multiple themes live on one page. \`apps/docs/src/lib/style/
|
|
393
|
+
A global \`@theme\` block (the built-in themes, the Theme Builder output) does NOT hit this — everything lands on \`:root\`, the same element where the derived tokens compute, so re-declaration is unnecessary. Prefer global themes unless you genuinely need multiple themes live on one page. \`apps/docs/src/lib/style/rooms-docs.css\` is the canonical scoped example.
|
|
387
394
|
|
|
388
395
|
## Component-Level Overrides
|
|
389
396
|
|
|
@@ -81,7 +81,7 @@ const IMPLEMENTATION_RULES = `## Implementation Rules
|
|
|
81
81
|
- **Vary visual weight** — Don't use the same Card variant/padding everywhere. Reading-flow content → \`variant="quiet"\` (default) + \`padding="md"\`. Architectural delineation → \`variant="outlined"\` + \`padding="md"\`. Lifted content (cards-on-page) → \`variant="elevated"\` + \`padding="lg"\`. Popover-family floating surfaces → \`variant="floating"\`.
|
|
82
82
|
- **Color = meaning** — Neutral surfaces dominate (80–90%). Use \`intent\` colors ONLY for semantic meaning (status, severity, actions) — never as decoration.
|
|
83
83
|
- **Spacing = hierarchy** — Tight (\`gap-2\`/\`gap-3\`) within related items. Generous (\`gap-8\`/\`gap-10\`) between sections. Don't use uniform spacing everywhere.
|
|
84
|
-
- **Commit to a radius** — Pick
|
|
84
|
+
- **Commit to a radius** — Pick one radius philosophy and apply it consistently — cleanest via the semantic tier tokens (\`--radius-commit\`/\`-modify\`/\`-contain\`) or \`BlocksProvider\` defaults, so a whole component family moves together. Don't round a single component out of step with its peers, and don't just leave the bland defaults untouched.
|
|
85
85
|
- **Data-driven styling** — Different states/severities should look visually distinct (vary padding, font-weight, Badge variant, text color) — not just carry a different label.
|
|
86
86
|
- **Don't copy recipe styling** — Recipes show ONE interpretation. Create YOUR visual identity with your own spacing rhythm, color distribution, and layout density.
|
|
87
87
|
`;
|
package/src/transports/http.ts
CHANGED
|
@@ -17,8 +17,9 @@ export async function startHttpTransport(port: number): Promise<void> {
|
|
|
17
17
|
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
|
18
18
|
|
|
19
19
|
if (req.method === 'POST') {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const session = sessionId ? sessions.get(sessionId) : undefined;
|
|
21
|
+
if (session) {
|
|
22
|
+
await session.handleRequest(req, res);
|
|
22
23
|
} else if (!sessionId) {
|
|
23
24
|
const transport = new StreamableHTTPServerTransport({
|
|
24
25
|
sessionIdGenerator: () => crypto.randomUUID()
|
|
@@ -41,8 +42,9 @@ export async function startHttpTransport(port: number): Promise<void> {
|
|
|
41
42
|
);
|
|
42
43
|
}
|
|
43
44
|
} else if (req.method === 'GET') {
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const session = sessionId ? sessions.get(sessionId) : undefined;
|
|
46
|
+
if (session) {
|
|
47
|
+
await session.handleRequest(req, res);
|
|
46
48
|
} else {
|
|
47
49
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
48
50
|
res.end(
|
|
@@ -54,8 +56,9 @@ export async function startHttpTransport(port: number): Promise<void> {
|
|
|
54
56
|
);
|
|
55
57
|
}
|
|
56
58
|
} else if (req.method === 'DELETE') {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
const session = sessionId ? sessions.get(sessionId) : undefined;
|
|
60
|
+
if (session && sessionId) {
|
|
61
|
+
await session.handleRequest(req, res);
|
|
59
62
|
sessions.delete(sessionId);
|
|
60
63
|
} else {
|
|
61
64
|
res.writeHead(204);
|
|
@@ -46,8 +46,9 @@ export function formatCompactCatalog(
|
|
|
46
46
|
for (const comp of filtered) {
|
|
47
47
|
const primaryTag = comp.tags[0];
|
|
48
48
|
if (primaryTag && TAG_LABELS[primaryTag]) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const bucket = grouped.get(primaryTag);
|
|
50
|
+
if (bucket) bucket.push(comp);
|
|
51
|
+
else grouped.set(primaryTag, [comp]);
|
|
51
52
|
} else {
|
|
52
53
|
ungrouped.push(comp);
|
|
53
54
|
}
|