From 5e358f187356916e1693ba78576dd767421dede5 Mon Sep 17 00:00:00 2001
From: Ben Senescu <44480372+bensenescu@users.noreply.github.com>
Date: Sun, 10 May 2026 17:23:37 -0400
Subject: [PATCH] Add an MCP landing page and update footer navigation (#176)
* Fix open-seo MCP auth discovery
* Refine MCP marketing page route and copy
---
web/src/components/site-footer.tsx | 58 +--
web/src/routeTree.gen.ts | 21 ++
web/src/routes/_marketing/features/mcp.tsx | 396 +++++++++++++++++++++
3 files changed, 453 insertions(+), 22 deletions(-)
create mode 100644 web/src/routes/_marketing/features/mcp.tsx
diff --git a/web/src/components/site-footer.tsx b/web/src/components/site-footer.tsx
index fed0f69..6fd0ddd 100644
--- a/web/src/components/site-footer.tsx
+++ b/web/src/components/site-footer.tsx
@@ -3,28 +3,42 @@ import { Link } from "@tanstack/react-router";
export function SiteFooter({ className }: { className?: string }) {
return (
-
-
- OpenSEO
-
-
Guides
-
- GitHub
-
-
- Discord
-
-
Pricing
-
Privacy
-
Terms
+
+
+
Product
+
+ OpenSEO
+ MCP
+ Pricing
+ Guides
+
+
+
+
+
Legal
+
+ Privacy
+ Terms
+
+
);
diff --git a/web/src/routeTree.gen.ts b/web/src/routeTree.gen.ts
index b51e8ab..96e578f 100644
--- a/web/src/routeTree.gen.ts
+++ b/web/src/routeTree.gen.ts
@@ -19,6 +19,7 @@ import { Route as GuidesSplatRouteImport } from './routes/guides/$'
import { Route as ApiSubscribeRouteImport } from './routes/api/subscribe'
import { Route as ApiEventRouteImport } from './routes/api/event'
import { Route as MarketingPricingRouteImport } from './routes/_marketing/pricing'
+import { Route as MarketingFeaturesMcpRouteImport } from './routes/_marketing/features/mcp'
const TermsAndConditionsRoute = TermsAndConditionsRouteImport.update({
id: '/terms-and-conditions',
@@ -69,6 +70,11 @@ const MarketingPricingRoute = MarketingPricingRouteImport.update({
path: '/pricing',
getParentRoute: () => MarketingRoute,
} as any)
+const MarketingFeaturesMcpRoute = MarketingFeaturesMcpRouteImport.update({
+ id: '/features/mcp',
+ path: '/features/mcp',
+ getParentRoute: () => MarketingRoute,
+} as any)
export interface FileRoutesByFullPath {
'/': typeof MarketingIndexRoute
@@ -80,6 +86,7 @@ export interface FileRoutesByFullPath {
'/guides/$': typeof GuidesSplatRoute
'/js/script.js': typeof JsScriptDotjsRoute
'/guides/': typeof GuidesIndexRoute
+ '/features/mcp': typeof MarketingFeaturesMcpRoute
}
export interface FileRoutesByTo {
'/privacy': typeof PrivacyRoute
@@ -91,6 +98,7 @@ export interface FileRoutesByTo {
'/js/script.js': typeof JsScriptDotjsRoute
'/': typeof MarketingIndexRoute
'/guides': typeof GuidesIndexRoute
+ '/features/mcp': typeof MarketingFeaturesMcpRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
@@ -104,6 +112,7 @@ export interface FileRoutesById {
'/js/script.js': typeof JsScriptDotjsRoute
'/_marketing/': typeof MarketingIndexRoute
'/guides/': typeof GuidesIndexRoute
+ '/_marketing/features/mcp': typeof MarketingFeaturesMcpRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
@@ -117,6 +126,7 @@ export interface FileRouteTypes {
| '/guides/$'
| '/js/script.js'
| '/guides/'
+ | '/features/mcp'
fileRoutesByTo: FileRoutesByTo
to:
| '/privacy'
@@ -128,6 +138,7 @@ export interface FileRouteTypes {
| '/js/script.js'
| '/'
| '/guides'
+ | '/features/mcp'
id:
| '__root__'
| '/_marketing'
@@ -140,6 +151,7 @@ export interface FileRouteTypes {
| '/js/script.js'
| '/_marketing/'
| '/guides/'
+ | '/_marketing/features/mcp'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
@@ -225,17 +237,26 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof MarketingPricingRouteImport
parentRoute: typeof MarketingRoute
}
+ '/_marketing/features/mcp': {
+ id: '/_marketing/features/mcp'
+ path: '/features/mcp'
+ fullPath: '/features/mcp'
+ preLoaderRoute: typeof MarketingFeaturesMcpRouteImport
+ parentRoute: typeof MarketingRoute
+ }
}
}
interface MarketingRouteChildren {
MarketingPricingRoute: typeof MarketingPricingRoute
MarketingIndexRoute: typeof MarketingIndexRoute
+ MarketingFeaturesMcpRoute: typeof MarketingFeaturesMcpRoute
}
const MarketingRouteChildren: MarketingRouteChildren = {
MarketingPricingRoute: MarketingPricingRoute,
MarketingIndexRoute: MarketingIndexRoute,
+ MarketingFeaturesMcpRoute: MarketingFeaturesMcpRoute,
}
const MarketingRouteWithChildren = MarketingRoute._addFileChildren(
diff --git a/web/src/routes/_marketing/features/mcp.tsx b/web/src/routes/_marketing/features/mcp.tsx
new file mode 100644
index 0000000..4dfd250
--- /dev/null
+++ b/web/src/routes/_marketing/features/mcp.tsx
@@ -0,0 +1,396 @@
+import { createFileRoute } from "@tanstack/react-router";
+import type { ReactNode, SVGProps } from "react";
+import { useState } from "react";
+import { buildPageSeo } from "@/lib/seo";
+
+const mcpDescription =
+ "Connect OpenSEO MCP to your AI agent so it can research keywords, SERPs, domains, backlinks, and rank tracking data.";
+
+const toolCategories = [
+ {
+ label: "Keywords",
+ tools: [
+ {
+ title: "Research keywords",
+ description: "Get keyword ideas with volume, difficulty, and CPC.",
+ },
+ {
+ title: "Get SERP results",
+ description: "See live Google results for a keyword.",
+ },
+ {
+ title: "Get rank tracking positions",
+ description: "Read tracked keyword positions.",
+ },
+ {
+ title: "Get saved keywords",
+ description: "Pull your saved keyword lists.",
+ },
+ {
+ title: "Save keywords",
+ description: "Save keywords back to OpenSEO.",
+ },
+ ],
+ },
+ {
+ label: "Domain",
+ tools: [
+ {
+ title: "Get domain overview",
+ description: "Summarize a domain's organic footprint.",
+ },
+ {
+ title: "Get domain keywords",
+ description: "Find keywords a domain already ranks for.",
+ },
+ {
+ title: "Get backlinks overview",
+ description: "Check backlink and referring-domain stats.",
+ },
+ ],
+ },
+] as const;
+
+export const Route = createFileRoute("/_marketing/features/mcp")({
+ head: () =>
+ buildPageSeo({
+ title: "OpenSEO MCP",
+ description: mcpDescription,
+ path: "/features/mcp",
+ titleSuffix: "OpenSEO",
+ }),
+ component: McpPage,
+});
+
+function McpPage() {
+ return (
+ <>
+
OpenSEO MCP
+
+ OpenSEO MCP for your AI agent
+
+
+ Connect OpenSEO to your AI agent so that it can research keywords,
+ SERPs, domains, backlinks, and rank tracking data for you.
+
+
+
+ Skip to setup
+ ↓
+
+
+
+ What it does
+
+ MCP lets your AI do the most tedious parts of your job faster. Connect
+ OpenSEO once, then ask your agent to research keywords, inspect search
+ results, compare domains, and save useful ideas back to your
+ workspace.
+
+
+
+
+ Available tools
+
+ {toolCategories.map((category) => (
+
+
+ {category.label}
+
+
+ {category.tools.map((tool) => (
+
+
+ {tool.title}
+
+
+ {tool.description}
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+ Set up OpenSEO MCP
+
+ The first connection sends you through OpenSEO login. After
+ authorization, your AI client can call OpenSEO tools using your
+ workspace permissions. The MCP server URL is{" "}
+ https://app.openseo.so/mcp. For the most current setup UI
+ and a copyable endpoint, open{" "}
+
+ AI & MCP in OpenSEO
+
+ .
+
+
+
+
+ Don't see your agent or AI app?
+
+
+ Copy the link to this page into your AI tool and ask it how to
+ configure OpenSEO MCP for your tool of choice.
+
+
+
+
+ Follow the setup steps for ChatGPT, Claude, Claude Code, Codex, or any
+ MCP-compatible AI tool.
+
+
+
+
}
+ >
+
+
+ Open ChatGPT settings, then go to{" "}
+ Apps .
+
+
+ If you do not see the custom app form, open{" "}
+
+ Advanced settings
+ {" "}
+ and turn on developer mode.
+
+
+ Return to Apps and click{" "}
+ Create app
+ .
+
+
+ Name the app OpenSEO.
+
+
+ Paste the OpenSEO MCP server URL into{" "}
+
+ MCP Server URL
+
+ .
+
+
+ Keep authentication set to{" "}
+ OAuth .
+
+
+ Review the custom MCP server warning, check{" "}
+
+ I understand and want to continue
+
+ , then click Create.
+
+
+ Approve the OpenSEO login when ChatGPT asks you to sign in.
+
+
+
+
+
+
}
+ >
+
+ Use user scope if you want OpenSEO available across projects, or
+ local scope for the current repository.
+
+
+
+
+
}
+ >
+
+ Open Settings -> Connectors.
+ Click Add custom connector.
+
+ Paste https://app.openseo.so/mcp.
+
+ Approve the OpenSEO login when prompted.
+
+
+ Requires a Claude plan that supports custom connectors.
+
+
+
+
}
+ >
+
+ Add OpenSEO as a hosted MCP server, then run the OAuth login flow.
+
+
+
+
+
}
+ >
+
+ Open Settings -> Integrations & MCP.
+ Click Add your own.
+
+ Paste https://app.openseo.so/mcp.
+
+ Approve the OpenSEO login when prompted.
+
+
+
+
+ >
+ );
+}
+
+function SetupOption({
+ id,
+ title,
+ subtitle,
+ icon,
+ children,
+}: {
+ id: string;
+ title: string;
+ subtitle: string;
+ icon?: ReactNode;
+ children: ReactNode;
+}) {
+ const [open, setOpen] = useState(false);
+ const contentId = `setup-${id}`;
+
+ return (
+
+ setOpen((value) => !value)}
+ className="flex w-full items-center justify-between gap-3 px-4 py-3.5 text-left transition-colors hover:bg-neutral-100"
+ >
+
+ {icon ? (
+
+ {icon}
+
+ ) : (
+
+ AI
+
+ )}
+
+ {title}
+ {subtitle}
+
+
+
+
+
+
+ {open ? (
+
+ {children}
+
+ ) : null}
+
+ );
+}
+
+function CodeBlock({ code }: { code: string }) {
+ return (
+
+ {code}
+
+ );
+}
+
+function ClaudeIcon(props: SVGProps
) {
+ return (
+
+
+
+ );
+}
+
+function CodexIcon(props: SVGProps) {
+ return (
+
+
+
+ );
+}
+
+function OpenAIIcon(props: SVGProps) {
+ return (
+
+
+
+ );
+}