feat(web): full SEO stack — metadata, JSON-LD, sitemap, robots, OG image
Some checks failed
Deploy to Production / deploy (push) Failing after 46s
Some checks failed
Deploy to Production / deploy (push) Failing after 46s
Ported and adapted from the BuildMyDiscord SEO setup: - lib/seo.ts — single source for site constants, the FAQ data (shared by the rendered FAQ and the FAQPage schema so they never drift) and JSON-LD builders. - Rich root metadata: title template, keywords, Open Graph, Twitter card, robots directives, canonical. - JSON-LD: Organization + WebSite + SoftwareApplication sitewide, FAQPage on the landing page. No AggregateRating — there are no real reviews yet. - app/robots.ts — allow all, explicit allow-list for AI answer-engine crawlers (GPTBot, ClaudeBot, PerplexityBot, …), disallow private routes. - app/sitemap.ts — every public marketing + docs route. - app/opengraph-image.tsx — monochrome on-brand 1200x630 share card. - app/manifest.ts + public/llms.txt. - Per-page metadata for pricing, changelog, security, privacy, terms, docs, templates and status. - opengraph-image + apple-icon pinned to the edge runtime — next/og crashes during a Node-runtime prerender. Verified: next build passes; /robots.txt, /sitemap.xml, /manifest.webmanifest and /opengraph-image all generate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,57 @@
|
||||
import { JsonLd } from '@/components/json-ld';
|
||||
import { SiteBanner } from '@/components/site-banner';
|
||||
import {
|
||||
SEO_KEYWORDS,
|
||||
SITE_DESCRIPTION,
|
||||
SITE_NAME,
|
||||
SITE_TAGLINE,
|
||||
SITE_URL,
|
||||
siteJsonLd,
|
||||
} from '@/lib/seo';
|
||||
import { GeistMono } from 'geist/font/mono';
|
||||
import { GeistSans } from 'geist/font/sans';
|
||||
import type { Metadata } from 'next';
|
||||
import './globals.css';
|
||||
|
||||
const TITLE = `${SITE_NAME} — ${SITE_TAGLINE}`;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'BuildMyMCPServer — Describe your tool. We host the server.',
|
||||
description:
|
||||
'From prompt to production MCP server in 60 seconds. OAuth 2.1, Streamable HTTP, ready for Claude, Cursor & ChatGPT.',
|
||||
metadataBase: new URL(process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3001'),
|
||||
metadataBase: new URL(SITE_URL),
|
||||
title: {
|
||||
default: TITLE,
|
||||
template: `%s | ${SITE_NAME}`,
|
||||
},
|
||||
description: SITE_DESCRIPTION,
|
||||
applicationName: SITE_NAME,
|
||||
keywords: SEO_KEYWORDS,
|
||||
authors: [{ name: SITE_NAME }],
|
||||
creator: SITE_NAME,
|
||||
publisher: SITE_NAME,
|
||||
alternates: { canonical: '/' },
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
locale: 'en_US',
|
||||
url: SITE_URL,
|
||||
siteName: SITE_NAME,
|
||||
title: TITLE,
|
||||
description: SITE_DESCRIPTION,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: TITLE,
|
||||
description: SITE_DESCRIPTION,
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
googleBot: {
|
||||
index: true,
|
||||
follow: true,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
'max-video-preview': -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
@@ -19,6 +62,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body>
|
||||
<JsonLd data={siteJsonLd()} />
|
||||
<SiteBanner />
|
||||
{children}
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user