# How MCP Handles Authentication for Hosted Deployments in OpenSEO

> Learn how OpenSEO handles MCP authentication for hosted deployments. Discover the five-step OAuth flow that validates auth context, scope, membership, and origin for secure access.

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

---

**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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/lib/oauth-resource.ts) lines 2-3; enforcement in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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

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

```typescript
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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/lib/oauth-resource.ts)** – Defines the `MCP_SCOPE` constant and helper utilities for constructing MCP resource URLs.
- **[`src/server/auth/repositories/AuthRepository.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts), specifically within the `handleAuthenticatedOpenSeoMcpRequest` function that orchestrates the five-step validation flow.