{"record":{"id":"33567ec540aadc63","repo":"payloadcms/payload","slug":"mcp-overrideaccess-is-only-available-in-developmen","errorCode":null,"errorMessage":"MCP overrideAccess is only available in development.","messagePattern":"MCP overrideAccess is only available in development\\.","errorType":"http","errorClass":"APIError","httpStatus":400,"severity":"error","filePath":"packages/plugin-mcp/src/endpoint/index.ts","lineNumber":25,"sourceCode":"} from '@modelcontextprotocol/server'\nimport { APIError } from 'payload'\n\nimport { buildMcpServer } from '../mcp/buildMcpServer.js'\nimport { getPluginConfig } from '../utils/getPluginConfig.js'\nimport { getAuthorizedMCP } from './access.js'\n\nexport const mcpEndpoint: PayloadHandler = async (req) => {\n  if (!req.url) {\n    throw new APIError('Missing request URL', 400)\n  }\n\n  req.payloadAPI = 'MCP' as const\n\n  const pluginConfig = getPluginConfig({ config: req.payload.config })\n  const overrideAccessParam = new URL(req.url).searchParams.get('overrideAccess')\n\n  if (overrideAccessParam !== null && process.env.NODE_ENV !== 'development') {\n    throw new APIError('MCP overrideAccess is only available in development.', 400)\n  }\n\n  let overrideAccess = false\n  if (overrideAccessParam === 'true') {\n    overrideAccess = true\n  } else if (overrideAccessParam !== null && overrideAccessParam !== 'false') {\n    throw new APIError('MCP overrideAccess must be \"true\" or \"false\".', 400)\n  }\n\n  const authorizedMCP = await getAuthorizedMCP({ overrideAccess, req })\n  // Payload augments the original web-standard Request in place.\n  const mcpRequest = req as PayloadRequest & Request\n\n  // Keep the old JSON-only, stateless behavior because the SDK's 2025 fallback uses SSE.\n  if (await isLegacyRequest(mcpRequest)) {\n    const server = buildMcpServer({ authorizedMCP, pluginConfig, req })\n    const transport = new WebStandardStreamableHTTPServerTransport({\n      enableJsonResponse: true,","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/plugin-mcp/src/endpoint/index.ts#L7-L43","documentation":"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.","triggerScenarios":"Hitting the MCP endpoint with ?overrideAccess=true (or any value) in production, staging, or any non-development NODE_ENV.","commonSituations":"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.","solutions":["Remove the overrideAccess query param in any non-development environment.","Set NODE_ENV=development only on local/dev machines when you need the bypass.","Audit MCP client URLs to ensure the param isn't hardcoded."],"exampleFix":"// before (prod)\nGET /api/mcp?overrideAccess=true\n// after (prod)\nGET /api/mcp","handlingStrategy":"validation","validationCode":"const url = new URL(req.url)\nif (url.searchParams.has('overrideAccess') && process.env.NODE_ENV !== 'development') {\n  url.searchParams.delete('overrideAccess') // never bypass access outside dev\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never include overrideAccess in client URLs destined for non-dev environments.","Keep NODE_ENV=development only on local/dev machines.","Audit MCP client configs for stray dev query params."],"tags":["security","mcp","config","env","plugin-mcp"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}