payloadcms/payload · error · APIError

MCP overrideAccess is only available in development.

Error message

MCP overrideAccess is only available in development.

What it means

The overrideAccess query parameter bypasses Payload and MCP item access checks, so it is gated to development. If the param is present (any value) and NODE_ENV !== 'development', mcpEndpoint throws APIError 400. This is a deliberate security guard against bypassing auth outside dev.

Source

Thrown at packages/plugin-mcp/src/endpoint/index.ts:25

} from '@modelcontextprotocol/server'
import { APIError } from 'payload'

import { buildMcpServer } from '../mcp/buildMcpServer.js'
import { getPluginConfig } from '../utils/getPluginConfig.js'
import { getAuthorizedMCP } from './access.js'

export const mcpEndpoint: PayloadHandler = async (req) => {
  if (!req.url) {
    throw new APIError('Missing request URL', 400)
  }

  req.payloadAPI = 'MCP' as const

  const pluginConfig = getPluginConfig({ config: req.payload.config })
  const overrideAccessParam = new URL(req.url).searchParams.get('overrideAccess')

  if (overrideAccessParam !== null && process.env.NODE_ENV !== 'development') {
    throw new APIError('MCP overrideAccess is only available in development.', 400)
  }

  let overrideAccess = false
  if (overrideAccessParam === 'true') {
    overrideAccess = true
  } else if (overrideAccessParam !== null && overrideAccessParam !== 'false') {
    throw new APIError('MCP overrideAccess must be "true" or "false".', 400)
  }

  const authorizedMCP = await getAuthorizedMCP({ overrideAccess, req })
  // Payload augments the original web-standard Request in place.
  const mcpRequest = req as PayloadRequest & Request

  // Keep the old JSON-only, stateless behavior because the SDK's 2025 fallback uses SSE.
  if (await isLegacyRequest(mcpRequest)) {
    const server = buildMcpServer({ authorizedMCP, pluginConfig, req })
    const transport = new WebStandardStreamableHTTPServerTransport({
      enableJsonResponse: true,

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Remove the overrideAccess query param in any non-development environment.
  2. Set NODE_ENV=development only on local/dev machines when you need the bypass.
  3. Audit MCP client URLs to ensure the param isn't hardcoded.

Example fix

// before (prod)
GET /api/mcp?overrideAccess=true
// after (prod)
GET /api/mcp
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(req.url)
if (url.searchParams.has('overrideAccess') && process.env.NODE_ENV !== 'development') {
  url.searchParams.delete('overrideAccess') // never bypass access outside dev
}

Prevention

When it happens

Trigger: Hitting the MCP endpoint with ?overrideAccess=true (or any value) in production, staging, or any non-development NODE_ENV.

Common situations: A dev URL with the param copied into a production client; NODE_ENV not set to 'development' locally; CI/staging inheriting a dev query string.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/33567ec540aadc63. Report an issue: GitHub.