From 69432443b7b7c3c23c70705c06f692bf1e2d8980 Mon Sep 17 00:00:00 2001 From: jubnl Date: Wed, 6 May 2026 10:35:23 +0200 Subject: [PATCH] fix(mcp): serve flat /.well-known/oauth-protected-resource for ChatGPT reconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clients such as ChatGPT probe the flat well-known URL on every fresh discovery cycle (i.e. after a full disconnect/reconnect where cached OAuth state is cleared). The SDK's mcpAuthMetadataRouter only serves the path-based form /.well-known/oauth-protected-resource/mcp, so the flat probe returned 404. Without the resource metadata, ChatGPT fell back to the issuer URL as the resource parameter (https://…/ instead of https://…/mcp). The authorize handler then rejected it with invalid_target and redirected back to ChatGPT's callback with an error — showing the user the TREK home page instead of the consent form. Add an explicit GET handler for the flat URL that returns the same protected resource metadata, so the resource URI is discovered correctly on the first probe. --- server/src/app.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/src/app.ts b/server/src/app.ts index 74c66af5..ec855925 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -436,6 +436,23 @@ export function createApp(): express.Application { }); }); + // RFC 9728 flat well-known URL — served alongside the path-based form the SDK already provides. + // Clients like ChatGPT probe /.well-known/oauth-protected-resource (no path suffix) on every + // fresh discovery. Without this, they get 404, fall back to the issuer URL as the resource + // parameter, and the authorize handler rejects them with invalid_target — showing the user + // the TREK home page instead of the consent form. + app.get('/.well-known/oauth-protected-resource', (_req: Request, res: Response) => { + if (!isAddonEnabled(ADDON_IDS.MCP)) return res.status(404).end(); + const meta = getOAuthMetadata(); + res.json({ + resource: `${meta.issuer}/mcp`, + authorization_servers: [meta.issuer], + bearer_methods_supported: ['header'], + scopes_supported: ALL_SCOPES, + resource_name: 'TREK MCP', + }); + }); + // SDK authorize handler: validates OAuth params, calls provider.authorize() which redirects // to the SPA consent page at /oauth/consent app.use('/oauth/authorize', mcpAddonGate, authorizationHandler({ provider: trekOAuthProvider }));