@neuctra/ui-mcp 0.2.5 → 0.4.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/data/theme.json CHANGED
@@ -1,12 +1,34 @@
1
1
  {
2
2
  "system": "Tailwind CSS v4, semantic CSS-variable tokens mapped via @theme, toggled by a .dark class (not prefers-color-scheme).",
3
3
  "rules": [
4
- "Never use hardcoded Tailwind palette colors (text-blue-500, bg-red-500, border-gray-200, etc.) or inline hex/rgb colors in generated UI.",
4
+ "Theme colors only, compulsory, no exceptions: never use hardcoded Tailwind palette colors (text-blue-500, bg-red-500, border-gray-200, etc.), inline hex/rgb colors, or any other extra/arbitrary color not in the token list below, anywhere in generated UI — not even for a one-off accent, illustration, or 'just this one element'. Every color used must be one of the semantic tokens below.",
5
5
  "Always use the semantic token classes below (bg-primary, text-foreground, border-border, ...) so generated UI automatically follows the consumer's theme in both light and dark mode.",
6
6
  "Every base token except background/foreground/border/input/ring has a matching '-foreground' token for text/icons placed on top of it (e.g. bg-primary + text-primary-foreground, bg-destructive + text-destructive-foreground).",
7
- "Page background: bg-background, text-foreground. Card/panel surfaces: bg-card (or bg-muted/bg-accent for secondary surfaces). Popovers/dropdowns/tooltips: bg-popover.",
7
+ "Page background: bg-background. Card surfaces: bg-card. A distinct section within a page (not the whole page, not a card): bg-accent, or bg-card again if it needs to read as a panel. Popovers/dropdowns/tooltips: bg-popover. Never use bg-muted as a page, section, or card background — muted is reserved for non-surface fills only (skeleton placeholders, disabled-state fills, inline code backgrounds), not for anything a user reads content on top of.",
8
8
  "Status colors: success (positive/confirmation), warning (caution), info (neutral notice), destructive (danger/errors) — use these instead of ad-hoc green/yellow/blue/red.",
9
- "Dark mode requires no extra work in generated code: the same class names resolve to the .dark palette automatically because the consumer's CSS defines both blocks. Do not write dark: variants for color — only use dark: for things the token system doesn't cover (rare)."
9
+ "Dark mode requires no extra work in generated code: the same class names resolve to the .dark palette automatically because the consumer's CSS defines both blocks. Do not write dark: variants for color — only use dark: for things the token system doesn't cover (rare).",
10
+ "Use the Card component, not a hand-rolled lookalike: this rule only applies once you've decided a group of content (a settings section, a stat/summary tile, a list item container, a form section, a feature block, etc.) needs a bounded/bordered panel treatment — it does not mean every grouping must be boxed (see the design rules' 'less UI is often better UI' guidance: whitespace-only grouping is often correct and a Card is often unnecessary). When a panel treatment is warranted, build it with <Card> — never a raw div styled to look like a card (e.g. bg-card border border-border rounded-lg p-4 by hand). <CardBody> is compulsory whenever the card has body content, not optional: <Card>'s own root element applies zero padding (only the border/background/radius) — all internal spacing lives in the sub-components via their shared padding context, so placing content directly as a child of <Card> without wrapping it in <CardBody> renders that content flush against the card's border with no padding, which visibly breaks the UI. <CardHeader> (title/description/icon/actions) and <CardFooter> (actions/meta) remain genuinely optional — include each only if that section is needed — but never skip <CardBody> when there is body content, and never place content as a direct child of <Card> itself.",
11
+ "Dropdown's trigger button: never call e.stopPropagation() (or preventDefault() for the same purpose) in an onClick you attach to the Dropdown's trigger element. The Dropdown component already stops propagation internally to manage its own open/close and outside-click behavior; adding it again on the consumer's trigger handler is redundant and can break the dropdown's outside-click / nested-dropdown / row-click interactions. If the trigger sits inside another clickable element (a table row, a card), rely on Dropdown's own handling instead of manually stopping propagation.",
12
+ "Do not overwrite a component's built-in design. Use each @neuctra/ui component's default classes/variants/sizes as-is by default — do not routinely pass a className that overrides its built-in spacing, radius, colors, or layout just to restyle it. Only override with Tailwind's `!` important-modifier (e.g. `!bg-red-500`, `!p-2`) when there's a real, specific need — the component's default would otherwise break the current page's existing layout/design, collide with a surrounding container, or the component itself has no prop for the needed variation — never as a routine styling habit or a stylistic preference."
13
+ ],
14
+ "antiAiLookRules": [
15
+ "This section exists because AI-generated UI has recognizable tells. Read it before generating anything visual — the goal is UI indistinguishable from a deliberate human design pass, not a demo that screams 'built by an LLM'.",
16
+ "No gradients, compulsory: never use gradients on backgrounds, buttons, or text (bg-gradient-to-r, from-purple-500 to-pink-500, bg-clip-text text-transparent, etc.). Use a flat bg-primary (or the relevant status token) instead. This is the single most common AI-UI tell — do not use it even 'just for one hero section'.",
17
+ "No shadows, compulsory: never add box-shadow/drop-shadow utilities (shadow-sm through shadow-2xl, glowing hover rings, colored blur halos) to any element YOU style directly — buttons, badges, cards, inputs, hand-built panels/overlays, or any other static surface you write classNames for. A flat surface with border-border is correct. Scope note (applies to every rule in this list, not just this one): this governs classNames/markup you author yourself. It does not mean stripping or fighting any pre-built @neuctra/ui component's own internal default styling, no matter which component or what that default is (several components ship their own shadow — e.g. Modal, Drawer, Popover, Dropdown, Tooltip, Table, DatePicker's calendar panel, Avatar's status dot, Kbd, and the Toast/ToastProvider notification — this is not an exhaustive list and isn't meant to be maintained as one). Per the 'don't overwrite built-in component design' rule: leave every component's shipped defaults exactly as they ship. Never add a decorative shadow yourself, and never override one away with `!important` either — both are the same mistake in opposite directions.",
18
+ "No blur effects, compulsory: never use backdrop-blur or any glassmorphism treatment (frosted-glass cards, translucent bg-white/10 panels) on any element YOU style directly, including a hand-built modal/drawer backdrop — use an opaque or semi-opaque solid backdrop (e.g. bg-background/80) instead of a blurred one. Surfaces you author are opaque (bg-card, bg-popover, bg-background), not translucent. Same scope note as the shadow rule above: this is about classNames/markup you write, not about a pre-built @neuctra/ui component's own internal backdrop-blur (e.g. Modal/Drawer's backdrop, Dropdown's menu panel, Carousel's nav buttons ship with backdrop-blur baked in, among others) — leave every component's shipped defaults exactly as they ship, whichever component it is.",
19
+ "No blanket border-radius escalation — don't reach for rounded-2xl/rounded-3xl/rounded-full on every card, input, and button by default. Match the radius already used by the surrounding neuctra-ui components (rounded-md/rounded-lg/rounded-xl per component, see each component's own classes) instead of picking a bigger one because it 'looks more modern'.",
20
+ "No decorative hover/focus glow rings. ring-* and focus-visible:ring-ring exist for accessibility (keyboard focus indication) only — never add a colored ring or glow as a pointer-hover decoration on cards, buttons, or images.",
21
+ "No animate-pulse / bounce / scale-105-on-hover / floating-icon motion sprinkled across static content as decoration. Motion should communicate state (loading, a toast entering, a modal opening) — components already handle that internally. Don't add extra hover-scale or idle animation to elements that aren't interactive or aren't mid-transition.",
22
+ "No emoji used as icons or bullet markers in generated UI (✨, 🚀, 💡, ✅ as list bullets, feature markers, or button icons). Use lucide-react icons — the same icon set every neuctra-ui component already uses — passed via each component's icon props (iconBefore, iconAfter, icon, etc.).",
23
+ "No 'icon in a colored circle badge' repeated on every single feature/stat/card by default (a bg-primary/10 rounded-full p-3 wrapper around every icon). Use it sparingly, only where the component's own design already calls for it — repeating the same decorative wrapper on every card in a grid is a generic-template tell.",
24
+ "No arbitrary one-off spacing/sizing values (p-[13px], gap-[7px], w-[327px]) — use the Tailwind spacing scale (p-3, gap-2, w-80, etc.) so spacing stays consistent with the rest of the design system.",
25
+ "Prefer information density and restraint over generic centered-hero-with-huge-padding layouts. Not every section needs max-w-7xl mx-auto text-center py-24 — match the density and layout the consumer's existing UI already uses.",
26
+ "Make every layout fully responsive by default — mobile, tablet, and desktop (sm:/md:/lg:/xl: breakpoints), stacking to a single column and adjusting spacing/type-scale at small widths. Never ship a fixed-width or desktop-only layout and call it done.",
27
+ "No em dashes (—), compulsory: never use an em dash in generated copy or UI text (headings, body copy, button labels, placeholder text). It's a recognizable AI-writing tell. Use a period, comma, colon, or restructure the sentence instead.",
28
+ "No over-effects: don't stack multiple decorative treatments on one element (e.g. a gradient plus a shadow plus a hover-scale plus a glow, all on the same button). Every visual effect that does appear should communicate a real state or interaction (hover, focus, loading, an entering toast) — never decoration for its own sake.",
29
+ "Use correct line-height and letter-spacing for readability. Body text: leading-relaxed or leading-normal, never leading-none/leading-tight on paragraphs. Letter-spacing: leave at the default (tracking-normal) for body and headings; only use tracking-wide for short uppercase labels (badges, eyebrow text, nav labels), and never tracking-wide on paragraph-length text.",
30
+ "The bar is modern, visually appealing UI achieved through typography hierarchy, whitespace, correct token usage, and responsive layout, not through gradients, shadows, blur, or glow effects standing in for actual design decisions.",
31
+ "Decent, restrained design, compulsory: build UI that is decent and intentional, never over-designed. When unsure whether an effect (shadow, gradient, blur, glow, animation, an extra color) is warranted on something you're authoring yourself: leave it out. A flat, token-colored, appropriately-radiused, unshadowed, responsive surface using only the semantic color tokens is the correct default for the overwhelming majority of neuctra-ui components — restraint is the default, not the exception."
10
32
  ],
11
33
  "tokens": [
12
34
  { "token": "primary", "className": "bg-primary / text-primary / border-primary", "pairsWith": "primary-foreground", "light": "#00c214", "dark": "#00c214", "usage": "Brand color: primary buttons, active states, links, focus accents." },
@@ -33,5 +55,18 @@
33
55
  { "token": "info", "className": "bg-info / text-info / border-info", "pairsWith": "info-foreground", "light": "#2563eb", "dark": "#3b82f6", "usage": "Neutral notices, informational banners/badges." },
34
56
  { "token": "info-foreground", "className": "text-info-foreground", "light": "#fafafa", "dark": "#eff6ff", "usage": "Text on bg-info." }
35
57
  ],
36
- "note": "Values shown are the CLI-generated defaults (npx @neuctra/ui-cli init). A consumer project may have redefined these hex values in its own CSS — the token NAMES and pairing rules are the stable contract; always prefer the class names over the hex values."
58
+ "note": "Values shown are the CLI-generated defaults (npx @neuctra/ui-cli init). A consumer project may have redefined these hex values in its own CSS — the token NAMES and pairing rules are the stable contract; always prefer the class names over the hex values.",
59
+ "setup": {
60
+ "recommendedCommand": "npx @neuctra/ui-cli@latest init",
61
+ "whatItDoes": [
62
+ "Installs @neuctra/ui.",
63
+ "Checks React (upgrades to >=18 if needed) and Tailwind CSS (upgrades to v4 if needed) — @neuctra/ui requires Tailwind v4 syntax (@theme, @source, @custom-variant).",
64
+ "Writes/updates the project's global CSS with the token blocks shown in this response (:root + .dark + @theme mapping) and the correct @source path for the project's own layout.",
65
+ "Generates a theme context file at src/contexts/ThemeContext.jsx (or .tsx if the project is TypeScript) exporting ThemeProvider and useTheme — handles localStorage persistence, system-preference detection with live updates, and avoids a flash of the wrong theme on load.",
66
+ "Wraps the app's entry point in <ThemeProvider>."
67
+ ],
68
+ "whenToSuggestIt": "If a project doesn't have @neuctra/ui installed yet, or has no theme token CSS / no ThemeContext, suggest running this command instead of hand-writing the CSS token blocks or a theme context from scratch — it is the maintained, correct source for both.",
69
+ "manualSetupStillDocumented": "If the consumer can't run a CLI (e.g. you're only shown existing code, not given shell access), the full manual step-by-step — including the exact ThemeContext implementation the CLI generates — is at the project's /docs/full-setup page (FullSetupDocPage) and is safe to reproduce by hand."
70
+ },
71
+ "toastNote": "@neuctra/ui's toast notification system is a standalone function, not hook-only: import { toast, ToastProvider } from '@neuctra/ui' and call toast(...) / toast.success(...) / toast.error(...) / toast.warning(...) / toast.info(...) / toast.loading(...) / toast.promise(...) from anywhere — event handlers, API clients, utility files — no useToast() hook required at the call site. Mount <ToastProvider> once near the app root so there is somewhere for toasts to render; mounting it more than once duplicates every toast. useToast() still exists for consumers who prefer a hook — it returns the identical global toast/dismiss, so both styles stay in sync. See get_component with name \"ToastProvider\" for the full prop list (including position, one of top-left/top-center/top-right/bottom-left/bottom-center/bottom-right)."
37
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuctra/ui-mcp",
3
- "version": "0.2.5",
3
+ "version": "0.4.1",
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.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,6 +26,7 @@
26
26
  ".": "./src/server.mjs",
27
27
  "./data/components.json": "./data/components.json",
28
28
  "./data/theme.json": "./data/theme.json",
29
+ "./data/aiDesignRules.json": "./data/aiDesignRules.json",
29
30
  "./package.json": "./package.json"
30
31
  },
31
32
  "files": [
package/src/server.mjs CHANGED
@@ -7,18 +7,23 @@ import { z } from "zod";
7
7
  // Reads data/*.json from disk. Only works on runtimes with a filesystem
8
8
  // (Node, Bun, Deno) — callers on edge/serverless runtimes without one
9
9
  // (Cloudflare Workers) must import the JSON files themselves at build time
10
- // and pass them into createServer({ registry, theme }) instead. Resolving
11
- // __dirname is deferred inside this function (not module top-level) because
12
- // import.meta.url is unavailable in some bundled edge runtimes even when
13
- // this function itself is never called there.
10
+ // and pass them into createServer({ registry, theme, aiDesignRules }) instead.
11
+ // Resolving __dirname is deferred inside this function (not module top-level)
12
+ // because import.meta.url is unavailable in some bundled edge runtimes even
13
+ // when this function itself is never called there.
14
14
  function loadDataFromDisk() {
15
15
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
16
  const DATA_DIR = path.resolve(__dirname, "..", "data");
17
17
  const registry = JSON.parse(
18
18
  fs.readFileSync(path.join(DATA_DIR, "components.json"), "utf8"),
19
19
  );
20
- const theme = JSON.parse(fs.readFileSync(path.join(DATA_DIR, "theme.json"), "utf8"));
21
- return { registry, theme };
20
+ const theme = JSON.parse(
21
+ fs.readFileSync(path.join(DATA_DIR, "theme.json"), "utf8"),
22
+ );
23
+ const aiDesignRules = JSON.parse(
24
+ fs.readFileSync(path.join(DATA_DIR, "aiDesignRules.json"), "utf8"),
25
+ );
26
+ return { registry, theme, aiDesignRules };
22
27
  }
23
28
 
24
29
  function summarize(component) {
@@ -39,12 +44,12 @@ function matchesQuery(component, query) {
39
44
  }
40
45
 
41
46
  export function createServer(data) {
42
- const { registry, theme } = data ?? loadDataFromDisk();
47
+ const { registry, theme, aiDesignRules } = data ?? loadDataFromDisk();
43
48
  const componentsByName = new Map(registry.components.map((c) => [c.name, c]));
44
49
 
45
50
  const server = new McpServer({
46
51
  name: "neuctra-ui",
47
- version: "0.1.0",
52
+ version: "0.4.0",
48
53
  });
49
54
 
50
55
  server.registerTool(
@@ -88,7 +93,9 @@ export function createServer(data) {
88
93
  inputSchema: {
89
94
  name: z
90
95
  .string()
91
- .describe('Exact component name, e.g. "Input", "Select", "Modal", "CardHeader".'),
96
+ .describe(
97
+ 'Exact component name, e.g. "Input", "Select", "Modal", "CardHeader".',
98
+ ),
92
99
  },
93
100
  },
94
101
  async ({ name }) => {
@@ -123,13 +130,15 @@ export function createServer(data) {
123
130
  {
124
131
  title: "Search Neuctra UI components",
125
132
  description:
126
- "Search components by keyword against name, category, description, and prop names. Use this when you know what you need (e.g. \"date picker\", \"loading state\", \"icon button\") but not the exact component name.",
133
+ 'Search components by keyword against name, category, description, and prop names. Use this when you know what you need (e.g. "date picker", "loading state", "icon button") but not the exact component name.',
127
134
  inputSchema: {
128
135
  query: z.string().describe("Keyword or short phrase to search for."),
129
136
  },
130
137
  },
131
138
  async ({ query }) => {
132
- const results = registry.components.filter((c) => matchesQuery(c, query)).map(summarize);
139
+ const results = registry.components
140
+ .filter((c) => matchesQuery(c, query))
141
+ .map(summarize);
133
142
  return {
134
143
  content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
135
144
  };
@@ -139,14 +148,40 @@ export function createServer(data) {
139
148
  server.registerTool(
140
149
  "get_theme",
141
150
  {
142
- title: "Get Neuctra UI theming rules and tokens",
151
+ title: "Get Neuctra UI theming tokens",
152
+ description:
153
+ "Get the semantic color token system (bg-primary, text-foreground, etc.), the recommended `@neuctra/ui-cli` setup command, and how the toast notification API works. Call get_design_rules alongside this before generating any UI — not just for colors.",
154
+ inputSchema: {},
155
+ },
156
+ async () => {
157
+ const { rules, antiAiLookRules, ...themeOnly } = theme;
158
+ return {
159
+ content: [{ type: "text", text: JSON.stringify(themeOnly, null, 2) }],
160
+ };
161
+ },
162
+ );
163
+
164
+ server.registerTool(
165
+ "get_design_rules",
166
+ {
167
+ title: "Get Neuctra UI design rules",
143
168
  description:
144
- "Get the semantic color token system (bg-primary, text-foreground, etc.) and the styling rules generated UI must follow. Call this before generating any UI so colors come from theme tokens instead of hardcoded Tailwind palette classes or hex values — hardcoded colors will not respect the consumer's light/dark theme.",
169
+ "Get every design rule generated UI must follow when using @neuctra/ui. Covers two levels: (1) styling rules — token usage, surface/background conventions, component composition requirements like CardBody being compulsory whenever Card has body content, component-specific gotchas like Dropdown's trigger propagation, and the full set of rules for avoiding the visual tells that make UI look AI-generated (gradients, decorative shadows/blurs/glows, emoji-as-icons, etc.); (2) product/UX design rules — a numbered 50-section guide covering design philosophy, visual hierarchy, page/sidebar/navigation structure, per-component usage guidance (when to use Card, Modal vs Drawer, Table vs List, etc.), spacing/color/border/radius/shadow conventions, responsive design, accessibility, interaction design, a final UI quality checklist, and the rule against overwriting a component's built-in design. Call this before generating any UI — not just get_theme.",
145
170
  inputSchema: {},
146
171
  },
147
172
  async () => {
173
+ const { rules, antiAiLookRules } = theme;
148
174
  return {
149
- content: [{ type: "text", text: JSON.stringify(theme, null, 2) }],
175
+ content: [
176
+ {
177
+ type: "text",
178
+ text: JSON.stringify(
179
+ { stylingRules: rules, antiAiLookRules, productDesignGuide: aiDesignRules },
180
+ null,
181
+ 2,
182
+ ),
183
+ },
184
+ ],
150
185
  };
151
186
  },
152
187
  );