@neuctra/ui-mcp 0.4.3 → 0.4.4
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 +32 -13
- package/data/components.json +4 -4
- package/package.json +1 -1
- package/src/server.mjs +170 -0
package/README.md
CHANGED
|
@@ -107,17 +107,16 @@ the result back into its context before continuing.
|
|
|
107
107
|
The tools are designed to be called in roughly this order when generating UI, not
|
|
108
108
|
grabbed ad hoc:
|
|
109
109
|
|
|
110
|
-
1. **`
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
rules just tell you which colors exist, not how to use them well.
|
|
110
|
+
1. **`neuctra-ui-builder`** — use this for routine UI generation. It infers the UI
|
|
111
|
+
category, returns a focused UX plan, core styling and accessibility rules, relevant
|
|
112
|
+
design-guide sections, component candidates, and preference questions when useful.
|
|
113
|
+
2. Ask any high-impact questions returned in `uxPlan.preferenceQuestions`, then pass
|
|
114
|
+
answers back through `preferences` if they change the implementation.
|
|
116
115
|
3. **`list_components`** (filtered by category) or **`search_components`** (keyword) —
|
|
117
116
|
confirm which real components exist for what you need. This matters specifically
|
|
118
117
|
because the design guide can describe a *pattern* (e.g. a sidebar nav item) without
|
|
119
118
|
there being a dedicated component for it — don't assume one exists, confirm here.
|
|
120
|
-
4. **`get_component`** — full spec for every component before writing code against it.
|
|
119
|
+
4. **`get_component`** — full spec for every unfamiliar component before writing code against it.
|
|
121
120
|
Do this every time you touch a component you haven't already pulled specs for in the
|
|
122
121
|
current conversation, not just once at the start. Never guess prop names from memory.
|
|
123
122
|
5. **Write the UI**, applying the design rules, the tokens, and the exact props from
|
|
@@ -131,13 +130,32 @@ grabbed ad hoc:
|
|
|
131
130
|
each page (meta, OpenGraph and a woven JSON-LD graph in the project's framework) and
|
|
132
131
|
**`audit_page_seo`** to check the finished page.
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
the
|
|
133
|
+
Call `get_design_rules` for broad design reviews or unresolved edge cases, and `get_theme`
|
|
134
|
+
when the complete token/setup reference is needed. The full guide remains the source of
|
|
135
|
+
truth; the builder selects relevant sections for each request.
|
|
137
136
|
|
|
138
137
|
---
|
|
139
138
|
|
|
140
|
-
## 5. The
|
|
139
|
+
## 5. The nine tools, in detail
|
|
140
|
+
|
|
141
|
+
### `neuctra-ui-builder`
|
|
142
|
+
|
|
143
|
+
Creates a compact, category-aware brief for the host model. It infers or accepts a UI
|
|
144
|
+
category, selects relevant UX rules and component candidates, includes essential Neuctra
|
|
145
|
+
styling rules and semantic token names, and asks about visual direction, density, or the
|
|
146
|
+
primary action when those preferences are missing. It plans and curates context; the host
|
|
147
|
+
model writes the UI. Use `get_component` for exact props before implementing an unfamiliar
|
|
148
|
+
component.
|
|
149
|
+
|
|
150
|
+
**Input:** `{ request: string, category?: "auto" | "dashboard" | "landing-page" | "form" | "settings" | "authentication" | "data-table" | "ecommerce" | "documentation" | "custom", preferences?: { visualDirection?, density?, primaryAction?, platform?, audience? } }`
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"request": "Build a project dashboard where a team can review activity and invite members.",
|
|
155
|
+
"category": "auto",
|
|
156
|
+
"preferences": { "density": "balanced", "platform": "responsive" }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
141
159
|
|
|
142
160
|
### `list_components`
|
|
143
161
|
|
|
@@ -247,7 +265,8 @@ per-component usage guidance (when to reach for `Card` vs plain whitespace, `Mod
|
|
|
247
265
|
`Drawer`, `Table` vs `List`, etc.), spacing/color/border/radius/shadow conventions,
|
|
248
266
|
responsive design, accessibility, interaction design, and a final UI quality checklist.
|
|
249
267
|
|
|
250
|
-
|
|
268
|
+
Use this for full-guide reviews and edge cases. For routine generation, use
|
|
269
|
+
`neuctra-ui-builder` to retrieve the rules relevant to the request.
|
|
251
270
|
|
|
252
271
|
**Response shape (trimmed):**
|
|
253
272
|
```jsonc
|
|
@@ -420,7 +439,7 @@ neuctra-ui-mcp/
|
|
|
420
439
|
├── bin/
|
|
421
440
|
│ └── cli.mjs # entry point: connects the server to a stdio transport
|
|
422
441
|
├── src/
|
|
423
|
-
│ ├── server.mjs # defines the
|
|
442
|
+
│ ├── server.mjs # defines the 9 tools and their handlers
|
|
424
443
|
│ └── seo.mjs # pure builders behind generate_page_seo / audit_page_seo
|
|
425
444
|
├── scripts/
|
|
426
445
|
│ └── sync-registry.mjs # copies registry/components.json from neuctra-ui-package
|
package/data/components.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema#",
|
|
3
3
|
"package": "@neuctra/ui",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "1.0.14",
|
|
5
|
+
"generatedAt": "2026-10-07T06:01:15.313Z",
|
|
6
6
|
"componentCount": 79,
|
|
7
7
|
"components": [
|
|
8
8
|
{
|
|
@@ -3111,7 +3111,7 @@
|
|
|
3111
3111
|
},
|
|
3112
3112
|
{
|
|
3113
3113
|
"name": "size",
|
|
3114
|
-
"type": "\"sm\" | \"md\" | \"lg\"",
|
|
3114
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\"",
|
|
3115
3115
|
"required": false,
|
|
3116
3116
|
"default": null,
|
|
3117
3117
|
"description": "Size variant controlling padding, font size and icon sizing."
|
|
@@ -5315,7 +5315,7 @@
|
|
|
5315
5315
|
},
|
|
5316
5316
|
{
|
|
5317
5317
|
"name": "size",
|
|
5318
|
-
"type": "\"sm\" | \"md\" | \"lg\"",
|
|
5318
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\"",
|
|
5319
5319
|
"required": false,
|
|
5320
5320
|
"default": null,
|
|
5321
5321
|
"description": "Size variant controlling trigger/item padding and icon sizing."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neuctra/ui-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "MCP server exposing the @neuctra/ui component registry (props, defaults, examples, theme tokens) so AI coding tools generate accurate Neuctra UI code, plus SEO/AEO/GEO tools (per-page meta, woven JSON-LD, audits).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/server.mjs
CHANGED
|
@@ -52,6 +52,150 @@ function matchesQuery(component, query) {
|
|
|
52
52
|
return component.props.some((p) => p.name.toLowerCase().includes(q));
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
const UI_CATEGORIES = [
|
|
56
|
+
"dashboard", "landing-page", "form", "settings", "authentication",
|
|
57
|
+
"data-table", "ecommerce", "documentation", "custom",
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const CATEGORY_RULES = {
|
|
61
|
+
dashboard: [3, 4, 5, 7, 22, 23, 24, 27, 31, 35, 55, 57, 59],
|
|
62
|
+
"landing-page": [3, 7, 13, 24, 33, 35, 53, 55, 56, 57, 58, 59, 60],
|
|
63
|
+
form: [15, 16, 17, 24, 25, 27, 35, 47, 48, 59],
|
|
64
|
+
settings: [3, 7, 13, 15, 16, 17, 18, 21, 24, 35, 47, 48, 59],
|
|
65
|
+
authentication: [3, 13, 15, 24, 25, 27, 35, 47, 48, 56, 59],
|
|
66
|
+
"data-table": [3, 7, 22, 23, 24, 27, 31, 35, 47, 48, 59],
|
|
67
|
+
ecommerce: [3, 7, 13, 15, 22, 24, 25, 31, 33, 35, 47, 48, 55, 57, 59, 60],
|
|
68
|
+
documentation: [3, 4, 5, 8, 9, 10, 21, 23, 30, 35, 45, 47, 48, 53, 59, 61],
|
|
69
|
+
custom: [3, 35, 44, 47, 48, 49, 53, 59],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const CATEGORY_TERMS = {
|
|
73
|
+
dashboard: "dashboard analytics metric chart sidebar",
|
|
74
|
+
"landing-page": "hero marketing testimonial feature pricing",
|
|
75
|
+
form: "form input select checkbox radio date picker",
|
|
76
|
+
settings: "settings preferences profile toggle tabs",
|
|
77
|
+
authentication: "login sign up password email verification",
|
|
78
|
+
"data-table": "table data pagination filter sort",
|
|
79
|
+
ecommerce: "product cart checkout price inventory",
|
|
80
|
+
documentation: "docs article navigation breadcrumb code",
|
|
81
|
+
custom: "",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
function inferUiCategory(request) {
|
|
85
|
+
const text = request.toLowerCase();
|
|
86
|
+
const patterns = [
|
|
87
|
+
["authentication", /\b(login|log in|sign.?up|sign.?in|authentication|password reset)\b/],
|
|
88
|
+
["data-table", /\b(data.?table|table|records|spreadsheet|listing)\b/],
|
|
89
|
+
["ecommerce", /\b(e.?commerce|checkout|cart|product page|storefront|shop)\b/],
|
|
90
|
+
["documentation", /\b(documentation|docs page|knowledge base|article)\b/],
|
|
91
|
+
["settings", /\b(settings|preferences|configuration|profile)\b/],
|
|
92
|
+
["form", /\b(form|survey|wizard|application form)\b/],
|
|
93
|
+
["landing-page", /\b(landing page|homepage|marketing page|hero section)\b/],
|
|
94
|
+
["dashboard", /\b(dashboard|admin panel|analytics|metrics)\b/],
|
|
95
|
+
];
|
|
96
|
+
return patterns.find(([, pattern]) => pattern.test(text))?.[0] ?? "custom";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function makeUiBuildBrief({ request, category, preferences, registry, theme, aiDesignRules }) {
|
|
100
|
+
const resolvedCategory = category === "auto" ? inferUiCategory(request) : category;
|
|
101
|
+
const requestSignals = [
|
|
102
|
+
["create", /\b(create|add|new|invite|register)\b/i],
|
|
103
|
+
["review", /\b(review|view|inspect|monitor|track|activity|analytics)\b/i],
|
|
104
|
+
["search", /\b(search|find|filter|sort)\b/i],
|
|
105
|
+
["edit", /\b(edit|update|change|manage|configure)\b/i],
|
|
106
|
+
["complete", /\b(complete|submit|checkout|send|publish|save)\b/i],
|
|
107
|
+
["compare", /\b(compare|pricing|plan|option|alternative)\b/i],
|
|
108
|
+
].filter(([, pattern]) => pattern.test(request)).map(([signal]) => signal);
|
|
109
|
+
const sections = new Map(aiDesignRules.sections.map((section) => [section.number, section]));
|
|
110
|
+
const sharedRuleNumbers = [1, 2, 35, 37, 43, 44, 47, 48, 49, 50, 51, 59];
|
|
111
|
+
const selectedNumbers = [...new Set([...sharedRuleNumbers, ...(CATEGORY_RULES[resolvedCategory] ?? [])])];
|
|
112
|
+
const relevantRules = selectedNumbers
|
|
113
|
+
.map((number) => sections.get(number))
|
|
114
|
+
.filter(Boolean)
|
|
115
|
+
.map(({ number, title, content }) => ({ number, title, content }));
|
|
116
|
+
const terms = CATEGORY_TERMS[resolvedCategory] ?? "";
|
|
117
|
+
const candidates = terms
|
|
118
|
+
? registry.components
|
|
119
|
+
.filter((component) => terms.split(" ").some((term) => matchesQuery(component, term)))
|
|
120
|
+
.slice(0, 8)
|
|
121
|
+
.map(summarize)
|
|
122
|
+
: [];
|
|
123
|
+
|
|
124
|
+
const missingPreferences = [];
|
|
125
|
+
if (!preferences.visualDirection) missingPreferences.push({
|
|
126
|
+
id: "visualDirection", question: "What visual direction should this use?",
|
|
127
|
+
options: ["Neuctra default", "Minimal", "Editorial", "Dense and utilitarian"],
|
|
128
|
+
});
|
|
129
|
+
if (!preferences.density) missingPreferences.push({
|
|
130
|
+
id: "density", question: "How dense should the interface feel?",
|
|
131
|
+
options: ["Balanced", "Compact", "Spacious"],
|
|
132
|
+
});
|
|
133
|
+
if (!preferences.primaryAction && resolvedCategory !== "documentation") missingPreferences.push({
|
|
134
|
+
id: "primaryAction", question: "What is the main action users should take?",
|
|
135
|
+
options: [],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const compactTheme = {
|
|
139
|
+
system: theme.system,
|
|
140
|
+
tokens: theme.tokens.map(({ token, className, pairsWith, usage }) => ({ token, className, ...(pairsWith ? { pairsWith } : {}), usage })),
|
|
141
|
+
toastNote: theme.toastNote,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
category: resolvedCategory,
|
|
146
|
+
categoryWasInferred: category === "auto",
|
|
147
|
+
request,
|
|
148
|
+
requestAnalysis: {
|
|
149
|
+
detectedTaskSignals: requestSignals,
|
|
150
|
+
userGoal: requestSignals.length
|
|
151
|
+
? `Support the requested ${requestSignals.join(", ")} task${requestSignals.length > 1 ? "s" : ""} with a clear primary path.`
|
|
152
|
+
: "Use the request's stated outcome to define the primary path; do not add unrelated features.",
|
|
153
|
+
categoryConsiderations: {
|
|
154
|
+
dashboard: "Prioritize the most actionable metrics and provide clear routes to underlying records.",
|
|
155
|
+
"landing-page": "Make the value proposition and primary conversion action clear before secondary details.",
|
|
156
|
+
form: "Group fields by meaning, explain validation, and preserve entered data when errors occur.",
|
|
157
|
+
settings: "Group controls by user mental model and make saved versus unsaved changes clear.",
|
|
158
|
+
authentication: "Keep the entry task focused and make recovery and validation paths easy to find.",
|
|
159
|
+
"data-table": "Make scanning, filtering, row actions, and narrow-screen behavior usable.",
|
|
160
|
+
ecommerce: "Keep product choice, price, availability, and the next purchase action easy to understand.",
|
|
161
|
+
documentation: "Prioritize readable content, table of contents, and predictable navigation.",
|
|
162
|
+
custom: "Choose patterns from the user's task and content; avoid adding interface chrome without a need.",
|
|
163
|
+
}[resolvedCategory],
|
|
164
|
+
},
|
|
165
|
+
preferences,
|
|
166
|
+
uxPlan: {
|
|
167
|
+
objective: "Make the user's primary task obvious, then keep supporting choices and information in a clear hierarchy.",
|
|
168
|
+
layoutApproach: resolvedCategory === "dashboard" || resolvedCategory === "documentation"
|
|
169
|
+
? "Use a responsive app shell with grouped navigation and a focused content area."
|
|
170
|
+
: resolvedCategory === "landing-page"
|
|
171
|
+
? "Lead with the value proposition and primary action, then vary section layouts to support the story."
|
|
172
|
+
: "Keep the primary task prominent, group related content, and avoid panels where whitespace is enough.",
|
|
173
|
+
interactionAndStates: ["Define loading, empty, error, success, and disabled states where applicable.", "Use clear labels, keyboard-accessible controls, visible focus, and touch-friendly targets."],
|
|
174
|
+
responsiveBehavior: preferences.platform === "desktop"
|
|
175
|
+
? "Prioritize desktop workflows while keeping the layout usable at narrower widths and preventing horizontal overflow."
|
|
176
|
+
: preferences.platform === "mobile"
|
|
177
|
+
? "Prioritize the mobile task flow, touch targets, and compact navigation; prevent horizontal overflow."
|
|
178
|
+
: "Design mobile-first; preserve task order, prevent horizontal overflow, and adapt navigation and data views to narrow screens.",
|
|
179
|
+
performanceGuidance: ["Keep client-side state limited to interactive needs and avoid unnecessary rerenders.", "Paginate or virtualize large data sets; lazy-load below-the-fold media."],
|
|
180
|
+
preferenceQuestions: missingPreferences,
|
|
181
|
+
},
|
|
182
|
+
recommendedComponents: candidates,
|
|
183
|
+
designContext: {
|
|
184
|
+
coreStylingRules: theme.rules,
|
|
185
|
+
antiAiLookRules: theme.antiAiLookRules,
|
|
186
|
+
theme: compactTheme,
|
|
187
|
+
relevantProductRules: relevantRules,
|
|
188
|
+
goldenRule: aiDesignRules.goldenRule,
|
|
189
|
+
},
|
|
190
|
+
implementationNotes: [
|
|
191
|
+
"Use semantic Neuctra theme classes so light and dark themes work; do not hardcode palette colors.",
|
|
192
|
+
"Use the selected components' built-in design and exact documented props.",
|
|
193
|
+
"Call get_component for each unfamiliar component before writing its code.",
|
|
194
|
+
"Apply only the included category rules; retrieve get_design_rules for any unresolved edge case.",
|
|
195
|
+
],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
55
199
|
export function createServer(data) {
|
|
56
200
|
const { registry, theme, aiDesignRules, seoGuide } = data ?? loadDataFromDisk();
|
|
57
201
|
const componentsByName = new Map(registry.components.map((c) => [c.name, c]));
|
|
@@ -154,6 +298,32 @@ export function createServer(data) {
|
|
|
154
298
|
},
|
|
155
299
|
);
|
|
156
300
|
|
|
301
|
+
server.registerTool(
|
|
302
|
+
"neuctra-ui-builder",
|
|
303
|
+
{
|
|
304
|
+
title: "Plan Neuctra UI with focused design context",
|
|
305
|
+
description:
|
|
306
|
+
"Prepare a compact, category-aware implementation brief for a UI request. Infers or accepts the UI category, identifies relevant Neuctra components and design-guide sections, includes essential theme/accessibility/responsive rules, and returns optional preference questions. Use this instead of loading the full design guide for routine UI generation. It plans and curates context; the host model writes the UI. Call get_component for exact props before using unfamiliar components, and get_design_rules for edge cases.",
|
|
307
|
+
inputSchema: {
|
|
308
|
+
request: z.string().min(8).describe("What UI to build and what users need to accomplish."),
|
|
309
|
+
category: z.enum(["auto", ...UI_CATEGORIES]).default("auto").describe("UI category, or auto to infer it from the request."),
|
|
310
|
+
preferences: z.object({
|
|
311
|
+
visualDirection: z.string().optional().describe("Visual style preference, if known."),
|
|
312
|
+
density: z.enum(["compact", "balanced", "spacious"]).optional(),
|
|
313
|
+
primaryAction: z.string().optional().describe("The main user action, if known."),
|
|
314
|
+
platform: z.enum(["desktop", "mobile", "responsive"]).optional(),
|
|
315
|
+
audience: z.string().optional(),
|
|
316
|
+
}).default({}).describe("Optional user preferences; unanswered high-impact choices are returned as concise questions."),
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
async ({ request, category, preferences }) => ({
|
|
320
|
+
content: [{
|
|
321
|
+
type: "text",
|
|
322
|
+
text: JSON.stringify(makeUiBuildBrief({ request, category, preferences, registry, theme, aiDesignRules }), null, 2),
|
|
323
|
+
}],
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
|
|
157
327
|
server.registerTool(
|
|
158
328
|
"get_theme",
|
|
159
329
|
{
|