fix: prevent Invalid URL crash when APP_URL lacks a protocol (#972)

* fix: prevent Invalid URL crash when APP_URL lacks a protocol (issue #970)

- Add getMcpSafeUrl() to notifications.ts: wraps getAppUrl() and
  guarantees a result that satisfies the MCP SDK's checkIssuerUrl
  requirement (https:// or http://localhost). Non-HTTPS, non-localhost
  URLs fall back to http://localhost:{PORT} instead of propagating an
  "Issuer URL must be HTTPS" error.
- Switch app.ts, mcp/index.ts, mcp/oauthProvider.ts, and oauthService.ts
  to import getMcpSafeUrl instead of getAppUrl for all MCP resource URL
  construction, so a misconfigured APP_URL never crashes the metadata
  router initialisation.
- Restrict the SDK metadata router middleware to /.well-known/* paths
  only. Previously it was invoked on every request; in production the
  lazy getMetaRouter() init ran on GET / and threw "Invalid URL" when
  APP_URL had no scheme, returning 500 for every page load.
- Log a startup warning when APP_URL is set but not usable, and include
  the resolved App URL in the startup banner so operators can confirm
  the correct value at a glance.
- Update oauth.test.ts mock to target notifications.getMcpSafeUrl.

* fix: show getAppUrl in banner and add two separate APP_URL startup checks

- Banner now displays getAppUrl() (the resolved app URL) rather than
  getMcpSafeUrl() so operators see the actual configured value
- Two independent startup warnings after the banner when APP_URL is set:
  1. whether APP_URL is a valid URL (parseable by new URL())
  2. whether APP_URL is MCP-safe (https:// or http://localhost)
- Fix getMcpSafeUrl() fallback port to use Number(PORT) || 3001,
  consistent with how index.ts parses PORT

* fix: update oidc.ts to import getAppUrl from notifications
This commit is contained in:
Julien G.
2026-05-07 13:49:39 +02:00
committed by GitHub
parent 9f1d05e886
commit de6c0fb781
9 changed files with 81 additions and 39 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ import { ADDON_IDS } from '../addons';
import { User } from '../types';
import { writeAudit, logWarn } from './auditLog';
import { revokeUserSessionsForClient } from '../mcp/sessionManager';
import { getAppUrl } from './oidcService';
import { getMcpSafeUrl } from './notifications';
// ---------------------------------------------------------------------------
// Constants
@@ -587,7 +587,7 @@ export function validateAuthorizeRequest(
// bind the token to the MCP endpoint by default — previously this
// left `audience = null`, and the audience-bind check on MCP requests
// then treated a null audience as "valid for any resource".
const mcpResource = `${(getAppUrl() || '').replace(/\/+$/, '')}/mcp`;
const mcpResource = `${getMcpSafeUrl().replace(/\/+$/, '')}/mcp`;
const resource = params.resource
? params.resource.replace(/\/+$/, '')
: mcpResource;