@neuctra/ui-mcp 0.4.4 → 0.4.5
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 +69 -150
- package/data/layouts.json +790 -0
- package/data/seoGuide.json +100 -6
- package/data/theme.json +1 -1
- package/package.json +1 -1
- package/src/load-data.mjs +20 -0
- package/src/seo.mjs +3 -3
- package/src/server.mjs +19 -495
- package/src/tools/get-design-rules/index.mjs +42 -0
- package/src/tools/get-neuctra-ui-component/index.mjs +160 -0
- package/src/tools/neuctra-ui-page-builder/index.mjs +157 -0
- package/src/tools/neuctra-ui-seo-setup/index.mjs +62 -0
- package/src/tools/neuctra-ui-setup/index.mjs +114 -0
- package/src/tools/neuctra-ui-setup/register.mjs +28 -0
- package/src/tools/neuctra-ui-theme-setup/index.mjs +17 -0
package/data/seoGuide.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"title": "Neuctra UI: SEO, AEO and GEO Guide",
|
|
3
|
-
"intro": "
|
|
4
|
-
"
|
|
3
|
+
"intro": "Complete SEO, AEO, and GEO rules for Neuctra UI projects, followed by practical implementation steps. Use the rules as the source of truth, identify the project framework before applying metadata, and adapt every implementation to that framework. The guidance covers crawlability, technical SEO, keywords, metadata, headings, content, structured data, FAQs, performance, trust, and measurement.",
|
|
4
|
+
"seoRules": [
|
|
5
5
|
{
|
|
6
6
|
"id": "technical",
|
|
7
7
|
"title": "Technical SEO Foundations",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
{
|
|
41
41
|
"id": "schema",
|
|
42
42
|
"title": "Structured Data and Schema Weaving",
|
|
43
|
-
"content": "Structured data (JSON-LD) tells engines exactly what a page is. **Schema weaving** means emitting one connected `@graph` per page instead of several disconnected blobs, with nodes referencing each other by stable `@id`.\n\n**Stable @id scheme** (reuse the exact same IDs on every page):\n\n```text\nOrganization https://example.com/#organization\nWebSite https://example.com/#website\nWebPage https://example.com/docs/card#webpage\nBreadcrumbList https://example.com/docs/card#breadcrumb\nMain entity https://example.com/docs/card#article (or #product, #software)\nFAQPage https://example.com/docs/card#faq\n```\n\n**Every page** gets Organization + WebSite + a WebPage (or subtype) + BreadcrumbList, wired together:\n* `WebSite.publisher` -> Organization\n* `WebPage.isPartOf` -> WebSite, `WebPage.breadcrumb` -> BreadcrumbList\n* main entity `mainEntityOfPage` -> WebPage, `publisher` -> Organization\n\n**Type map**\n\n```text\nhome WebPage (+ Organization, WebSite as the main entities)\ndocs WebPage + TechArticle\nblog WebPage + BlogPosting (author, datePublished, dateModified, image)\nproduct WebPage + SoftwareApplication or Product\npricing WebPage + Product with Offer items (real prices only)\nlanding WebPage\nfaq FAQPage (as the page type)\nabout AboutPage\ncontact ContactPage\ncategory CollectionPage\n```\n\n**Example graph**\n\n```json\n{\n \"@context\": \"https://schema.org\",\n \"@graph\": [\n { \"@type\": \"Organization\", \"@id\": \"https://example.com/#organization\", \"name\": \"Example\", \"url\": \"https://example.com\", \"logo\": \"https://example.com/logo.png\", \"sameAs\": [\"https://github.com/example\"] },\n { \"@type\": \"WebSite\", \"@id\": \"https://example.com/#website\", \"url\": \"https://example.com\", \"name\": \"Example\", \"publisher\": { \"@id\": \"https://example.com/#organization\" } },\n { \"@type\": \"WebPage\", \"@id\": \"https://example.com/docs/card#webpage\", \"url\": \"https://example.com/docs/card\", \"name\": \"Card Component | Example\", \"isPartOf\": { \"@id\": \"https://example.com/#website\" }, \"breadcrumb\": { \"@id\": \"https://example.com/docs/card#breadcrumb\" } },\n { \"@type\": \"BreadcrumbList\", \"@id\": \"https://example.com/docs/card#breadcrumb\", \"itemListElement\": [\n { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https://example.com/\" },\n { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Docs\", \"item\": \"https://example.com/docs\" },\n { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Card\", \"item\": \"https://example.com/docs/card\" }\n ] },\n { \"@type\": \"TechArticle\", \"@id\": \"https://example.com/docs/card#article\", \"headline\": \"Card Component\", \"mainEntityOfPage\": { \"@id\": \"https://example.com/docs/card#webpage\" }, \"publisher\": { \"@id\": \"https://example.com/#organization\" } }\n ]\n}\n```\n\n**Rules**\n* Structured data must describe what is visible on the page. Never mark up content users cannot see.\n* Never invent reviews, ratings, prices or authors. Fake markup risks a manual action.\n* Since 2023 Google shows FAQ rich results only for well-known government and health sites, and HowTo rich results are gone. Still add `FAQPage` where there is a visible FAQ: it helps AI Overviews and generative engines understand the Q&A.\n* Emit JSON-LD in the server-rendered HTML. Escape `<` as `\\u003c` when injecting it into a `<script>` tag.\n* Validate every template with Google's Rich Results Test and validator.schema.org before shipping.\n\
|
|
43
|
+
"content": "Structured data (JSON-LD) tells engines exactly what a page is. **Schema weaving** means emitting one connected `@graph` per page instead of several disconnected blobs, with nodes referencing each other by stable `@id`.\n\n**Stable @id scheme** (reuse the exact same IDs on every page):\n\n```text\nOrganization https://example.com/#organization\nWebSite https://example.com/#website\nWebPage https://example.com/docs/card#webpage\nBreadcrumbList https://example.com/docs/card#breadcrumb\nMain entity https://example.com/docs/card#article (or #product, #software)\nFAQPage https://example.com/docs/card#faq\n```\n\n**Every page** gets Organization + WebSite + a WebPage (or subtype) + BreadcrumbList, wired together:\n* `WebSite.publisher` -> Organization\n* `WebPage.isPartOf` -> WebSite, `WebPage.breadcrumb` -> BreadcrumbList\n* main entity `mainEntityOfPage` -> WebPage, `publisher` -> Organization\n\n**Type map**\n\n```text\nhome WebPage (+ Organization, WebSite as the main entities)\ndocs WebPage + TechArticle\nblog WebPage + BlogPosting (author, datePublished, dateModified, image)\nproduct WebPage + SoftwareApplication or Product\npricing WebPage + Product with Offer items (real prices only)\nlanding WebPage\nfaq FAQPage (as the page type)\nabout AboutPage\ncontact ContactPage\ncategory CollectionPage\n```\n\n**Example graph**\n\n```json\n{\n \"@context\": \"https://schema.org\",\n \"@graph\": [\n { \"@type\": \"Organization\", \"@id\": \"https://example.com/#organization\", \"name\": \"Example\", \"url\": \"https://example.com\", \"logo\": \"https://example.com/logo.png\", \"sameAs\": [\"https://github.com/example\"] },\n { \"@type\": \"WebSite\", \"@id\": \"https://example.com/#website\", \"url\": \"https://example.com\", \"name\": \"Example\", \"publisher\": { \"@id\": \"https://example.com/#organization\" } },\n { \"@type\": \"WebPage\", \"@id\": \"https://example.com/docs/card#webpage\", \"url\": \"https://example.com/docs/card\", \"name\": \"Card Component | Example\", \"isPartOf\": { \"@id\": \"https://example.com/#website\" }, \"breadcrumb\": { \"@id\": \"https://example.com/docs/card#breadcrumb\" } },\n { \"@type\": \"BreadcrumbList\", \"@id\": \"https://example.com/docs/card#breadcrumb\", \"itemListElement\": [\n { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https://example.com/\" },\n { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Docs\", \"item\": \"https://example.com/docs\" },\n { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Card\", \"item\": \"https://example.com/docs/card\" }\n ] },\n { \"@type\": \"TechArticle\", \"@id\": \"https://example.com/docs/card#article\", \"headline\": \"Card Component\", \"mainEntityOfPage\": { \"@id\": \"https://example.com/docs/card#webpage\" }, \"publisher\": { \"@id\": \"https://example.com/#organization\" } }\n ]\n}\n```\n\n**Rules**\n* Structured data must describe what is visible on the page. Never mark up content users cannot see.\n* Never invent reviews, ratings, prices or authors. Fake markup risks a manual action.\n* Since 2023 Google shows FAQ rich results only for well-known government and health sites, and HowTo rich results are gone. Still add `FAQPage` where there is a visible FAQ: it helps AI Overviews and generative engines understand the Q&A.\n* Emit JSON-LD in the server-rendered HTML. Escape `<` as `\\u003c` when injecting it into a `<script>` tag.\n* Validate every template with Google's Rich Results Test and validator.schema.org before shipping.\n\nFollow the implementation steps from `neuctra-ui-seo-setup` when adding a woven graph to a page.\n\n**One node per type per page.** A page must not emit two `SoftwareApplication`, two `FAQPage` or two `BreadcrumbList` nodes. This happens when a shared page shell emits schema and the individual page adds its own `<script type=\"application/ld+json\">` too. Pick one owner (the shell), pass the data into it, and delete the page-level duplicate.\n\n**One data source for visible content and schema.** Build the visible steps list and the `HowTo` node from the same array, and the visible FAQ accordion and the `FAQPage` node from the same array. They then can't drift apart, and nothing is shown twice.\n\n**Tool and web-app pages** (free online tools, converters, calculators, generators):\n* `SoftwareApplication` with `applicationCategory` (e.g. `UtilitiesApplication`, `DeveloperApplication`, `MultimediaApplication`), `operatingSystem` (\"Web\" or \"All\"), `featureList`, `isAccessibleForFree: true` and an `Offer` with `price: \"0\"` only if the tool really is free.\n* `HowTo` with the visible usage steps. Google no longer shows HowTo rich results, but engines still use the steps for answers.\n* `BreadcrumbList` (Home > Category > Tool) and `FAQPage`.\n\n**Listing pages**\n* Category or collection pages: `CollectionPage` + an `ItemList` of the pages it links to, in visible order.\n* Home page: `ItemList`s for the main categories and popular items, next to Organization and WebSite.\n\n**Optional extras, with honest expectations**\n* `WebSite` `potentialAction` `SearchAction`: fine if the site has search, but Google retired the sitelinks search box in 2024, so it no longer changes how results look.\n* `speakable`: a beta mostly limited to news publishers; low value for other sites."
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
"id": "faq",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
{
|
|
56
56
|
"id": "frameworks",
|
|
57
57
|
"title": "Where Metadata Goes in Each Framework",
|
|
58
|
-
"content": "Neuctra UI runs anywhere React 18+ and Tailwind v4 run. The rule is the same everywhere: metadata and JSON-LD must be in the server-rendered HTML.
|
|
58
|
+
"content": "Neuctra UI runs anywhere React 18+ and Tailwind v4 run. The rule is the same everywhere: metadata and JSON-LD must be in the server-rendered HTML. Use the implementation steps from `neuctra-ui-seo-setup` for the matching framework; do not apply metadata APIs from another framework.\n\n**Next.js App Router** (`framework: \"nextjs-app\"`)\n* `export const metadata = {...}` or `export async function generateMetadata()` in `page.js` or `layout.js`. Use `alternates.canonical`, `openGraph`, `twitter`, `robots`.\n* Set `metadataBase: new URL(\"https://example.com\")` in the root layout so relative URLs resolve.\n* A `\"use client\"` page cannot export metadata: put it in the route's `layout.js` instead.\n* **Metadata merging is shallow.** A child segment's `openGraph` object replaces the parent's completely. If a page sets `openGraph` at all, it must repeat `images` (and `siteName`, `locale`), or the page ships without an `og:image`. The same applies to `twitter`.\n* **Title template.** `title: { template: \"%s | Brand\", default: \"...\" }` in the root layout adds the suffix to child titles. Child titles must then not include the brand themselves, or it doubles. When a builder already returns a full title, use `title: { absolute: \"...\" }` so the template is skipped.\n* **Robots for Googlebot** uses kebab-case keys: `robots: { index: true, follow: true, googleBot: { index: true, follow: true, \"max-image-preview\": \"large\", \"max-snippet\": -1, \"max-video-preview\": -1 } }`. camelCase keys like `maxImagePreview` are ignored.\n* **Client-only widgets:** `dynamic(() => import(\"./Widget\"), { ssr: false })` must wrap only the widget. Keep the H1, text, FAQ and JSON-LD in the server-rendered `page.js`.\n* `output: \"export\"` (static export) is fully crawlable: everything above still applies, and you can check the result in the `out/` HTML.\n* JSON-LD: render `<script type=\"application/ld+json\" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, \"\\\\u003c\") }} />` in the page or layout.\n* `app/sitemap.js` and `app/robots.js` generate those files natively.\n\n**Next.js Pages Router** (`\"nextjs-pages\"`)\n* `import Head from \"next/head\"` and render the tags inside `<Head>`. Pages are pre-rendered, so crawlers see them.\n\n**Vite + React SPA** (`\"react-vite\"`)\n* React 19 hoists `<title>`, `<meta>` and `<link>` rendered anywhere in the tree into `<head>`. On React 18 use `react-helmet-async`.\n* Warning: a client-only SPA ships an empty HTML shell. Prerender the routes at build time or move to an SSR framework (Next.js, React Router, Astro, TanStack Start) for anything that must rank or be cited.\n\n**React Router v7 / Remix** (`\"react-router\"`)\n* Export `meta()` from the route module returning `[{ title }, { name: \"description\", content }, { tagName: \"link\", rel: \"canonical\", href }, { \"script:ld+json\": jsonLd }]`.\n\n**Astro** (`\"astro\"`)\n* Put the tags in the layout's `<head>`, passing title/description as props. JSON-LD: `<script type=\"application/ld+json\" set:html={JSON.stringify(jsonLd)} />`. Use React islands for Neuctra UI components. `@astrojs/sitemap` builds the sitemap.\n\n**TanStack Start** (`\"tanstack-start\"`)\n* Return `{ meta, links, scripts }` from the route's `head()` option; JSON-LD goes in `scripts` as `{ type: \"application/ld+json\", children: JSON.stringify(jsonLd) }`.\n\n**Gatsby** (`\"gatsby\"`)\n* `export const Head = () => (<>...tags...</>)` from the page component.\n\n**Plain HTML / other** (`\"html\"`)\n* Put the tags from the `meta-checklist` topic directly in `<head>`."
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
"id": "page-templates",
|
|
@@ -80,7 +80,101 @@
|
|
|
80
80
|
{
|
|
81
81
|
"id": "launch-checklist",
|
|
82
82
|
"title": "SEO / AEO / GEO Launch Checklist",
|
|
83
|
-
"content": "Run this before any page or site goes live. Every item is pass or fail.\n\n**Check the built HTML, not the source code.** Run the production build and inspect the output (`view-source:`, or grep the static `out/` / `dist/` HTML). Bugs like client-only pages, duplicate schema and missing images only show up there.\n\n**Crawl and index**\n- [ ] Content, meta and JSON-LD are in the server-rendered HTML (view source, not DevTools)\n- [ ] One absolute canonical per page; all host/protocol/slash variants 301 to it\n- [ ] robots.txt allows the site, references the sitemap, has an explicit AI bot policy\n- [ ] sitemap.xml lists only canonical 200 pages with accurate lastmod, submitted to Google and Bing\n- [ ] Utility and thin pages are noindex; no important page is noindex by mistake\n- [ ] No orphan pages; every key page within 3 clicks of home\n- [ ] Real 404 status for missing pages\n- [ ] Every page has exactly one H1 in the built HTML (a missing H1 usually means the page renders client-only)\n- [ ] Sitemap and llms.txt list only final URLs (no 301s), generated from one route registry\n- [ ] Sitemap lastmod reflects real content changes\n\n**On-page**\n- [ ] Unique title, 50 to 60 characters, keyword first, brand last\n- [ ] Unique meta description, 140 to 155 characters\n- [ ] Exactly one H1; H2/H3 in order; several H2s phrased as questions\n- [ ] Primary keyword in the first 100 words\n- [ ] Descriptive alt text on meaningful images\n- [ ] OpenGraph + Twitter tags with a 1200x630 image on every page, including category and static pages\n- [ ] Brand suffix identical on every title, never missing and never doubled\n\n**Structured data**\n- [ ] One woven `@graph`: Organization, WebSite, WebPage, BreadcrumbList + page type node\n- [ ] FAQPage only where the FAQ is visible, text identical\n- [ ] At most one node of each type per page (no duplicate SoftwareApplication, FAQPage, BreadcrumbList)\n- [ ] Visible steps/FAQs and their schema come from the same data, and nothing is rendered twice\n- [ ] No invented reviews, ratings or prices\n- [ ] Passes Rich Results Test and validator.schema.org\n\n**AEO / GEO**\n- [ ] 40 to 60 word direct answer under each question heading\n- [ ] 4 to 8 page-specific FAQs\n- [ ] /llms.txt published and generated from the route list\n- [ ] Organization `sameAs` links and consistent brand naming\n- [ ] Visible \"Last updated\" date on content that ages\n\n**Performance**\n- [ ] LCP < 2.5s, INP < 200ms, CLS < 0.1 on mobile\n- [ ] LCP image high priority, below-fold media lazy, all media sized\n\nUse
|
|
83
|
+
"content": "Run this before any page or site goes live. Every item is pass or fail.\n\n**Check the built HTML, not the source code.** Run the production build and inspect the output (`view-source:`, or grep the static `out/` / `dist/` HTML). Bugs like client-only pages, duplicate schema and missing images only show up there.\n\n**Crawl and index**\n- [ ] Content, meta and JSON-LD are in the server-rendered HTML (view source, not DevTools)\n- [ ] One absolute canonical per page; all host/protocol/slash variants 301 to it\n- [ ] robots.txt allows the site, references the sitemap, has an explicit AI bot policy\n- [ ] sitemap.xml lists only canonical 200 pages with accurate lastmod, submitted to Google and Bing\n- [ ] Utility and thin pages are noindex; no important page is noindex by mistake\n- [ ] No orphan pages; every key page within 3 clicks of home\n- [ ] Real 404 status for missing pages\n- [ ] Every page has exactly one H1 in the built HTML (a missing H1 usually means the page renders client-only)\n- [ ] Sitemap and llms.txt list only final URLs (no 301s), generated from one route registry\n- [ ] Sitemap lastmod reflects real content changes\n\n**On-page**\n- [ ] Unique title, 50 to 60 characters, keyword first, brand last\n- [ ] Unique meta description, 140 to 155 characters\n- [ ] Exactly one H1; H2/H3 in order; several H2s phrased as questions\n- [ ] Primary keyword in the first 100 words\n- [ ] Descriptive alt text on meaningful images\n- [ ] OpenGraph + Twitter tags with a 1200x630 image on every page, including category and static pages\n- [ ] Brand suffix identical on every title, never missing and never doubled\n\n**Structured data**\n- [ ] One woven `@graph`: Organization, WebSite, WebPage, BreadcrumbList + page type node\n- [ ] FAQPage only where the FAQ is visible, text identical\n- [ ] At most one node of each type per page (no duplicate SoftwareApplication, FAQPage, BreadcrumbList)\n- [ ] Visible steps/FAQs and their schema come from the same data, and nothing is rendered twice\n- [ ] No invented reviews, ratings or prices\n- [ ] Passes Rich Results Test and validator.schema.org\n\n**AEO / GEO**\n- [ ] 40 to 60 word direct answer under each question heading\n- [ ] 4 to 8 page-specific FAQs\n- [ ] /llms.txt published and generated from the route list\n- [ ] Organization `sameAs` links and consistent brand naming\n- [ ] Visible \"Last updated\" date on content that ages\n\n**Performance**\n- [ ] LCP < 2.5s, INP < 200ms, CLS < 0.1 on mobile\n- [ ] LCP image high priority, below-fold media lazy, all media sized\n\nUse `neuctra-ui-seo-setup` with action `audit` to check a page against the on-page and schema items automatically."
|
|
84
84
|
}
|
|
85
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"implementationSteps": {
|
|
87
|
+
"purpose": "Apply the SEO rules to the project in order. First identify framework, router, rendering mode, and current metadata conventions. Keep content and structured data consistent with what users can see.",
|
|
88
|
+
"steps": [
|
|
89
|
+
{
|
|
90
|
+
"number": 1,
|
|
91
|
+
"title": "Identify framework and rendering",
|
|
92
|
+
"details": "Inspect package.json, framework config, router, and build output. Determine whether each route is server rendered, statically generated, prerendered, or client only. Keep page title, description, canonical, visible content, and JSON-LD in the initial HTML whenever possible."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"number": 2,
|
|
96
|
+
"title": "Map search intent and keywords",
|
|
97
|
+
"details": "Assign one primary search intent and one primary keyword to each canonical URL. Record close variants, audience, and task. Check existing routes to prevent cannibalization; use variants naturally in the title, H1, introduction, subheadings, body, links, and real FAQ questions. Do not add a meta keywords tag for ranking."
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"number": 3,
|
|
101
|
+
"title": "Write page content and headings",
|
|
102
|
+
"details": "Write a unique title and useful page content. Use exactly one descriptive H1 that matches intent and is close to the title. Keep H2/H3 hierarchy in order. Put the primary keyword or close variant in the opening 100 words. Answer question headings directly and completely; use semantic HTML, descriptive links, and useful image alt text."
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"number": 4,
|
|
106
|
+
"title": "Set metadata and canonical URL",
|
|
107
|
+
"details": "Set a unique title (usually 50–60 characters) with primary topic first and the brand suffix exactly once. Set a unique 140–155 character description that accurately describes the page and reason to click. Add one absolute canonical, robots directives, language, and OpenGraph/Twitter metadata with a real 1200×630 social image. Noindex private or low-value utility pages; do not block a page in robots.txt if crawlers must read its noindex."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"number": 5,
|
|
111
|
+
"title": "Add JSON-LD for entities and GEO clarity",
|
|
112
|
+
"details": "Emit valid JSON-LD in the initial HTML. Use one connected @graph per page with Organization, WebSite, WebPage (or suitable subtype), BreadcrumbList, and the relevant main entity. Link nodes with stable @id values. Add FAQPage only for questions and answers visible on the page; use Product, Offer, SoftwareApplication, BlogPosting, TechArticle, HowTo, or ItemList only when the corresponding facts and content are visible. Never invent prices, ratings, reviews, authors, or features. Structured data helps machines understand entities but does not guarantee rankings or citations."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"number": 6,
|
|
116
|
+
"title": "Implement AEO with useful FAQs and direct answers",
|
|
117
|
+
"details": "For important questions, use a clear question heading followed immediately by a concise, self-contained answer, then supporting detail. Add around 4–8 genuine page-specific FAQs when they help users. Keep FAQ text identical between visible content and JSON-LD. Prefer semantic lists for steps and real tables for comparisons."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"number": 7,
|
|
121
|
+
"title": "Complete technical SEO",
|
|
122
|
+
"details": "Use stable lowercase URLs, working internal links, correct 200/301/404/410 status codes, HTTPS, a robots.txt with the sitemap location, and an XML sitemap containing only canonical indexable URLs with meaningful lastmod dates. Add redirects for changed URLs. Ensure important pages are reachable and not orphaned. Provide hreflang only for actual localized variants."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"number": 8,
|
|
126
|
+
"title": "Optimize performance and accessibility",
|
|
127
|
+
"details": "Measure field Core Web Vitals and target LCP under 2.5s, INP under 200ms, and CLS under 0.1 at the 75th percentile. Prioritize the actual LCP asset, lazy-load below-fold media, size images, reserve layout space, reduce main-thread work, and use semantic accessible HTML."
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"number": 9,
|
|
131
|
+
"title": "Build trust and freshness",
|
|
132
|
+
"details": "Show real authorship, evidence, contact/about/policy information, and accurate published/updated dates where relevant. Update dateModified only after a material content change. Keep brand name, logo, and description consistent across the site and trusted profiles."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"number": 10,
|
|
136
|
+
"title": "Verify production output",
|
|
137
|
+
"details": "Build the site and inspect the generated or server-rendered HTML, not only source components or the browser DOM after JavaScript runs. Confirm title, description, canonical, robots, H1, links, social tags, JSON-LD validity, and sitemap URLs. Check status codes, mobile performance, Search Console, and Bing Webmaster Tools; fix issues and repeat."
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
"frameworkSpecific": {
|
|
141
|
+
"nextjs-app": {
|
|
142
|
+
"identify": "Next.js App Router routes live under app/ or src/app/.",
|
|
143
|
+
"metadata": "Export metadata or generateMetadata from each server page/layout. Set metadataBase in the root layout. Use title.template once for the brand suffix; use title.absolute for a prebuilt full title. A page marked use client cannot export metadata, so place metadata in its server parent/layout.",
|
|
144
|
+
"jsonLd": "Render a script type application/ld+json from a Server Component. Serialize safely, escaping < as \\u003c. Keep JSON-LD aligned with visible page content.",
|
|
145
|
+
"technical": "Use app/sitemap.js and app/robots.js or equivalent route files. Set canonical with alternates.canonical; configure OpenGraph, Twitter, robots and images. With output: export, confirm every dynamic route is statically generated and inspect out/ HTML."
|
|
146
|
+
},
|
|
147
|
+
"nextjs-pages": {
|
|
148
|
+
"identify": "Next.js Pages Router routes live under pages/ or src/pages/.",
|
|
149
|
+
"metadata": "Use next/head in each page for title, description, canonical, robots and social meta tags. Ensure metadata is present during SSR/SSG, not only after client effects.",
|
|
150
|
+
"jsonLd": "Render a JSON-LD script in the page with dangerouslySetInnerHTML and safely escaped serialized data.",
|
|
151
|
+
"technical": "Use getStaticProps/getStaticPaths or SSR as appropriate. Generate sitemap and robots files from canonical route data, and inspect the built HTML."
|
|
152
|
+
},
|
|
153
|
+
"react-vite": {
|
|
154
|
+
"identify": "Vite with React is commonly a client-rendered SPA; inspect whether routes are prerendered or SSR.",
|
|
155
|
+
"metadata": "For React 19, native title/meta/link hoisting may be used; for React 18 use a head manager such as react-helmet-async. Client-only updates are not sufficient for crawler visibility: prerender routes or add SSR for indexable pages.",
|
|
156
|
+
"jsonLd": "Place the JSON-LD script in the rendered document head/body and verify it is present in the prerendered HTML. Avoid adding schema only in an effect.",
|
|
157
|
+
"technical": "Generate a static HTML page per indexable route or use an SSR/prerender solution. Configure host fallback and real 404 handling, canonical URLs, robots.txt, and sitemap.xml; inspect dist output."
|
|
158
|
+
},
|
|
159
|
+
"react-router": {
|
|
160
|
+
"identify": "Determine React Router version and whether framework/SSR mode, prerendering, or SPA mode is enabled.",
|
|
161
|
+
"metadata": "In React Router framework mode, export a route meta function returning title, description, canonical link and social metadata. Ensure route metadata is rendered server-side or during prerender.",
|
|
162
|
+
"jsonLd": "Render JSON-LD in the route component or root document using route data; escape serialized JSON and ensure it appears in built HTML.",
|
|
163
|
+
"technical": "Use framework-mode SSR/prerender where pages need indexing. Generate sitemap and robots from route data; inspect actual production responses and missing-route status codes."
|
|
164
|
+
},
|
|
165
|
+
"remix": {
|
|
166
|
+
"identify": "Inspect whether the project uses Remix conventions or React Router framework mode; follow its installed version and route structure.",
|
|
167
|
+
"metadata": "Export meta from each route module. Return title, description, canonical and social meta descriptors. Compose parent and route metadata deliberately so child routes do not lose required tags.",
|
|
168
|
+
"jsonLd": "Render a script tag in the route document using loader-backed page data and safely serialized JSON-LD.",
|
|
169
|
+
"technical": "Use server rendering for indexable route content. Build sitemap.xml and robots.txt from route definitions, return correct HTTP status codes, and verify the response HTML."
|
|
170
|
+
},
|
|
171
|
+
"other": {
|
|
172
|
+
"identify": "Confirm the framework, router, rendering strategy, and metadata API from the project files and installed version.",
|
|
173
|
+
"metadata": "Use the framework’s server-rendered or build-time head/metadata API. Do not rely solely on client-side effects for indexable pages.",
|
|
174
|
+
"jsonLd": "Add a valid JSON-LD script to initial HTML using safe serialization.",
|
|
175
|
+
"technical": "Generate canonical URLs, robots rules, sitemap and redirects using the framework or hosting platform, then verify production responses."
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"assistantGuidance": "When framework is supplied, use only its matching implementation recipe plus shared rules. When framework is auto or unknown, inspect project files and infer the framework and render mode; ask the user only if repository evidence is ambiguous. Return implementation guidance tailored to the project rather than blindly applying snippets from another framework."
|
|
179
|
+
}
|
|
86
180
|
}
|
package/data/theme.json
CHANGED
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"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.",
|
|
74
74
|
"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."
|
|
75
75
|
},
|
|
76
|
-
"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.
|
|
76
|
+
"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. Use get-neuctra-ui-component with componentName \"ToastProvider\" for the full prop list (including position, one of top-left/top-center/top-right/bottom-left/bottom-center/bottom-right)."
|
|
77
77
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neuctra/ui-mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
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",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
// Disk loading is for Node, Bun, and Deno. Edge callers should pass imported
|
|
6
|
+
// data to createServer({ registry, theme, aiDesignRules, seoGuide, layouts }).
|
|
7
|
+
export function loadDataFromDisk() {
|
|
8
|
+
const directory = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const dataDirectory = path.resolve(directory, "..", "data");
|
|
10
|
+
const readJson = (name) =>
|
|
11
|
+
JSON.parse(fs.readFileSync(path.join(dataDirectory, name), "utf8"));
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
registry: readJson("components.json"),
|
|
15
|
+
theme: readJson("theme.json"),
|
|
16
|
+
aiDesignRules: readJson("aiDesignRules.json"),
|
|
17
|
+
seoGuide: readJson("seoGuide.json"),
|
|
18
|
+
layouts: readJson("layouts.json"),
|
|
19
|
+
};
|
|
20
|
+
}
|
package/src/seo.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Pure, deterministic SEO builders
|
|
2
|
-
//
|
|
1
|
+
// Pure, deterministic SEO builders used by internal page generation and audit logic.
|
|
2
|
+
// No I/O and no LLM calls: the same input always gives
|
|
3
3
|
// the same output, so it runs identically on Node and on edge runtimes.
|
|
4
4
|
//
|
|
5
5
|
// The @id scheme and node shapes mirror neuctra-ui-docs-v2/lib/seo/schemas
|
|
@@ -1050,7 +1050,7 @@ export function auditPageSeo(input) {
|
|
|
1050
1050
|
const pageNodes = types.filter((t) => WEBPAGE_TYPES.includes(t) && t !== "FAQPage");
|
|
1051
1051
|
if (pageNodes.length) add("pass", "Schema: WebPage", `Present (${pageNodes[0]}).`);
|
|
1052
1052
|
else if (types.includes("FAQPage")) add("warn", "Schema: WebPage", "Only an FAQPage node stands in for the page.", "Fine for a dedicated FAQ page. Any other page needs its own WebPage (or subtype) node, with the FAQPage linked to it via isPartOf.");
|
|
1053
|
-
else add("warn", "Schema: WebPage", "No WebPage (or subtype) node.", "Weave a WebPage node linked to WebSite and Organization (use
|
|
1053
|
+
else add("warn", "Schema: WebPage", "No WebPage (or subtype) node.", "Weave a WebPage node linked to WebSite and Organization (use the SEO rules and implementation steps in neuctra-ui-seo-setup).");
|
|
1054
1054
|
const isHome = sameUrl(p.url, p.url.replace(/^(https?:\/\/[^/]+).*$/, "$1"));
|
|
1055
1055
|
if (!isHome) types.includes("BreadcrumbList") ? add("pass", "Schema: BreadcrumbList", "Present.") : add("warn", "Schema: BreadcrumbList", "Missing.", "Add a BreadcrumbList matching the visible breadcrumb/URL path.");
|
|
1056
1056
|
types.includes("Organization") ? add("pass", "Schema: Organization", "Present.") : add("warn", "Schema: Organization", "Missing.", "Add Organization with logo and sameAs, referenced by @id from every page.");
|