From 02fcfb9834d192af205194c6c7708a33868b6ab1 Mon Sep 17 00:00:00 2001 From: Ben Senescu Date: Fri, 8 May 2026 13:40:51 -0400 Subject: [PATCH 1/4] Update AI page for MCP setup guidance --- src/client/navigation/items.ts | 2 +- src/routes/_project/p/$projectId/ai.tsx | 295 ++++++++++++++++++------ 2 files changed, 231 insertions(+), 66 deletions(-) diff --git a/src/client/navigation/items.ts b/src/client/navigation/items.ts index 20068d2..643378d 100644 --- a/src/client/navigation/items.ts +++ b/src/client/navigation/items.ts @@ -62,7 +62,7 @@ const projectNavItems = [ }, { to: "/p/$projectId/ai" as const, - label: "AI & Agents", + label: "AI & MCP", icon: Bot, matchSegment: "/ai", }, diff --git a/src/routes/_project/p/$projectId/ai.tsx b/src/routes/_project/p/$projectId/ai.tsx index 0aa141d..2802322 100644 --- a/src/routes/_project/p/$projectId/ai.tsx +++ b/src/routes/_project/p/$projectId/ai.tsx @@ -4,27 +4,94 @@ import { ArrowUpRight } from "lucide-react"; const DISCORD_URL = "https://discord.gg/c9uGs3cFXr"; const SUPPORT_EMAIL = "ben@openseo.so"; const SAM_GITHUB_URL = "https://github.com/every-app/sam"; -const DATAFORSEO_MCP_DOCS_URL = - "https://dataforseo.com/help-center/setting-up-the-official-dataforseo-mcp-server-simple-guide"; +const CHATGPT_MCP_DOCS_URL = "https://platform.openai.com/docs/mcp"; +const CLAUDE_CONNECTORS_DOCS_URL = + "https://support.anthropic.com/en/articles/11503834-building-custom-integrations-via-remote-mcp-servers"; + +const mcpTools = [ + { + name: "whoami", + description: + "Check which OpenSEO user and workspace the client can access.", + }, + { + name: "list_projects", + description: "Find your OpenSEO projects and copy the right projectId.", + }, + { + name: "research_keywords", + description: + "Turn seed terms into keyword ideas with volume, difficulty, and CPC.", + }, + { + name: "get_serp_results", + description: + "Inspect live Google results for keyword and competitor research.", + }, + { + name: "get_domain_overview", + description: + "Summarize a domain's organic footprint before deeper research.", + }, + { + name: "get_domain_keyword_suggestions", + description: + "Find keywords a domain already ranks for or could expand into.", + }, + { + name: "get_backlinks_overview", + description: "Review backlink authority and referring-domain signals.", + }, + { + name: "get_rank_tracker", + description: "Read your tracked keyword positions from OpenSEO.", + }, + { + name: "list_saved_keywords", + description: "Pull saved keyword lists into an agent workflow.", + }, + { + name: "save_keywords", + description: "Save promising keyword ideas back to OpenSEO.", + }, +] as const; + +const promptExamples = [ + { + title: "Keyword research", + prompt: + "Use OpenSEO to research keywords for my project. Start with these seed topics: [seed 1], [seed 2], [seed 3]. Group the best opportunities by search intent, prioritize low-difficulty terms, and suggest which keywords I should save.", + }, + { + title: "Competitor research", + prompt: + "Use OpenSEO to compare my domain [yourdomain.com] against these competitors: [competitor1.com], [competitor2.com]. Summarize their strongest keyword themes, backlink advantages, and the first content opportunities I should pursue.", + }, + { + title: "SERP brief", + prompt: + "Use OpenSEO to inspect the live SERP for [keyword]. Tell me who ranks, what search intent Google is rewarding, what pages are cited repeatedly, and what a better page should include.", + }, +] as const; export const Route = createFileRoute("/_project/p/$projectId/ai")({ component: AiPage, }); function AiPage() { + const mcpUrl = + typeof window === "undefined" + ? "https://app.openseo.so/mcp" + : `${window.location.origin}/mcp`; + return (
-
-

AI & Agents

+
+

AI & MCP

- OpenSEO is brand new. Our goal is to build the most powerful and - intuitive tool to help your websites rank better. AI will play a large - role in that. -

-

- Lots of other SEO tools have an overwhelming number of - “features”. We think many of these can be hidden by - default, only shown to you when you want them. + Connect OpenSEO to your coding agent or AI workspace so it can run SEO + research with your project data: keyword ideas, SERP results, domain + overviews, backlinks, rank tracking, and saved keywords.

This is community-driven — we want to hear what matters to you. Reach @@ -44,75 +111,171 @@ function AiPage() { .

- {/* Sam */}
-

Sam: AI SEO Teammate

+

MCP server

- Sam turns your Claude Code or favorite coding agent into a powerful - SEO teammate. Sam has a specialized workflow for writing great - content, but you can teach Sam about your SEO workflow and needs and - it will customize itself for you. + Use this endpoint when a client asks for the OpenSEO MCP server URL. + You will be sent through OpenSEO login the first time a client + connects.

+
+

+ MCP endpoint +

+ + {mcpUrl} + +
+
+ +
+

Connect Codex

+

+ Codex supports hosted MCP servers from the CLI. Add OpenSEO, then + run the OAuth login flow. +

+
+            {`codex mcp add openseo --url ${mcpUrl}
+codex mcp login openseo`}
+          

- Sam handles keyword research, source discovery, and drafting — with - a built-in QA loop that checks quality and accuracy before you - publish. + After login, ask Codex to call whoami, then{" "} + list_projects. Use the returned projectId{" "} + for keyword, SERP, domain, backlink, and rank-tracking requests.

+
+ +
+

Connect Claude Code CLI

+

+ Claude Code can add OpenSEO as an HTTP MCP server. Use the user + scope if you want it available across projects, or local scope if + this repository is the only place you need it. +

+
+            {`claude mcp add --transport http --scope user openseo ${mcpUrl}`}
+          

- Works with DataForSEO MCP for real SEO data. Model-agnostic — use it - with Claude Code, GPT, or others. + If Claude Code prompts you to authorize the server, approve the + OpenSEO login flow. Then ask it to list MCP tools or call{" "} + whoami to confirm the connection.

-
    - {[ - "Keyword research & topic analysis", - "Source discovery & verification", - "Content drafting with QA", - ].map((item) => ( -
  • - - — - - {item} -
  • - ))} +
+ +
+

+ ChatGPT, Claude web, and Claude Desktop +

+

+ Web AI clients handle MCP setup differently from coding tools. Use + the same endpoint above when adding a custom connector or MCP + server, then authorize OpenSEO when the client asks you to sign in. +

+
    +
  • + + — + + + In ChatGPT, enable developer mode if needed, then create a + custom MCP connector from Apps & Connectors and use the endpoint + above. + +
  • +
  • + + — + + + In Claude web or Claude Desktop, add OpenSEO from Settings, + Connectors. Remote MCP servers should be added there, not by + editing the desktop JSON config. + +
+

+ Start by asking the client to check whoami and{" "} + list_projects. Once it can see your projects, it can + help with research planning, SERP analysis, competitor comparisons, + and saving useful keywords back to OpenSEO. +

+ +
+ +
+

Available tools

+
+ {mcpTools.map((tool) => ( +
+ + {tool.name} + +

+ {tool.description} +

+
+ ))} +
+
+ +
+

Prompts to try

+
+ {promptExamples.map((example) => ( +
+

{example.title}

+

+ {example.prompt} +

+
+ ))} +
+
+ +
+

Sam: AI SEO teammate

+

+ Sam is an experimental content workflow for Claude Code and other + coding agents. It combines keyword research, source discovery, + drafting, and QA for teams that want an agent to help produce + publishable SEO content. +

- View on GitHub + View Sam on GitHub
- {/* Use any coding agent */} -
-

Use any coding agent

-

- Pair your preferred agent with the DataForSEO MCP server to access - keyword volumes, SERP results, backlink profiles, and more. -

-

- Claude Code · Cursor · Windsurf · Codex - · any MCP-compatible agent -

- - DataForSEO MCP setup guide - - -
- - {/* Roadmap */}

Roadmap

    @@ -128,7 +291,9 @@ function AiPage() { "Generate drafts using saved keywords and business context", }, { - title: "More MCP integrations", + title: "More MCP clients", + description: + "Better setup guidance for ChatGPT, Claude web, OpenCode, and other clients as their MCP support matures", }, ].map((item) => (
  • From 93ee343c631377aa959f1bd4fd55a86f49fc8492 Mon Sep 17 00:00:00 2001 From: Ben Senescu Date: Fri, 8 May 2026 14:56:26 -0400 Subject: [PATCH 2/4] Polish AI & MCP page and move out of project scope - Move /p/$projectId/ai to /ai so the page can be linked directly - Lead with a copyable MCP server URL and trim the surrounding copy - Make Claude Code and Codex setup guides collapsible accordions - Add an Available tools section grouped into Keywords and Domain --- src/client/features/ai-mcp/AvailableTools.tsx | 89 +++++ src/client/navigation/items.ts | 15 +- src/routeTree.gen.ts | 42 +-- src/routes/_app/ai.tsx | 286 ++++++++++++++++ src/routes/_project/p/$projectId/ai.tsx | 323 ------------------ 5 files changed, 404 insertions(+), 351 deletions(-) create mode 100644 src/client/features/ai-mcp/AvailableTools.tsx create mode 100644 src/routes/_app/ai.tsx delete mode 100644 src/routes/_project/p/$projectId/ai.tsx diff --git a/src/client/features/ai-mcp/AvailableTools.tsx b/src/client/features/ai-mcp/AvailableTools.tsx new file mode 100644 index 0000000..58663a7 --- /dev/null +++ b/src/client/features/ai-mcp/AvailableTools.tsx @@ -0,0 +1,89 @@ +type McpTool = { + name: string; + title: string; + description: string; +}; + +type ToolCategory = { + label: string; + tools: McpTool[]; +}; + +const toolCategories: ToolCategory[] = [ + { + label: "Keywords", + tools: [ + { + name: "research_keywords", + title: "Research keywords", + description: "Get keyword ideas with volume, difficulty, and CPC.", + }, + { + name: "get_serp_results", + title: "Get SERP results", + description: "See live Google results for a keyword.", + }, + { + name: "get_rank_tracker", + title: "Get rank tracking positions", + description: "Read tracked keyword positions.", + }, + { + name: "list_saved_keywords", + title: "Get saved keywords", + description: "Pull your saved keyword lists.", + }, + { + name: "save_keywords", + title: "Save keywords", + description: "Save keywords back to OpenSEO.", + }, + ], + }, + { + label: "Domain", + tools: [ + { + name: "get_domain_overview", + title: "Get domain overview", + description: "Summarize a domain's organic footprint.", + }, + { + name: "get_domain_keyword_suggestions", + title: "Get domain keywords", + description: "Find keywords a domain already ranks for.", + }, + { + name: "get_backlinks_overview", + title: "Get backlinks overview", + description: "Check backlink and referring-domain stats.", + }, + ], + }, +]; + +export function AvailableTools() { + return ( +
    + {toolCategories.map((cat) => ( +
    +

    + {cat.label} +

    +
      + {cat.tools.map((tool) => ( +
    • + + {tool.title} + +

      + {tool.description} +

      +
    • + ))} +
    +
    + ))} +
    + ); +} diff --git a/src/client/navigation/items.ts b/src/client/navigation/items.ts index 643378d..9635dff 100644 --- a/src/client/navigation/items.ts +++ b/src/client/navigation/items.ts @@ -60,14 +60,15 @@ const projectNavItems = [ icon: MessageSquare, matchSegment: "/prompt-explorer", }, - { - to: "/p/$projectId/ai" as const, - label: "AI & MCP", - icon: Bot, - matchSegment: "/ai", - }, ] as const; +const aiNavItem = linkOptions({ + to: "/ai" as const, + label: "AI & MCP", + icon: Bot, + matchSegment: "/ai", +}); + function getProjectNavItems(projectId: string) { return linkOptions( projectNavItems.map((item) => ({ @@ -114,7 +115,7 @@ export function getProjectNavGroups(projectId: string) { }, { type: "standalone" as const, - item: bySegment("/ai"), + item: aiNavItem, }, ]; } diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 7ac9750..7a9e9d6 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -24,6 +24,7 @@ import { Route as AuthSignInRouteImport } from './routes/_auth.sign-in' import { Route as AppSupportRouteImport } from './routes/_app/support' import { Route as AppSettingsRouteImport } from './routes/_app/settings' import { Route as AppBillingRouteImport } from './routes/_app/billing' +import { Route as AppAiRouteImport } from './routes/_app/ai' import { Route as DotwellKnownOpenidConfigurationRouteImport } from './routes/[.]well-known/openid-configuration' import { Route as DotwellKnownOauthAuthorizationServerRouteImport } from './routes/[.]well-known/oauth-authorization-server' import { Route as ApiAutumnSplatRouteImport } from './routes/api/autumn/$' @@ -40,7 +41,6 @@ import { Route as ProjectPProjectIdDomainRouteImport } from './routes/_project/p import { Route as ProjectPProjectIdBrandLookupRouteImport } from './routes/_project/p/$projectId/brand-lookup' import { Route as ProjectPProjectIdBacklinksRouteImport } from './routes/_project/p/$projectId/backlinks' import { Route as ProjectPProjectIdAuditRouteImport } from './routes/_project/p/$projectId/audit' -import { Route as ProjectPProjectIdAiRouteImport } from './routes/_project/p/$projectId/ai' import { Route as DotwellKnownOauthAuthorizationServerApiAuthRouteImport } from './routes/[.]well-known/oauth-authorization-server/api/auth' import { Route as ProjectPProjectIdRankTrackingIndexRouteImport } from './routes/_project/p/$projectId/rank-tracking/index' import { Route as ProjectPProjectIdAuditIndexRouteImport } from './routes/_project/p/$projectId/audit/index' @@ -119,6 +119,11 @@ const AppBillingRoute = AppBillingRouteImport.update({ path: '/billing', getParentRoute: () => AppRouteRoute, } as any) +const AppAiRoute = AppAiRouteImport.update({ + id: '/ai', + path: '/ai', + getParentRoute: () => AppRouteRoute, +} as any) const DotwellKnownOpenidConfigurationRoute = DotwellKnownOpenidConfigurationRouteImport.update({ id: '/.well-known/openid-configuration', @@ -207,11 +212,6 @@ const ProjectPProjectIdAuditRoute = ProjectPProjectIdAuditRouteImport.update({ path: '/audit', getParentRoute: () => ProjectPProjectIdRouteRoute, } as any) -const ProjectPProjectIdAiRoute = ProjectPProjectIdAiRouteImport.update({ - id: '/ai', - path: '/ai', - getParentRoute: () => ProjectPProjectIdRouteRoute, -} as any) const DotwellKnownOauthAuthorizationServerApiAuthRoute = DotwellKnownOauthAuthorizationServerApiAuthRouteImport.update({ id: '/api/auth', @@ -250,6 +250,7 @@ export interface FileRoutesByFullPath { '/verify-email': typeof VerifyEmailRoute '/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren '/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute + '/ai': typeof AppAiRoute '/billing': typeof AppBillingRoute '/settings': typeof AppSettingsRoute '/support': typeof AppSupportRoute @@ -263,7 +264,6 @@ export interface FileRoutesByFullPath { '/api/auth/$': typeof ApiAuthSplatRoute '/api/autumn/$': typeof ApiAutumnSplatRoute '/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute - '/p/$projectId/ai': typeof ProjectPProjectIdAiRoute '/p/$projectId/audit': typeof ProjectPProjectIdAuditRouteWithChildren '/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute '/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute @@ -285,6 +285,7 @@ export interface FileRoutesByTo { '/verify-email': typeof VerifyEmailRoute '/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren '/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute + '/ai': typeof AppAiRoute '/billing': typeof AppBillingRoute '/settings': typeof AppSettingsRoute '/support': typeof AppSupportRoute @@ -297,7 +298,6 @@ export interface FileRoutesByTo { '/api/auth/$': typeof ApiAuthSplatRoute '/api/autumn/$': typeof ApiAutumnSplatRoute '/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute - '/p/$projectId/ai': typeof ProjectPProjectIdAiRoute '/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute '/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute '/p/$projectId/domain': typeof ProjectPProjectIdDomainRoute @@ -321,6 +321,7 @@ export interface FileRoutesById { '/verify-email': typeof VerifyEmailRoute '/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren '/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute + '/_app/ai': typeof AppAiRoute '/_app/billing': typeof AppBillingRoute '/_app/settings': typeof AppSettingsRoute '/_app/support': typeof AppSupportRoute @@ -335,7 +336,6 @@ export interface FileRoutesById { '/api/auth/$': typeof ApiAuthSplatRoute '/api/autumn/$': typeof ApiAutumnSplatRoute '/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute - '/_project/p/$projectId/ai': typeof ProjectPProjectIdAiRoute '/_project/p/$projectId/audit': typeof ProjectPProjectIdAuditRouteWithChildren '/_project/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute '/_project/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute @@ -359,6 +359,7 @@ export interface FileRouteTypes { | '/verify-email' | '/.well-known/oauth-authorization-server' | '/.well-known/openid-configuration' + | '/ai' | '/billing' | '/settings' | '/support' @@ -372,7 +373,6 @@ export interface FileRouteTypes { | '/api/auth/$' | '/api/autumn/$' | '/.well-known/oauth-authorization-server/api/auth' - | '/p/$projectId/ai' | '/p/$projectId/audit' | '/p/$projectId/backlinks' | '/p/$projectId/brand-lookup' @@ -394,6 +394,7 @@ export interface FileRouteTypes { | '/verify-email' | '/.well-known/oauth-authorization-server' | '/.well-known/openid-configuration' + | '/ai' | '/billing' | '/settings' | '/support' @@ -406,7 +407,6 @@ export interface FileRouteTypes { | '/api/auth/$' | '/api/autumn/$' | '/.well-known/oauth-authorization-server/api/auth' - | '/p/$projectId/ai' | '/p/$projectId/backlinks' | '/p/$projectId/brand-lookup' | '/p/$projectId/domain' @@ -429,6 +429,7 @@ export interface FileRouteTypes { | '/verify-email' | '/.well-known/oauth-authorization-server' | '/.well-known/openid-configuration' + | '/_app/ai' | '/_app/billing' | '/_app/settings' | '/_app/support' @@ -443,7 +444,6 @@ export interface FileRouteTypes { | '/api/auth/$' | '/api/autumn/$' | '/.well-known/oauth-authorization-server/api/auth' - | '/_project/p/$projectId/ai' | '/_project/p/$projectId/audit' | '/_project/p/$projectId/backlinks' | '/_project/p/$projectId/brand-lookup' @@ -581,6 +581,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppBillingRouteImport parentRoute: typeof AppRouteRoute } + '/_app/ai': { + id: '/_app/ai' + path: '/ai' + fullPath: '/ai' + preLoaderRoute: typeof AppAiRouteImport + parentRoute: typeof AppRouteRoute + } '/.well-known/openid-configuration': { id: '/.well-known/openid-configuration' path: '/.well-known/openid-configuration' @@ -693,13 +700,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ProjectPProjectIdAuditRouteImport parentRoute: typeof ProjectPProjectIdRouteRoute } - '/_project/p/$projectId/ai': { - id: '/_project/p/$projectId/ai' - path: '/ai' - fullPath: '/p/$projectId/ai' - preLoaderRoute: typeof ProjectPProjectIdAiRouteImport - parentRoute: typeof ProjectPProjectIdRouteRoute - } '/.well-known/oauth-authorization-server/api/auth': { id: '/.well-known/oauth-authorization-server/api/auth' path: '/api/auth' @@ -739,6 +739,7 @@ declare module '@tanstack/react-router' { } interface AppRouteRouteChildren { + AppAiRoute: typeof AppAiRoute AppBillingRoute: typeof AppBillingRoute AppSettingsRoute: typeof AppSettingsRoute AppSupportRoute: typeof AppSupportRoute @@ -747,6 +748,7 @@ interface AppRouteRouteChildren { } const AppRouteRouteChildren: AppRouteRouteChildren = { + AppAiRoute: AppAiRoute, AppBillingRoute: AppBillingRoute, AppSettingsRoute: AppSettingsRoute, AppSupportRoute: AppSupportRoute, @@ -794,7 +796,6 @@ const ProjectPProjectIdRankTrackingRouteWithChildren = ) interface ProjectPProjectIdRouteRouteChildren { - ProjectPProjectIdAiRoute: typeof ProjectPProjectIdAiRoute ProjectPProjectIdAuditRoute: typeof ProjectPProjectIdAuditRouteWithChildren ProjectPProjectIdBacklinksRoute: typeof ProjectPProjectIdBacklinksRoute ProjectPProjectIdBrandLookupRoute: typeof ProjectPProjectIdBrandLookupRoute @@ -808,7 +809,6 @@ interface ProjectPProjectIdRouteRouteChildren { const ProjectPProjectIdRouteRouteChildren: ProjectPProjectIdRouteRouteChildren = { - ProjectPProjectIdAiRoute: ProjectPProjectIdAiRoute, ProjectPProjectIdAuditRoute: ProjectPProjectIdAuditRouteWithChildren, ProjectPProjectIdBacklinksRoute: ProjectPProjectIdBacklinksRoute, ProjectPProjectIdBrandLookupRoute: ProjectPProjectIdBrandLookupRoute, diff --git a/src/routes/_app/ai.tsx b/src/routes/_app/ai.tsx new file mode 100644 index 0000000..94a86b1 --- /dev/null +++ b/src/routes/_app/ai.tsx @@ -0,0 +1,286 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { ArrowUpRight, Check, ChevronDown, Copy } from "lucide-react"; +import { useState } from "react"; +import { toast } from "sonner"; +import { AvailableTools } from "@/client/features/ai-mcp/AvailableTools"; + +const DISCORD_URL = "https://discord.gg/c9uGs3cFXr"; +const SUPPORT_EMAIL = "ben@openseo.so"; +const SAM_GITHUB_URL = "https://github.com/every-app/sam"; + +export const Route = createFileRoute("/_app/ai")({ + component: AiPage, +}); + +function AiPage() { + const mcpUrl = + typeof window === "undefined" + ? "https://app.openseo.so/mcp" + : `${window.location.origin}/mcp`; + + return ( +
    +
    +

    AI & MCP

    +

    + Connect your AI agent to OpenSEO. Run keyword research, SERP analysis, + and domain lookups from your editor or chat. +

    + +
    +
    +
    +

    + MCP server URL +

    + +
    + + {mcpUrl} + +
    +

    + Paste this into any MCP client. Sign in with OpenSEO when prompted. +

    +
    + +
    +

    Setup guides

    +

    + Pick your agent. +

    +
    + +

    + Run this in your terminal: +

    + +

    + Approve the login when prompted. Then ask Claude Code to call{" "} + whoami. +

    +
    + + +

    + Run these commands: +

    + +

    + Approve the login when prompted. Then ask Codex to call{" "} + whoami. +

    +
    +
    +
    + +
    +

    Available tools

    +
    + +
    +
    + +
    +

    Sam: AI SEO teammate

    +

    + Sam is an experimental content workflow for Claude Code and other + coding agents. It combines keyword research, source discovery, + drafting, and QA. +

    + + View Sam on GitHub + + +
    + +
    +

    Roadmap

    +
      + {[ + { + title: "In-app SEO Research Agent", + description: + "Ask questions and run research without leaving OpenSEO", + }, + { + title: "Content Assistant", + description: + "Generate drafts using saved keywords and business context", + }, + ].map((item) => ( +
    • + + — + + + + {item.title} + +
      + {item.description} +
      +
    • + ))} +
    +
    + +

    + Have feedback? Reach out on{" "} + + Discord + {" "} + or email{" "} + + {SUPPORT_EMAIL} + + . +

    +
    +
    + ); +} + +function Collapsible({ + id, + title, + subtitle, + children, +}: { + id: string; + title: string; + subtitle?: string; + children: React.ReactNode; +}) { + const [open, setOpen] = useState(false); + const contentId = `collapsible-${id}`; + + return ( +
    + + {open ? ( +
    + {children} +
    + ) : null} +
    + ); +} + +function CodeBlock({ code }: { code: string }) { + return ( +
    +
    +        {code}
    +      
    +
    + +
    +
    + ); +} + +function CopyButton({ + value, + successMessage, + label, + iconOnly = false, +}: { + value: string; + successMessage: string; + label?: string; + iconOnly?: boolean; +}) { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + if (typeof navigator === "undefined" || !navigator.clipboard?.writeText) { + toast.error("Clipboard not available"); + return; + } + try { + await navigator.clipboard.writeText(value); + toast.success(successMessage); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch { + toast.error("Could not copy to clipboard"); + } + }; + + if (iconOnly) { + return ( + + ); + } + + return ( + + ); +} diff --git a/src/routes/_project/p/$projectId/ai.tsx b/src/routes/_project/p/$projectId/ai.tsx deleted file mode 100644 index 2802322..0000000 --- a/src/routes/_project/p/$projectId/ai.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { ArrowUpRight } from "lucide-react"; - -const DISCORD_URL = "https://discord.gg/c9uGs3cFXr"; -const SUPPORT_EMAIL = "ben@openseo.so"; -const SAM_GITHUB_URL = "https://github.com/every-app/sam"; -const CHATGPT_MCP_DOCS_URL = "https://platform.openai.com/docs/mcp"; -const CLAUDE_CONNECTORS_DOCS_URL = - "https://support.anthropic.com/en/articles/11503834-building-custom-integrations-via-remote-mcp-servers"; - -const mcpTools = [ - { - name: "whoami", - description: - "Check which OpenSEO user and workspace the client can access.", - }, - { - name: "list_projects", - description: "Find your OpenSEO projects and copy the right projectId.", - }, - { - name: "research_keywords", - description: - "Turn seed terms into keyword ideas with volume, difficulty, and CPC.", - }, - { - name: "get_serp_results", - description: - "Inspect live Google results for keyword and competitor research.", - }, - { - name: "get_domain_overview", - description: - "Summarize a domain's organic footprint before deeper research.", - }, - { - name: "get_domain_keyword_suggestions", - description: - "Find keywords a domain already ranks for or could expand into.", - }, - { - name: "get_backlinks_overview", - description: "Review backlink authority and referring-domain signals.", - }, - { - name: "get_rank_tracker", - description: "Read your tracked keyword positions from OpenSEO.", - }, - { - name: "list_saved_keywords", - description: "Pull saved keyword lists into an agent workflow.", - }, - { - name: "save_keywords", - description: "Save promising keyword ideas back to OpenSEO.", - }, -] as const; - -const promptExamples = [ - { - title: "Keyword research", - prompt: - "Use OpenSEO to research keywords for my project. Start with these seed topics: [seed 1], [seed 2], [seed 3]. Group the best opportunities by search intent, prioritize low-difficulty terms, and suggest which keywords I should save.", - }, - { - title: "Competitor research", - prompt: - "Use OpenSEO to compare my domain [yourdomain.com] against these competitors: [competitor1.com], [competitor2.com]. Summarize their strongest keyword themes, backlink advantages, and the first content opportunities I should pursue.", - }, - { - title: "SERP brief", - prompt: - "Use OpenSEO to inspect the live SERP for [keyword]. Tell me who ranks, what search intent Google is rewarding, what pages are cited repeatedly, and what a better page should include.", - }, -] as const; - -export const Route = createFileRoute("/_project/p/$projectId/ai")({ - component: AiPage, -}); - -function AiPage() { - const mcpUrl = - typeof window === "undefined" - ? "https://app.openseo.so/mcp" - : `${window.location.origin}/mcp`; - - return ( -
    -
    -

    AI & MCP

    -

    - Connect OpenSEO to your coding agent or AI workspace so it can run SEO - research with your project data: keyword ideas, SERP results, domain - overviews, backlinks, rank tracking, and saved keywords. -

    -

    - This is community-driven — we want to hear what matters to you. Reach - out on{" "} - - Discord - {" "} - or email{" "} - - {SUPPORT_EMAIL} - - . -

    - -
    -

    MCP server

    -

    - Use this endpoint when a client asks for the OpenSEO MCP server URL. - You will be sent through OpenSEO login the first time a client - connects. -

    -
    -

    - MCP endpoint -

    - - {mcpUrl} - -
    -
    - -
    -

    Connect Codex

    -

    - Codex supports hosted MCP servers from the CLI. Add OpenSEO, then - run the OAuth login flow. -

    -
    -            {`codex mcp add openseo --url ${mcpUrl}
    -codex mcp login openseo`}
    -          
    -

    - After login, ask Codex to call whoami, then{" "} - list_projects. Use the returned projectId{" "} - for keyword, SERP, domain, backlink, and rank-tracking requests. -

    -
    - -
    -

    Connect Claude Code CLI

    -

    - Claude Code can add OpenSEO as an HTTP MCP server. Use the user - scope if you want it available across projects, or local scope if - this repository is the only place you need it. -

    -
    -            {`claude mcp add --transport http --scope user openseo ${mcpUrl}`}
    -          
    -

    - If Claude Code prompts you to authorize the server, approve the - OpenSEO login flow. Then ask it to list MCP tools or call{" "} - whoami to confirm the connection. -

    -
    - -
    -

    - ChatGPT, Claude web, and Claude Desktop -

    -

    - Web AI clients handle MCP setup differently from coding tools. Use - the same endpoint above when adding a custom connector or MCP - server, then authorize OpenSEO when the client asks you to sign in. -

    -
      -
    • - - — - - - In ChatGPT, enable developer mode if needed, then create a - custom MCP connector from Apps & Connectors and use the endpoint - above. - -
    • -
    • - - — - - - In Claude web or Claude Desktop, add OpenSEO from Settings, - Connectors. Remote MCP servers should be added there, not by - editing the desktop JSON config. - -
    • -
    -

    - Start by asking the client to check whoami and{" "} - list_projects. Once it can see your projects, it can - help with research planning, SERP analysis, competitor comparisons, - and saving useful keywords back to OpenSEO. -

    - -
    - -
    -

    Available tools

    -
    - {mcpTools.map((tool) => ( -
    - - {tool.name} - -

    - {tool.description} -

    -
    - ))} -
    -
    - -
    -

    Prompts to try

    -
    - {promptExamples.map((example) => ( -
    -

    {example.title}

    -

    - {example.prompt} -

    -
    - ))} -
    -
    - -
    -

    Sam: AI SEO teammate

    -

    - Sam is an experimental content workflow for Claude Code and other - coding agents. It combines keyword research, source discovery, - drafting, and QA for teams that want an agent to help produce - publishable SEO content. -

    - - View Sam on GitHub - - -
    - -
    -

    Roadmap

    -
      - {[ - { - title: "In-app SEO Research Agent", - description: - "Ask questions and run research without leaving OpenSEO", - }, - { - title: "Content Assistant", - description: - "Generate drafts using saved keywords and business context", - }, - { - title: "More MCP clients", - description: - "Better setup guidance for ChatGPT, Claude web, OpenCode, and other clients as their MCP support matures", - }, - ].map((item) => ( -
    • -

      - - — - - - - {item.title} - - {item.description ? ( - <> -
      - {item.description} - - ) : null} -
      -

      -
    • - ))} -
    -
    -
    -
    - ); -} From 239a5a9e2b4d43c9519ffcb9b4939ddd165dcd1f Mon Sep 17 00:00:00 2001 From: Ben Senescu Date: Fri, 8 May 2026 15:20:42 -0400 Subject: [PATCH 3/4] Add Claude and Codex desktop setup guides with brand logos - Drop redundant codex mcp login step from the CLI flow - Add Claude Desktop and Codex Desktop accordion entries - Lead each setup-guide row with its product logo --- src/client/features/ai-mcp/AgentIcons.tsx | 34 +++++++++ src/routes/_app/ai.tsx | 93 +++++++++++++++++++---- 2 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 src/client/features/ai-mcp/AgentIcons.tsx diff --git a/src/client/features/ai-mcp/AgentIcons.tsx b/src/client/features/ai-mcp/AgentIcons.tsx new file mode 100644 index 0000000..d61537c --- /dev/null +++ b/src/client/features/ai-mcp/AgentIcons.tsx @@ -0,0 +1,34 @@ +import type { SVGProps } from "react"; + +export function ClaudeIcon(props: SVGProps) { + return ( + + ); +} + +export function CodexIcon(props: SVGProps) { + return ( + + ); +} diff --git a/src/routes/_app/ai.tsx b/src/routes/_app/ai.tsx index 94a86b1..52f200d 100644 --- a/src/routes/_app/ai.tsx +++ b/src/routes/_app/ai.tsx @@ -2,6 +2,10 @@ import { createFileRoute } from "@tanstack/react-router"; import { ArrowUpRight, Check, ChevronDown, Copy } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; +import { + ClaudeIcon, + CodexIcon, +} from "@/client/features/ai-mcp/AgentIcons"; import { AvailableTools } from "@/client/features/ai-mcp/AvailableTools"; const DISCORD_URL = "https://discord.gg/c9uGs3cFXr"; @@ -57,7 +61,8 @@ function AiPage() { } >

    Run this in your terminal: @@ -66,27 +71,76 @@ function AiPage() { code={`claude mcp add --transport http --scope user openseo ${mcpUrl}`} />

    - Approve the login when prompted. Then ask Claude Code to call{" "} - whoami. + Approve the login when prompted. +

    +
    + + } + > +
      +
    1. + Open Settings →{" "} + Connectors. +
    2. +
    3. + Click{" "} + + Add custom connector + + . +
    4. +
    5. Paste the MCP URL above and click Add.
    6. +
    7. Approve the OpenSEO login when prompted.
    8. +
    +

    + Requires a Claude Pro, Max, Team, or Enterprise plan.

    } >

    - Run these commands: + Run this in your terminal:

    - +

    - Approve the login when prompted. Then ask Codex to call{" "} - whoami. + Approve the login when prompted.

    + + } + > +
      +
    1. + Open{" "} + + Settings → Integrations & MCP + + . +
    2. +
    3. + Click{" "} + + Add your own + + . +
    4. +
    5. Paste the MCP URL above.
    6. +
    7. Approve the OpenSEO login when prompted.
    8. +
    +
@@ -171,11 +225,13 @@ function Collapsible({ id, title, subtitle, + icon, children, }: { id: string; title: string; subtitle?: string; + icon?: React.ReactNode; children: React.ReactNode; }) { const [open, setOpen] = useState(false); @@ -190,11 +246,20 @@ function Collapsible({ aria-controls={contentId} className="flex w-full items-center justify-between gap-3 px-4 py-3.5 text-left transition-colors hover:bg-base-300/50" > -
- {title} - {subtitle ? ( - {subtitle} +
+ {icon ? ( + + {icon} + ) : null} +
+ + {title} + + {subtitle ? ( + {subtitle} + ) : null} +
Date: Fri, 8 May 2026 15:27:12 -0400 Subject: [PATCH 4/4] ci: run prettier on AI page --- src/routes/_app/ai.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/routes/_app/ai.tsx b/src/routes/_app/ai.tsx index 52f200d..a973ea5 100644 --- a/src/routes/_app/ai.tsx +++ b/src/routes/_app/ai.tsx @@ -2,10 +2,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { ArrowUpRight, Check, ChevronDown, Copy } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; -import { - ClaudeIcon, - CodexIcon, -} from "@/client/features/ai-mcp/AgentIcons"; +import { ClaudeIcon, CodexIcon } from "@/client/features/ai-mcp/AgentIcons"; import { AvailableTools } from "@/client/features/ai-mcp/AvailableTools"; const DISCORD_URL = "https://discord.gg/c9uGs3cFXr"; @@ -283,7 +280,11 @@ function CodeBlock({ code }: { code: string }) { {code}
- +
);