diff --git a/packages/landing/src/app/docs/[slug]/page.tsx b/packages/landing/src/app/docs/[slug]/page.tsx index 2cf339b..492dc51 100644 --- a/packages/landing/src/app/docs/[slug]/page.tsx +++ b/packages/landing/src/app/docs/[slug]/page.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { DocPageView } from '@/docs/components/doc-page-view'; import { docSlugs, getDoc } from '@/docs/registry'; +import { docMetadata } from '@/docs/seo'; export function generateStaticParams(): { slug: string }[] { return docSlugs().map((slug) => ({ slug })); @@ -15,12 +16,7 @@ export async function generateMetadata({ const { slug } = await params; const doc = getDoc(slug); if (!doc) return {}; - return { - title: `${doc.title} - Docs`, - description: doc.lede, - alternates: { canonical: `/docs/${slug}` }, - openGraph: { url: `/docs/${slug}` }, - }; + return docMetadata(doc); } export default async function DocSlugPage({ diff --git a/packages/landing/src/app/docs/page.tsx b/packages/landing/src/app/docs/page.tsx index f126ec6..ec49931 100644 --- a/packages/landing/src/app/docs/page.tsx +++ b/packages/landing/src/app/docs/page.tsx @@ -2,15 +2,11 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { DocPageView } from '@/docs/components/doc-page-view'; import { getDoc } from '@/docs/registry'; +import { docMetadata } from '@/docs/seo'; const doc = getDoc(''); -export const metadata: Metadata = { - title: 'Docs - Agentage Memory', - description: doc?.lede, - alternates: { canonical: '/docs' }, - openGraph: { url: '/docs' }, -}; +export const metadata: Metadata = doc ? docMetadata(doc) : {}; export default function DocsIndexPage() { if (!doc) notFound(); diff --git a/packages/landing/src/docs/components/doc-nav.tsx b/packages/landing/src/docs/components/doc-nav.tsx index 463d8df..21fe698 100644 --- a/packages/landing/src/docs/components/doc-nav.tsx +++ b/packages/landing/src/docs/components/doc-nav.tsx @@ -10,6 +10,7 @@ import { Code, MousePointerClick, Bot, + Sparkles, Wrench, BookOpen, Search, @@ -29,6 +30,7 @@ const ICONS: Record = { 'VS Code': Code, Cursor: MousePointerClick, ChatGPT: Bot, + Grok: Sparkles, CLI: Terminal, 'MCP tools': Wrench, }; diff --git a/packages/landing/src/docs/content/clients.ts b/packages/landing/src/docs/content/clients.ts index 3987928..946e0ce 100644 --- a/packages/landing/src/docs/content/clients.ts +++ b/packages/landing/src/docs/content/clients.ts @@ -85,6 +85,19 @@ VS Code opens the browser for the OAuth sign-in on first use.`, Paste \`${ENDPOINT}\` and sign in when prompted.`, }, + { + slug: 'grok', + nav: 'Grok', + title: 'Grok', + lede: 'Add Agentage Memory to Grok by pasting the MCP server URL.', + setup: `In Grok, add a custom MCP server and paste the endpoint URL: + +\`\`\`text +${ENDPOINT} +\`\`\` + +Grok opens the browser for the OAuth sign-in on first use - no API key.`, + }, ]; const clientHref = (slug: string): string => `/docs/${slug}`; @@ -93,6 +106,7 @@ export const clientDocs: DocPage[] = CLIENTS.map((c) => ({ slug: c.slug, title: c.title, lede: c.lede, + keywords: [c.nav, `${c.nav} MCP`, `connect ${c.nav}`, `${c.nav} memory`, `${c.nav} MCP server`], sections: [ { id: 'setup', diff --git a/packages/landing/src/docs/content/connect.ts b/packages/landing/src/docs/content/connect.ts index 51ccea3..f2488a5 100644 --- a/packages/landing/src/docs/content/connect.ts +++ b/packages/landing/src/docs/content/connect.ts @@ -8,6 +8,14 @@ export const connectDoc: DocPage = { slug: 'connect', title: 'Connect any MCP client', lede: 'A vendor-neutral guide to wiring any MCP-capable agent to Agentage Memory. The same endpoint and OAuth flow work everywhere - no API key.', + keywords: [ + 'connect MCP client', + 'add MCP server', + 'MCP setup', + 'OAuth MCP', + 'how to connect MCP', + 'MCP server URL', + ], sections: [ { id: 'basics', diff --git a/packages/landing/src/docs/content/mcp-server.ts b/packages/landing/src/docs/content/mcp-server.ts index 61444aa..6d004dc 100644 --- a/packages/landing/src/docs/content/mcp-server.ts +++ b/packages/landing/src/docs/content/mcp-server.ts @@ -8,6 +8,14 @@ export const mcpServerDoc: DocPage = { slug: 'mcp-server', title: 'MCP server', lede: 'One cloud endpoint that every AI tool connects to over the Model Context Protocol. Sign in with OAuth - no API key - and read and write your memory through six tools.', + keywords: [ + 'MCP server', + 'MCP endpoint', + 'Streamable HTTP MCP', + 'OAuth 2.1 MCP', + 'memory tools', + 'memory__search', + ], sections: [ { id: 'endpoint', diff --git a/packages/landing/src/docs/content/overview.ts b/packages/landing/src/docs/content/overview.ts index f95441d..e9a9120 100644 --- a/packages/landing/src/docs/content/overview.ts +++ b/packages/landing/src/docs/content/overview.ts @@ -6,6 +6,13 @@ export const overviewDoc: DocPage = { slug: '', title: 'Agentage Memory', lede: 'One memory. Every AI. Owned by you. A shared markdown memory that all of your AI tools read and write through a single MCP endpoint.', + keywords: [ + 'one memory every AI', + 'cross-vendor AI memory', + 'files-first memory', + 'own your AI memory', + 'Claude ChatGPT Cursor memory', + ], sections: [ { id: 'what-it-is', diff --git a/packages/landing/src/docs/seo.ts b/packages/landing/src/docs/seo.ts new file mode 100644 index 0000000..7808eb1 --- /dev/null +++ b/packages/landing/src/docs/seo.ts @@ -0,0 +1,36 @@ +import type { Metadata } from 'next'; +import type { DocPage } from './types'; + +// Keywords shared by every docs page; per-page `keywords` are merged on top. +const BASE_KEYWORDS = [ + 'Agentage Memory', + 'MCP', + 'Model Context Protocol', + 'shared memory', + 'AI memory', + 'markdown memory', + 'memory.agentage.io', +]; + +export const docPath = (doc: DocPage): string => (doc.slug === '' ? '/docs' : `/docs/${doc.slug}`); + +// Full, crawl-ready metadata for a docs page: title, description, merged +// keywords, self-canonical, and OpenGraph (the file-based OG image still +// applies). Used by both the index route and the [slug] route. +export function docMetadata(doc: DocPage): Metadata { + const path = docPath(doc); + const title = doc.slug === '' ? 'Docs - Agentage Memory' : `${doc.title} - Agentage Memory docs`; + const keywords = [...new Set([...BASE_KEYWORDS, ...(doc.keywords ?? [])])]; + return { + title, + description: doc.lede, + keywords, + alternates: { canonical: path }, + openGraph: { + title, + description: doc.lede, + url: path, + type: 'article', + }, + }; +} diff --git a/packages/landing/src/docs/types.ts b/packages/landing/src/docs/types.ts index 05b8896..1f02aa2 100644 --- a/packages/landing/src/docs/types.ts +++ b/packages/landing/src/docs/types.ts @@ -51,6 +51,8 @@ export interface DocPage { title: string; /** One-line intro under the H1. */ lede: string; + /** Page-specific SEO keywords (merged with the shared docs keywords). */ + keywords?: string[]; sections: DocSection[]; }