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 Accesslocal_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 insrc/server/mcp/context.ts - Auth-mode guard: MCP handling only activates in
cloudflare_accessorlocal_noauthmodes, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →