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

> Learn how MCP tools register in OpenSEO. Discover the centralized registration flow using `registerOpenSeoTool` and `server.registerTool` for seamless integration.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: deep-dive
- Published: 2026-09-05

---

**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`](https://github.com/every-app/open-seo/blob/main/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.

```typescript
// 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`](https://github.com/every-app/open-seo/blob/main/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.

```typescript
// 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

```typescript
// 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

```typescript
// 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`](https://github.com/every-app/open-seo/blob/main/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)](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)](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)](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)](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)](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)](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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/oauth-registration.ts) manages OAuth client registration and token refresh automatically.