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
This commit is contained in:
parent
02fcfb9834
commit
93ee343c63
89
src/client/features/ai-mcp/AvailableTools.tsx
Normal file
89
src/client/features/ai-mcp/AvailableTools.tsx
Normal file
@ -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 (
|
||||||
|
<div className="grid gap-x-8 gap-y-8 md:grid-cols-2">
|
||||||
|
{toolCategories.map((cat) => (
|
||||||
|
<div key={cat.label}>
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-base-content/50">
|
||||||
|
{cat.label}
|
||||||
|
</h3>
|
||||||
|
<ul className="mt-3 space-y-3">
|
||||||
|
{cat.tools.map((tool) => (
|
||||||
|
<li key={tool.name} className="flex flex-col gap-0.5">
|
||||||
|
<span className="text-sm font-medium text-base-content">
|
||||||
|
{tool.title}
|
||||||
|
</span>
|
||||||
|
<p className="text-xs text-base-content/60 leading-relaxed">
|
||||||
|
{tool.description}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -60,14 +60,15 @@ const projectNavItems = [
|
|||||||
icon: MessageSquare,
|
icon: MessageSquare,
|
||||||
matchSegment: "/prompt-explorer",
|
matchSegment: "/prompt-explorer",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
to: "/p/$projectId/ai" as const,
|
|
||||||
label: "AI & MCP",
|
|
||||||
icon: Bot,
|
|
||||||
matchSegment: "/ai",
|
|
||||||
},
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
const aiNavItem = linkOptions({
|
||||||
|
to: "/ai" as const,
|
||||||
|
label: "AI & MCP",
|
||||||
|
icon: Bot,
|
||||||
|
matchSegment: "/ai",
|
||||||
|
});
|
||||||
|
|
||||||
function getProjectNavItems(projectId: string) {
|
function getProjectNavItems(projectId: string) {
|
||||||
return linkOptions(
|
return linkOptions(
|
||||||
projectNavItems.map((item) => ({
|
projectNavItems.map((item) => ({
|
||||||
@ -114,7 +115,7 @@ export function getProjectNavGroups(projectId: string) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "standalone" as const,
|
type: "standalone" as const,
|
||||||
item: bySegment("/ai"),
|
item: aiNavItem,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import { Route as AuthSignInRouteImport } from './routes/_auth.sign-in'
|
|||||||
import { Route as AppSupportRouteImport } from './routes/_app/support'
|
import { Route as AppSupportRouteImport } from './routes/_app/support'
|
||||||
import { Route as AppSettingsRouteImport } from './routes/_app/settings'
|
import { Route as AppSettingsRouteImport } from './routes/_app/settings'
|
||||||
import { Route as AppBillingRouteImport } from './routes/_app/billing'
|
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 DotwellKnownOpenidConfigurationRouteImport } from './routes/[.]well-known/openid-configuration'
|
||||||
import { Route as DotwellKnownOauthAuthorizationServerRouteImport } from './routes/[.]well-known/oauth-authorization-server'
|
import { Route as DotwellKnownOauthAuthorizationServerRouteImport } from './routes/[.]well-known/oauth-authorization-server'
|
||||||
import { Route as ApiAutumnSplatRouteImport } from './routes/api/autumn/$'
|
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 ProjectPProjectIdBrandLookupRouteImport } from './routes/_project/p/$projectId/brand-lookup'
|
||||||
import { Route as ProjectPProjectIdBacklinksRouteImport } from './routes/_project/p/$projectId/backlinks'
|
import { Route as ProjectPProjectIdBacklinksRouteImport } from './routes/_project/p/$projectId/backlinks'
|
||||||
import { Route as ProjectPProjectIdAuditRouteImport } from './routes/_project/p/$projectId/audit'
|
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 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 ProjectPProjectIdRankTrackingIndexRouteImport } from './routes/_project/p/$projectId/rank-tracking/index'
|
||||||
import { Route as ProjectPProjectIdAuditIndexRouteImport } from './routes/_project/p/$projectId/audit/index'
|
import { Route as ProjectPProjectIdAuditIndexRouteImport } from './routes/_project/p/$projectId/audit/index'
|
||||||
@ -119,6 +119,11 @@ const AppBillingRoute = AppBillingRouteImport.update({
|
|||||||
path: '/billing',
|
path: '/billing',
|
||||||
getParentRoute: () => AppRouteRoute,
|
getParentRoute: () => AppRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AppAiRoute = AppAiRouteImport.update({
|
||||||
|
id: '/ai',
|
||||||
|
path: '/ai',
|
||||||
|
getParentRoute: () => AppRouteRoute,
|
||||||
|
} as any)
|
||||||
const DotwellKnownOpenidConfigurationRoute =
|
const DotwellKnownOpenidConfigurationRoute =
|
||||||
DotwellKnownOpenidConfigurationRouteImport.update({
|
DotwellKnownOpenidConfigurationRouteImport.update({
|
||||||
id: '/.well-known/openid-configuration',
|
id: '/.well-known/openid-configuration',
|
||||||
@ -207,11 +212,6 @@ const ProjectPProjectIdAuditRoute = ProjectPProjectIdAuditRouteImport.update({
|
|||||||
path: '/audit',
|
path: '/audit',
|
||||||
getParentRoute: () => ProjectPProjectIdRouteRoute,
|
getParentRoute: () => ProjectPProjectIdRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const ProjectPProjectIdAiRoute = ProjectPProjectIdAiRouteImport.update({
|
|
||||||
id: '/ai',
|
|
||||||
path: '/ai',
|
|
||||||
getParentRoute: () => ProjectPProjectIdRouteRoute,
|
|
||||||
} as any)
|
|
||||||
const DotwellKnownOauthAuthorizationServerApiAuthRoute =
|
const DotwellKnownOauthAuthorizationServerApiAuthRoute =
|
||||||
DotwellKnownOauthAuthorizationServerApiAuthRouteImport.update({
|
DotwellKnownOauthAuthorizationServerApiAuthRouteImport.update({
|
||||||
id: '/api/auth',
|
id: '/api/auth',
|
||||||
@ -250,6 +250,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/verify-email': typeof VerifyEmailRoute
|
'/verify-email': typeof VerifyEmailRoute
|
||||||
'/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren
|
'/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren
|
||||||
'/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute
|
'/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute
|
||||||
|
'/ai': typeof AppAiRoute
|
||||||
'/billing': typeof AppBillingRoute
|
'/billing': typeof AppBillingRoute
|
||||||
'/settings': typeof AppSettingsRoute
|
'/settings': typeof AppSettingsRoute
|
||||||
'/support': typeof AppSupportRoute
|
'/support': typeof AppSupportRoute
|
||||||
@ -263,7 +264,6 @@ export interface FileRoutesByFullPath {
|
|||||||
'/api/auth/$': typeof ApiAuthSplatRoute
|
'/api/auth/$': typeof ApiAuthSplatRoute
|
||||||
'/api/autumn/$': typeof ApiAutumnSplatRoute
|
'/api/autumn/$': typeof ApiAutumnSplatRoute
|
||||||
'/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute
|
'/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute
|
||||||
'/p/$projectId/ai': typeof ProjectPProjectIdAiRoute
|
|
||||||
'/p/$projectId/audit': typeof ProjectPProjectIdAuditRouteWithChildren
|
'/p/$projectId/audit': typeof ProjectPProjectIdAuditRouteWithChildren
|
||||||
'/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute
|
'/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute
|
||||||
'/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute
|
'/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute
|
||||||
@ -285,6 +285,7 @@ export interface FileRoutesByTo {
|
|||||||
'/verify-email': typeof VerifyEmailRoute
|
'/verify-email': typeof VerifyEmailRoute
|
||||||
'/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren
|
'/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren
|
||||||
'/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute
|
'/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute
|
||||||
|
'/ai': typeof AppAiRoute
|
||||||
'/billing': typeof AppBillingRoute
|
'/billing': typeof AppBillingRoute
|
||||||
'/settings': typeof AppSettingsRoute
|
'/settings': typeof AppSettingsRoute
|
||||||
'/support': typeof AppSupportRoute
|
'/support': typeof AppSupportRoute
|
||||||
@ -297,7 +298,6 @@ export interface FileRoutesByTo {
|
|||||||
'/api/auth/$': typeof ApiAuthSplatRoute
|
'/api/auth/$': typeof ApiAuthSplatRoute
|
||||||
'/api/autumn/$': typeof ApiAutumnSplatRoute
|
'/api/autumn/$': typeof ApiAutumnSplatRoute
|
||||||
'/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute
|
'/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute
|
||||||
'/p/$projectId/ai': typeof ProjectPProjectIdAiRoute
|
|
||||||
'/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute
|
'/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute
|
||||||
'/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute
|
'/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute
|
||||||
'/p/$projectId/domain': typeof ProjectPProjectIdDomainRoute
|
'/p/$projectId/domain': typeof ProjectPProjectIdDomainRoute
|
||||||
@ -321,6 +321,7 @@ export interface FileRoutesById {
|
|||||||
'/verify-email': typeof VerifyEmailRoute
|
'/verify-email': typeof VerifyEmailRoute
|
||||||
'/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren
|
'/.well-known/oauth-authorization-server': typeof DotwellKnownOauthAuthorizationServerRouteWithChildren
|
||||||
'/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute
|
'/.well-known/openid-configuration': typeof DotwellKnownOpenidConfigurationRoute
|
||||||
|
'/_app/ai': typeof AppAiRoute
|
||||||
'/_app/billing': typeof AppBillingRoute
|
'/_app/billing': typeof AppBillingRoute
|
||||||
'/_app/settings': typeof AppSettingsRoute
|
'/_app/settings': typeof AppSettingsRoute
|
||||||
'/_app/support': typeof AppSupportRoute
|
'/_app/support': typeof AppSupportRoute
|
||||||
@ -335,7 +336,6 @@ export interface FileRoutesById {
|
|||||||
'/api/auth/$': typeof ApiAuthSplatRoute
|
'/api/auth/$': typeof ApiAuthSplatRoute
|
||||||
'/api/autumn/$': typeof ApiAutumnSplatRoute
|
'/api/autumn/$': typeof ApiAutumnSplatRoute
|
||||||
'/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute
|
'/.well-known/oauth-authorization-server/api/auth': typeof DotwellKnownOauthAuthorizationServerApiAuthRoute
|
||||||
'/_project/p/$projectId/ai': typeof ProjectPProjectIdAiRoute
|
|
||||||
'/_project/p/$projectId/audit': typeof ProjectPProjectIdAuditRouteWithChildren
|
'/_project/p/$projectId/audit': typeof ProjectPProjectIdAuditRouteWithChildren
|
||||||
'/_project/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute
|
'/_project/p/$projectId/backlinks': typeof ProjectPProjectIdBacklinksRoute
|
||||||
'/_project/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute
|
'/_project/p/$projectId/brand-lookup': typeof ProjectPProjectIdBrandLookupRoute
|
||||||
@ -359,6 +359,7 @@ export interface FileRouteTypes {
|
|||||||
| '/verify-email'
|
| '/verify-email'
|
||||||
| '/.well-known/oauth-authorization-server'
|
| '/.well-known/oauth-authorization-server'
|
||||||
| '/.well-known/openid-configuration'
|
| '/.well-known/openid-configuration'
|
||||||
|
| '/ai'
|
||||||
| '/billing'
|
| '/billing'
|
||||||
| '/settings'
|
| '/settings'
|
||||||
| '/support'
|
| '/support'
|
||||||
@ -372,7 +373,6 @@ export interface FileRouteTypes {
|
|||||||
| '/api/auth/$'
|
| '/api/auth/$'
|
||||||
| '/api/autumn/$'
|
| '/api/autumn/$'
|
||||||
| '/.well-known/oauth-authorization-server/api/auth'
|
| '/.well-known/oauth-authorization-server/api/auth'
|
||||||
| '/p/$projectId/ai'
|
|
||||||
| '/p/$projectId/audit'
|
| '/p/$projectId/audit'
|
||||||
| '/p/$projectId/backlinks'
|
| '/p/$projectId/backlinks'
|
||||||
| '/p/$projectId/brand-lookup'
|
| '/p/$projectId/brand-lookup'
|
||||||
@ -394,6 +394,7 @@ export interface FileRouteTypes {
|
|||||||
| '/verify-email'
|
| '/verify-email'
|
||||||
| '/.well-known/oauth-authorization-server'
|
| '/.well-known/oauth-authorization-server'
|
||||||
| '/.well-known/openid-configuration'
|
| '/.well-known/openid-configuration'
|
||||||
|
| '/ai'
|
||||||
| '/billing'
|
| '/billing'
|
||||||
| '/settings'
|
| '/settings'
|
||||||
| '/support'
|
| '/support'
|
||||||
@ -406,7 +407,6 @@ export interface FileRouteTypes {
|
|||||||
| '/api/auth/$'
|
| '/api/auth/$'
|
||||||
| '/api/autumn/$'
|
| '/api/autumn/$'
|
||||||
| '/.well-known/oauth-authorization-server/api/auth'
|
| '/.well-known/oauth-authorization-server/api/auth'
|
||||||
| '/p/$projectId/ai'
|
|
||||||
| '/p/$projectId/backlinks'
|
| '/p/$projectId/backlinks'
|
||||||
| '/p/$projectId/brand-lookup'
|
| '/p/$projectId/brand-lookup'
|
||||||
| '/p/$projectId/domain'
|
| '/p/$projectId/domain'
|
||||||
@ -429,6 +429,7 @@ export interface FileRouteTypes {
|
|||||||
| '/verify-email'
|
| '/verify-email'
|
||||||
| '/.well-known/oauth-authorization-server'
|
| '/.well-known/oauth-authorization-server'
|
||||||
| '/.well-known/openid-configuration'
|
| '/.well-known/openid-configuration'
|
||||||
|
| '/_app/ai'
|
||||||
| '/_app/billing'
|
| '/_app/billing'
|
||||||
| '/_app/settings'
|
| '/_app/settings'
|
||||||
| '/_app/support'
|
| '/_app/support'
|
||||||
@ -443,7 +444,6 @@ export interface FileRouteTypes {
|
|||||||
| '/api/auth/$'
|
| '/api/auth/$'
|
||||||
| '/api/autumn/$'
|
| '/api/autumn/$'
|
||||||
| '/.well-known/oauth-authorization-server/api/auth'
|
| '/.well-known/oauth-authorization-server/api/auth'
|
||||||
| '/_project/p/$projectId/ai'
|
|
||||||
| '/_project/p/$projectId/audit'
|
| '/_project/p/$projectId/audit'
|
||||||
| '/_project/p/$projectId/backlinks'
|
| '/_project/p/$projectId/backlinks'
|
||||||
| '/_project/p/$projectId/brand-lookup'
|
| '/_project/p/$projectId/brand-lookup'
|
||||||
@ -581,6 +581,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AppBillingRouteImport
|
preLoaderRoute: typeof AppBillingRouteImport
|
||||||
parentRoute: typeof AppRouteRoute
|
parentRoute: typeof AppRouteRoute
|
||||||
}
|
}
|
||||||
|
'/_app/ai': {
|
||||||
|
id: '/_app/ai'
|
||||||
|
path: '/ai'
|
||||||
|
fullPath: '/ai'
|
||||||
|
preLoaderRoute: typeof AppAiRouteImport
|
||||||
|
parentRoute: typeof AppRouteRoute
|
||||||
|
}
|
||||||
'/.well-known/openid-configuration': {
|
'/.well-known/openid-configuration': {
|
||||||
id: '/.well-known/openid-configuration'
|
id: '/.well-known/openid-configuration'
|
||||||
path: '/.well-known/openid-configuration'
|
path: '/.well-known/openid-configuration'
|
||||||
@ -693,13 +700,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof ProjectPProjectIdAuditRouteImport
|
preLoaderRoute: typeof ProjectPProjectIdAuditRouteImport
|
||||||
parentRoute: typeof ProjectPProjectIdRouteRoute
|
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': {
|
'/.well-known/oauth-authorization-server/api/auth': {
|
||||||
id: '/.well-known/oauth-authorization-server/api/auth'
|
id: '/.well-known/oauth-authorization-server/api/auth'
|
||||||
path: '/api/auth'
|
path: '/api/auth'
|
||||||
@ -739,6 +739,7 @@ declare module '@tanstack/react-router' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AppRouteRouteChildren {
|
interface AppRouteRouteChildren {
|
||||||
|
AppAiRoute: typeof AppAiRoute
|
||||||
AppBillingRoute: typeof AppBillingRoute
|
AppBillingRoute: typeof AppBillingRoute
|
||||||
AppSettingsRoute: typeof AppSettingsRoute
|
AppSettingsRoute: typeof AppSettingsRoute
|
||||||
AppSupportRoute: typeof AppSupportRoute
|
AppSupportRoute: typeof AppSupportRoute
|
||||||
@ -747,6 +748,7 @@ interface AppRouteRouteChildren {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AppRouteRouteChildren: AppRouteRouteChildren = {
|
const AppRouteRouteChildren: AppRouteRouteChildren = {
|
||||||
|
AppAiRoute: AppAiRoute,
|
||||||
AppBillingRoute: AppBillingRoute,
|
AppBillingRoute: AppBillingRoute,
|
||||||
AppSettingsRoute: AppSettingsRoute,
|
AppSettingsRoute: AppSettingsRoute,
|
||||||
AppSupportRoute: AppSupportRoute,
|
AppSupportRoute: AppSupportRoute,
|
||||||
@ -794,7 +796,6 @@ const ProjectPProjectIdRankTrackingRouteWithChildren =
|
|||||||
)
|
)
|
||||||
|
|
||||||
interface ProjectPProjectIdRouteRouteChildren {
|
interface ProjectPProjectIdRouteRouteChildren {
|
||||||
ProjectPProjectIdAiRoute: typeof ProjectPProjectIdAiRoute
|
|
||||||
ProjectPProjectIdAuditRoute: typeof ProjectPProjectIdAuditRouteWithChildren
|
ProjectPProjectIdAuditRoute: typeof ProjectPProjectIdAuditRouteWithChildren
|
||||||
ProjectPProjectIdBacklinksRoute: typeof ProjectPProjectIdBacklinksRoute
|
ProjectPProjectIdBacklinksRoute: typeof ProjectPProjectIdBacklinksRoute
|
||||||
ProjectPProjectIdBrandLookupRoute: typeof ProjectPProjectIdBrandLookupRoute
|
ProjectPProjectIdBrandLookupRoute: typeof ProjectPProjectIdBrandLookupRoute
|
||||||
@ -808,7 +809,6 @@ interface ProjectPProjectIdRouteRouteChildren {
|
|||||||
|
|
||||||
const ProjectPProjectIdRouteRouteChildren: ProjectPProjectIdRouteRouteChildren =
|
const ProjectPProjectIdRouteRouteChildren: ProjectPProjectIdRouteRouteChildren =
|
||||||
{
|
{
|
||||||
ProjectPProjectIdAiRoute: ProjectPProjectIdAiRoute,
|
|
||||||
ProjectPProjectIdAuditRoute: ProjectPProjectIdAuditRouteWithChildren,
|
ProjectPProjectIdAuditRoute: ProjectPProjectIdAuditRouteWithChildren,
|
||||||
ProjectPProjectIdBacklinksRoute: ProjectPProjectIdBacklinksRoute,
|
ProjectPProjectIdBacklinksRoute: ProjectPProjectIdBacklinksRoute,
|
||||||
ProjectPProjectIdBrandLookupRoute: ProjectPProjectIdBrandLookupRoute,
|
ProjectPProjectIdBrandLookupRoute: ProjectPProjectIdBrandLookupRoute,
|
||||||
|
|||||||
286
src/routes/_app/ai.tsx
Normal file
286
src/routes/_app/ai.tsx
Normal file
@ -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 (
|
||||||
|
<div className="h-full overflow-auto bg-base-100 px-4 py-12 md:px-6 md:py-16 pb-24 md:pb-12">
|
||||||
|
<div className="mx-auto max-w-3xl">
|
||||||
|
<h1 className="text-2xl font-semibold">AI & MCP</h1>
|
||||||
|
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
||||||
|
Connect your AI agent to OpenSEO. Run keyword research, SERP analysis,
|
||||||
|
and domain lookups from your editor or chat.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<section className="mt-8">
|
||||||
|
<div className="rounded-lg border border-base-300 bg-base-200 px-4 py-3.5">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<p className="text-xs font-medium uppercase tracking-wide text-base-content/50">
|
||||||
|
MCP server URL
|
||||||
|
</p>
|
||||||
|
<CopyButton
|
||||||
|
value={mcpUrl}
|
||||||
|
successMessage="MCP URL copied"
|
||||||
|
label="Copy"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<code className="mt-2 block break-all font-mono text-sm text-base-content">
|
||||||
|
{mcpUrl}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
<p className="mt-2.5 text-xs text-base-content/55 leading-relaxed">
|
||||||
|
Paste this into any MCP client. Sign in with OpenSEO when prompted.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-10">
|
||||||
|
<h2 className="text-base font-semibold">Setup guides</h2>
|
||||||
|
<p className="mt-1.5 text-sm text-base-content/70">
|
||||||
|
Pick your agent.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4 divide-y divide-base-300 overflow-hidden rounded-lg border border-base-300 bg-base-200">
|
||||||
|
<Collapsible
|
||||||
|
id="claude-code"
|
||||||
|
title="Claude Code"
|
||||||
|
subtitle="HTTP MCP server via the CLI"
|
||||||
|
>
|
||||||
|
<p className="text-sm text-base-content/70">
|
||||||
|
Run this in your terminal:
|
||||||
|
</p>
|
||||||
|
<CodeBlock
|
||||||
|
code={`claude mcp add --transport http --scope user openseo ${mcpUrl}`}
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-base-content/70">
|
||||||
|
Approve the login when prompted. Then ask Claude Code to call{" "}
|
||||||
|
<code className="font-mono text-[0.85em]">whoami</code>.
|
||||||
|
</p>
|
||||||
|
</Collapsible>
|
||||||
|
|
||||||
|
<Collapsible
|
||||||
|
id="codex"
|
||||||
|
title="Codex"
|
||||||
|
subtitle="Hosted MCP server via the CLI"
|
||||||
|
>
|
||||||
|
<p className="text-sm text-base-content/70">
|
||||||
|
Run these commands:
|
||||||
|
</p>
|
||||||
|
<CodeBlock
|
||||||
|
code={`codex mcp add openseo --url ${mcpUrl}\ncodex mcp login openseo`}
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-base-content/70">
|
||||||
|
Approve the login when prompted. Then ask Codex to call{" "}
|
||||||
|
<code className="font-mono text-[0.85em]">whoami</code>.
|
||||||
|
</p>
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-12">
|
||||||
|
<h2 className="text-base font-semibold">Available tools</h2>
|
||||||
|
<div className="mt-5">
|
||||||
|
<AvailableTools />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-12">
|
||||||
|
<h2 className="text-base font-semibold">Sam: AI SEO teammate</h2>
|
||||||
|
<p className="mt-1.5 text-sm text-base-content/70 leading-relaxed">
|
||||||
|
Sam is an experimental content workflow for Claude Code and other
|
||||||
|
coding agents. It combines keyword research, source discovery,
|
||||||
|
drafting, and QA.
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={SAM_GITHUB_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="mt-3 inline-flex items-center gap-1.5 text-sm font-medium text-base-content transition-colors hover:text-base-content/60"
|
||||||
|
>
|
||||||
|
View Sam on GitHub
|
||||||
|
<ArrowUpRight className="size-3.5" />
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-12">
|
||||||
|
<h2 className="text-base font-semibold">Roadmap</h2>
|
||||||
|
<ul className="mt-4 space-y-3">
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
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) => (
|
||||||
|
<li key={item.title} className="flex gap-2.5 text-sm">
|
||||||
|
<span className="mt-[2px] shrink-0 text-base-content/40">
|
||||||
|
—
|
||||||
|
</span>
|
||||||
|
<span className="text-base-content/70">
|
||||||
|
<span className="font-medium text-base-content">
|
||||||
|
{item.title}
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
{item.description}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<p className="mt-12 text-xs text-base-content/55 leading-relaxed">
|
||||||
|
Have feedback? Reach out on{" "}
|
||||||
|
<a
|
||||||
|
className="link link-primary"
|
||||||
|
href={DISCORD_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Discord
|
||||||
|
</a>{" "}
|
||||||
|
or email{" "}
|
||||||
|
<a className="link link-primary" href={`mailto:${SUPPORT_EMAIL}`}>
|
||||||
|
{SUPPORT_EMAIL}
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setOpen((value) => !value)}
|
||||||
|
aria-expanded={open}
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="text-sm font-medium text-base-content">{title}</span>
|
||||||
|
{subtitle ? (
|
||||||
|
<span className="text-xs text-base-content/55">{subtitle}</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<ChevronDown
|
||||||
|
className={`size-4 shrink-0 text-base-content/50 transition-transform ${
|
||||||
|
open ? "rotate-180" : ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{open ? (
|
||||||
|
<div id={contentId} className="space-y-3 px-4 pb-4 pt-1">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CodeBlock({ code }: { code: string }) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-stretch overflow-hidden rounded-md border border-base-300 bg-base-100">
|
||||||
|
<pre className="min-w-0 flex-1 overflow-x-auto p-3 text-xs leading-relaxed text-base-content">
|
||||||
|
<code className="font-mono">{code}</code>
|
||||||
|
</pre>
|
||||||
|
<div className="flex shrink-0 items-start border-l border-base-300 p-1.5">
|
||||||
|
<CopyButton value={code} successMessage="Copied to clipboard" iconOnly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleCopy}
|
||||||
|
aria-label="Copy"
|
||||||
|
className="flex size-7 items-center justify-center rounded-md text-base-content/60 transition-colors hover:bg-base-200 hover:text-base-content"
|
||||||
|
>
|
||||||
|
{copied ? (
|
||||||
|
<Check className="size-3.5 text-success" />
|
||||||
|
) : (
|
||||||
|
<Copy className="size-3.5" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleCopy}
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-md border border-base-300 bg-base-100 px-2 py-1 text-xs font-medium text-base-content/70 transition-colors hover:bg-base-300/50 hover:text-base-content"
|
||||||
|
>
|
||||||
|
{copied ? (
|
||||||
|
<Check className="size-3 text-success" />
|
||||||
|
) : (
|
||||||
|
<Copy className="size-3" />
|
||||||
|
)}
|
||||||
|
{label ?? "Copy"}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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 (
|
|
||||||
<div className="px-4 py-12 md:px-6 md:py-20 pb-24 md:pb-8 overflow-auto">
|
|
||||||
<div className="mx-auto max-w-3xl">
|
|
||||||
<h1 className="text-2xl font-semibold">AI & MCP</h1>
|
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
<p className="mt-3 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
This is community-driven — we want to hear what matters to you. Reach
|
|
||||||
out on{" "}
|
|
||||||
<a
|
|
||||||
className="link link-primary"
|
|
||||||
href={DISCORD_URL}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Discord
|
|
||||||
</a>{" "}
|
|
||||||
or email{" "}
|
|
||||||
<a className="link link-primary" href={`mailto:${SUPPORT_EMAIL}`}>
|
|
||||||
{SUPPORT_EMAIL}
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">MCP server</h2>
|
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
<div className="mt-4 rounded-lg border border-base-300 bg-base-200/50 px-4 py-3">
|
|
||||||
<p className="text-xs font-medium uppercase tracking-wide text-base-content/50">
|
|
||||||
MCP endpoint
|
|
||||||
</p>
|
|
||||||
<code className="mt-2 block break-all text-sm text-base-content">
|
|
||||||
{mcpUrl}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">Connect Codex</h2>
|
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
Codex supports hosted MCP servers from the CLI. Add OpenSEO, then
|
|
||||||
run the OAuth login flow.
|
|
||||||
</p>
|
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-base-300 bg-base-200/50 p-4 text-xs leading-relaxed text-base-content">
|
|
||||||
<code>{`codex mcp add openseo --url ${mcpUrl}
|
|
||||||
codex mcp login openseo`}</code>
|
|
||||||
</pre>
|
|
||||||
<p className="mt-3 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
After login, ask Codex to call <code>whoami</code>, then{" "}
|
|
||||||
<code>list_projects</code>. Use the returned <code>projectId</code>{" "}
|
|
||||||
for keyword, SERP, domain, backlink, and rank-tracking requests.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">Connect Claude Code CLI</h2>
|
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
<pre className="mt-4 overflow-x-auto rounded-lg border border-base-300 bg-base-200/50 p-4 text-xs leading-relaxed text-base-content">
|
|
||||||
<code>{`claude mcp add --transport http --scope user openseo ${mcpUrl}`}</code>
|
|
||||||
</pre>
|
|
||||||
<p className="mt-3 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
If Claude Code prompts you to authorize the server, approve the
|
|
||||||
OpenSEO login flow. Then ask it to list MCP tools or call{" "}
|
|
||||||
<code>whoami</code> to confirm the connection.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">
|
|
||||||
ChatGPT, Claude web, and Claude Desktop
|
|
||||||
</h2>
|
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
<ul className="mt-4 space-y-3">
|
|
||||||
<li className="flex gap-2.5 text-sm text-base-content/70">
|
|
||||||
<span className="mt-[2px] shrink-0 text-base-content/40">
|
|
||||||
—
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
In ChatGPT, enable developer mode if needed, then create a
|
|
||||||
custom MCP connector from Apps & Connectors and use the endpoint
|
|
||||||
above.
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-2.5 text-sm text-base-content/70">
|
|
||||||
<span className="mt-[2px] shrink-0 text-base-content/40">
|
|
||||||
—
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
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.
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p className="mt-3 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
Start by asking the client to check <code>whoami</code> and{" "}
|
|
||||||
<code>list_projects</code>. Once it can see your projects, it can
|
|
||||||
help with research planning, SERP analysis, competitor comparisons,
|
|
||||||
and saving useful keywords back to OpenSEO.
|
|
||||||
</p>
|
|
||||||
<div className="mt-4 flex flex-wrap gap-4">
|
|
||||||
<a
|
|
||||||
href={CHATGPT_MCP_DOCS_URL}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="inline-flex items-center gap-1.5 text-sm font-medium text-base-content transition-colors hover:text-base-content/60"
|
|
||||||
>
|
|
||||||
ChatGPT MCP docs
|
|
||||||
<ArrowUpRight className="size-3.5" />
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href={CLAUDE_CONNECTORS_DOCS_URL}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="inline-flex items-center gap-1.5 text-sm font-medium text-base-content transition-colors hover:text-base-content/60"
|
|
||||||
>
|
|
||||||
Claude connector docs
|
|
||||||
<ArrowUpRight className="size-3.5" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">Available tools</h2>
|
|
||||||
<div className="mt-4 grid gap-3 md:grid-cols-2">
|
|
||||||
{mcpTools.map((tool) => (
|
|
||||||
<div
|
|
||||||
key={tool.name}
|
|
||||||
className="rounded-lg border border-base-300 bg-base-100 p-4"
|
|
||||||
>
|
|
||||||
<code className="text-sm font-medium text-base-content">
|
|
||||||
{tool.name}
|
|
||||||
</code>
|
|
||||||
<p className="mt-2 text-sm leading-relaxed text-base-content/70">
|
|
||||||
{tool.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">Prompts to try</h2>
|
|
||||||
<div className="mt-4 space-y-4">
|
|
||||||
{promptExamples.map((example) => (
|
|
||||||
<div
|
|
||||||
key={example.title}
|
|
||||||
className="rounded-lg border border-base-300 bg-base-100 p-4"
|
|
||||||
>
|
|
||||||
<h3 className="text-sm font-medium">{example.title}</h3>
|
|
||||||
<p className="mt-2 text-sm leading-relaxed text-base-content/70">
|
|
||||||
{example.prompt}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">Sam: AI SEO teammate</h2>
|
|
||||||
<p className="mt-2 text-sm text-base-content/70 leading-relaxed">
|
|
||||||
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.
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
href={SAM_GITHUB_URL}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="mt-4 inline-flex items-center gap-1.5 text-sm font-medium text-base-content transition-colors hover:text-base-content/60"
|
|
||||||
>
|
|
||||||
View Sam on GitHub
|
|
||||||
<ArrowUpRight className="size-3.5" />
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-12">
|
|
||||||
<h2 className="text-xl font-semibold">Roadmap</h2>
|
|
||||||
<ul className="mt-4 space-y-4">
|
|
||||||
{[
|
|
||||||
{
|
|
||||||
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) => (
|
|
||||||
<li key={item.title}>
|
|
||||||
<p className="flex gap-2.5 text-sm text-base-content/70">
|
|
||||||
<span className="mt-[2px] shrink-0 text-base-content/40">
|
|
||||||
—
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<span className="font-medium text-base-content">
|
|
||||||
{item.title}
|
|
||||||
</span>
|
|
||||||
{item.description ? (
|
|
||||||
<>
|
|
||||||
<br />
|
|
||||||
{item.description}
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user