How to Troubleshoot MCP Server Connection Failures in OpenSEO

OpenSEO MCP server connection failures typically stem from four layers: HTTP request handling, authentication context resolution, MCP server construction, or handler wiring—each producing distinct error codes that pinpoint the root cause.

The Model-Context-Protocol (MCP) server in every-app/open-seo serves as the bridge between client-side agents and the backend OpenSEO implementation. When this connection breaks, you need a systematic approach to diagnose whether the issue lies in transport routing, authentication, server initialization, or request validation. This guide walks through the exact code paths in src/server/mcp/ to resolve failures fast.

Understanding the Four Failure Layers

OpenSEO's MCP architecture splits connection handling into distinct layers. Knowing which layer produces which symptom accelerates debugging.

Layer 1: HTTP Request Handling

The entry points handleSelfHostedOpenSeoMcpRequest and handleAuthenticatedOpenSeoMcpRequest in src/server/mcp/transport.ts parse incoming requests and orchestrate the response flow.

  • 401/403 errors → Authentication context failed to resolve
  • 400 errors → Malformed JSON-RPC payload
  • 405 errors → Wrong HTTP method (not POST or OPTIONS)
  • 500 errors → Internal error before handler creation

These handlers validate request method, headers, CORS pre-flight status, and route to the appropriate auth resolver (cloudflare_access or local_noauth).

Layer 2: Auth Context Resolution

The helper functions resolveCloudflareAccessContext and resolveLocalNoAuthContext populate MCP_AUTH_CONTEXT_PROP with user identity, organization ID, and base URL.

Failures here produce incomplete auth props:

  • Missing userId → server rejects all tool calls
  • Missing organizationId → multi-tenant isolation breaks
  • Missing baseUrl → generated links point to wrong origin

Check src/server/mcp/context.ts for the expected shape and src/middleware/ensure-user/ for resolver implementations.

Layer 3: MCP Server Construction

createOpenSeoMcpServer in src/server/mcp/server.ts registers approximately 30 tools (whoami, search-console, rank-tracker, etc.) and returns a configured McpServer instance.

Failure symptoms at this layer:

  • 404 errors → Route not found in handler mapping
  • 502 errors → Server failed to instantiate
  • "Method not found" errors → Specific tool failed to register

The tool registration block starts at line 44—verify no import errors prevent loading.

Layer 4: Handler Wiring

createMcpHandler (from agents/mcp/server) wraps the server with CORS handling and legacy request processing.

Configuration in src/server/mcp/transport.test.ts shows:

  • allowedOriginHostnames: undefined → accepts any origin (self-hosted default)
  • legacy: "reject" → forces modern JSON-RPC format

403 errors here indicate origin rejection; 400 errors indicate legacy request format rejection.

Step-by-Step Troubleshooting Workflow

1. Verify Request Shape

The MCP endpoint requires strict formatting:

// Valid MCP request structure
const payload = {
  jsonrpc: "2.0",
  id: 1,
  method: "tools/list",
};

const response = await fetch("https://your-domain.com/mcp", {
  method: "POST",
  headers: {
    Accept: "application/json, text/event-stream",
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

Reference the test helper createMcpRequest in src/server/mcp/transport.test.ts (lines 69-82) for the canonical implementation.

2. Test CORS Pre-Flight Handling

Browsers automatically send OPTIONS requests before POST. The handler must short-circuit auth resolution and return 200:

// Simulate pre-flight for debugging
const preflight = await fetch("https://your-domain.com/mcp", {
  method: "OPTIONS",
  headers: {
    Origin: "https://client.example.com",
    "Access-Control-Request-Method": "POST",
    "Access-Control-Request-Headers": "content-type,accept",
  },
});

console.assert(preflight.status === 200, "CORS pre-flight failed");

If you see 401/403 on OPTIONS, the transport layer isn't recognizing pre-flight. Check handleSelfHostedOpenSeoMcpRequest lines 85-99 for the early-return logic.

3. Validate Auth Mode Configuration

Self-hosted deployments pass an auth mode string as the second argument to handleSelfHostedOpenSeoMcpRequest:

Mode Behavior Required Headers
"local_noauth" Creates dummy admin context None
"cloudflare_access" Extracts user from Cloudflare headers Cf-Access-Client-Id, Cf-Access-Jwt-Assertion

Test assertions at lines 71-74 in transport.test.ts verify resolveCloudflareAccessContext receives Headers. Missing headers produce incomplete auth context.

4. Inspect Constructed Auth Props

The symbol MCP_AUTH_CONTEXT_PROP carries auth data. Required fields per lines 45-52 in transport.test.ts:

  • userId
  • userEmail
  • organizationId
  • baseUrl

Any undefined value causes runtime rejections. Debug by logging the props before server construction:

import { createWorkersOAuthMcpProps } from "@/server/mcp/context";

const authProps = createWorkersOAuthMcpProps({
  userId: "admin",
  userEmail: "admin@localhost",
  organizationId: "delegated-local-admin",
  baseUrl: "https://open-seo.test",
});

// Verify before passing to handler
console.log("Auth props:", authProps);

5. Confirm Tool Registry Completion

createOpenSeoMcpServer registers tools in a block starting at line 44 of server.ts. If a tool fails to load due to import errors, you'll get "method not found" for that specific capability.

Common tool registration pattern:

register(whoamiTool);
register(searchConsoleTools);
register(rankTrackerTools);
register(getAuditPagesTool);
// ... ~30 total tools

6. Review Handler Options

createMcpHandler options control request acceptance:

createMcpHandler(server, {
  allowedOriginHostnames: undefined, // self-hosted: accept any origin
  legacy: "reject", // force modern JSON-RPC format
});

Critical: legacy: "reject" causes 400 errors for old request envelopes. The test at lines 155-159 in transport.test.ts documents this behavior.

7. Check Instrumentation Logs

src/server/mcp/instrumentation.ts wraps tool execution with tracing. For 500 errors, the stack trace points to the exact failing tool handler. Enable detailed logging to capture:

  • Tool name being invoked
  • Input parameters
  • Execution timing
  • Error stacks

8. Run the Test Suite

Execute the comprehensive tests to validate your deployment:

pnpm test  # or npm test

The transport.test.ts suite validates:

  • Request parsing
  • Auth resolution paths
  • CORS handling
  • Tool availability
  • Error responses

A failing test pinpoints the broken layer without manual debugging.

Quick Diagnostic Checklist

Use this checklist to systematically eliminate causes:

  • Request method is POST (or OPTIONS for pre-flight)
  • Headers include Accept: application/json, text/event-stream and Content-Type: application/json
  • JSON-RPC payload contains jsonrpc: "2.0", id, and method fields
  • Auth mode string matches "local_noauth" or "cloudflare_access"
  • Auth context resolves with all four required fields populated
  • createOpenSeoMcpServer receives complete auth props under MCP_AUTH_CONTEXT_PROP
  • createMcpHandler uses legacy: "reject" with appropriate origin settings
  • All required tools appear in the server registration list
  • src/server/mcp/instrumentation.ts shows no unhandled exceptions

Key Source Files Reference

File Purpose
src/server/mcp/transport.ts Request routing, auth resolution, handler orchestration
src/server/mcp/server.ts Tool registration and McpServer construction
src/server/mcp/transport.test.ts Canonical test cases documenting expected behavior
src/server/mcp/context.ts Auth context shape and factory functions
src/server/mcp/instrumentation.ts Production tracing and error capture
src/server/mcp/public-origin.ts Public URL determination for CORS and redirects

Summary

  • MCP connection failures in OpenSEO fall into four diagnosable layers: HTTP transport, auth resolution, server construction, and handler wiring
  • Error codes map directly to layers: 401/403 → auth, 400 → payload/format, 405 → method, 500 → server/internal
  • The test suite in transport.test.ts serves as the authoritative specification for correct behavior
  • Auth context completeness is the most common root cause—verify all four required fields
  • CORS pre-flight must short-circuit before auth resolution or browsers will fail silently

Frequently Asked Questions

What causes "method not found" errors in OpenSEO MCP?

The MCP server returns "method not found" when a tool wasn't registered in createOpenSeoMcpServer. Check src/server/mcp/server.ts around line 44 for the registration block. Import failures or missing tool files prevent registration—verify all ~30 tools load without errors. The tools/list method should return all available tools; compare its output against your expected set.

Why do I get 403 errors on self-hosted OpenSEO deployments?

403 errors indicate origin rejection or authentication failure. For self-hosted deployments, allowedOriginHostnames defaults to undefined (accept any origin), so check that your auth mode ("local_noauth" or "cloudflare_access") matches your deployment. With "cloudflare_access", missing Cf-Access-* headers cause immediate 403. With "local_noauth", ensure the dummy context generator runs successfully.

How do I debug CORS failures with OpenSEO MCP?

CORS issues manifest as failed pre-flight OPTIONS requests. The handler must return 200 with appropriate headers before processing POST. Test with explicit OPTIONS requests and verify handleSelfHostedOpenSeoMcpRequest returns early (lines 85-99 in transport.ts). If auth resolution runs on OPTIONS, the pre-flight fails—check that the method check precedes all auth logic.

What's the difference between handleSelfHostedOpenSeoMcpRequest and handleAuthenticatedOpenSeoMcpRequest?

handleSelfHostedOpenSeoMcpRequest accepts an explicit auth mode parameter for deployments you control, supporting "local_noauth" for development or "cloudflare_access" for Cloudflare-protected origins. handleAuthenticatedOpenSeoMcpRequest infers auth from the request environment for managed deployments. Use the self-hosted variant when running your own infrastructure; the authenticated variant for platform-managed instances.

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 →