204 lines
7.0 KiB
TypeScript
204 lines
7.0 KiB
TypeScript
import {
|
|
type CallToolResult,
|
|
McpServer,
|
|
type ToolAnnotations,
|
|
} from "@modelcontextprotocol/server";
|
|
import type { z } from "zod";
|
|
import {
|
|
createMcpToolContext,
|
|
type McpProps,
|
|
type ToolContext,
|
|
} from "@/server/mcp/context";
|
|
import { objectSchema } from "@/server/mcp/output-schemas";
|
|
import { instrumentMcpToolHandler } from "@/server/mcp/instrumentation";
|
|
import { getBacklinksOverviewTool } from "@/server/mcp/tools/get-backlinks-overview";
|
|
import { getBacklinksProfileTool } from "@/server/mcp/tools/get-backlinks-profile";
|
|
import { getDomainKeywordSuggestionsTool } from "@/server/mcp/tools/get-domain-keyword-suggestions";
|
|
import { getDomainOverviewTool } from "@/server/mcp/tools/get-domain-overview";
|
|
import { addRankTrackingKeywordsTool } from "@/server/mcp/tools/add-rank-tracking-keywords";
|
|
import { createRankTrackerTool } from "@/server/mcp/tools/create-rank-tracker";
|
|
import { estimateRankTrackerCostTool } from "@/server/mcp/tools/estimate-rank-tracker-cost";
|
|
import { getRankTrackerTool } from "@/server/mcp/tools/get-rank-tracker";
|
|
import { removeRankTrackingKeywordsTool } from "@/server/mcp/tools/remove-rank-tracking-keywords";
|
|
import { runRankTrackerTool } from "@/server/mcp/tools/run-rank-tracker";
|
|
import { getSerpResultsTool } from "@/server/mcp/tools/get-serp-results";
|
|
import {
|
|
getGoogleAnalyticsAudienceBreakdownTool,
|
|
getGoogleAnalyticsEcommercePerformanceTool,
|
|
getGoogleAnalyticsKeyEventsTool,
|
|
getGoogleAnalyticsMeasurementHealthTool,
|
|
getGoogleAnalyticsOrganicLandingPagesTool,
|
|
getGoogleAnalyticsOrganicOverviewTool,
|
|
getGoogleAnalyticsPagePerformanceTool,
|
|
getGoogleAnalyticsSiteSearchTool,
|
|
getGoogleAnalyticsTrafficAcquisitionTool,
|
|
getSearchOpportunitiesTool,
|
|
} from "@/server/mcp/tools/google-analytics-tools";
|
|
import { createProjectTool } from "@/server/mcp/tools/create-project";
|
|
import { listProjectsTool } from "@/server/mcp/tools/list-projects";
|
|
import {
|
|
getProjectContextTool,
|
|
updateProjectContextTool,
|
|
} from "@/server/mcp/tools/project-context";
|
|
import { listSavedKeywordsTool } from "@/server/mcp/tools/list-saved-keywords";
|
|
import {
|
|
findSerpCompetitorsTool,
|
|
getGoogleBusinessQuestionsTool,
|
|
getKeywordMetricsTool,
|
|
getLocalSerpResultsTool,
|
|
getRankedKeywordsTool,
|
|
searchLocalBusinessesTool,
|
|
} from "@/server/mcp/tools/dataforseo-research-tools";
|
|
import {
|
|
getBusinessProfileTool,
|
|
getBusinessReviewsTool,
|
|
getBusinessUpdatesTool,
|
|
getLocalRankGridTool,
|
|
listBusinessCategoriesTool,
|
|
} from "@/server/mcp/tools/local-seo-tools";
|
|
import { researchKeywordsTool } from "@/server/mcp/tools/research-keywords";
|
|
import { saveKeywordsTool } from "@/server/mcp/tools/save-keywords";
|
|
import {
|
|
getSearchConsolePerformanceTool,
|
|
inspectUrlsTool,
|
|
} from "@/server/mcp/tools/search-console-tools";
|
|
import {
|
|
getAuditIssuesTool,
|
|
getAuditPagesTool,
|
|
getAuditStatusTool,
|
|
runSiteAuditTool,
|
|
} from "@/server/mcp/tools/site-audit-tools";
|
|
import { whoamiTool } from "@/server/mcp/tools/whoami";
|
|
|
|
type ToolSchema = z.ZodType | z.ZodRawShape;
|
|
|
|
// Tools declare inputSchema as either a raw Zod shape (most tools) or a full
|
|
// z.object (the GA4 tools); both normalize to one object schema at
|
|
// registration.
|
|
type ToolArgs<Input extends ToolSchema> = Input extends z.ZodType
|
|
? z.infer<Input>
|
|
: Input extends z.ZodRawShape
|
|
? z.infer<z.ZodObject<Input>>
|
|
: never;
|
|
|
|
type OpenSeoToolDefinition<Input extends ToolSchema> = {
|
|
name: string;
|
|
config: {
|
|
title?: string;
|
|
description?: string;
|
|
inputSchema: Input;
|
|
outputSchema?: ToolSchema;
|
|
annotations?: ToolAnnotations;
|
|
};
|
|
handler: (
|
|
args: ToolArgs<Input>,
|
|
context: ToolContext,
|
|
) => CallToolResult | Promise<CallToolResult>;
|
|
};
|
|
|
|
function registerOpenSeoTool<Input extends ToolSchema>(
|
|
server: McpServer,
|
|
tool: OpenSeoToolDefinition<Input>,
|
|
authProps: McpProps,
|
|
) {
|
|
const outputSchema = objectSchema(tool.config.outputSchema);
|
|
const handler = instrumentMcpToolHandler(
|
|
tool.name,
|
|
outputSchema,
|
|
tool.handler,
|
|
);
|
|
|
|
server.registerTool(
|
|
tool.name,
|
|
{
|
|
...tool.config,
|
|
inputSchema: objectSchema(tool.config.inputSchema),
|
|
outputSchema,
|
|
},
|
|
(args, context) => {
|
|
return handler(
|
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- args were validated against the tool's own inputSchema just above
|
|
args as ToolArgs<Input>,
|
|
createMcpToolContext(context, authProps),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
export function createOpenSeoMcpServer(authProps: McpProps) {
|
|
const server = new McpServer(
|
|
{
|
|
name: "OpenSEO MCP",
|
|
title: "OpenSEO",
|
|
version: "0.0.12",
|
|
description:
|
|
"SEO research tools for AI agents: keyword research and metrics, SERP and local SERP results, domain and backlink analysis, rank tracking, and Google Search Console performance.",
|
|
websiteUrl: "https://openseo.so",
|
|
icons: [
|
|
{
|
|
src: "https://openseo.so/android-chrome-512x512.png",
|
|
mimeType: "image/png",
|
|
sizes: ["512x512"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
instructions:
|
|
"OpenSEO research tools use credits. Proceed with normal focused research, but ask the user for confirmation before planned batches over 2,000 credits.",
|
|
},
|
|
);
|
|
|
|
const register = <Input extends ToolSchema>(
|
|
tool: OpenSeoToolDefinition<Input>,
|
|
) => registerOpenSeoTool(server, tool, authProps);
|
|
|
|
register(whoamiTool);
|
|
register(listProjectsTool);
|
|
register(createProjectTool);
|
|
register(getProjectContextTool);
|
|
register(updateProjectContextTool);
|
|
register(listSavedKeywordsTool);
|
|
register(researchKeywordsTool);
|
|
register(saveKeywordsTool);
|
|
register(getDomainOverviewTool);
|
|
register(getDomainKeywordSuggestionsTool);
|
|
register(getBacklinksOverviewTool);
|
|
register(getBacklinksProfileTool);
|
|
register(getSerpResultsTool);
|
|
register(createRankTrackerTool);
|
|
register(getRankTrackerTool);
|
|
register(addRankTrackingKeywordsTool);
|
|
register(removeRankTrackingKeywordsTool);
|
|
register(estimateRankTrackerCostTool);
|
|
register(runRankTrackerTool);
|
|
register(getRankedKeywordsTool);
|
|
register(findSerpCompetitorsTool);
|
|
register(searchLocalBusinessesTool);
|
|
register(getLocalSerpResultsTool);
|
|
register(getGoogleBusinessQuestionsTool);
|
|
register(getBusinessProfileTool);
|
|
register(getBusinessReviewsTool);
|
|
register(getBusinessUpdatesTool);
|
|
register(listBusinessCategoriesTool);
|
|
register(getLocalRankGridTool);
|
|
register(getKeywordMetricsTool);
|
|
register(getSearchConsolePerformanceTool);
|
|
register(inspectUrlsTool);
|
|
register(getGoogleAnalyticsOrganicLandingPagesTool);
|
|
register(getGoogleAnalyticsPagePerformanceTool);
|
|
register(getGoogleAnalyticsKeyEventsTool);
|
|
register(getSearchOpportunitiesTool);
|
|
register(getGoogleAnalyticsOrganicOverviewTool);
|
|
register(getGoogleAnalyticsTrafficAcquisitionTool);
|
|
register(getGoogleAnalyticsMeasurementHealthTool);
|
|
register(getGoogleAnalyticsEcommercePerformanceTool);
|
|
register(getGoogleAnalyticsSiteSearchTool);
|
|
register(getGoogleAnalyticsAudienceBreakdownTool);
|
|
register(runSiteAuditTool);
|
|
register(getAuditStatusTool);
|
|
register(getAuditIssuesTool);
|
|
register(getAuditPagesTool);
|
|
|
|
return server;
|
|
}
|