How AI Agents Are Authorized in OpenSEO: MCP Token Validation and Project Scoping

AI agents in OpenSEO are authorized per-project through the withMcpProjectAuth wrapper, which validates that the caller's organization token owns the requested project before executing MCP tool handlers.

OpenSEO uses the Model Context Protocol (MCP) to expose SEO tools—such as SERP scraping and backlink analysis—to AI agents. Understanding how AI agents are authorized in OpenSEO requires examining the token-based validation pipeline that runs through the MCP transport layer and project-specific access controls implemented in the every-app/open-seo repository.

The MCP Authorization Architecture

AI agents interact with OpenSEO by calling MCP tools exposed via server endpoints. Authorization is enforced at two critical layers: the transport layer validates the authentication token, and the project-auth layer verifies organizational ownership of the target resource.

Token Validation at the Transport Layer

According to the OpenSEO source code, incoming MCP requests first pass through the transport layer defined in src/server/mcp/transport.ts. This layer inspects the request's authMode and extracts the authentication context—containing fields such as userId and organizationId—from the bearer token. The validated auth object is then propagated to downstream tool handlers.

Project-Scope Verification with withMcpProjectAuth

Once the token is validated, the withMcpProjectAuth function—located in src/server/mcp/project-auth.ts—acts as a gatekeeper. This higher-order function wraps individual MCP tool handlers and performs the following checks:

  1. Extracts the projectId from the tool arguments.
  2. Calls ProjectService.getProjectForOrganization to verify the project belongs to the organization encoded in the auth token.
  3. Throws a FORBIDDEN error if the association fails.
  4. Returns an enriched context containing auth, baseUrl, billing helpers, and the full project record if validation succeeds.

Authorization Wrapper Implementation

The withMcpProjectAuth utility ensures that AI agents cannot access projects outside their authorized organization. Below is the core implementation pattern from src/server/mcp/project-auth.ts:

// src/server/mcp/project-auth.ts
export function withMcpProjectAuth<TArgs extends { projectId: string }, TResult>(
  handler: (args: TArgs, ctx: McpProjectAuthContext) => Promise<TResult> | TResult,
) {
  return async (args: TArgs, toolContext: ToolContext) => {
    // 1️⃣ Verify the caller's organization owns the project
    const context = await requireProjectAccess(toolContext, args.projectId);
    // 2️⃣ Run the actual tool with the verified context
    return handler(args, context);
  };
}

To expose a tool to AI agents, you wrap the business logic handler with this utility:

// Example: exposing a keyword-research tool to AI agents
import { withMcpProjectAuth } from "@/server/mcp/project-auth";

async function getKeywordSuggestions(
  args: { projectId: string; query: string },
  ctx: McpProjectAuthContext
) {
  // ctx.auth contains verified userId and organizationId
  // ctx.project contains the full project record
  return await fetchKeywordData(args.query, ctx.project.id);
}

export const getKeywordSuggestionsTool = withMcpProjectAuth(getKeywordSuggestions);

Protecting Server Functions with Project Context

Beyond MCP tools, OpenSEO also protects server-side functions using middleware that enforces the same authorization rules. The requireProjectContext middleware—defined in src/serverFunctions/middleware.ts—extracts the authenticated user context and guarantees that a valid project is present before the handler executes.

For example, the AI-visibility endpoint in src/serverFunctions/ai-search.ts uses this pattern to ensure agents can only access paid features for authorized projects:

// src/serverFunctions/ai-search.ts
export const lookupBrand = createServerFn({ method: "POST" })
  .middleware(requireProjectContext)           // ✅ Ensures authenticated project context
  .validator(brandLookupInputSchema)
  .handler(async ({ data, context }) => {
    await assertPaidPlan(context.organizationId); // Paid-plan gate
    return getBrandLookup({ ...data, projectId: context.projectId }, context);
  });

Key Files in the Authorization Pipeline

The following files collectively implement the authorization chain that secures AI agent access in OpenSEO:

File Role
src/server/mcp/project-auth.ts Core wrapper that validates a project ID against the caller's organization token.
src/server/mcp/transport.ts Validates incoming MCP request auth modes and populates the auth object.
src/server/mcp/tools/* Directory containing AI-agent tools (keyword research, SERP fetch) that use withMcpProjectAuth.
src/serverFunctions/middleware.ts Middleware extracting authenticated user context and enforcing project presence.
src/serverFunctions/ai-search.ts Example server function applying project-context middleware and paid-plan checks.

Summary

  • Transport-layer validation in src/server/mcp/transport.ts authenticates the organization token before any tool logic executes.
  • Project-scope gates via withMcpProjectAuth in src/server/mcp/project-auth.ts ensure AI agents can only access projects belonging to their authenticated organization.
  • Context enrichment provides handlers with verified auth data, project records, and billing helpers after successful authorization.
  • Server-function middleware (requireProjectContext) extends the same security model to non-MCP server endpoints.
  • Paid-plan enforcement occurs after authorization checks, ensuring proper feature gating.

Frequently Asked Questions

What is MCP in OpenSEO?

MCP stands for Model Context Protocol, the communication standard used by OpenSEO to expose SEO tools—such as backlink analysis and keyword research—to AI agents. MCP defines how agents request data and how the server validates those requests before returning results.

How does OpenSEO prevent AI agents from accessing other organizations' projects?

OpenSEO prevents cross-organization access through the withMcpProjectAuth wrapper in src/server/mcp/project-auth.ts. This function queries the database to verify that the requested projectId belongs to the organizationId encoded in the caller's authentication token. If the project is not found or belongs to a different organization, the wrapper immediately throws a FORBIDDEN error before the tool handler executes.

Can AI agents access paid features without proper authorization?

No. After passing the initial project authorization checks, handlers like lookupBrand in src/serverFunctions/ai-search.ts explicitly call assertPaidPlan(context.organizationId). This secondary check ensures the organization has an active paid subscription before allowing access to premium AI-visibility features, preventing unauthorized usage even if the agent has valid project access.

Where is the auth token validated in the MCP pipeline?

The auth token is first validated in src/server/mcp/transport.ts, where the transport layer inspects the request's authMode and extracts the authentication context. This validated context—containing the user and organization identifiers—is then passed to tool wrappers like withMcpProjectAuth, which perform additional project-specific authorization before executing business logic.

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 →