How MCP Tools Are Registered in OpenSEO: A Deep Dive into the Registration Flow

OpenSEO registers MCP tools through a centralized registerOpenSeoTool helper that wraps the SDK's server.registerTool method, with each tool defined in separate modules under src/server/mcp/tools/ and registered at server startup.

OpenSEO implements a Model Context Protocol (MCP) server that exposes SEO capabilities to AI assistants. Understanding how tools are registered is essential for extending functionality or debugging integration issues. This article breaks down the registration architecture using the actual source code from the every-app/open-seo repository.

The MCP Server Architecture

At the core of OpenSEO's MCP implementation is a server instance created using the @opencode-ai/sdk library. Rather than calling the SDK's registration method directly for each tool, the codebase uses a abstraction layer that standardizes validation, authentication, and error handling across all tools.

The registration flow follows three distinct layers:

  • Server initialization — creating the MCP server instance
  • Registration helper — the registerOpenSeoTool function that standardizes tool registration
  • Tool definitions — individual tool modules that export name, schema, and handler

The Registration Helper: registerOpenSeoTool

In src/server/mcp/server.ts, the core registration logic resides in a helper function that wraps the SDK's primitive. This function is located around lines 99-120 and provides the uniform interface that all OpenSEO tools use.

// src/server/mcp/server.ts
function registerOpenSeoTool<Input extends ToolSchema>(
  server: MServer,
  tool: OpenSeoTool<Input>,
  authProps?: AuthProps,
) {
  server.registerTool(
    tool.name,
    {
      input: tool.inputSchema,
      output: tool.outputSchema,
      auth: authProps,
    },
    async (ctx, input) => {
      // Authentication handling and validation
      return await tool.handler(ctx, input as Input);
    },
  );
}

The helper accepts three parameters: the server instance, a tool definition conforming to the OpenSeoTool interface, and optional authentication properties. It extracts the tool's name, input schema, output schema, and handler, then delegates to the SDK's server.registerTool with additional context management.

Tool Registration at Startup

Immediately after defining the helper, src/server/mcp/server.ts executes a series of registration calls that wire up the complete OpenSEO toolset. These calls typically appear between lines 160-185 and following sections.

// Sequential tool registration in server.ts
register(whoamiTool);
register(listProjectsTool);
register(createProjectTool);
register(getDomainOverviewTool);
register(getSerpResultsTool);
register(getBacklinksTool);
register(getGoogleAnalyticsPagePerformanceTool);
register(getGoogleAnalyticsSiteSearchTool);
register(getSearchConsoleQueryPerformanceTool);
// ... additional tools

Each call passes a pre-defined tool object imported from the src/server/mcp/tools/ directory. The registration is synchronous and occurs during server initialization, ensuring all tools are available before the server begins accepting MCP connections.

Tool Definition Structure

Individual tools are defined in dedicated files under src/server/mcp/tools/. Each file exports a constant satisfying the OpenSeoTool interface, which includes four required properties: name, inputSchema, outputSchema, and handler.

Example: SERP Results Tool

// src/server/mcp/tools/get-serp-results.ts
export const getSerpResultsTool: OpenSeoTool<GetSerpResultsInput> = {
  name: "getSerpResults",
  inputSchema: z.object({
    query: z.string().describe("Search query to analyze"),
    location: z.string().optional(),
    device: z.enum(["desktop", "mobile"]).default("desktop"),
  }),
  outputSchema: z.object({
    results: z.array(z.object({
      title: z.string(),
      url: z.string(),
      position: z.number(),
    })),
    totalResults: z.number(),
  }),
  async handler(_ctx, input) {
    // Delegates to DataForSEO API client
    return await dataForSeoClient.getSerpResults(input);
  },
};

The handler receives a context object and validated input, then executes the actual SEO operation—often calling external APIs like DataForSEO, Google Search Console, or Google Analytics.

Example: Whoami Tool

// src/server/mcp/tools/whoami.ts
export const whoamiTool = createSimpleTool({
  name: "whoami",
  description: "Returns the current authenticated user identity",
  async handler(ctx) {
    return { userId: ctx.auth.userId, organizationId: ctx.auth.orgId };
  },
});

Simpler tools may use factory functions like createSimpleTool to reduce boilerplate while still conforming to the registration interface.

Authentication-Aware Registration

Some tools require OAuth credentials for third-party services. The registration system handles this through the optional authProps parameter in registerOpenSeoTool. OAuth-specific registration logic is modularized in src/server/mcp/oauth-registration.ts, which manages client registration and token refresh flows.

When a tool with authProps is registered, the server:

  1. Associates the OAuth scope requirements with the tool name
  2. Intercepts tool invocations to check credential validity
  3. Initiates OAuth flows automatically when credentials are missing or expired

Dynamic vs. Static Registration

While the core OpenSEO toolset uses static registration at startup, the underlying SDK supports dynamic registration through an addTool API. The current implementation favors explicit static registration for predictability and testability, but the architecture allows runtime extension if needed.

Key Files in the Registration Flow

File Purpose
[src/server/mcp/server.ts](https://github.com/every-app/open-seo/blob/main/src/server/mcp/server.ts) MCP server initialization and registerOpenSeoTool helper (~L99-L120)
[src/server/mcp/tools/whoami.ts](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/whoami.ts) Simple user identity tool
[src/server/mcp/tools/get-serp-results.ts](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/get-serp-results.ts) SERP fetching via DataForSEO
[src/server/mcp/tools/google-analytics-tools.ts](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/google-analytics-tools.ts) GA4 performance and site search tools
[src/server/mcp/tools/search-console-tools.ts](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts) Google Search Console query data
[src/server/mcp/oauth-registration.ts](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-registration.ts) OAuth client registration for authenticated tools

Summary

  • Centralized registration happens through registerOpenSeoTool in src/server/mcp/server.ts, which wraps the SDK's server.registerTool with standardized validation and authentication
  • Tool definitions live in src/server/mcp/tools/ and export name, Zod schemas, and async handlers
  • Sequential registration calls wire up the complete toolset at server startup
  • OAuth-aware tools receive special handling through authProps and oauth-registration.ts
  • Explicit static registration provides predictable behavior while preserving option for dynamic extension

Frequently Asked Questions

What interface must a tool implement to be registered in OpenSEO?

Tools must satisfy the OpenSeoTool<Input> interface with four properties: name (string), inputSchema (Zod schema), outputSchema (Zod schema), and handler (async function receiving context and validated input). The generic Input parameter ensures type safety between schema and handler.

Where does the actual MCP server instance come from?

The server instance is created using the @opencode-ai/sdk library, likely through a factory function like createServer() or new MServer() near the top of src/server/mcp/server.ts. This SDK instance provides the underlying registerTool method that registerOpenSeoTool wraps.

Can I add custom tools to OpenSEO without modifying core files?

Yes. You can create a new file in src/server/mcp/tools/ following the OpenSeoTool pattern, then add a registration call in src/server/mcp/server.ts. For runtime extension, the SDK's dynamic addTool API is available but not currently used for core tools.

How does authentication work for tools requiring Google credentials?

Tools declare OAuth requirements through the authProps parameter passed to registerOpenSeoTool. The helper checks credentials before invoking the handler, and src/server/mcp/oauth-registration.ts manages OAuth client registration and token refresh automatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →