How MCP Handles Authentication for Hosted Deployments in OpenSEO

OpenSEO validates every request to hosted MCP endpoints through a five-step OAuth flow that checks the auth context against a strict schema, enforces the "mcp" scope, verifies organization membership, validates the Origin header, and instantiates the request handler with authenticated user roles.

OpenSEO's Model Context Protocol (MCP) server implements strict authentication for hosted deployments to ensure only authorized users can access SEO automation tools. The authentication logic resides in src/server/mcp/transport.ts and protects the /mcp endpoint through multi-layered validation. Understanding this flow is essential for developers integrating with OpenSEO's hosted MCP infrastructure.

The MCP Authentication Flow for Hosted Deployments

The handleAuthenticatedOpenSeoMcpRequest function in src/server/mcp/transport.ts orchestrates validation through five sequential checks. Each step hardens the endpoint against unauthorized access or cross-origin misuse.

Step 1: OAuth Payload Schema Validation

The incoming request’s auth context is parsed against hostedWorkersOAuthMcpPropsSchema using safe parsing. If the payload structure does not match the expected schema, the server immediately rejects the request with 403 "MCP auth context required".

Source: hostedWorkersOAuthMcpPropsSchema.safeParse(props) at lines 62-64 in src/server/mcp/transport.ts.

Step 2: MCP Scope Enforcement

After parsing, the system verifies that the auth context includes the MCP scope ("mcp"). This constant is defined in src/lib/oauth-resource.ts as MCP_SCOPE. Requests missing this scope receive 403 "MCP scope required".

Source: Scope constant definition in src/lib/oauth-resource.ts lines 2-3; enforcement in src/server/mcp/transport.ts lines 66-68.

Step 3: Organization Membership Verification

Using the AuthRepository, the code fetches the user-organization membership record for the userId and organizationId supplied by the OAuth token. If the membership no longer exists—indicating the user was removed from the organization—the server responds with 401 "Organization access revoked" and includes a WWW-Authenticate header prompting the client to refresh its token.

Source: Membership lookup and error handling in src/server/mcp/transport.ts lines 84-93.

Step 4: Origin Header Validation

The request’s Origin header must match either the hosted base URL (retrieved via getHostedBaseUrl()) or the dedicated Chrome-extension origin used by the Surfmind extension. Any other origin triggers 403 "Invalid Origin", preventing cross-site request forgery.

Source: Origin validation logic in src/server/mcp/transport.ts lines 70-78.

Step 5: MCP Request Handler Instantiation

Upon passing all validations, the server constructs a McpProps object containing the authenticated user’s role (fetched from the membership record). It then instantiates the MCP request handler via createRequestHandler with these props, adding appropriate CORS headers to the response before processing the JSON-RPC request.

Source: Props creation at lines 100-104 and handler invocation at lines 105-108 in src/server/mcp/transport.ts.

Implementing MCP Authentication in Client Applications

To interact with OpenSEO's hosted MCP endpoints, clients must obtain an OAuth token with the mcp scope and include proper headers.

Making Authenticated Requests

import fetch from "node-fetch";

const MCP_BASE = "https://my-openseo-hosted.com";
const MCP_ENDPOINT = `${MCP_BASE}/mcp`;

async function callMcp(method: string, params: any, accessToken: string) {
  const body = {
    jsonrpc: "2.0",
    method,
    params,
    id: Date.now(),
  };

  const response = await fetch(MCP_ENDPOINT, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
      Origin: MCP_BASE,
    },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    throw new Error(`MCP error ${response.status}: ${await response.text()}`);
  }
  return response.json();
}

Obtaining OAuth Tokens with MCP Scope

const tokenResponse = await fetch("https://openseo.com/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code: authCode,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    scope: "offline_access mcp",
    redirect_uri: REDIRECT_URI,
  }),
});

const { access_token } = await tokenResponse.json();

Key Source Files for MCP Authentication

  • src/server/mcp/transport.ts – Contains handleAuthenticatedOpenSeoMcpRequest, the core function that validates OAuth payloads, scopes, organization membership, and origin headers for hosted MCP requests.
  • src/lib/oauth-resource.ts – Defines the MCP_SCOPE constant and helper utilities for constructing MCP resource URLs.
  • src/server/auth/repositories/AuthRepository.ts – Provides the getMembership method used to confirm active user-organization relationships during the authentication flow.

Summary

  • Schema validation ensures the auth context matches hostedWorkersOAuthMcpPropsSchema before processing continues.
  • Scope enforcement requires the "mcp" scope defined in src/lib/oauth-resource.ts for all hosted requests.
  • Membership verification uses AuthRepository.getMembership to confirm the user still belongs to the specified organization.
  • Origin validation restricts requests to the hosted base URL or approved Chrome extension origins via getHostedBaseUrl().
  • Request instantiation creates a McpProps object with the user’s role and invokes createRequestHandler to process valid requests.

Frequently Asked Questions

What error does OpenSEO return if the MCP scope is missing?

The server responds with 403 "MCP scope required" when the parsed auth context does not include the "mcp" scope defined in src/lib/oauth-resource.ts.

How does OpenSEO verify that a user still has access to an organization?

During authentication, the system calls AuthRepository.getMembership with the userId and organizationId from the OAuth token. If no active membership exists, it returns 401 "Organization access revoked" with a WWW-Authenticate header instructing the client to refresh the token.

Can MCP requests originate from any browser domain?

No. The Origin header must match either the hosted deployment's base URL (via getHostedBaseUrl()) or the specific Chrome-extension origin used by the Surfmind extension. Mismatched origins receive 403 "Invalid Origin".

Which file contains the main authentication logic for hosted MCP deployments?

The primary implementation resides in src/server/mcp/transport.ts, specifically within the handleAuthenticatedOpenSeoMcpRequest function that orchestrates the five-step validation flow.

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 →