How the OpenSEO Router Distinguishes MCP Requests from Standard App Requests

The OpenSEO server uses a pathname and authentication mode check to route Model-Context-Protocol (MCP) requests to dedicated handlers while sending all other traffic to the standard application.

OpenSEO is an open-source SEO platform maintained in the every-app/open-seo repository that supports both hosted OAuth flows and self-hosted deployments. Understanding how the OpenSEO router distinguishes MCP requests from standard app requests is essential for developers integrating AI agents or debugging routing issues in self-hosted environments.

The Routing Logic

The OpenSEO router implements a simple but strict pathname plus auth-mode validation to separate MCP traffic from regular application traffic. This logic lives in the central request handler within src/server.ts.

Authentication Mode Constraints

The router first determines the current authentication mode using getAuthMode(env.AUTH_MODE). The MCP endpoint is only exposed when the server runs in specific self-hosted configurations:

  • cloudflare_access – Self-hosted deployments protected by Cloudflare Access
  • local_noauth – Local development environments without authentication

Hosted deployments using OAuth mode never expose the MCP endpoint directly. Instead, they route AI interactions through the OpenSEO OAuth provider.

Pathname Matching

The router extracts the pathname from the incoming request URL using new URL(publicRequest.url).pathname. It compares this against the constant MCP_ROUTE, defined in src/server/mcp/context.ts as the string "/mcp".

The conditional check in src/server.ts follows this exact logic:

if (
  (authMode === "cloudflare_access" || authMode === "local_noauth") &&
  pathname === MCP_ROUTE
) {
  return handleSelfHostedOpenSeoMcpRequest(publicRequest, authMode, env, ctx);
}

When this condition evaluates to true, the request forwards to the MCP transport layer. Otherwise, execution falls through to the standard application handler.

Implementation Details

The routing decision relies on two critical source files that work together to enforce the separation of concerns.

Central Request Router

In src/server.ts, the router coordinates the branching logic. The code inspects the raw request URL, determines the deployment context, and decides whether to invoke handleSelfHostedOpenSeoMcpRequest or the standard fetch handler (appFetch).

MCP Context Definitions

The file src/server/mcp/context.ts exports the route constant and defines the authorization schema. This centralization ensures that both the router and the MCP handler reference the same path definition, preventing drift between the routing logic and the actual endpoint implementation.

Code Examples

Routing Logic Implementation

The following snippet from src/server.ts demonstrates the complete decision tree:

const authMode = getAuthMode(env.AUTH_MODE);
const pathname = new URL(publicRequest.url).pathname;

// MCP request?
if (
  (authMode === "cloudflare_access" || authMode === "local_noauth") &&
  pathname === MCP_ROUTE
) {
  // Forward to the MCP transport
  return handleSelfHostedOpenSeoMcpRequest(publicRequest, authMode, env, ctx);
}

// Anything else → normal app
return appFetch(request);

Self-Hosted MCP Request

In a self-hosted environment running local_noauth mode, MCP requests reach the transport layer:


# Server running in local_noauth mode

curl -i http://localhost:8787/mcp \
  -H "Accept: application/json"

This request passes the auth-mode check and reaches handleSelfHostedOpenSeoMcpRequest, where the MCP server processes the Model-Context-Protocol payload.

Hosted Deployment Behavior

On hosted deployments using OAuth authentication, the same request path behaves differently:


# Hosted deployment (auth mode = "oauth")

curl -i https://my-open-seo.example.com/mcp

Here, the condition fails because authMode is neither "cloudflare_access" nor "local_noauth". The request falls through to the OAuth provider (openSeoOAuthProvider.fetch), typically returning a 404 or redirect rather than reaching the MCP transport.

Summary

  • Pathname check: The router validates against MCP_ROUTE ("/mcp") defined in src/server/mcp/context.ts
  • Auth-mode guard: MCP handling only activates in cloudflare_access or local_noauth modes, protecting hosted OAuth deployments from unauthorized MCP access
  • Clean separation: Requests meeting both criteria route to handleSelfHostedOpenSeoMcpRequest; all others fallback to standard application handlers
  • Security by default: Hosted deployments intentionally exclude the MCP endpoint, requiring AI integrations to use the OAuth provider instead

Frequently Asked Questions

What happens if I send an MCP request to a hosted OpenSEO instance?

The request fails the authentication mode check and routes to the standard OAuth handler. Hosted deployments (OAuth mode) intentionally disable direct MCP access to prevent unauthorized AI agent connections, returning a 404 or redirect instead.

Can I change the MCP route path from /mcp to something else?

The path is defined as a constant in src/server/mcp/context.ts. While you could modify the MCP_ROUTE export, doing so would require updating any client configurations expecting the standard /mcp endpoint. The router strictly enforces this pathname check according to the every-app/open-seo source code.

Why does the router check both pathname and auth mode instead of just the path?

The dual-check approach provides defense in depth. Checking only the pathname would expose the MCP endpoint on hosted deployments, creating a security risk. The auth-mode constraint ensures that MCP functionality is only available in self-hosted contexts where administrators explicitly control access through Cloudflare Access or local development settings.

Where is the actual MCP protocol handling implemented?

After the router validates the request in src/server.ts, it delegates to handleSelfHostedOpenSeoMcpRequest (the MCP transport layer). This function, along with the route constant MCP_ROUTE, is defined in src/server/mcp/context.ts, which manages the Model-Context-Protocol-specific authorization and context schema.

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 →