From af10ab1c9350db4e9faea6cc282532b44764c45f Mon Sep 17 00:00:00 2001 From: jubnl Date: Tue, 5 May 2026 13:57:52 +0200 Subject: [PATCH] fix: add /mcp to open-CORS pre-middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit External MCP clients (ChatGPT, Claude.ai, MCP Inspector) call /mcp cross-origin with Bearer tokens. The OPTIONS preflight was hitting the SPA catch-all because the global cors({ origin: false }) didn't add Access-Control-Allow-Origin. Without a valid CORS response the browser blocked the subsequent POST, preventing the 401 WWW-Authenticate header from being read — ChatGPT reported 'does not implement OAuth'. --- server/src/app.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index e86de386..916dbb28 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -95,18 +95,18 @@ export function createApp(): express.Application { const hstsActive = shouldForceHttps || process.env.NODE_ENV === 'production'; const hstsIncludeSubdomains = process.env.HSTS_INCLUDE_SUBDOMAINS === 'true'; - // RFC 8414 / RFC 9728 / RFC 7591: discovery docs and DCR are world-readable/writable — - // open CORS for external MCP clients regardless of the deployment's ALLOWED_ORIGINS config. - // /oauth/register and /oauth/authorize need it because browser-based clients (ChatGPT, etc.) - // send a CORS preflight that the global cors({ origin: false }) would answer WITHOUT - // Access-Control-Allow-Origin, causing the browser to reject the response before the - // SDK's own cors() middleware inside clientRegistrationHandler/authorizationHandler runs. + // RFC 8414 / RFC 9728 / RFC 7591: discovery docs and DCR are world-readable/writable. + // /mcp needs open CORS so external MCP clients (ChatGPT, Claude.ai, Inspector) can call it + // with Bearer tokens from any origin. /oauth/register and /oauth/authorize need it for + // browser-based DCR/authorization preflights — the global cors({ origin: false }) would + // answer OPTIONS without Access-Control-Allow-Origin before the SDK's own cors() runs. app.use( (req: Request, _res: Response, next: NextFunction) => { if ( req.path.startsWith('/.well-known/oauth-') || req.path === '/oauth/register' || - req.path === '/oauth/authorize' + req.path === '/oauth/authorize' || + req.path === '/mcp' ) { cors({ origin: '*', credentials: false })(req, _res, next); } else {