@onyxsecurity/mcp-gateway 1.0.45 → 1.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"main-CC1n-gTE.js","names":["config","config","command: string","args: string[]","resolveCallback: (result: CallbackServerResult) => void","rejectCallback: (error: Error) => void","timeoutRef: { id?: ReturnType<typeof setTimeout> }","boundPort: number","URL","attemptedPorts: number[]","lastError: Error | undefined","portToTry: number","credentials: StoredCredentials","OAUTH_PROVIDERS: OAuthProviderConfig[]","hostname","resourceMetadata: OAuthProtectedResourceMetadata | undefined","authServerUrl: URL","clientInfo: OAuthClientInformationFull","clientMetadata: OAuthClientMetadata","headers: Record<string, string>","tokenData: Record<string, unknown>","codeVerifier: string | undefined","codeChallenge: string | undefined","codeChallengeMethod: string | undefined","TRANSPORT_PRIORITY: TransportType[]","lastError: unknown","message: JSONRPCMessage | null","yargs","proxyType: ProxyType","finalCommand: string | undefined","finalArgs: string[] | undefined","remoteUrl: string | undefined","usedTransport: TransportType | undefined","remoteHeaders: Record<string, string>","customEnv: Record<string, string>","accessControlClient: AccessControlClient | undefined","headers: Record<string, string>","connectedClient: Client","client: Client | undefined","initialClient","serverVersion","client","serverCapabilities","server"],"sources":["../node_modules/eventsource/dist/index.js","../src/accessControl/authorizer.ts","../src/accessControl/client.ts","../src/auth/browser.ts","../src/auth/callbackServer.ts","../src/auth/OAuthCredentialStore.ts","../src/auth/providers.ts","../src/auth/oauthFlow.ts","../src/lib/transportDetection.ts","../src/JSONFilterTransform.ts","../src/StdioClientTransport.ts","../src/bin/main.ts"],"sourcesContent":["import { createParser } from \"eventsource-parser\";\nclass ErrorEvent extends Event {\n /**\n * Constructs a new `ErrorEvent` instance. This is typically not called directly,\n * but rather emitted by the `EventSource` object when an error occurs.\n *\n * @param type - The type of the event (should be \"error\")\n * @param errorEventInitDict - Optional properties to include in the error event\n */\n constructor(type, errorEventInitDict) {\n var _a, _b;\n super(type), this.code = (_a = errorEventInitDict == null ? void 0 : errorEventInitDict.code) != null ? _a : void 0, this.message = (_b = errorEventInitDict == null ? void 0 : errorEventInitDict.message) != null ? _b : void 0;\n }\n /**\n * Node.js \"hides\" the `message` and `code` properties of the `ErrorEvent` instance,\n * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,\n * we explicitly include the properties in the `inspect` method.\n *\n * This is automatically called by Node.js when you `console.log` an instance of this class.\n *\n * @param _depth - The current depth\n * @param options - The options passed to `util.inspect`\n * @param inspect - The inspect function to use (prevents having to import it from `util`)\n * @returns A string representation of the error\n */\n [Symbol.for(\"nodejs.util.inspect.custom\")](_depth, options, inspect) {\n return inspect(inspectableError(this), options);\n }\n /**\n * Deno \"hides\" the `message` and `code` properties of the `ErrorEvent` instance,\n * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,\n * we explicitly include the properties in the `inspect` method.\n *\n * This is automatically called by Deno when you `console.log` an instance of this class.\n *\n * @param inspect - The inspect function to use (prevents having to import it from `util`)\n * @param options - The options passed to `Deno.inspect`\n * @returns A string representation of the error\n */\n [Symbol.for(\"Deno.customInspect\")](inspect, options) {\n return inspect(inspectableError(this), options);\n }\n}\nfunction syntaxError(message) {\n const DomException = globalThis.DOMException;\n return typeof DomException == \"function\" ? new DomException(message, \"SyntaxError\") : new SyntaxError(message);\n}\nfunction flattenError(err) {\n return err instanceof Error ? \"errors\" in err && Array.isArray(err.errors) ? err.errors.map(flattenError).join(\", \") : \"cause\" in err && err.cause instanceof Error ? `${err}: ${flattenError(err.cause)}` : err.message : `${err}`;\n}\nfunction inspectableError(err) {\n return {\n type: err.type,\n message: err.message,\n code: err.code,\n defaultPrevented: err.defaultPrevented,\n cancelable: err.cancelable,\n timeStamp: err.timeStamp\n };\n}\nvar __typeError = (msg) => {\n throw TypeError(msg);\n}, __accessCheck = (obj, member, msg) => member.has(obj) || __typeError(\"Cannot \" + msg), __privateGet = (obj, member, getter) => (__accessCheck(obj, member, \"read from private field\"), getter ? getter.call(obj) : member.get(obj)), __privateAdd = (obj, member, value) => member.has(obj) ? __typeError(\"Cannot add the same private member more than once\") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value), __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, \"write to private field\"), member.set(obj, value), value), __privateMethod = (obj, member, method) => (__accessCheck(obj, member, \"access private method\"), method), _readyState, _url, _redirectUrl, _withCredentials, _fetch, _reconnectInterval, _reconnectTimer, _lastEventId, _controller, _parser, _onError, _onMessage, _onOpen, _EventSource_instances, connect_fn, _onFetchResponse, _onFetchError, getRequestOptions_fn, _onEvent, _onRetryChange, failConnection_fn, scheduleReconnect_fn, _reconnect;\nclass EventSource extends EventTarget {\n constructor(url, eventSourceInitDict) {\n var _a, _b;\n super(), __privateAdd(this, _EventSource_instances), this.CONNECTING = 0, this.OPEN = 1, this.CLOSED = 2, __privateAdd(this, _readyState), __privateAdd(this, _url), __privateAdd(this, _redirectUrl), __privateAdd(this, _withCredentials), __privateAdd(this, _fetch), __privateAdd(this, _reconnectInterval), __privateAdd(this, _reconnectTimer), __privateAdd(this, _lastEventId, null), __privateAdd(this, _controller), __privateAdd(this, _parser), __privateAdd(this, _onError, null), __privateAdd(this, _onMessage, null), __privateAdd(this, _onOpen, null), __privateAdd(this, _onFetchResponse, async (response) => {\n var _a2;\n __privateGet(this, _parser).reset();\n const { body, redirected, status, headers } = response;\n if (status === 204) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, \"Server sent HTTP 204, not reconnecting\", 204), this.close();\n return;\n }\n if (redirected ? __privateSet(this, _redirectUrl, new URL(response.url)) : __privateSet(this, _redirectUrl, void 0), status !== 200) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, `Non-200 status code (${status})`, status);\n return;\n }\n if (!(headers.get(\"content-type\") || \"\").startsWith(\"text/event-stream\")) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, 'Invalid content type, expected \"text/event-stream\"', status);\n return;\n }\n if (__privateGet(this, _readyState) === this.CLOSED)\n return;\n __privateSet(this, _readyState, this.OPEN);\n const openEvent = new Event(\"open\");\n if ((_a2 = __privateGet(this, _onOpen)) == null || _a2.call(this, openEvent), this.dispatchEvent(openEvent), typeof body != \"object\" || !body || !(\"getReader\" in body)) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, \"Invalid response body, expected a web ReadableStream\", status), this.close();\n return;\n }\n const decoder = new TextDecoder(), reader = body.getReader();\n let open = !0;\n do {\n const { done, value } = await reader.read();\n value && __privateGet(this, _parser).feed(decoder.decode(value, { stream: !done })), done && (open = !1, __privateGet(this, _parser).reset(), __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this));\n } while (open);\n }), __privateAdd(this, _onFetchError, (err) => {\n __privateSet(this, _controller, void 0), !(err.name === \"AbortError\" || err.type === \"aborted\") && __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this, flattenError(err));\n }), __privateAdd(this, _onEvent, (event) => {\n typeof event.id == \"string\" && __privateSet(this, _lastEventId, event.id);\n const messageEvent = new MessageEvent(event.event || \"message\", {\n data: event.data,\n origin: __privateGet(this, _redirectUrl) ? __privateGet(this, _redirectUrl).origin : __privateGet(this, _url).origin,\n lastEventId: event.id || \"\"\n });\n __privateGet(this, _onMessage) && (!event.event || event.event === \"message\") && __privateGet(this, _onMessage).call(this, messageEvent), this.dispatchEvent(messageEvent);\n }), __privateAdd(this, _onRetryChange, (value) => {\n __privateSet(this, _reconnectInterval, value);\n }), __privateAdd(this, _reconnect, () => {\n __privateSet(this, _reconnectTimer, void 0), __privateGet(this, _readyState) === this.CONNECTING && __privateMethod(this, _EventSource_instances, connect_fn).call(this);\n });\n try {\n if (url instanceof URL)\n __privateSet(this, _url, url);\n else if (typeof url == \"string\")\n __privateSet(this, _url, new URL(url, getBaseURL()));\n else\n throw new Error(\"Invalid URL\");\n } catch {\n throw syntaxError(\"An invalid or illegal string was specified\");\n }\n __privateSet(this, _parser, createParser({\n onEvent: __privateGet(this, _onEvent),\n onRetry: __privateGet(this, _onRetryChange)\n })), __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _reconnectInterval, 3e3), __privateSet(this, _fetch, (_a = eventSourceInitDict == null ? void 0 : eventSourceInitDict.fetch) != null ? _a : globalThis.fetch), __privateSet(this, _withCredentials, (_b = eventSourceInitDict == null ? void 0 : eventSourceInitDict.withCredentials) != null ? _b : !1), __privateMethod(this, _EventSource_instances, connect_fn).call(this);\n }\n /**\n * Returns the state of this EventSource object's connection. It can have the values described below.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState)\n *\n * Note: typed as `number` instead of `0 | 1 | 2` for compatibility with the `EventSource` interface,\n * defined in the TypeScript `dom` library.\n *\n * @public\n */\n get readyState() {\n return __privateGet(this, _readyState);\n }\n /**\n * Returns the URL providing the event stream.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url)\n *\n * @public\n */\n get url() {\n return __privateGet(this, _url).href;\n }\n /**\n * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to \"include\", and false otherwise.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials)\n */\n get withCredentials() {\n return __privateGet(this, _withCredentials);\n }\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */\n get onerror() {\n return __privateGet(this, _onError);\n }\n set onerror(value) {\n __privateSet(this, _onError, value);\n }\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */\n get onmessage() {\n return __privateGet(this, _onMessage);\n }\n set onmessage(value) {\n __privateSet(this, _onMessage, value);\n }\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */\n get onopen() {\n return __privateGet(this, _onOpen);\n }\n set onopen(value) {\n __privateSet(this, _onOpen, value);\n }\n addEventListener(type, listener, options) {\n const listen = listener;\n super.addEventListener(type, listen, options);\n }\n removeEventListener(type, listener, options) {\n const listen = listener;\n super.removeEventListener(type, listen, options);\n }\n /**\n * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close)\n *\n * @public\n */\n close() {\n __privateGet(this, _reconnectTimer) && clearTimeout(__privateGet(this, _reconnectTimer)), __privateGet(this, _readyState) !== this.CLOSED && (__privateGet(this, _controller) && __privateGet(this, _controller).abort(), __privateSet(this, _readyState, this.CLOSED), __privateSet(this, _controller, void 0));\n }\n}\n_readyState = /* @__PURE__ */ new WeakMap(), _url = /* @__PURE__ */ new WeakMap(), _redirectUrl = /* @__PURE__ */ new WeakMap(), _withCredentials = /* @__PURE__ */ new WeakMap(), _fetch = /* @__PURE__ */ new WeakMap(), _reconnectInterval = /* @__PURE__ */ new WeakMap(), _reconnectTimer = /* @__PURE__ */ new WeakMap(), _lastEventId = /* @__PURE__ */ new WeakMap(), _controller = /* @__PURE__ */ new WeakMap(), _parser = /* @__PURE__ */ new WeakMap(), _onError = /* @__PURE__ */ new WeakMap(), _onMessage = /* @__PURE__ */ new WeakMap(), _onOpen = /* @__PURE__ */ new WeakMap(), _EventSource_instances = /* @__PURE__ */ new WeakSet(), /**\n* Connect to the given URL and start receiving events\n*\n* @internal\n*/\nconnect_fn = function() {\n __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _controller, new AbortController()), __privateGet(this, _fetch)(__privateGet(this, _url), __privateMethod(this, _EventSource_instances, getRequestOptions_fn).call(this)).then(__privateGet(this, _onFetchResponse)).catch(__privateGet(this, _onFetchError));\n}, _onFetchResponse = /* @__PURE__ */ new WeakMap(), _onFetchError = /* @__PURE__ */ new WeakMap(), /**\n* Get request options for the `fetch()` request\n*\n* @returns The request options\n* @internal\n*/\ngetRequestOptions_fn = function() {\n var _a;\n const init = {\n // [spec] Let `corsAttributeState` be `Anonymous`…\n // [spec] …will have their mode set to \"cors\"…\n mode: \"cors\",\n redirect: \"follow\",\n headers: { Accept: \"text/event-stream\", ...__privateGet(this, _lastEventId) ? { \"Last-Event-ID\": __privateGet(this, _lastEventId) } : void 0 },\n cache: \"no-store\",\n signal: (_a = __privateGet(this, _controller)) == null ? void 0 : _a.signal\n };\n return \"window\" in globalThis && (init.credentials = this.withCredentials ? \"include\" : \"same-origin\"), init;\n}, _onEvent = /* @__PURE__ */ new WeakMap(), _onRetryChange = /* @__PURE__ */ new WeakMap(), /**\n* Handles the process referred to in the EventSource specification as \"failing a connection\".\n*\n* @param error - The error causing the connection to fail\n* @param code - The HTTP status code, if available\n* @internal\n*/\nfailConnection_fn = function(message, code) {\n var _a;\n __privateGet(this, _readyState) !== this.CLOSED && __privateSet(this, _readyState, this.CLOSED);\n const errorEvent = new ErrorEvent(\"error\", { code, message });\n (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent);\n}, /**\n* Schedules a reconnection attempt against the EventSource endpoint.\n*\n* @param message - The error causing the connection to fail\n* @param code - The HTTP status code, if available\n* @internal\n*/\nscheduleReconnect_fn = function(message, code) {\n var _a;\n if (__privateGet(this, _readyState) === this.CLOSED)\n return;\n __privateSet(this, _readyState, this.CONNECTING);\n const errorEvent = new ErrorEvent(\"error\", { code, message });\n (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent), __privateSet(this, _reconnectTimer, setTimeout(__privateGet(this, _reconnect), __privateGet(this, _reconnectInterval)));\n}, _reconnect = /* @__PURE__ */ new WeakMap(), /**\n* ReadyState representing an EventSource currently trying to connect\n*\n* @public\n*/\nEventSource.CONNECTING = 0, /**\n* ReadyState representing an EventSource connection that is open (eg connected)\n*\n* @public\n*/\nEventSource.OPEN = 1, /**\n* ReadyState representing an EventSource connection that is closed (eg disconnected)\n*\n* @public\n*/\nEventSource.CLOSED = 2;\nfunction getBaseURL() {\n const doc = \"document\" in globalThis ? globalThis.document : void 0;\n return doc && typeof doc == \"object\" && \"baseURI\" in doc && typeof doc.baseURI == \"string\" ? doc.baseURI : void 0;\n}\nexport {\n ErrorEvent,\n EventSource\n};\n//# sourceMappingURL=index.js.map\n","import type { AccessControlClient } from \"./client.js\";\n\nimport { logger } from \"../lib/logger.js\";\n\nexport interface AccessControlConfig {\n client?: AccessControlClient;\n enabled: boolean;\n serverName?: string;\n}\n\n/**\n * Authorizes MCP servers based on access control configuration.\n * Queries backend with full client info for authorization decisions.\n */\nexport class AccessControlAuthorizer {\n private client?: AccessControlClient;\n private enabled: boolean;\n private lastBlockReason?: string;\n private serverName?: string;\n\n constructor(config: AccessControlConfig) {\n this.enabled = config.enabled;\n this.client = config.client;\n this.serverName = config.serverName;\n\n logger.info(\"AccessControlAuthorizer initialized\", {\n enabled: this.enabled,\n hasClient: !!this.client,\n serverName: this.serverName,\n });\n }\n\n /**\n * Get human-readable reason for why a server was blocked.\n * Returns the reason from the last authorization check, or a default message.\n *\n * @returns Reason message\n */\n getBlockReason(): string {\n if (this.lastBlockReason) {\n return this.lastBlockReason;\n }\n // Default message with server name if available\n const serverPart = this.serverName ? ` '${this.serverName}'` : \"\";\n return `MCP server${serverPart} is not authorized for use in your organization and has been blocked by Onyx.`;\n }\n\n /**\n * Check if access is allowed by querying the backend.\n * Always fails open (allows) if backend is unavailable or not configured.\n * Stores the block reason from the backend for later retrieval.\n *\n * @returns Promise resolving to true if allowed, false if blocked\n */\n async isAllowed(): Promise<boolean> {\n // If access control not enabled, allow everything\n if (!this.enabled) {\n return true;\n }\n\n // Check with backend if client is available\n if (this.client) {\n try {\n const response = await this.client.authorize();\n if (response.action === \"block\") {\n // Store the reason from backend for user-facing error messages, or use default with server name\n if (response.reason) {\n this.lastBlockReason = response.reason;\n } else {\n const serverPart = this.serverName ? ` '${this.serverName}'` : \"\";\n this.lastBlockReason = `MCP server${serverPart} is not authorized for use in your organization and has been blocked by Onyx.`;\n }\n return false;\n }\n return true;\n } catch (error) {\n logger.error(\"Access control authorization failed with unexpected error\", {\n error: String(error),\n });\n // DEFENSIVE: client.authorize() handles all errors internally and always\n // returns an AccessControlResponse, so this catch is normally unreachable.\n // However, we keep it as defensive fail-open behavior for unexpected errors.\n return true;\n }\n }\n\n // No client configured - allow by default (shouldn't happen if enabled=true with URL)\n logger.warn(\"No access control client configured, allowing by default\");\n return true;\n }\n}\n","import type { SessionData } from \"../utils/getUserData.js\";\n\nimport { compressClientInfo } from \"../lib/compress-client-info.js\";\nimport { logger } from \"../lib/logger.js\";\n\nexport interface AccessControlClientConfig {\n apiKey: string;\n headers: Record<string, string>;\n sessionData: SessionData;\n timeoutMs: number;\n url: string;\n}\n\nexport interface AccessControlResponse {\n action: \"allow\" | \"block\";\n reason?: string;\n}\n\n/**\n * Client for checking MCP server access control with backend API.\n * Always fails open (allows) when backend is unreachable.\n */\nexport class AccessControlClient {\n private clientInfoBase64: string;\n private config: AccessControlClientConfig;\n\n constructor(config: AccessControlClientConfig) {\n this.config = config;\n this.clientInfoBase64 = this.getClientInfoBase64();\n logger.info(\"AccessControlClient initialized\", {\n timeoutMs: config.timeoutMs,\n url: config.url,\n });\n }\n\n /**\n * Authorize by querying the backend with full client info.\n * Always fails open (allows) on any error.\n *\n * @returns Access control response\n */\n async authorize(): Promise<AccessControlResponse> {\n try {\n // Try to get decision from backend\n const response = await this.sendAuthorizeRequest();\n\n logger.debug(\"Access control check successful\", {\n action: response.action,\n });\n\n return response;\n } catch (error) {\n logger.warn(\"Access control check failed, failing open (allowing by default)\", {\n error: String(error),\n });\n\n // Always fail open (allow by default for better UX)\n return { action: \"allow\" };\n }\n }\n\n /**\n * Compress and base64-encode client info for URL.\n * Matches scanner pattern.\n */\n private getClientInfoBase64(): string {\n return compressClientInfo(this.config.sessionData);\n }\n\n /**\n * Send authorization request to backend with compressed client info in URL.\n * Matches scanner URL pattern: ${url}/${apiKey}/mcp/${clientInfoBase64}\n *\n * @returns Access control response\n * @throws Error if request fails\n */\n private async sendAuthorizeRequest(): Promise<AccessControlResponse> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n // Build URL with API key and compressed client info (matching scanner pattern)\n const checkUrl = `${this.config.url}/${this.config.apiKey}/mcp/${this.clientInfoBase64}`;\n\n const response = await fetch(checkUrl, {\n headers: {\n ...this.config.headers,\n },\n method: \"POST\",\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Access control service returned ${response.status}: ${response.statusText}`);\n }\n\n const result = (await response.json()) as AccessControlResponse;\n\n // Validate response format\n if (!result.action || ![\"allow\", \"block\"].includes(result.action)) {\n throw new Error(`Invalid access control response format: action=\"${result.action}\"`);\n }\n\n return result;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new Error(`Access control check timed out after ${this.config.timeoutMs}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n}\n","import { execFile } from \"node:child_process\";\nimport { platform } from \"node:os\";\n\nimport { logger } from \"../lib/logger.js\";\nimport { captureExceptionSafe } from \"../lib/sentry.js\";\n\n/**\n * Opens the given URL in the user's default browser.\n *\n * Cross-platform support:\n * - macOS: uses `open`\n * - Windows: uses PowerShell Start-Process with Base64-encoded command (injection-safe)\n * - Linux: uses `xdg-open`\n *\n * @param url The URL to open\n * @returns A promise that resolves when the browser command is executed\n */\nexport async function openBrowser(url: string | URL): Promise<void> {\n const urlString = url.toString();\n const os = platform();\n const redactedUrl = redactUrlForLogging(urlString);\n\n return new Promise((resolve, reject) => {\n let command: string;\n let args: string[];\n\n if (os === \"win32\") {\n // Windows - use PowerShell with Base64-encoded command to prevent injection\n // This is the same approach used by the popular 'open' npm package.\n // The command is Base64-encoded (UTF-16LE) so no shell metacharacters can escape.\n const psCommand = `Start-Process ${escapePowerShellArgument(urlString)}`;\n const encodedCommand = encodePowerShellCommand(psCommand);\n\n command = getPowerShellPath();\n args = [\"-NoProfile\", \"-NonInteractive\", \"-ExecutionPolicy\", \"Bypass\", \"-EncodedCommand\", encodedCommand];\n } else if (os === \"darwin\") {\n // macOS\n command = \"open\";\n args = [urlString];\n } else {\n // Linux and others - use xdg-open\n command = \"xdg-open\";\n args = [urlString];\n }\n\n execFile(command, args, (error, _stdout, stderr) => {\n if (error) {\n const errorContext = {\n command,\n os,\n stderr,\n url: redactedUrl,\n };\n\n logger.warn(\"Failed to open browser automatically\", {\n ...errorContext,\n error: error.message,\n });\n\n captureExceptionSafe(error, errorContext, {\n feature: \"auth\",\n module: \"browser\",\n operation: \"openBrowser\",\n });\n\n const launchError = new Error(\n `Failed to open browser: ${error.message} (command: ${command}, os: ${os}, url: ${redactedUrl})`\n );\n launchError.cause = error;\n reject(launchError);\n } else {\n logger.debug(\"Browser opened successfully\", { url: redactedUrl });\n resolve();\n }\n });\n });\n}\n\n/**\n * Encodes a PowerShell command as Base64 UTF-16LE for use with -EncodedCommand.\n * This is the safest way to pass commands to PowerShell as it prevents any\n * shell metacharacter interpretation.\n */\nfunction encodePowerShellCommand(command: string): string {\n // PowerShell's -EncodedCommand expects UTF-16LE encoded Base64\n const buffer = Buffer.from(command, \"utf16le\");\n return buffer.toString(\"base64\");\n}\n\n/**\n * Escapes a string for use as a PowerShell single-quoted string argument.\n * Single quotes inside the string are escaped by doubling them.\n */\nfunction escapePowerShellArgument(value: string): string {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\n/**\n * Gets the path to Windows PowerShell.\n * PowerShell is included by default since Windows 7 SP1 and Windows Server 2008 R2.\n */\nfunction getPowerShellPath(): string {\n const systemRoot = process.env.SYSTEMROOT || process.env.windir || \"C:\\\\Windows\";\n return `${systemRoot}\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe`;\n}\n\n/**\n * Redacts sensitive query parameters from a URL for safe logging.\n * Preserves the base URL structure while hiding potentially sensitive data.\n *\n * @param url The URL to redact\n * @returns A redacted version of the URL safe for logging\n */\nfunction redactUrlForLogging(url: string): string {\n try {\n const parsed = new URL(url);\n if (parsed.search) {\n return `${parsed.protocol}//${parsed.host}${parsed.pathname}?[REDACTED]`;\n }\n return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;\n } catch {\n // If URL parsing fails, return a generic redacted string\n return \"[INVALID_URL]\";\n }\n}\n","import { createServer, type Server } from \"node:http\";\nimport { URL } from \"node:url\";\n\nimport { logger } from \"../lib/logger.js\";\n\n/**\n * Port range for OAuth callback server (IANA dynamic/private ports)\n */\nconst MIN_PORT = 49152;\nconst MAX_PORT = 65535;\nconst MAX_PORT_ATTEMPTS = 5;\n\nexport interface CallbackServerOptions {\n /**\n * Maximum number of port binding attempts before giving up.\n * Default: 5\n */\n maxAttempts?: number;\n /**\n * Preferred port to use. If unavailable, random ports will be tried.\n */\n preferredPort?: number;\n /**\n * Timeout in milliseconds to wait for the callback.\n * Default: 5 minutes (300000ms)\n */\n timeoutMs?: number;\n}\n\nexport interface CallbackServerResult {\n /**\n * The authorization code received from the OAuth server.\n */\n code: string;\n /**\n * The state parameter, if provided.\n */\n state?: string;\n}\n\n/**\n * Generates a random port number in the dynamic/private port range.\n */\nfunction getRandomPort(): number {\n return Math.floor(Math.random() * (MAX_PORT - MIN_PORT + 1)) + MIN_PORT;\n}\n\n/**\n * Attempts to start the HTTP server on the given port.\n * Returns a promise that resolves with the server if successful, or rejects if the port is in use.\n */\nfunction tryListenOnPort(server: Server, port: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const onError = (err: NodeJS.ErrnoException) => {\n server.removeListener(\"listening\", onListening);\n reject(err);\n };\n\n const onListening = () => {\n server.removeListener(\"error\", onError);\n resolve();\n };\n\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * HTML page shown to the user after successful authorization.\n */\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Authorization Successful</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n color: #fff;\n }\n .container {\n text-align: center;\n padding: 2rem;\n }\n .checkmark {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: #10b981;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 1.5rem;\n animation: pop 0.3s ease-out;\n }\n .checkmark svg {\n width: 40px;\n height: 40px;\n stroke: white;\n stroke-width: 3;\n }\n h1 {\n margin: 0 0 0.5rem;\n font-size: 1.75rem;\n font-weight: 600;\n }\n p {\n margin: 0;\n color: #a0aec0;\n font-size: 1rem;\n }\n @keyframes pop {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.1); }\n 100% { transform: scale(1); }\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"checkmark\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M5 13l4 4L19 7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n </div>\n <script>setTimeout(() => window.close(), 3000);</script>\n</body>\n</html>`;\n\n/**\n * Starts a local HTTP server to receive the OAuth callback.\n * Uses random port selection with retry logic to handle port conflicts.\n *\n * @returns A promise that resolves with the port number and a function to wait for the callback.\n */\nexport async function startCallbackServer(options: CallbackServerOptions = {}): Promise<{\n callbackUrl: string;\n close: () => Promise<void>;\n port: number;\n waitForCallback: () => Promise<CallbackServerResult>;\n}> {\n const { maxAttempts = MAX_PORT_ATTEMPTS, preferredPort, timeoutMs = DEFAULT_TIMEOUT_MS } = options;\n\n let resolveCallback: (result: CallbackServerResult) => void;\n let rejectCallback: (error: Error) => void;\n // Using an object to hold the timeout ID so it can be set after server creation\n // but still referenced in the close() function\n const timeoutRef: { id?: ReturnType<typeof setTimeout> } = {};\n\n const callbackPromise = new Promise<CallbackServerResult>((resolve, reject) => {\n resolveCallback = resolve;\n rejectCallback = reject;\n });\n\n // Track the actual bound port for use in request handler\n let boundPort: number;\n\n const server = createServer((req, res) => {\n // Ignore favicon requests\n if (req.url === \"/favicon.ico\") {\n res.writeHead(404);\n res.end();\n return;\n }\n\n // Only handle the callback path\n if (!req.url?.startsWith(\"/callback\")) {\n res.writeHead(404);\n res.end(\"Not Found\");\n return;\n }\n\n try {\n const parsedUrl = new URL(req.url, `http://localhost:${boundPort}`);\n const code = parsedUrl.searchParams.get(\"code\");\n const error = parsedUrl.searchParams.get(\"error\");\n const errorDescription = parsedUrl.searchParams.get(\"error_description\");\n const state = parsedUrl.searchParams.get(\"state\");\n\n if (error) {\n logger.error(\"OAuth authorization error\", { error, errorDescription });\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(getErrorHtml(error, errorDescription || undefined));\n clearTimeout(timeoutRef.id);\n rejectCallback(\n new Error(`OAuth authorization failed: ${error}${errorDescription ? ` - ${errorDescription}` : \"\"}`)\n );\n return;\n }\n\n if (!code) {\n logger.error(\"OAuth callback missing authorization code\");\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(getErrorHtml(\"missing_code\", \"No authorization code was provided\"));\n clearTimeout(timeoutRef.id);\n rejectCallback(new Error(\"OAuth callback missing authorization code\"));\n return;\n }\n\n logger.info(\"OAuth authorization code received\", {\n codePrefix: `${code.substring(0, 10)}...`,\n hasState: !!state,\n });\n\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(SUCCESS_HTML);\n\n clearTimeout(timeoutRef.id);\n resolveCallback({ code, state: state || undefined });\n\n // Close server after a short delay to ensure response is sent\n setTimeout(() => server.close(), 1000);\n } catch (err) {\n logger.error(\"Error processing OAuth callback\", { error: String(err) });\n res.writeHead(500);\n res.end(\"Internal Server Error\");\n clearTimeout(timeoutRef.id);\n rejectCallback(err instanceof Error ? err : new Error(String(err)));\n }\n });\n\n // Set up timeout (now that server exists)\n timeoutRef.id = setTimeout(() => {\n rejectCallback(new Error(`OAuth callback timeout after ${timeoutMs}ms`));\n server.close();\n }, timeoutMs);\n\n // Try to bind to a port with retry logic\n const attemptedPorts: number[] = [];\n let lastError: Error | undefined;\n\n while (attemptedPorts.length < maxAttempts) {\n // First attempt uses preferred port if provided, otherwise random\n let portToTry: number;\n if (attemptedPorts.length === 0 && preferredPort !== undefined) {\n portToTry = preferredPort;\n } else {\n // Generate a random port that hasn't been tried yet\n portToTry = getRandomPort();\n while (attemptedPorts.includes(portToTry)) {\n portToTry = getRandomPort();\n }\n }\n attemptedPorts.push(portToTry);\n\n try {\n await tryListenOnPort(server, portToTry);\n boundPort = portToTry;\n const callbackUrl = `http://localhost:${boundPort}/callback`;\n logger.info(\"OAuth callback server started\", { attempt: attemptedPorts.length, callbackUrl, port: boundPort });\n\n return {\n callbackUrl,\n close: () =>\n new Promise<void>((resolve) => {\n clearTimeout(timeoutRef.id);\n server.close(() => resolve());\n }),\n port: boundPort,\n waitForCallback: () => callbackPromise,\n };\n } catch (err) {\n const nodeErr = err as NodeJS.ErrnoException;\n if (nodeErr.code === \"EADDRINUSE\") {\n logger.debug(\"Port in use, trying another port\", {\n attempt: attemptedPorts.length,\n maxAttempts,\n port: portToTry,\n });\n lastError = new Error(`Port ${portToTry} is already in use`);\n } else {\n // For non-EADDRINUSE errors, clean up and throw immediately\n clearTimeout(timeoutRef.id);\n server.close();\n throw err;\n }\n }\n }\n\n // All attempts failed\n clearTimeout(timeoutRef.id);\n server.close();\n throw new Error(\n `Failed to start OAuth callback server after ${maxAttempts} attempts. ` +\n `Tried ports: ${attemptedPorts.join(\", \")}. Last error: ${lastError?.message || \"unknown\"}`\n );\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\n/**\n * HTML page shown to the user when authorization fails.\n */\nfunction getErrorHtml(error: string, description?: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Authorization Failed</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n color: #fff;\n }\n .container {\n text-align: center;\n padding: 2rem;\n max-width: 400px;\n }\n .error-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: #ef4444;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 1.5rem;\n }\n .error-icon svg {\n width: 40px;\n height: 40px;\n stroke: white;\n stroke-width: 3;\n }\n h1 {\n margin: 0 0 0.5rem;\n font-size: 1.75rem;\n font-weight: 600;\n }\n .error-code {\n color: #f87171;\n font-family: monospace;\n margin-bottom: 0.5rem;\n }\n p {\n margin: 0;\n color: #a0aec0;\n font-size: 1rem;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"error-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M6 18L18 6M6 6l12 12\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n <h1>Authorization Failed</h1>\n <p class=\"error-code\">${escapeHtml(error)}</p>\n ${description ? `<p>${escapeHtml(description)}</p>` : \"\"}\n </div>\n</body>\n</html>`;\n}\n","import type { OAuthClientInformationFull, OAuthTokens } from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\nimport { createHash } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { logger } from \"../lib/logger.js\";\nimport { normalizeUrl } from \"../utils/normalizeUrl.js\";\n\nexport interface OAuthCredentialStoreOptions {\n /**\n * Directory to store OAuth credentials.\n * Defaults to ~/.onyx/mcp-gateway/oauth/\n */\n storageDir: string;\n}\n\n/**\n * Stored credentials structure persisted to disk.\n */\nexport interface StoredCredentials {\n /**\n * DCR client registration information.\n */\n clientInfo?: OAuthClientInformationFull;\n /**\n * Timestamp when the credentials were stored (ISO string).\n */\n createdAt: string;\n /**\n * The original server URL these credentials are for.\n */\n serverUrl: string;\n /**\n * OAuth tokens (access_token, refresh_token, etc.).\n */\n tokens?: OAuthTokens;\n /**\n * Timestamp when tokens were obtained (ISO string).\n * Used to calculate token expiration.\n */\n tokensObtainedAt?: string;\n /**\n * Last update timestamp (ISO string).\n */\n updatedAt: string;\n}\n\n/**\n * File-based credential store for OAuth credentials.\n * Stores credentials in JSON files keyed by server URL hash.\n */\nexport class OAuthCredentialStore {\n private readonly storageDir: string;\n\n constructor(options: OAuthCredentialStoreOptions) {\n this.storageDir = options.storageDir;\n }\n\n /**\n * Deletes stored credentials for a server URL.\n */\n async delete(serverUrl: string): Promise<void> {\n // Normalize URL to ensure consistent key generation\n const normalizedUrl = normalizeUrl(serverUrl);\n const filePath = this.getFilePath(normalizedUrl);\n\n try {\n await rm(filePath);\n logger.debug(\"Deleted stored credentials\", { filePath, serverUrl: normalizedUrl });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n logger.warn(\"Failed to delete stored credentials\", {\n error: String(error),\n filePath,\n serverUrl: normalizedUrl,\n });\n }\n }\n }\n\n /**\n * Checks if stored credentials have a refresh token.\n */\n hasRefreshToken(credentials: StoredCredentials): boolean {\n return !!credentials.tokens?.refresh_token;\n }\n\n /**\n * Checks if stored credentials have a valid (non-expired) access token.\n */\n hasValidAccessToken(credentials: StoredCredentials): boolean {\n return !!credentials.tokens?.access_token && !this.isAccessTokenExpired(credentials);\n }\n\n /**\n * Checks if the stored access token is expired.\n * Returns true if expired or if expiration cannot be determined.\n */\n isAccessTokenExpired(credentials: StoredCredentials): boolean {\n if (!credentials.tokens || !credentials.tokensObtainedAt) {\n return true;\n }\n\n const { expires_in } = credentials.tokens;\n if (!expires_in) {\n // No expiration info - assume not expired\n return false;\n }\n\n const obtainedAt = new Date(credentials.tokensObtainedAt).getTime();\n const expiresAt = obtainedAt + expires_in * 1000;\n const now = Date.now();\n\n // Add 30 second buffer to avoid edge cases\n const isExpired = now >= expiresAt - 30000;\n\n logger.debug(\"Checked access token expiration\", {\n expiresAt: new Date(expiresAt).toISOString(),\n expiresIn: expires_in,\n isExpired,\n now: new Date(now).toISOString(),\n obtainedAt: credentials.tokensObtainedAt,\n });\n\n return isExpired;\n }\n\n /**\n * Loads stored credentials for a server URL.\n * Returns undefined if no credentials exist or if they're invalid.\n */\n async load(serverUrl: string): Promise<StoredCredentials | undefined> {\n // Normalize URL to ensure consistent key generation\n const normalizedUrl = normalizeUrl(serverUrl);\n const filePath = this.getFilePath(normalizedUrl);\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const credentials = JSON.parse(content) as StoredCredentials;\n\n // Validate the stored credentials match the requested server (using normalized URL)\n // Also accept credentials stored with the original URL format for backward compatibility\n const storedUrlNormalized = normalizeUrl(credentials.serverUrl);\n if (storedUrlNormalized !== normalizedUrl) {\n logger.warn(\"Credential file server URL mismatch\", {\n expected: normalizedUrl,\n filePath,\n found: credentials.serverUrl,\n });\n return undefined;\n }\n\n logger.debug(\"Loaded stored credentials\", {\n clientId: credentials.clientInfo?.client_id,\n hasRefreshToken: !!credentials.tokens?.refresh_token,\n hasTokens: !!credentials.tokens,\n serverUrl: normalizedUrl,\n });\n\n return credentials;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n // File doesn't exist - no stored credentials\n logger.debug(\"No stored credentials found\", { serverUrl: normalizedUrl });\n return undefined;\n }\n\n logger.warn(\"Failed to load stored credentials\", {\n error: String(error),\n filePath,\n serverUrl: normalizedUrl,\n });\n return undefined;\n }\n }\n\n /**\n * Saves credentials to disk.\n */\n async save(credentials: StoredCredentials): Promise<void> {\n await this.ensureStorageDir();\n\n const filePath = this.getFilePath(credentials.serverUrl);\n const content = JSON.stringify(credentials, null, 2);\n\n try {\n await writeFile(filePath, content, { encoding: \"utf-8\", mode: 0o600 });\n // Ensure permissions are correct even if file existed\n await chmod(filePath, 0o600);\n\n logger.debug(\"Saved credentials to disk\", {\n clientId: credentials.clientInfo?.client_id,\n filePath,\n hasTokens: !!credentials.tokens,\n serverUrl: credentials.serverUrl,\n });\n } catch (error) {\n logger.error(\"Failed to save credentials\", {\n error: String(error),\n filePath,\n serverUrl: credentials.serverUrl,\n });\n throw error;\n }\n }\n\n /**\n * Updates specific fields in stored credentials.\n * Creates new credentials if none exist.\n */\n async update(\n serverUrl: string,\n updates: Partial<Pick<StoredCredentials, \"clientInfo\" | \"tokens\" | \"tokensObtainedAt\">>\n ): Promise<StoredCredentials> {\n // Normalize URL to ensure consistent key generation and storage\n const normalizedUrl = normalizeUrl(serverUrl);\n const existing = await this.load(normalizedUrl);\n const now = new Date().toISOString();\n\n const credentials: StoredCredentials = existing\n ? {\n ...existing,\n ...updates,\n // Update serverUrl to normalized form for consistency\n serverUrl: normalizedUrl,\n updatedAt: now,\n }\n : {\n createdAt: now,\n serverUrl: normalizedUrl,\n updatedAt: now,\n ...updates,\n };\n\n await this.save(credentials);\n return credentials;\n }\n\n /**\n * Ensures the storage directory exists with proper permissions.\n */\n private async ensureStorageDir(): Promise<void> {\n try {\n await mkdir(this.storageDir, { mode: 0o700, recursive: true });\n } catch (error) {\n // Directory may already exist\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw error;\n }\n }\n }\n\n /**\n * Generates a URL-safe filename from a server URL.\n * Uses SHA-256 hash truncated to 16 chars for brevity.\n */\n private generateKey(serverUrl: string): string {\n const hash = createHash(\"sha256\").update(serverUrl).digest(\"hex\");\n return hash.substring(0, 16);\n }\n\n /**\n * Gets the file path for a server's credentials.\n */\n private getFilePath(serverUrl: string): string {\n const key = this.generateKey(serverUrl);\n return join(this.storageDir, `${key}.json`);\n }\n}\n\n","import type { OAuthProviderConfig } from \"../config/config.js\";\n\nimport { BUILD_TIME_CONFIG } from \"../config/config.generated.js\";\nimport { logger } from \"../lib/logger.js\";\n\n/**\n * Pre-configured OAuth providers loaded from build-time config.\n * These providers have their credentials embedded at build time.\n */\nexport const OAUTH_PROVIDERS: OAuthProviderConfig[] = BUILD_TIME_CONFIG.OAUTH_PROVIDERS;\n\n/**\n * Finds a pre-configured OAuth provider that matches the given server URL.\n *\n * @param serverUrl The MCP server URL to match against provider URL patterns\n * @returns The matching provider config, or undefined if no match found\n */\nexport function findProviderForUrl(serverUrl: string | URL): OAuthProviderConfig | undefined {\n const serverUrlObj = typeof serverUrl === \"string\" ? new URL(serverUrl) : serverUrl;\n const hostname = serverUrlObj.hostname.toLowerCase();\n\n for (const provider of OAUTH_PROVIDERS) {\n for (const pattern of provider.urlPatterns) {\n // Pattern can be a hostname or a hostname with path prefix\n const patternLower = pattern.toLowerCase();\n\n // Check if pattern matches hostname (with or without subdomains)\n if (matchesHostname(hostname, patternLower)) {\n logger.debug(\"Found matching OAuth provider for URL\", {\n hostname,\n pattern,\n providerId: provider.id,\n providerName: provider.name,\n });\n return provider;\n }\n }\n }\n\n logger.debug(\"No pre-configured OAuth provider found for URL\", {\n hostname,\n serverUrl: serverUrlObj.toString(),\n });\n return undefined;\n}\n\n/**\n * Gets a provider by its ID.\n *\n * @param providerId The provider ID (e.g., \"github\")\n * @returns The provider config, or undefined if not found\n */\nexport function getProviderById(providerId: string): OAuthProviderConfig | undefined {\n return OAUTH_PROVIDERS.find((p) => p.id === providerId);\n}\n\n/**\n * Lists all configured OAuth providers.\n * Useful for debugging and admin interfaces.\n *\n * @returns Array of provider summaries (without secrets)\n */\nexport function listProviders(): Array<{\n hasClientSecret: boolean;\n id: string;\n name: string;\n urlPatterns: string[];\n}> {\n return OAUTH_PROVIDERS.map((p) => ({\n hasClientSecret: !!p.clientSecret,\n id: p.id,\n name: p.name,\n urlPatterns: p.urlPatterns,\n }));\n}\n\n/**\n * Checks if a hostname matches a pattern.\n * Supports exact matches and subdomain matches.\n *\n * @param hostname The hostname to check (e.g., \"api.github.com\")\n * @param pattern The pattern to match against (e.g., \"github.com\")\n * @returns true if the hostname matches the pattern\n */\nfunction matchesHostname(hostname: string, pattern: string): boolean {\n // Exact match\n if (hostname === pattern) {\n return true;\n }\n\n // Subdomain match: hostname ends with \".pattern\"\n // e.g., \"api.github.com\" matches \"github.com\"\n if (hostname.endsWith(`.${pattern}`)) {\n return true;\n }\n\n return false;\n}\n","import {\n discoverAuthorizationServerMetadata,\n discoverOAuthProtectedResourceMetadata,\n exchangeAuthorization,\n extractResourceMetadataUrl,\n refreshAuthorization,\n registerClient,\n startAuthorization,\n} from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport type {\n OAuthClientInformationFull,\n OAuthClientMetadata,\n OAuthProtectedResourceMetadata,\n OAuthTokens,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\nimport type { OAuthProviderConfig } from \"../config/config.js\";\nimport { logger } from \"../lib/logger.js\";\nimport { normalizeUrl } from \"../utils/normalizeUrl.js\";\nimport { openBrowser } from \"./browser.js\";\nimport { startCallbackServer } from \"./callbackServer.js\";\nimport type { OAuthCredentialStore, StoredCredentials } from \"./OAuthCredentialStore.js\";\nimport { findProviderForUrl } from \"./providers.js\";\n\nexport interface OAuthFlowOptions {\n /**\n * Custom client metadata for DCR registration.\n * If not provided, default metadata will be used.\n */\n clientMetadata?: Partial<OAuthClientMetadata>;\n /**\n * Credential store for persisting OAuth credentials.\n * If provided, credentials will be loaded from and saved to disk.\n */\n credentialStore?: OAuthCredentialStore;\n /**\n * OAuth scope to request.\n */\n scope?: string;\n /**\n * Timeout in milliseconds for the OAuth callback.\n * Default: 5 minutes\n */\n timeoutMs?: number;\n}\n\nexport interface OAuthFlowResult {\n clientInfo: OAuthClientInformationFull;\n /**\n * Whether the result came from stored credentials (true) or a fresh OAuth flow (false).\n */\n fromCache?: boolean;\n tokens: OAuthTokens;\n}\n\n/**\n * Attempts to detect if a server requires OAuth by making a test request.\n * Returns the resource metadata URL from the WWW-Authenticate header if OAuth is required.\n *\n * @param serverUrl The MCP server URL to check\n * @returns The resource metadata URL if OAuth is required, undefined otherwise\n */\nexport async function detectOAuthRequirement(serverUrl: string | URL): Promise<undefined | URL> {\n // Normalize URL to avoid redirect issues on Windows\n const normalizedUrl = normalizeUrl(serverUrl);\n const serverUrlObj = new URL(normalizedUrl);\n\n try {\n const response = await fetch(serverUrlObj, {\n headers: {\n Accept: \"application/json\",\n },\n method: \"POST\",\n });\n\n if (response.status === 401) {\n // Check for resource metadata URL in response\n const resourceMetadataUrl = extractResourceMetadataUrl(response);\n if (resourceMetadataUrl) {\n logger.debug(\"OAuth required, found resource metadata URL\", {\n resourceMetadataUrl: resourceMetadataUrl.toString(),\n });\n return resourceMetadataUrl;\n }\n\n // Check WWW-Authenticate header for Bearer realm\n const wwwAuth = response.headers.get(\"WWW-Authenticate\");\n if (wwwAuth && wwwAuth.toLowerCase().includes(\"bearer\")) {\n logger.debug(\"OAuth required based on WWW-Authenticate header\", { wwwAuth });\n return undefined; // OAuth required but no metadata URL\n }\n }\n\n return undefined;\n } catch (err) {\n logger.debug(\"Error detecting OAuth requirement\", { error: String(err) });\n return undefined;\n }\n}\n\n/**\n * Performs the complete OAuth 2.0 Authorization Code flow with DCR.\n *\n * This function:\n * 1. Checks for existing valid credentials (if credentialStore provided)\n * 2. Attempts token refresh if access token expired (if credentialStore provided)\n * 3. Discovers OAuth protected resource metadata\n * 4. Discovers authorization server metadata\n * 5. Dynamically registers the client (DCR)\n * 6. Opens browser for user authorization\n * 7. Waits for the callback with authorization code\n * 8. Exchanges the code for tokens\n * 9. Persists credentials (if credentialStore provided)\n *\n * @param serverUrl The MCP server URL that requires OAuth\n * @param options Optional configuration\n * @returns The client info and tokens from successful authorization\n */\nexport async function performOAuthFlow(\n serverUrl: string | URL,\n options: OAuthFlowOptions = {}\n): Promise<OAuthFlowResult> {\n const { credentialStore, scope, timeoutMs } = options;\n // Normalize URL to avoid redirect issues on Windows (POST to GET conversion)\n const serverUrlString = normalizeUrl(serverUrl);\n const serverUrlObj = new URL(serverUrlString);\n\n logger.info(\"Starting OAuth flow\", { serverUrl: serverUrlString });\n\n // Step 0: Check for existing valid credentials\n if (credentialStore) {\n const existingCredentials = await tryLoadExistingCredentials(serverUrlObj, credentialStore);\n if (existingCredentials) {\n logger.info(\"Using existing OAuth credentials\", {\n clientId: existingCredentials.clientInfo.client_id,\n serverUrl: serverUrlString,\n });\n return existingCredentials;\n }\n }\n\n // Step 1: Start the callback server to get a dynamic port\n logger.debug(\"Starting OAuth callback server...\");\n const callbackServer = await startCallbackServer({ timeoutMs });\n const redirectUrl = callbackServer.callbackUrl;\n\n logger.info(\"OAuth callback server started\", {\n callbackUrl: redirectUrl,\n port: callbackServer.port,\n });\n\n try {\n // Step 2: Check for pre-configured OAuth provider (non-DCR servers)\n const preConfiguredProvider = findProviderForUrl(serverUrlObj);\n\n if (preConfiguredProvider) {\n logger.info(\"Using pre-configured OAuth provider\", {\n providerId: preConfiguredProvider.id,\n providerName: preConfiguredProvider.name,\n });\n\n // Use pre-configured provider flow - pass the callback server instance\n return await performPreConfiguredProviderFlow(\n serverUrlObj,\n preConfiguredProvider,\n callbackServer,\n credentialStore,\n scope\n );\n }\n\n // Step 3: Discover OAuth protected resource metadata (DCR flow)\n logger.debug(\"Discovering OAuth protected resource metadata...\");\n let resourceMetadata: OAuthProtectedResourceMetadata | undefined;\n\n try {\n resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrlObj);\n logger.info(\"Discovered protected resource metadata\", {\n authorizationServers: resourceMetadata?.authorization_servers,\n resource: resourceMetadata?.resource,\n scopesSupported: resourceMetadata?.scopes_supported,\n });\n } catch (err) {\n logger.warn(\"Could not discover protected resource metadata, will try direct discovery\", {\n error: String(err),\n });\n }\n\n // Step 4: Determine authorization server URL\n let authServerUrl: URL;\n if (resourceMetadata?.authorization_servers?.length) {\n authServerUrl = new URL(resourceMetadata.authorization_servers[0]);\n logger.debug(\"Using authorization server from resource metadata\", {\n authServerUrl: authServerUrl.toString(),\n });\n } else {\n // Fall back to using the server URL itself\n authServerUrl = serverUrlObj;\n logger.debug(\"Using server URL as authorization server\", {\n authServerUrl: authServerUrl.toString(),\n });\n }\n\n // Step 5: Discover authorization server metadata\n logger.debug(\"Discovering authorization server metadata...\");\n const authMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n\n if (!authMetadata) {\n throw new Error(\n `Could not discover OAuth metadata from ${authServerUrl}. ` +\n `The server may not support OAuth 2.0 or the metadata endpoint is not accessible.`\n );\n }\n\n logger.info(\"Discovered authorization server metadata\", {\n authorizationEndpoint: authMetadata.authorization_endpoint,\n issuer: authMetadata.issuer,\n registrationEndpoint: authMetadata.registration_endpoint,\n tokenEndpoint: authMetadata.token_endpoint,\n });\n\n // Step 6: Check if we have existing client info that can be reused\n let clientInfo: OAuthClientInformationFull;\n const existingCredentials = credentialStore ? await credentialStore.load(serverUrlString) : undefined;\n\n // Check if existing client registration has a matching redirect_uri\n const existingRedirectUris = existingCredentials?.clientInfo?.redirect_uris || [];\n const canReuseClient = existingCredentials?.clientInfo && existingRedirectUris.includes(redirectUrl);\n\n if (canReuseClient && existingCredentials?.clientInfo) {\n // Reuse existing client registration - redirect_uri matches\n logger.info(\"Reusing existing DCR client registration\", {\n clientId: existingCredentials.clientInfo.client_id,\n clientName: existingCredentials.clientInfo.client_name,\n redirectUri: redirectUrl,\n });\n clientInfo = existingCredentials.clientInfo;\n } else {\n // Need to register new client (either no existing client, or redirect_uri doesn't match)\n if (existingCredentials?.clientInfo) {\n logger.info(\"Existing DCR client has different redirect_uri, re-registering\", {\n existingRedirectUris,\n newRedirectUri: redirectUrl,\n });\n }\n\n // Step 6b: Build client metadata for DCR\n const clientMetadata: OAuthClientMetadata = {\n client_name: options.clientMetadata?.client_name || \"MCP Gateway\",\n grant_types: options.clientMetadata?.grant_types || [\"authorization_code\", \"refresh_token\"],\n redirect_uris: [redirectUrl],\n response_types: options.clientMetadata?.response_types || [\"code\"],\n // Public client - no client secret\n token_endpoint_auth_method: options.clientMetadata?.token_endpoint_auth_method || \"none\",\n ...(scope && { scope }),\n };\n\n logger.debug(\"Client metadata for DCR\", { clientMetadata });\n\n // Step 7: Register client via DCR\n if (!authMetadata.registration_endpoint) {\n throw new Error(\n `Authorization server does not support Dynamic Client Registration. ` +\n `No registration_endpoint found in metadata.`\n );\n }\n\n logger.info(\"Registering client via DCR...\", {\n registrationEndpoint: authMetadata.registration_endpoint,\n });\n\n clientInfo = await registerClient(authServerUrl, {\n clientMetadata,\n metadata: authMetadata,\n });\n\n logger.info(\"Client registered successfully via DCR\", {\n clientId: clientInfo.client_id,\n clientName: clientInfo.client_name,\n clientSecretExpiresAt: clientInfo.client_secret_expires_at,\n });\n\n // Persist the client info immediately\n if (credentialStore) {\n await credentialStore.update(serverUrlString, { clientInfo });\n }\n }\n\n // Step 8: RFC 8707 Resource Indicators\n // NOTE: We intentionally do NOT send the resource parameter. Some OAuth servers (e.g., Notion)\n // ignore the client's requested resource and always issue tokens with their configured default\n // audience. When we then present that token to a different path (e.g., /mcp), the resource\n // server rejects it with \"Token audience does not match resource server\".\n // By omitting the resource parameter entirely, we let the OAuth server use its default\n // behavior, which typically issues tokens that work with the resource server.\n // This matches how Linear works (no protected resource metadata = no resource param sent).\n\n // Step 9: Start authorization flow (generates PKCE challenge)\n logger.debug(\"Starting authorization flow...\");\n const { authorizationUrl, codeVerifier } = await startAuthorization(authServerUrl, {\n clientInformation: clientInfo,\n metadata: authMetadata,\n redirectUrl,\n scope,\n });\n\n await openBrowser(authorizationUrl);\n\n // Step 11: Wait for callback with authorization code\n logger.debug(\"Waiting for OAuth callback...\");\n const callbackResult = await callbackServer.waitForCallback();\n\n logger.info(\"Authorization code received\", {\n codePrefix: `${callbackResult.code.substring(0, 10)}...`,\n hasState: !!callbackResult.state,\n });\n\n // Step 12: Exchange authorization code for tokens\n logger.debug(\"Exchanging authorization code for tokens...\");\n const tokens = await exchangeAuthorization(authServerUrl, {\n authorizationCode: callbackResult.code,\n clientInformation: clientInfo,\n codeVerifier,\n metadata: authMetadata,\n redirectUri: redirectUrl,\n });\n\n logger.info(\"OAuth tokens obtained successfully\", {\n expiresIn: tokens.expires_in,\n hasRefreshToken: !!tokens.refresh_token,\n tokenType: tokens.token_type,\n });\n\n // Step 13: Persist tokens\n if (credentialStore) {\n const tokensObtainedAt = new Date().toISOString();\n await credentialStore.update(serverUrlString, {\n tokens,\n tokensObtainedAt,\n });\n logger.info(\"OAuth credentials persisted to disk\", { serverUrl: serverUrlString });\n }\n\n return {\n clientInfo,\n fromCache: false,\n tokens,\n };\n } finally {\n // Always close the callback server\n await callbackServer.close();\n }\n}\n\n/**\n * Exchanges an authorization code for tokens using a pre-configured provider.\n * Handles different token endpoint authentication methods.\n */\nasync function exchangeCodeForTokens(\n provider: OAuthProviderConfig,\n code: string,\n redirectUri: string,\n codeVerifier?: string\n): Promise<OAuthTokens> {\n const tokenEndpoint = provider.tokenEndpoint;\n const authMethod = provider.tokenEndpointAuthMethod || (provider.clientSecret ? \"client_secret_post\" : \"none\");\n\n // Build token request body\n const body = new URLSearchParams();\n body.set(\"grant_type\", \"authorization_code\");\n body.set(\"code\", code);\n body.set(\"redirect_uri\", redirectUri);\n\n // Add PKCE verifier if present\n if (codeVerifier) {\n body.set(\"code_verifier\", codeVerifier);\n }\n\n // Build request headers\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n };\n\n // Add client authentication based on method\n if (authMethod === \"client_secret_basic\" && provider.clientSecret) {\n // HTTP Basic authentication\n const credentials = Buffer.from(`${provider.clientId}:${provider.clientSecret}`).toString(\"base64\");\n headers.Authorization = `Basic ${credentials}`;\n } else if (authMethod === \"client_secret_post\" && provider.clientSecret) {\n // Client credentials in POST body\n body.set(\"client_id\", provider.clientId);\n body.set(\"client_secret\", provider.clientSecret);\n } else {\n // Public client - just client_id\n body.set(\"client_id\", provider.clientId);\n }\n\n logger.debug(\"Sending token request\", {\n authMethod,\n providerId: provider.id,\n tokenEndpoint,\n });\n\n const response = await fetch(tokenEndpoint, {\n body: body.toString(),\n headers,\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n logger.error(\"Token exchange failed\", {\n error: errorText,\n providerId: provider.id,\n status: response.status,\n });\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n // GitHub returns tokens as application/x-www-form-urlencoded by default\n // unless Accept: application/json is set\n const contentType = response.headers.get(\"content-type\") || \"\";\n let tokenData: Record<string, unknown>;\n\n if (contentType.includes(\"application/json\")) {\n tokenData = (await response.json()) as Record<string, unknown>;\n } else if (contentType.includes(\"application/x-www-form-urlencoded\") || contentType.includes(\"text/plain\")) {\n // Parse URL-encoded response (GitHub's default format)\n const text = await response.text();\n const params = new URLSearchParams(text);\n tokenData = Object.fromEntries(params.entries()) as Record<string, unknown>;\n } else {\n // Try JSON first, fall back to URL-encoded\n const text = await response.text();\n try {\n tokenData = JSON.parse(text) as Record<string, unknown>;\n } catch {\n const params = new URLSearchParams(text);\n tokenData = Object.fromEntries(params.entries()) as Record<string, unknown>;\n }\n }\n\n // Convert expires_in to number if it's a string\n if (typeof tokenData.expires_in === \"string\") {\n tokenData.expires_in = parseInt(tokenData.expires_in, 10);\n }\n\n return {\n access_token: tokenData.access_token as string,\n expires_in: tokenData.expires_in as number | undefined,\n refresh_token: tokenData.refresh_token as string | undefined,\n scope: tokenData.scope as string | undefined,\n token_type: (tokenData.token_type as string) || \"bearer\",\n };\n}\n\n/**\n * Performs OAuth flow using a pre-configured provider (non-DCR).\n * This is used for OAuth servers that don't support Dynamic Client Registration.\n */\nasync function performPreConfiguredProviderFlow(\n serverUrl: URL,\n provider: OAuthProviderConfig,\n callbackServerInstance: {\n callbackUrl: string;\n close: () => Promise<void>;\n waitForCallback: () => Promise<{ code: string; state?: string }>;\n },\n credentialStore: OAuthCredentialStore | undefined,\n scope: string | undefined\n): Promise<OAuthFlowResult> {\n const serverUrlString = serverUrl.toString();\n const redirectUrl = callbackServerInstance.callbackUrl;\n\n // Build client info from pre-configured provider\n const clientInfo: OAuthClientInformationFull = {\n client_id: provider.clientId,\n client_name: provider.name,\n client_secret: provider.clientSecret,\n redirect_uris: [redirectUrl],\n token_endpoint_auth_method:\n provider.tokenEndpointAuthMethod || (provider.clientSecret ? \"client_secret_post\" : \"none\"),\n };\n\n logger.info(\"Using pre-configured OAuth client\", {\n clientId: provider.clientId,\n hasClientSecret: !!provider.clientSecret,\n providerId: provider.id,\n tokenEndpointAuthMethod: clientInfo.token_endpoint_auth_method,\n });\n\n // Determine scope to use\n const effectiveScope = scope || provider.scopes?.join(\" \");\n\n // Generate PKCE challenge if enabled for this provider\n const usePkce = provider.usePkce !== false; // Default to true unless explicitly disabled\n let codeVerifier: string | undefined;\n let codeChallenge: string | undefined;\n let codeChallengeMethod: string | undefined;\n\n if (usePkce) {\n // Generate PKCE code verifier and challenge\n const { generateCodeChallenge, generateCodeVerifier } = await import(\"./pkce.js\");\n codeVerifier = generateCodeVerifier();\n codeChallenge = await generateCodeChallenge(codeVerifier);\n codeChallengeMethod = \"S256\";\n }\n\n // Build authorization URL manually\n const authUrl = new URL(provider.authorizationEndpoint);\n authUrl.searchParams.set(\"client_id\", provider.clientId);\n authUrl.searchParams.set(\"redirect_uri\", redirectUrl);\n authUrl.searchParams.set(\"response_type\", \"code\");\n\n if (effectiveScope) {\n authUrl.searchParams.set(\"scope\", effectiveScope);\n }\n\n // Add state for security\n const state = crypto.randomUUID();\n authUrl.searchParams.set(\"state\", state);\n\n // Add PKCE challenge if enabled\n if (codeChallenge && codeChallengeMethod) {\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", codeChallengeMethod);\n }\n\n await openBrowser(authUrl);\n\n // Wait for callback on the existing callback server\n logger.debug(\"Waiting for OAuth callback...\");\n const callbackResult = await callbackServerInstance.waitForCallback();\n\n // Verify state matches\n if (callbackResult.state !== state) {\n throw new Error(\"OAuth state mismatch - possible CSRF attack\");\n }\n\n logger.info(\"Authorization code received\", {\n codePrefix: `${callbackResult.code.substring(0, 10)}...`,\n providerId: provider.id,\n });\n\n // Exchange authorization code for tokens\n logger.debug(\"Exchanging authorization code for tokens...\");\n const tokens = await exchangeCodeForTokens(provider, callbackResult.code, redirectUrl, codeVerifier);\n\n logger.info(\"OAuth tokens obtained successfully\", {\n expiresIn: tokens.expires_in,\n hasRefreshToken: !!tokens.refresh_token,\n providerId: provider.id,\n tokenType: tokens.token_type,\n });\n\n // Persist credentials\n if (credentialStore) {\n const tokensObtainedAt = new Date().toISOString();\n await credentialStore.update(serverUrlString, {\n clientInfo,\n tokens,\n tokensObtainedAt,\n });\n logger.info(\"OAuth credentials persisted to disk\", {\n providerId: provider.id,\n serverUrl: serverUrlString,\n });\n }\n\n return {\n clientInfo,\n fromCache: false,\n tokens,\n };\n}\n\n/**\n * Checks for existing valid credentials and returns them if available.\n * Handles token refresh if the access token is expired but refresh token exists.\n */\nasync function tryLoadExistingCredentials(\n serverUrl: URL,\n credentialStore: OAuthCredentialStore\n): Promise<OAuthFlowResult | undefined> {\n const credentials = await credentialStore.load(serverUrl.toString());\n\n if (!credentials) {\n logger.debug(\"No stored credentials found\", { serverUrl: serverUrl.toString() });\n return undefined;\n }\n\n if (!credentials.clientInfo) {\n logger.debug(\"Stored credentials have no client info\", { serverUrl: serverUrl.toString() });\n return undefined;\n }\n\n // Check if we have a valid access token\n if (credentialStore.hasValidAccessToken(credentials) && credentials.tokens) {\n logger.info(\"Using cached OAuth credentials with valid access token\", {\n clientId: credentials.clientInfo.client_id,\n serverUrl: serverUrl.toString(),\n });\n\n return {\n clientInfo: credentials.clientInfo,\n fromCache: true,\n tokens: credentials.tokens,\n };\n }\n\n // Access token is expired or missing, try to refresh\n if (credentialStore.hasRefreshToken(credentials)) {\n const refreshResult = await tryRefreshToken(serverUrl, credentials, credentialStore);\n if (refreshResult) {\n return refreshResult;\n }\n }\n\n logger.debug(\"Stored credentials are expired and no valid refresh token\", {\n serverUrl: serverUrl.toString(),\n });\n\n return undefined;\n}\n\n/**\n * Attempts to refresh an expired access token using the refresh token.\n * Returns new tokens if successful, undefined if refresh fails.\n */\nasync function tryRefreshToken(\n serverUrl: URL,\n credentials: StoredCredentials,\n credentialStore: OAuthCredentialStore\n): Promise<OAuthFlowResult | undefined> {\n if (!credentials.clientInfo || !credentials.tokens?.refresh_token) {\n return undefined;\n }\n\n logger.info(\"Attempting to refresh expired access token\", {\n clientId: credentials.clientInfo.client_id,\n serverUrl: serverUrl.toString(),\n });\n\n try {\n // Discover authorization server metadata\n let resourceMetadata: OAuthProtectedResourceMetadata | undefined;\n try {\n resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl);\n } catch {\n // Continue without resource metadata\n }\n\n let authServerUrl: URL;\n if (resourceMetadata?.authorization_servers?.length) {\n authServerUrl = new URL(resourceMetadata.authorization_servers[0]);\n } else {\n authServerUrl = serverUrl;\n }\n\n const authMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n // NOTE: We don't send the resource parameter (same reasoning as in performOAuthFlow)\n\n // Attempt token refresh\n const newTokens = await refreshAuthorization(authServerUrl, {\n clientInformation: credentials.clientInfo,\n metadata: authMetadata,\n refreshToken: credentials.tokens.refresh_token,\n });\n\n logger.info(\"Successfully refreshed access token\", {\n clientId: credentials.clientInfo.client_id,\n expiresIn: newTokens.expires_in,\n hasNewRefreshToken: !!newTokens.refresh_token,\n });\n\n // Persist the new tokens\n const tokensObtainedAt = new Date().toISOString();\n await credentialStore.update(serverUrl.toString(), {\n tokens: newTokens,\n tokensObtainedAt,\n });\n\n return {\n clientInfo: credentials.clientInfo,\n fromCache: true,\n tokens: newTokens,\n };\n } catch (error) {\n logger.warn(\"Failed to refresh access token, will require re-authorization\", {\n error: String(error),\n serverUrl: serverUrl.toString(),\n });\n\n // Invalidate the tokens but keep the client info\n await credentialStore.update(serverUrl.toString(), {\n tokens: undefined,\n tokensObtainedAt: undefined,\n });\n\n return undefined;\n }\n}\n","/**\n * MCP Transport Utilities\n *\n * Simple transport creation for remote MCP servers.\n * Transport type is determined by trying Streamable HTTP first,\n * falling back to SSE if the endpoint doesn't support it.\n */\n\nimport type { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n\nimport { logger } from \"./logger.js\";\n\nexport type TransportType = \"sse\" | \"streamable-http\";\n\n/** Order in which transports are tried (per MCP spec: Streamable HTTP first, SSE fallback) */\nconst TRANSPORT_PRIORITY: TransportType[] = [\"streamable-http\", \"sse\"];\n\n/** Result of a successful transport connection */\nexport interface TransportConnectionResult {\n /** The connected client instance */\n client: Client;\n /** The transport type that successfully connected */\n transportType: TransportType;\n}\n\n/**\n * Connects to a remote MCP server, trying transports in order.\n * Per MCP spec: tries Streamable HTTP first, falls back to SSE if not supported (404/405).\n *\n * Creates a fresh client for each transport attempt to avoid state issues.\n * The MCP SDK Client maintains internal connection state that does not properly\n * reset after a failed connect attempt, so reusing the same client instance\n * across transport fallbacks can cause \"already connected\" or similar errors.\n *\n * @param createClient - Factory function to create a new Client instance\n * @param url - The URL of the remote MCP server\n * @param requestInit - Optional request init for the transport (headers, etc.)\n * @returns The connected client and transport type\n * @throws The error from the last transport attempt if none succeed,\n * or immediately if an error is not a \"transport not supported\" error\n */\nexport async function connectWithTransportFallback(\n createClient: () => Client,\n url: string,\n requestInit?: RequestInit\n): Promise<TransportConnectionResult> {\n let lastError: unknown;\n\n for (const transportType of TRANSPORT_PRIORITY) {\n // Create a fresh client for each transport attempt to avoid state issues\n const client = createClient();\n try {\n const transport = createTransport(url, transportType, requestInit);\n await client.connect(transport);\n return { client, transportType };\n } catch (err) {\n lastError = err;\n\n if (isTransportNotSupportedError(err)) {\n logger.info(`${transportType} not supported, trying next transport...`);\n continue;\n }\n\n // Non-transport error (e.g., 401, network error) - propagate immediately\n throw err;\n }\n }\n\n // All transports failed with \"not supported\" errors\n throw lastError ?? new Error(\"No compatible transport found\");\n}\n\n/**\n * Creates a transport instance for the given type.\n */\nexport function createTransport(\n url: string,\n type: TransportType,\n requestInit?: RequestInit\n): SSEClientTransport | StreamableHTTPClientTransport {\n const parsedUrl = new URL(url);\n\n if (type === \"streamable-http\") {\n return new StreamableHTTPClientTransport(parsedUrl, { requestInit });\n }\n\n return new SSEClientTransport(parsedUrl, { requestInit });\n}\n\n/**\n * Checks if an error indicates the transport/endpoint is not supported (404/405).\n * This means we should try a different transport type.\n */\nexport function isTransportNotSupportedError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n\n const status = (err as { status?: number }).status;\n const code = (err as { code?: number }).code;\n const message = (err as { message?: string }).message;\n\n // Check status codes that indicate endpoint doesn't support this transport\n if (status === 404 || status === 405 || code === 404 || code === 405) {\n return true;\n }\n\n // Check error message for these status codes\n if (message && /404|not found|405|method not allowed/i.test(String(message))) {\n return true;\n }\n\n return false;\n}\n","import { Transform } from \"node:stream\";\n\n/**\n * Filters out lines that do not start with '{' from the input stream.\n * We use this to drop anything that is obviously not a JSON-RPC message.\n */\nexport class JSONFilterTransform extends Transform {\n private buffer = \"\";\n\n constructor() {\n super({ objectMode: false });\n }\n\n _flush(callback: (error: Error | null, chunk: Buffer | null) => void) {\n // Handle any remaining data in buffer\n if (this.buffer.trim().startsWith(\"{\")) {\n callback(null, Buffer.from(this.buffer));\n } else {\n callback(null, null);\n }\n }\n\n _transform(chunk: Buffer, _encoding: string, callback: (error: Error | null, chunk: Buffer | null) => void) {\n this.buffer += chunk.toString();\n const lines = this.buffer.split(\"\\n\");\n\n // Keep the last incomplete line in the buffer\n this.buffer = lines.pop() || \"\";\n\n // Filter lines that start with '{' - silently ignore non-JSON lines\n const jsonLines = lines.filter((line) => line.trim().startsWith(\"{\"));\n\n if (jsonLines.length > 0) {\n // Send filtered lines with newlines\n const output = `${jsonLines.join(\"\\n\")}\\n`;\n\n callback(null, Buffer.from(output));\n } else {\n callback(null, null);\n }\n }\n}\n","/**\n * Forked from https://github.com/modelcontextprotocol/typescript-sdk/blob/a1608a6513d18eb965266286904760f830de96fe/src/client/stdio.ts\n */\n\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\n\nimport {\n ReadBuffer,\n serializeMessage,\n} from \"@modelcontextprotocol/sdk/shared/stdio.js\";\nimport { type ChildProcess, type IOType, spawn } from \"node:child_process\";\nimport { PassThrough, type Stream } from \"node:stream\";\n\nimport { JSONFilterTransform } from \"./JSONFilterTransform.js\";\nimport { ProcessSpawnError, TransportError } from \"./lib/errors.js\";\nimport { addBreadcrumbSafe, captureExceptionSafe } from \"./lib/sentry.js\";\n\nexport type StdioServerParameters = {\n\t/**\n\t * Command line arguments to pass to the executable.\n\t */\n\targs?: string[];\n\n\t/**\n\t * The executable to run to start the server.\n\t */\n\tcommand: string;\n\n\t/**\n\t * The working directory to use when spawning the process.\n\t *\n\t * If not specified, the current working directory will be inherited.\n\t */\n\tcwd?: string;\n\n\t/**\n\t * The environment to use when spawning the process.\n\t *\n\t * If not specified, the result of getDefaultEnvironment() will be used.\n\t */\n\tenv: Record<string, string>;\n\n\t/**\n\t * A function to call when an event occurs.\n\t */\n\tonEvent?: (event: TransportEvent) => void;\n\n\t/**\n\t * When true, spawn the child process using the user's shell.\n\t */\n\tshell?: boolean;\n\n\t/**\n\t * How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.\n\t *\n\t * The default is \"inherit\", meaning messages to stderr will be printed to the parent process's stderr.\n\t */\n\tstderr?: IOType | number | Stream;\n};\n\ntype TransportEvent =\n\t| {\n\t\t\tchunk: string;\n\t\t\ttype: \"data\";\n\t }\n\t| {\n\t\t\terror: Error;\n\t\t\ttype: \"error\";\n\t }\n\t| {\n\t\t\tmessage: JSONRPCMessage;\n\t\t\ttype: \"message\";\n\t }\n\t| {\n\t\t\ttype: \"close\";\n\t };\n\n/**\n * Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.\n *\n * This transport is only available in Node.js environments.\n */\nexport class StdioClientTransport implements Transport {\n\tonclose?: () => void;\n\n\tonerror?: (error: Error) => void;\n\tonmessage?: (message: JSONRPCMessage) => void;\n\t/**\n\t * The child process pid spawned by this transport.\n\t *\n\t * This is only available after the transport has been started.\n\t */\n\tget pid(): null | number {\n\t\treturn this._process?.pid ?? null;\n\t}\n\t/**\n\t * The stderr stream of the child process, if `StdioServerParameters.stderr` was set to \"pipe\" or \"overlapped\".\n\t *\n\t * If stderr piping was requested, a PassThrough stream is returned _immediately_, allowing callers to\n\t * attach listeners before the start method is invoked. This prevents loss of any early\n\t * error output emitted by the child process.\n\t */\n\tget stderr(): null | Stream {\n\t\tif (this._stderrStream) {\n\t\t\treturn this._stderrStream;\n\t\t}\n\n\t\treturn this._process?.stderr ?? null;\n\t}\n\tprivate _abortController: AbortController = new AbortController();\n\tprivate _process?: ChildProcess;\n\tprivate _readBuffer: ReadBuffer = new ReadBuffer();\n\tprivate _serverParams: StdioServerParameters;\n\tprivate _stderrStream: null | PassThrough = null;\n\n\tprivate onEvent?: (event: TransportEvent) => void;\n\n\tconstructor(server: StdioServerParameters) {\n\t\tthis._serverParams = server;\n\t\tif (server.stderr === \"pipe\" || server.stderr === \"overlapped\") {\n\t\t\tthis._stderrStream = new PassThrough();\n\t\t}\n\t\tthis.onEvent = server.onEvent;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.onEvent?.({\n\t\t\ttype: \"close\",\n\t\t});\n\n\t\tthis._abortController.abort();\n\t\tthis._process = undefined;\n\t\tthis._readBuffer.clear();\n\t}\n\n\tsend(message: JSONRPCMessage): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (!this._process?.stdin) {\n\t\t\t\tthrow new Error(\"Not connected\");\n\t\t\t}\n\n\t\t\tconst json = serializeMessage(message);\n\t\t\tif (this._process.stdin.write(json)) {\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\tthis._process.stdin.once(\"drain\", resolve);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Starts the server process and prepares to communicate with it.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this._process) {\n\t\t\tthrow new Error(\n\t\t\t\t\"StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.\",\n\t\t\t);\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis._process = spawn(\n\t\t\t\tthis._serverParams.command,\n\t\t\t\tthis._serverParams.args ?? [],\n\t\t\t\t{\n\t\t\t\t\tcwd: this._serverParams.cwd,\n\t\t\t\t\tenv: this._serverParams.env,\n\t\t\t\t\tshell: this._serverParams.shell ?? false,\n\t\t\t\t\tsignal: this._abortController.signal,\n\t\t\t\t\tstdio: [\"pipe\", \"pipe\", this._serverParams.stderr ?? \"inherit\"],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tthis._process.on(\"error\", (error) => {\n\t\t\t\tif (error.name === \"AbortError\") {\n\t\t\t\t\t// Expected when close() is called.\n\t\t\t\t\tthis.onclose?.();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Capture spawn errors with context\n\t\t\t\tconst spawnError = new ProcessSpawnError(\n\t\t\t\t\t`Process spawn error: ${error.message}`,\n\t\t\t\t\tthis._serverParams.command,\n\t\t\t\t\tthis._serverParams.args,\n\t\t\t\t\t{\n\t\t\t\t\t\tcwd: this._serverParams.cwd,\n\t\t\t\t\t\terrorCode: (error as NodeJS.ErrnoException).code,\n\t\t\t\t\t\terrorErrno: (error as NodeJS.ErrnoException).errno,\n\t\t\t\t\t\terrorSyscall: (error as NodeJS.ErrnoException).syscall,\n\t\t\t\t\t\tshell: this._serverParams.shell,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(spawnError);\n\n\t\t\t\treject(error);\n\t\t\t\tthis.onerror?.(error);\n\t\t\t});\n\n\t\t\tthis._process.on(\"spawn\", () => {\n\t\t\t\taddBreadcrumbSafe(\"Process spawned successfully\", \"transport\", \"info\", {\n\t\t\t\t\targCount: this._serverParams.args?.length,\n\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\t\t\tthis._process.on(\"close\", (_code) => {\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\ttype: \"close\",\n\t\t\t\t});\n\n\t\t\t\tthis._process = undefined;\n\t\t\t\tthis.onclose?.();\n\t\t\t});\n\n\t\t\tthis._process.stdin?.on(\"error\", (error) => {\n\t\t\t\tconst transportError = new TransportError(\n\t\t\t\t\t`Stdin error: ${error.message}`,\n\t\t\t\t\t\"stdio\",\n\t\t\t\t\t\"write\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(transportError);\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\terror,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t});\n\n\t\t\t\tthis.onerror?.(error);\n\t\t\t});\n\n\t\t\tconst jsonFilterTransform = new JSONFilterTransform();\n\n\t\t\tthis._process.stdout?.pipe(jsonFilterTransform);\n\n\t\t\tjsonFilterTransform.on(\"data\", (chunk) => {\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\tchunk: chunk.toString(),\n\t\t\t\t\ttype: \"data\",\n\t\t\t\t});\n\n\t\t\t\tthis._readBuffer.append(chunk);\n\t\t\t\tthis.processReadBuffer();\n\t\t\t});\n\n\t\t\tjsonFilterTransform.on(\"error\", (error) => {\n\t\t\t\tconst transportError = new TransportError(\n\t\t\t\t\t`JSON filter error: ${error.message}`,\n\t\t\t\t\t\"stdio\",\n\t\t\t\t\t\"parse\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(transportError);\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\terror,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t});\n\n\t\t\t\tthis.onerror?.(error);\n\t\t\t});\n\n\t\t\tif (this._stderrStream && this._process.stderr) {\n\t\t\t\tthis._process.stderr.pipe(this._stderrStream);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate processReadBuffer() {\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tconst message = this._readBuffer.readMessage();\n\n\t\t\t\tif (message === null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\tmessage,\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t});\n\n\t\t\t\tthis.onmessage?.(message);\n\t\t\t} catch (error) {\n\t\t\t\tlet message: JSONRPCMessage | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tmessage = this._readBuffer.readMessage();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore error when trying to read message for context\n\t\t\t\t}\n\n\t\t\t\tconst transportError = new TransportError(\n\t\t\t\t\t`Message processing error: ${(error as Error).message}`,\n\t\t\t\t\t\"stdio\",\n\t\t\t\t\t\"process\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\t\tmessageType: message ? typeof message : \"unknown\",\n\t\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(transportError);\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\terror: error as Error,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t});\n\n\t\t\t\tthis.onerror?.(error as Error);\n\t\t\t}\n\t\t}\n\t}\n}\n","import { setTimeout } from \"node:timers\";\nimport util from \"node:util\";\nimport { UnauthorizedError } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { EventSource } from \"eventsource\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nimport { AccessControlAuthorizer } from \"../accessControl/authorizer.js\";\nimport { AccessControlClient } from \"../accessControl/client.js\";\nimport { OAuthCredentialStore, performOAuthFlow } from \"../auth/index.js\";\nimport { config } from \"../config/env.js\";\nimport { AccessControlBlockError, ConfigurationError, ProxyConnectionError } from \"../lib/errors.js\";\nimport { logger } from \"../lib/logger.js\";\nimport { addBreadcrumbSafe, addGlobalTags, captureExceptionSafe, initSentrySafe } from \"../lib/sentry.js\";\nimport { initializeTrafficMirror } from \"../lib/trafficMirror.js\";\nimport { connectWithTransportFallback, type TransportType } from \"../lib/transportDetection.js\";\nimport { proxyServer } from \"../proxyServer.js\";\nimport { StdioClientTransport } from \"../StdioClientTransport.js\";\nimport { startHTTPServer } from \"../startHTTPServer.js\";\nimport { getSessionData, type RemoteSessionCliArgs, type StdioSessionCliArgs } from \"../utils/getUserData.js\";\nimport { normalizeUrl } from \"../utils/normalizeUrl.js\";\n\nutil.inspect.defaultOptions.depth = 8;\n\nif (!(\"EventSource\" in global)) {\n // @ts-expect-error - figure out how to use --experimental-eventsource with vitest\n global.EventSource = EventSource;\n}\n\n// Export ProxyType for use by other modules\nexport type ProxyType = \"remote\" | \"stdio\";\n\nexport const main = async () => {\n const argv = await yargs(hideBin(process.argv))\n .scriptName(\"mcp-gateway\")\n .command(\"stdio [command] [args...]\", \"Run a local command with stdio transport (default)\", (yargs) => {\n return yargs\n .positional(\"command\", {\n describe: \"The command to run\",\n type: \"string\",\n })\n .positional(\"args\", {\n array: true,\n describe: \"The arguments to pass to the command\",\n type: \"string\",\n });\n })\n .command(\"remote\", \"Connect to a remote MCP server\", (yargs) => {\n return yargs.options({\n headers: {\n describe: \"Headers for remote authentication (format: key:value)\",\n string: true,\n type: \"array\",\n },\n \"remote-transport\": {\n choices: [\"sse\", \"stream\"],\n deprecated: \"Transport is now auto-detected per MCP spec. This option is ignored.\",\n describe: \"(Deprecated) The transport type - now auto-detected\",\n type: \"string\",\n },\n url: {\n demandOption: true,\n describe: \"The URL of the remote MCP server\",\n type: \"string\",\n },\n });\n })\n .command(\"$0 [command] [args...]\", \"Run a command with MCP arguments (backward compatible)\", (yargs) => {\n return yargs\n .positional(\"command\", {\n describe: \"The command to run\",\n type: \"string\",\n })\n .positional(\"args\", {\n array: true,\n describe: \"The arguments to pass to the command\",\n type: \"string\",\n });\n })\n .env(\"MCP_GATEWAY\")\n .parserConfiguration({\n \"populate--\": true,\n })\n .options({\n // NEW: Scanner/Gateway configuration CLI args (these override env vars)\n // Note: These are pre-parsed in mcp-gateway.ts and set as env vars before this file loads\n \"access-control-url\": {\n describe: \"URL of the access control service (overrides MCP_GATEWAY_ACCESS_CONTROL_URL)\",\n type: \"string\",\n },\n debug: {\n default: false,\n describe: \"Enable debug logging\",\n type: \"boolean\",\n },\n env: {\n describe: \"Environment variables to pass to the command (format: KEY=value)\",\n string: true,\n type: \"array\",\n },\n gracefulShutdownTimeout: {\n default: 5000,\n describe: \"The timeout (in milliseconds) for graceful shutdown\",\n type: \"number\",\n },\n port: {\n describe: \"Run as HTTP server on the specified port instead of stdio\",\n type: \"number\",\n },\n \"scanner-api-key\": {\n describe: \"API key for scanner authentication (overrides MCP_GATEWAY_SCANNER_API_KEY)\",\n type: \"string\",\n },\n \"scanner-fail-open\": {\n describe: \"Allow requests when scanner is unavailable (overrides MCP_GATEWAY_SCANNER_FAIL_OPEN)\",\n type: \"boolean\",\n },\n \"scanner-timeout-ms\": {\n describe: \"Scanner request timeout in milliseconds (overrides MCP_GATEWAY_SCANNER_TIMEOUT_MS)\",\n type: \"number\",\n },\n \"scanner-url\": {\n describe: \"URL of the scanner/evaluator service (overrides MCP_GATEWAY_SCANNER_URL)\",\n type: \"string\",\n },\n shell: {\n default: process.platform === \"win32\",\n describe: \"Spawn the server via the user's shell (defaults to true on Windows)\",\n type: \"boolean\",\n },\n })\n .help()\n .parseAsync();\n\n // Determine the proxy type based on the command\n let proxyType: ProxyType;\n let finalCommand: string | undefined;\n let finalArgs: string[] | undefined;\n let remoteUrl: string | undefined;\n // Transport type used for connection (for logging)\n let usedTransport: TransportType | undefined;\n const remoteHeaders: Record<string, string> = {};\n\n // Parse headers if provided\n if (argv.headers) {\n for (const header of argv.headers as string[]) {\n const [key, ...valueParts] = header.split(\":\");\n if (key && valueParts.length > 0) {\n remoteHeaders[key.trim()] = valueParts.join(\":\").trim();\n }\n }\n }\n\n // Initialize traffic scanner from config\n const scannerHeaders = { ...config.scanner.headers };\n\n logger.debug(\"Initializing traffic scanner\", {\n failOpen: config.scanner.failOpen,\n hasApiKey: !!config.scanner.apiKey,\n scannerEnabled: config.scanner.enabled,\n scannerUrl: config.scanner.url,\n timeoutMs: config.scanner.timeoutMs,\n });\n\n // Determine proxy type based on command\n if (argv._[0] === \"remote\") {\n proxyType = \"remote\";\n\n // Warn if deprecated remote-transport argument is used\n if (argv[\"remote-transport\"]) {\n logger.warn(\n \"The --remote-transport option is deprecated and will be ignored. \" +\n \"Transport is now auto-detected per MCP spec (Streamable HTTP with SSE fallback).\",\n { providedTransport: argv[\"remote-transport\"] }\n );\n }\n\n const rawUrl = argv.url as string;\n if (!rawUrl) {\n throw new ConfigurationError(\"Remote URL is required for remote proxy type\", \"url\");\n }\n // Normalize URL to avoid redirect issues on Windows\n // Windows may convert POST to GET on 301/302 redirects (e.g., trailing slash differences)\n remoteUrl = normalizeUrl(rawUrl);\n if (remoteUrl !== rawUrl) {\n logger.debug(\"Normalized remote URL\", { normalizedUrl: remoteUrl, originalUrl: rawUrl });\n }\n } else {\n // Default to stdio for backward compatibility\n proxyType = \"stdio\";\n\n // If -- separator was used, first item in argv[\"--\"] is the command\n if (argv[\"--\"] && (argv[\"--\"] as string[]).length > 0) {\n const dashDashArgs = argv[\"--\"] as string[];\n finalCommand = dashDashArgs[0];\n finalArgs = dashDashArgs.slice(1);\n } else {\n // Use positional arguments\n finalCommand = argv.command as string;\n finalArgs = (argv.args as string[]) || [];\n }\n\n if (!finalCommand) {\n throw new ConfigurationError(\"No command specified for stdio proxy\", \"command\");\n }\n }\n\n // Parse environment variables\n const customEnv: Record<string, string> = {};\n if (argv.env) {\n for (const envVar of argv.env as string[]) {\n const [key, ...valueParts] = envVar.split(\"=\");\n if (key && valueParts.length > 0) {\n customEnv[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n }\n\n // Build session data and initialize access control client if configured\n let accessControlClient: AccessControlClient | undefined;\n if (config.accessControl.url && config.scanner.apiKey) {\n const sessionDataBase = await getSessionData();\n\n const sessionData =\n proxyType === \"stdio\"\n ? {\n ...sessionDataBase,\n proxyType: \"stdio\" as const,\n stdioCliArgs: {\n args: finalArgs as string[],\n command: finalCommand as string,\n proxyType: \"stdio\" as const,\n },\n }\n : {\n ...sessionDataBase,\n proxyType: \"remote\" as const,\n remoteCliArgs: {\n proxyType: \"remote\" as const,\n url: remoteUrl as string,\n },\n };\n\n accessControlClient = new AccessControlClient({\n apiKey: config.scanner.apiKey,\n headers: config.accessControl.headers,\n sessionData,\n timeoutMs: config.accessControl.timeoutMs,\n url: config.accessControl.url,\n });\n }\n\n // Determine server name for error messages\n const serverName = proxyType === \"remote\" ? remoteUrl : finalCommand;\n\n // Initialize access control authorizer\n const accessControlAuthorizer = new AccessControlAuthorizer({\n client: accessControlClient,\n enabled: config.accessControl.enabled,\n serverName,\n });\n\n // Initialize OAuth credential store for remote connections\n const oauthCredentialStore = new OAuthCredentialStore({\n storageDir: config.oauthDir,\n });\n\n // Factory function to create a new Client instance\n const createClient = (): Client =>\n new Client(\n {\n name: \"mcp-gateway\",\n version: config.appVersion,\n },\n {\n capabilities: {},\n }\n );\n\n const connect = async (client: Client): Promise<Client> => {\n // Check access control (async - may query backend)\n const isAllowed = await accessControlAuthorizer.isAllowed();\n if (!isAllowed) {\n const reason = accessControlAuthorizer.getBlockReason();\n logger.warn(\"MCP server blocked by access control\", {\n proxyType,\n reason,\n });\n\n throw new AccessControlBlockError(reason, \"policy_block\");\n }\n\n logger.info(\"MCP server allowed by access control\");\n\n if (proxyType === \"stdio\") {\n // Merge custom CLI environment variables with process.env for child processes\n // Note: This is for MCP server processes, not the proxy's internal configuration\n const mergedEnv = {\n ...process.env,\n ...customEnv,\n } as Record<string, string>;\n\n const transport = new StdioClientTransport({\n args: finalArgs,\n command: finalCommand as string,\n env: mergedEnv,\n onEvent: (event) => {\n if (argv.debug) {\n logger.debug(\"Transport event\", { event });\n }\n },\n shell: argv.shell,\n // We want to passthrough stderr from the MCP server to enable better debugging\n stderr: \"inherit\",\n });\n\n try {\n await client.connect(transport);\n return client;\n } catch (error) {\n throw new ProxyConnectionError(`Failed to connect to stdio process: ${error}`, undefined, \"stdio\", {\n argCount: finalArgs?.length,\n command: finalCommand,\n });\n }\n } else if (proxyType === \"remote\") {\n // Connect to remote server\n // Strategy: Try Streamable HTTP first, fall back to SSE if not supported (404/405)\n // Handle OAuth (401) separately - if auth fails, get token and retry the whole process\n\n const url = remoteUrl as string;\n\n // Build request init with headers and optional auth token\n const buildRequestInit = (authToken?: string): RequestInit | undefined => {\n const headers: Record<string, string> = { ...remoteHeaders };\n if (authToken) {\n headers.Authorization = `Bearer ${authToken}`;\n }\n return Object.keys(headers).length > 0 ? { headers } : undefined;\n };\n\n // Check if error is a 401 (requires OAuth)\n const is401Error = (err: unknown): boolean => {\n if (err instanceof UnauthorizedError) return true;\n if (err && typeof err === \"object\") {\n const status = (err as { status?: number }).status;\n const code = (err as { code?: number }).code;\n const message = (err as { message?: string }).message;\n if (status === 401 || code === 401) return true;\n if (message && /unauthorized|401/i.test(String(message))) return true;\n }\n return false;\n };\n\n // Get OAuth token (from cache or via flow)\n const getOAuthToken = async (): Promise<string> => {\n const oauthResult = await performOAuthFlow(url, { credentialStore: oauthCredentialStore });\n logger.info(oauthResult.fromCache ? \"Using cached OAuth credentials\" : \"OAuth flow completed successfully\");\n return oauthResult.tokens.access_token;\n };\n\n let connectedClient: Client;\n try {\n // Check for cached OAuth token\n const cached = await oauthCredentialStore.load(url);\n let authToken =\n cached && oauthCredentialStore.hasValidAccessToken(cached) ? cached.tokens?.access_token : undefined;\n let usedFreshOAuth = false; // Track if we've already tried with a fresh token\n\n try {\n // Try connecting with transport fallback (Streamable HTTP → SSE)\n // Uses a client factory to create fresh clients for each transport attempt\n const result = await connectWithTransportFallback(createClient, url, buildRequestInit(authToken));\n connectedClient = result.client;\n usedTransport = result.transportType;\n logger.info(\"Connected to remote server\", { hasAuth: !!authToken, transport: usedTransport });\n } catch (err) {\n // Handle 401 errors - need OAuth authentication or token refresh\n if (is401Error(err)) {\n // If we already tried with a fresh OAuth token, don't retry again (avoid infinite loop)\n if (usedFreshOAuth) {\n throw err;\n }\n\n // If cached token was rejected, invalidate it so performOAuthFlow doesn't return it again\n if (authToken) {\n logger.info(\"Cached OAuth token rejected (possibly revoked), invalidating and re-authorizing...\");\n await oauthCredentialStore.update(url, {\n tokens: undefined,\n tokensObtainedAt: undefined,\n });\n } else {\n logger.info(\"Server requires OAuth, starting authentication flow...\");\n }\n\n authToken = await getOAuthToken();\n usedFreshOAuth = true;\n const result = await connectWithTransportFallback(createClient, url, buildRequestInit(authToken));\n connectedClient = result.client;\n usedTransport = result.transportType;\n logger.info(\"Connected to remote server with OAuth\", { transport: usedTransport });\n } else {\n throw err;\n }\n }\n return connectedClient;\n } catch (error) {\n if (error instanceof ProxyConnectionError) throw error;\n throw new ProxyConnectionError(\n `Failed to connect to remote server: ${error instanceof Error ? error.message : String(error)}`,\n url,\n usedTransport,\n { headerCount: Object.keys(remoteHeaders).length }\n );\n }\n }\n\n // Should never reach here, but satisfy TypeScript\n return client;\n };\n\n const proxy = async () => {\n // If port is specified, start HTTP server mode\n if (argv.port) {\n const cliArgs =\n proxyType === \"stdio\"\n ? ({\n args: finalArgs as string[],\n command: finalCommand as string,\n proxyType,\n } as StdioSessionCliArgs)\n : ({\n proxyType,\n url: remoteUrl as string,\n } as RemoteSessionCliArgs);\n\n // Initialize traffic evaluator with scanner config once for HTTP server mode\n await initializeTrafficMirror({\n cliArgs,\n scanApiKey: config.scanner.apiKey,\n // Scanner config from env vars\n scanEnabled: config.scanner.enabled,\n scanFailOpen: config.scanner.failOpen,\n scanHeaders: scannerHeaders,\n scanTimeoutMs: config.scanner.timeoutMs,\n scanUrl: config.scanner.url || \"\",\n });\n\n let client: Client | undefined;\n const httpServer = await startHTTPServer({\n createServer: async () => {\n // Create initial client, but connect() may return a different instance for remote connections\n const initialClient = createClient();\n client = await connect(initialClient);\n\n const serverVersion = client.getServerVersion() as {\n name: string;\n version: string;\n };\n\n const serverCapabilities = client.getServerCapabilities() as {\n capabilities: Record<string, unknown>;\n };\n\n const server = new Server(serverVersion, {\n capabilities: serverCapabilities,\n });\n\n proxyServer({\n authorizer: accessControlAuthorizer,\n client,\n server,\n serverCapabilities,\n });\n\n return server;\n },\n onClose: async (server) => {\n if (client) {\n await client.close();\n }\n await server.close();\n },\n port: argv.port,\n });\n\n logger.info(\"MCP gateway started as HTTP server\", { port: argv.port, proxyType });\n if (proxyType === \"stdio\") {\n logger.info(\"Running command\", { args: finalArgs?.join(\" \") || \"\", command: finalCommand });\n } else {\n logger.info(\"Remote server configured (connection deferred until client connects)\", { remoteUrl });\n }\n\n return {\n close: () => {\n return httpServer.close();\n },\n };\n }\n\n // Original stdio mode\n // Create initial client, but connect() may return a different instance for remote connections\n const initialClient = createClient();\n const client = await connect(initialClient);\n\n const serverVersion = client.getServerVersion() as {\n name: string;\n version: string;\n };\n\n const serverCapabilities = client.getServerCapabilities() as {\n capabilities: Record<string, unknown>;\n };\n\n // Always create a stdio server for client-facing interface\n const server = new Server(serverVersion, {\n capabilities: serverCapabilities,\n });\n\n proxyServer({\n authorizer: accessControlAuthorizer,\n client,\n server,\n serverCapabilities,\n });\n\n const stdioTransport = new StdioServerTransport();\n\n const cliArgs =\n proxyType === \"stdio\"\n ? ({\n args: finalArgs as string[],\n command: finalCommand as string,\n proxyType,\n } as StdioSessionCliArgs)\n : ({\n proxyType,\n url: remoteUrl as string,\n } as RemoteSessionCliArgs);\n\n // Initialize traffic evaluator with scanner config\n await initializeTrafficMirror({\n cliArgs,\n scanApiKey: config.scanner.apiKey,\n // Scanner config from env vars\n scanEnabled: config.scanner.enabled,\n scanFailOpen: config.scanner.failOpen,\n scanHeaders: scannerHeaders,\n scanTimeoutMs: config.scanner.timeoutMs,\n scanUrl: config.scanner.url || \"\",\n });\n\n await server.connect(stdioTransport);\n\n logger.info(\"MCP gateway started\", { proxyType });\n if (proxyType === \"stdio\") {\n logger.info(\"Running command\", { args: finalArgs?.join(\" \") || \"\", command: finalCommand });\n } else {\n logger.info(\"Connected to remote server\", { remoteUrl, transport: usedTransport });\n }\n\n return {\n close: () => {\n // Close the client connection\n return client.close();\n },\n };\n };\n\n const createGracefulShutdown = ({\n server,\n timeout,\n }: {\n server: { close: () => Promise<void> | void };\n timeout: number;\n }) => {\n const gracefulShutdown = async () => {\n logger.info(\"Received shutdown signal; shutting down\");\n\n try {\n await server.close();\n process.exit(0);\n } catch (error) {\n const errorMessage = `Error during shutdown: ${error}`;\n logger.error(\"Error during shutdown\", { error: errorMessage });\n\n captureExceptionSafe(\n error instanceof Error ? error : new Error(errorMessage),\n {\n shutdownTimeout: timeout,\n },\n {\n feature: \"shutdown\",\n module: \"cli\",\n operation: \"close\",\n }\n );\n\n process.exit(1);\n }\n };\n\n const timeoutHandler = async () => {\n setTimeout(() => {\n logger.error(\"Graceful shutdown timeout exceeded, forcing exit\");\n process.exit(1);\n }, timeout).unref();\n\n await gracefulShutdown();\n };\n\n process.once(\"SIGTERM\", timeoutHandler);\n process.once(\"SIGINT\", timeoutHandler);\n\n return () => {\n return server.close();\n };\n };\n\n // Initialize Sentry before any other operations\n initSentrySafe();\n\n // Add global context for all Sentry events\n addGlobalTags({\n \"proxy.mode\": proxyType === \"stdio\" ? \"local\" : \"remote\",\n \"proxy.transport\": proxyType === \"remote\" ? \"auto-detect\" : \"stdio\",\n \"proxy.type\": proxyType,\n });\n\n addBreadcrumbSafe(\"MCP Gateway starting\", \"lifecycle\", \"info\", {\n command: finalCommand,\n proxyType,\n transport: proxyType === \"remote\" ? \"auto-detect\" : \"stdio\",\n url: remoteUrl,\n });\n\n try {\n const server = await proxy();\n\n createGracefulShutdown({\n server,\n timeout: argv.gracefulShutdownTimeout,\n });\n } catch (error) {\n // Handle access control blocks gracefully\n if (error instanceof AccessControlBlockError) {\n // Show user-visible error message\n console.error(\"\\n❌ ACCESS DENIED\");\n console.error(\"━\".repeat(60));\n console.error(\n \"\\nThis MCP server has been blocked by Onyx because it is not authorized by your organization's access control policy. Please contact your administrator if you believe this is an error.\\n\"\n );\n\n // Also log for debugging\n logger.error(\"MCP server blocked by access control policy\", {\n error: error.message,\n reason: error.reason,\n });\n\n // Give logs time to flush before exiting cleanly\n setTimeout(() => {\n process.exit(1);\n }, 1000);\n return;\n }\n\n // Handle unexpected errors\n const errorMessage = `Could not start the proxy: ${error}`;\n logger.error(\"Unhandled error occurred\", { error: errorMessage });\n\n // Capture the startup error with context\n if (error instanceof Error) {\n captureExceptionSafe(\n error,\n {\n args: finalArgs,\n command: finalCommand,\n proxyType,\n transport: usedTransport,\n url: remoteUrl,\n },\n {\n feature: \"startup\",\n module: \"cli\",\n operation: \"initialize\",\n proxyType,\n transport: proxyType === \"remote\" ? usedTransport || \"unknown\" : \"stdio\",\n }\n );\n } else {\n captureExceptionSafe(\n new Error(errorMessage),\n {\n command: finalCommand,\n originalError: String(error),\n proxyType,\n },\n {\n feature: \"startup\",\n module: \"cli\",\n operation: \"initialize\",\n }\n );\n }\n\n // We give an extra second for logs to flush\n setTimeout(() => {\n process.exit(1);\n }, 1000);\n }\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}
1
+ {"version":3,"file":"main-J71qLHRW.js","names":["config","config","command: string","args: string[]","resolveCallback: (result: CallbackServerResult) => void","rejectCallback: (error: Error) => void","timeoutRef: { id?: ReturnType<typeof setTimeout> }","boundPort: number","URL","attemptedPorts: number[]","lastError: Error | undefined","portToTry: number","credentials: StoredCredentials","OAUTH_PROVIDERS: OAuthProviderConfig[]","hostname","resourceMetadata: OAuthProtectedResourceMetadata | undefined","authServerUrl: URL","clientInfo: OAuthClientInformationFull","clientMetadata: OAuthClientMetadata","headers: Record<string, string>","tokenData: Record<string, unknown>","codeVerifier: string | undefined","codeChallenge: string | undefined","codeChallengeMethod: string | undefined","TRANSPORT_PRIORITY: TransportType[]","lastError: unknown","message: JSONRPCMessage | null","yargs","proxyType: ProxyType","finalCommand: string | undefined","finalArgs: string[] | undefined","remoteUrl: string | undefined","usedTransport: TransportType | undefined","remoteHeaders: Record<string, string>","customEnv: Record<string, string>","accessControlClient: AccessControlClient | undefined","headers: Record<string, string>","connectedClient: Client","client: Client | undefined","initialClient","serverVersion","client","serverCapabilities","server"],"sources":["../node_modules/eventsource/dist/index.js","../src/accessControl/authorizer.ts","../src/accessControl/client.ts","../src/auth/browser.ts","../src/auth/callbackServer.ts","../src/auth/OAuthCredentialStore.ts","../src/auth/providers.ts","../src/auth/oauthFlow.ts","../src/lib/transportDetection.ts","../src/JSONFilterTransform.ts","../src/StdioClientTransport.ts","../src/bin/main.ts"],"sourcesContent":["import { createParser } from \"eventsource-parser\";\nclass ErrorEvent extends Event {\n /**\n * Constructs a new `ErrorEvent` instance. This is typically not called directly,\n * but rather emitted by the `EventSource` object when an error occurs.\n *\n * @param type - The type of the event (should be \"error\")\n * @param errorEventInitDict - Optional properties to include in the error event\n */\n constructor(type, errorEventInitDict) {\n var _a, _b;\n super(type), this.code = (_a = errorEventInitDict == null ? void 0 : errorEventInitDict.code) != null ? _a : void 0, this.message = (_b = errorEventInitDict == null ? void 0 : errorEventInitDict.message) != null ? _b : void 0;\n }\n /**\n * Node.js \"hides\" the `message` and `code` properties of the `ErrorEvent` instance,\n * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,\n * we explicitly include the properties in the `inspect` method.\n *\n * This is automatically called by Node.js when you `console.log` an instance of this class.\n *\n * @param _depth - The current depth\n * @param options - The options passed to `util.inspect`\n * @param inspect - The inspect function to use (prevents having to import it from `util`)\n * @returns A string representation of the error\n */\n [Symbol.for(\"nodejs.util.inspect.custom\")](_depth, options, inspect) {\n return inspect(inspectableError(this), options);\n }\n /**\n * Deno \"hides\" the `message` and `code` properties of the `ErrorEvent` instance,\n * when it is `console.log`'ed. This makes it harder to debug errors. To ease debugging,\n * we explicitly include the properties in the `inspect` method.\n *\n * This is automatically called by Deno when you `console.log` an instance of this class.\n *\n * @param inspect - The inspect function to use (prevents having to import it from `util`)\n * @param options - The options passed to `Deno.inspect`\n * @returns A string representation of the error\n */\n [Symbol.for(\"Deno.customInspect\")](inspect, options) {\n return inspect(inspectableError(this), options);\n }\n}\nfunction syntaxError(message) {\n const DomException = globalThis.DOMException;\n return typeof DomException == \"function\" ? new DomException(message, \"SyntaxError\") : new SyntaxError(message);\n}\nfunction flattenError(err) {\n return err instanceof Error ? \"errors\" in err && Array.isArray(err.errors) ? err.errors.map(flattenError).join(\", \") : \"cause\" in err && err.cause instanceof Error ? `${err}: ${flattenError(err.cause)}` : err.message : `${err}`;\n}\nfunction inspectableError(err) {\n return {\n type: err.type,\n message: err.message,\n code: err.code,\n defaultPrevented: err.defaultPrevented,\n cancelable: err.cancelable,\n timeStamp: err.timeStamp\n };\n}\nvar __typeError = (msg) => {\n throw TypeError(msg);\n}, __accessCheck = (obj, member, msg) => member.has(obj) || __typeError(\"Cannot \" + msg), __privateGet = (obj, member, getter) => (__accessCheck(obj, member, \"read from private field\"), getter ? getter.call(obj) : member.get(obj)), __privateAdd = (obj, member, value) => member.has(obj) ? __typeError(\"Cannot add the same private member more than once\") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value), __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, \"write to private field\"), member.set(obj, value), value), __privateMethod = (obj, member, method) => (__accessCheck(obj, member, \"access private method\"), method), _readyState, _url, _redirectUrl, _withCredentials, _fetch, _reconnectInterval, _reconnectTimer, _lastEventId, _controller, _parser, _onError, _onMessage, _onOpen, _EventSource_instances, connect_fn, _onFetchResponse, _onFetchError, getRequestOptions_fn, _onEvent, _onRetryChange, failConnection_fn, scheduleReconnect_fn, _reconnect;\nclass EventSource extends EventTarget {\n constructor(url, eventSourceInitDict) {\n var _a, _b;\n super(), __privateAdd(this, _EventSource_instances), this.CONNECTING = 0, this.OPEN = 1, this.CLOSED = 2, __privateAdd(this, _readyState), __privateAdd(this, _url), __privateAdd(this, _redirectUrl), __privateAdd(this, _withCredentials), __privateAdd(this, _fetch), __privateAdd(this, _reconnectInterval), __privateAdd(this, _reconnectTimer), __privateAdd(this, _lastEventId, null), __privateAdd(this, _controller), __privateAdd(this, _parser), __privateAdd(this, _onError, null), __privateAdd(this, _onMessage, null), __privateAdd(this, _onOpen, null), __privateAdd(this, _onFetchResponse, async (response) => {\n var _a2;\n __privateGet(this, _parser).reset();\n const { body, redirected, status, headers } = response;\n if (status === 204) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, \"Server sent HTTP 204, not reconnecting\", 204), this.close();\n return;\n }\n if (redirected ? __privateSet(this, _redirectUrl, new URL(response.url)) : __privateSet(this, _redirectUrl, void 0), status !== 200) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, `Non-200 status code (${status})`, status);\n return;\n }\n if (!(headers.get(\"content-type\") || \"\").startsWith(\"text/event-stream\")) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, 'Invalid content type, expected \"text/event-stream\"', status);\n return;\n }\n if (__privateGet(this, _readyState) === this.CLOSED)\n return;\n __privateSet(this, _readyState, this.OPEN);\n const openEvent = new Event(\"open\");\n if ((_a2 = __privateGet(this, _onOpen)) == null || _a2.call(this, openEvent), this.dispatchEvent(openEvent), typeof body != \"object\" || !body || !(\"getReader\" in body)) {\n __privateMethod(this, _EventSource_instances, failConnection_fn).call(this, \"Invalid response body, expected a web ReadableStream\", status), this.close();\n return;\n }\n const decoder = new TextDecoder(), reader = body.getReader();\n let open = !0;\n do {\n const { done, value } = await reader.read();\n value && __privateGet(this, _parser).feed(decoder.decode(value, { stream: !done })), done && (open = !1, __privateGet(this, _parser).reset(), __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this));\n } while (open);\n }), __privateAdd(this, _onFetchError, (err) => {\n __privateSet(this, _controller, void 0), !(err.name === \"AbortError\" || err.type === \"aborted\") && __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this, flattenError(err));\n }), __privateAdd(this, _onEvent, (event) => {\n typeof event.id == \"string\" && __privateSet(this, _lastEventId, event.id);\n const messageEvent = new MessageEvent(event.event || \"message\", {\n data: event.data,\n origin: __privateGet(this, _redirectUrl) ? __privateGet(this, _redirectUrl).origin : __privateGet(this, _url).origin,\n lastEventId: event.id || \"\"\n });\n __privateGet(this, _onMessage) && (!event.event || event.event === \"message\") && __privateGet(this, _onMessage).call(this, messageEvent), this.dispatchEvent(messageEvent);\n }), __privateAdd(this, _onRetryChange, (value) => {\n __privateSet(this, _reconnectInterval, value);\n }), __privateAdd(this, _reconnect, () => {\n __privateSet(this, _reconnectTimer, void 0), __privateGet(this, _readyState) === this.CONNECTING && __privateMethod(this, _EventSource_instances, connect_fn).call(this);\n });\n try {\n if (url instanceof URL)\n __privateSet(this, _url, url);\n else if (typeof url == \"string\")\n __privateSet(this, _url, new URL(url, getBaseURL()));\n else\n throw new Error(\"Invalid URL\");\n } catch {\n throw syntaxError(\"An invalid or illegal string was specified\");\n }\n __privateSet(this, _parser, createParser({\n onEvent: __privateGet(this, _onEvent),\n onRetry: __privateGet(this, _onRetryChange)\n })), __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _reconnectInterval, 3e3), __privateSet(this, _fetch, (_a = eventSourceInitDict == null ? void 0 : eventSourceInitDict.fetch) != null ? _a : globalThis.fetch), __privateSet(this, _withCredentials, (_b = eventSourceInitDict == null ? void 0 : eventSourceInitDict.withCredentials) != null ? _b : !1), __privateMethod(this, _EventSource_instances, connect_fn).call(this);\n }\n /**\n * Returns the state of this EventSource object's connection. It can have the values described below.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState)\n *\n * Note: typed as `number` instead of `0 | 1 | 2` for compatibility with the `EventSource` interface,\n * defined in the TypeScript `dom` library.\n *\n * @public\n */\n get readyState() {\n return __privateGet(this, _readyState);\n }\n /**\n * Returns the URL providing the event stream.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url)\n *\n * @public\n */\n get url() {\n return __privateGet(this, _url).href;\n }\n /**\n * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to \"include\", and false otherwise.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials)\n */\n get withCredentials() {\n return __privateGet(this, _withCredentials);\n }\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */\n get onerror() {\n return __privateGet(this, _onError);\n }\n set onerror(value) {\n __privateSet(this, _onError, value);\n }\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */\n get onmessage() {\n return __privateGet(this, _onMessage);\n }\n set onmessage(value) {\n __privateSet(this, _onMessage, value);\n }\n /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */\n get onopen() {\n return __privateGet(this, _onOpen);\n }\n set onopen(value) {\n __privateSet(this, _onOpen, value);\n }\n addEventListener(type, listener, options) {\n const listen = listener;\n super.addEventListener(type, listen, options);\n }\n removeEventListener(type, listener, options) {\n const listen = listener;\n super.removeEventListener(type, listen, options);\n }\n /**\n * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED.\n *\n * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close)\n *\n * @public\n */\n close() {\n __privateGet(this, _reconnectTimer) && clearTimeout(__privateGet(this, _reconnectTimer)), __privateGet(this, _readyState) !== this.CLOSED && (__privateGet(this, _controller) && __privateGet(this, _controller).abort(), __privateSet(this, _readyState, this.CLOSED), __privateSet(this, _controller, void 0));\n }\n}\n_readyState = /* @__PURE__ */ new WeakMap(), _url = /* @__PURE__ */ new WeakMap(), _redirectUrl = /* @__PURE__ */ new WeakMap(), _withCredentials = /* @__PURE__ */ new WeakMap(), _fetch = /* @__PURE__ */ new WeakMap(), _reconnectInterval = /* @__PURE__ */ new WeakMap(), _reconnectTimer = /* @__PURE__ */ new WeakMap(), _lastEventId = /* @__PURE__ */ new WeakMap(), _controller = /* @__PURE__ */ new WeakMap(), _parser = /* @__PURE__ */ new WeakMap(), _onError = /* @__PURE__ */ new WeakMap(), _onMessage = /* @__PURE__ */ new WeakMap(), _onOpen = /* @__PURE__ */ new WeakMap(), _EventSource_instances = /* @__PURE__ */ new WeakSet(), /**\n* Connect to the given URL and start receiving events\n*\n* @internal\n*/\nconnect_fn = function() {\n __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _controller, new AbortController()), __privateGet(this, _fetch)(__privateGet(this, _url), __privateMethod(this, _EventSource_instances, getRequestOptions_fn).call(this)).then(__privateGet(this, _onFetchResponse)).catch(__privateGet(this, _onFetchError));\n}, _onFetchResponse = /* @__PURE__ */ new WeakMap(), _onFetchError = /* @__PURE__ */ new WeakMap(), /**\n* Get request options for the `fetch()` request\n*\n* @returns The request options\n* @internal\n*/\ngetRequestOptions_fn = function() {\n var _a;\n const init = {\n // [spec] Let `corsAttributeState` be `Anonymous`…\n // [spec] …will have their mode set to \"cors\"…\n mode: \"cors\",\n redirect: \"follow\",\n headers: { Accept: \"text/event-stream\", ...__privateGet(this, _lastEventId) ? { \"Last-Event-ID\": __privateGet(this, _lastEventId) } : void 0 },\n cache: \"no-store\",\n signal: (_a = __privateGet(this, _controller)) == null ? void 0 : _a.signal\n };\n return \"window\" in globalThis && (init.credentials = this.withCredentials ? \"include\" : \"same-origin\"), init;\n}, _onEvent = /* @__PURE__ */ new WeakMap(), _onRetryChange = /* @__PURE__ */ new WeakMap(), /**\n* Handles the process referred to in the EventSource specification as \"failing a connection\".\n*\n* @param error - The error causing the connection to fail\n* @param code - The HTTP status code, if available\n* @internal\n*/\nfailConnection_fn = function(message, code) {\n var _a;\n __privateGet(this, _readyState) !== this.CLOSED && __privateSet(this, _readyState, this.CLOSED);\n const errorEvent = new ErrorEvent(\"error\", { code, message });\n (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent);\n}, /**\n* Schedules a reconnection attempt against the EventSource endpoint.\n*\n* @param message - The error causing the connection to fail\n* @param code - The HTTP status code, if available\n* @internal\n*/\nscheduleReconnect_fn = function(message, code) {\n var _a;\n if (__privateGet(this, _readyState) === this.CLOSED)\n return;\n __privateSet(this, _readyState, this.CONNECTING);\n const errorEvent = new ErrorEvent(\"error\", { code, message });\n (_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent), __privateSet(this, _reconnectTimer, setTimeout(__privateGet(this, _reconnect), __privateGet(this, _reconnectInterval)));\n}, _reconnect = /* @__PURE__ */ new WeakMap(), /**\n* ReadyState representing an EventSource currently trying to connect\n*\n* @public\n*/\nEventSource.CONNECTING = 0, /**\n* ReadyState representing an EventSource connection that is open (eg connected)\n*\n* @public\n*/\nEventSource.OPEN = 1, /**\n* ReadyState representing an EventSource connection that is closed (eg disconnected)\n*\n* @public\n*/\nEventSource.CLOSED = 2;\nfunction getBaseURL() {\n const doc = \"document\" in globalThis ? globalThis.document : void 0;\n return doc && typeof doc == \"object\" && \"baseURI\" in doc && typeof doc.baseURI == \"string\" ? doc.baseURI : void 0;\n}\nexport {\n ErrorEvent,\n EventSource\n};\n//# sourceMappingURL=index.js.map\n","import type { AccessControlClient } from \"./client.js\";\n\nimport { logger } from \"../lib/logger.js\";\n\nexport interface AccessControlConfig {\n client?: AccessControlClient;\n enabled: boolean;\n serverName?: string;\n}\n\n/**\n * Authorizes MCP servers based on access control configuration.\n * Queries backend with full client info for authorization decisions.\n */\nexport class AccessControlAuthorizer {\n private client?: AccessControlClient;\n private enabled: boolean;\n private lastBlockReason?: string;\n private serverName?: string;\n\n constructor(config: AccessControlConfig) {\n this.enabled = config.enabled;\n this.client = config.client;\n this.serverName = config.serverName;\n\n logger.info(\"AccessControlAuthorizer initialized\", {\n enabled: this.enabled,\n hasClient: !!this.client,\n serverName: this.serverName,\n });\n }\n\n /**\n * Get human-readable reason for why a server was blocked.\n * Returns the reason from the last authorization check, or a default message.\n *\n * @returns Reason message\n */\n getBlockReason(): string {\n if (this.lastBlockReason) {\n return this.lastBlockReason;\n }\n // Default message with server name if available\n const serverPart = this.serverName ? ` '${this.serverName}'` : \"\";\n return `MCP server${serverPart} is not authorized for use in your organization and has been blocked by Onyx.`;\n }\n\n /**\n * Check if access is allowed by querying the backend.\n * Always fails open (allows) if backend is unavailable or not configured.\n * Stores the block reason from the backend for later retrieval.\n *\n * @returns Promise resolving to true if allowed, false if blocked\n */\n async isAllowed(): Promise<boolean> {\n // If access control not enabled, allow everything\n if (!this.enabled) {\n return true;\n }\n\n // Check with backend if client is available\n if (this.client) {\n try {\n const response = await this.client.authorize();\n if (response.action === \"block\") {\n // Store the reason from backend for user-facing error messages, or use default with server name\n if (response.reason) {\n this.lastBlockReason = response.reason;\n } else {\n const serverPart = this.serverName ? ` '${this.serverName}'` : \"\";\n this.lastBlockReason = `MCP server${serverPart} is not authorized for use in your organization and has been blocked by Onyx.`;\n }\n return false;\n }\n return true;\n } catch (error) {\n logger.error(\"Access control authorization failed with unexpected error\", {\n error: String(error),\n });\n // DEFENSIVE: client.authorize() handles all errors internally and always\n // returns an AccessControlResponse, so this catch is normally unreachable.\n // However, we keep it as defensive fail-open behavior for unexpected errors.\n return true;\n }\n }\n\n // No client configured - allow by default (shouldn't happen if enabled=true with URL)\n logger.warn(\"No access control client configured, allowing by default\");\n return true;\n }\n}\n","import type { SessionData } from \"../utils/getUserData.js\";\n\nimport { compressClientInfo } from \"../lib/compress-client-info.js\";\nimport { logger } from \"../lib/logger.js\";\n\nexport interface AccessControlClientConfig {\n apiKey: string;\n headers: Record<string, string>;\n sessionData: SessionData;\n timeoutMs: number;\n url: string;\n}\n\nexport interface AccessControlResponse {\n action: \"allow\" | \"block\";\n reason?: string;\n}\n\n/**\n * Client for checking MCP server access control with backend API.\n * Always fails open (allows) when backend is unreachable.\n */\nexport class AccessControlClient {\n private clientInfoBase64: string;\n private config: AccessControlClientConfig;\n\n constructor(config: AccessControlClientConfig) {\n this.config = config;\n this.clientInfoBase64 = this.getClientInfoBase64();\n logger.info(\"AccessControlClient initialized\", {\n timeoutMs: config.timeoutMs,\n url: config.url,\n });\n }\n\n /**\n * Authorize by querying the backend with full client info.\n * Always fails open (allows) on any error.\n *\n * @returns Access control response\n */\n async authorize(): Promise<AccessControlResponse> {\n try {\n // Try to get decision from backend\n const response = await this.sendAuthorizeRequest();\n\n logger.debug(\"Access control check successful\", {\n action: response.action,\n });\n\n return response;\n } catch (error) {\n logger.warn(\"Access control check failed, failing open (allowing by default)\", {\n error: String(error),\n });\n\n // Always fail open (allow by default for better UX)\n return { action: \"allow\" };\n }\n }\n\n /**\n * Compress and base64-encode client info for URL.\n * Matches scanner pattern.\n */\n private getClientInfoBase64(): string {\n return compressClientInfo(this.config.sessionData);\n }\n\n /**\n * Send authorization request to backend with compressed client info in URL.\n * Matches scanner URL pattern: ${url}/${apiKey}/mcp/${clientInfoBase64}\n *\n * @returns Access control response\n * @throws Error if request fails\n */\n private async sendAuthorizeRequest(): Promise<AccessControlResponse> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n // Build URL with API key and compressed client info (matching scanner pattern)\n const checkUrl = `${this.config.url}/${this.config.apiKey}/mcp/${this.clientInfoBase64}`;\n\n const response = await fetch(checkUrl, {\n headers: {\n ...this.config.headers,\n },\n method: \"POST\",\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Access control service returned ${response.status}: ${response.statusText}`);\n }\n\n const result = (await response.json()) as AccessControlResponse;\n\n // Validate response format\n if (!result.action || ![\"allow\", \"block\"].includes(result.action)) {\n throw new Error(`Invalid access control response format: action=\"${result.action}\"`);\n }\n\n return result;\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new Error(`Access control check timed out after ${this.config.timeoutMs}ms`);\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n}\n","import { execFile } from \"node:child_process\";\nimport { platform } from \"node:os\";\n\nimport { logger } from \"../lib/logger.js\";\nimport { captureExceptionSafe } from \"../lib/sentry.js\";\n\n/**\n * Opens the given URL in the user's default browser.\n *\n * Cross-platform support:\n * - macOS: uses `open`\n * - Windows: uses PowerShell Start-Process with Base64-encoded command (injection-safe)\n * - Linux: uses `xdg-open`\n *\n * @param url The URL to open\n * @returns A promise that resolves when the browser command is executed\n */\nexport async function openBrowser(url: string | URL): Promise<void> {\n const urlString = url.toString();\n const os = platform();\n const redactedUrl = redactUrlForLogging(urlString);\n\n return new Promise((resolve, reject) => {\n let command: string;\n let args: string[];\n\n if (os === \"win32\") {\n // Windows - use PowerShell with Base64-encoded command to prevent injection\n // This is the same approach used by the popular 'open' npm package.\n // The command is Base64-encoded (UTF-16LE) so no shell metacharacters can escape.\n const psCommand = `Start-Process ${escapePowerShellArgument(urlString)}`;\n const encodedCommand = encodePowerShellCommand(psCommand);\n\n command = getPowerShellPath();\n args = [\"-NoProfile\", \"-NonInteractive\", \"-ExecutionPolicy\", \"Bypass\", \"-EncodedCommand\", encodedCommand];\n } else if (os === \"darwin\") {\n // macOS\n command = \"open\";\n args = [urlString];\n } else {\n // Linux and others - use xdg-open\n command = \"xdg-open\";\n args = [urlString];\n }\n\n execFile(command, args, (error, _stdout, stderr) => {\n if (error) {\n const errorContext = {\n command,\n os,\n stderr,\n url: redactedUrl,\n };\n\n logger.warn(\"Failed to open browser automatically\", {\n ...errorContext,\n error: error.message,\n });\n\n captureExceptionSafe(error, errorContext, {\n feature: \"auth\",\n module: \"browser\",\n operation: \"openBrowser\",\n });\n\n const launchError = new Error(\n `Failed to open browser: ${error.message} (command: ${command}, os: ${os}, url: ${redactedUrl})`\n );\n launchError.cause = error;\n reject(launchError);\n } else {\n logger.debug(\"Browser opened successfully\", { url: redactedUrl });\n resolve();\n }\n });\n });\n}\n\n/**\n * Encodes a PowerShell command as Base64 UTF-16LE for use with -EncodedCommand.\n * This is the safest way to pass commands to PowerShell as it prevents any\n * shell metacharacter interpretation.\n */\nfunction encodePowerShellCommand(command: string): string {\n // PowerShell's -EncodedCommand expects UTF-16LE encoded Base64\n const buffer = Buffer.from(command, \"utf16le\");\n return buffer.toString(\"base64\");\n}\n\n/**\n * Escapes a string for use as a PowerShell single-quoted string argument.\n * Single quotes inside the string are escaped by doubling them.\n */\nfunction escapePowerShellArgument(value: string): string {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\n/**\n * Gets the path to Windows PowerShell.\n * PowerShell is included by default since Windows 7 SP1 and Windows Server 2008 R2.\n */\nfunction getPowerShellPath(): string {\n const systemRoot = process.env.SYSTEMROOT || process.env.windir || \"C:\\\\Windows\";\n return `${systemRoot}\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe`;\n}\n\n/**\n * Redacts sensitive query parameters from a URL for safe logging.\n * Preserves the base URL structure while hiding potentially sensitive data.\n *\n * @param url The URL to redact\n * @returns A redacted version of the URL safe for logging\n */\nfunction redactUrlForLogging(url: string): string {\n try {\n const parsed = new URL(url);\n if (parsed.search) {\n return `${parsed.protocol}//${parsed.host}${parsed.pathname}?[REDACTED]`;\n }\n return `${parsed.protocol}//${parsed.host}${parsed.pathname}`;\n } catch {\n // If URL parsing fails, return a generic redacted string\n return \"[INVALID_URL]\";\n }\n}\n","import { createServer, type Server } from \"node:http\";\nimport { URL } from \"node:url\";\n\nimport { logger } from \"../lib/logger.js\";\n\n/**\n * Port range for OAuth callback server (IANA dynamic/private ports)\n */\nconst MIN_PORT = 49152;\nconst MAX_PORT = 65535;\nconst MAX_PORT_ATTEMPTS = 5;\n\nexport interface CallbackServerOptions {\n /**\n * Maximum number of port binding attempts before giving up.\n * Default: 5\n */\n maxAttempts?: number;\n /**\n * Preferred port to use. If unavailable, random ports will be tried.\n */\n preferredPort?: number;\n /**\n * Timeout in milliseconds to wait for the callback.\n * Default: 5 minutes (300000ms)\n */\n timeoutMs?: number;\n}\n\nexport interface CallbackServerResult {\n /**\n * The authorization code received from the OAuth server.\n */\n code: string;\n /**\n * The state parameter, if provided.\n */\n state?: string;\n}\n\n/**\n * Generates a random port number in the dynamic/private port range.\n */\nfunction getRandomPort(): number {\n return Math.floor(Math.random() * (MAX_PORT - MIN_PORT + 1)) + MIN_PORT;\n}\n\n/**\n * Attempts to start the HTTP server on the given port.\n * Returns a promise that resolves with the server if successful, or rejects if the port is in use.\n */\nfunction tryListenOnPort(server: Server, port: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const onError = (err: NodeJS.ErrnoException) => {\n server.removeListener(\"listening\", onListening);\n reject(err);\n };\n\n const onListening = () => {\n server.removeListener(\"error\", onError);\n resolve();\n };\n\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * HTML page shown to the user after successful authorization.\n */\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Authorization Successful</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n color: #fff;\n }\n .container {\n text-align: center;\n padding: 2rem;\n }\n .checkmark {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: #10b981;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 1.5rem;\n animation: pop 0.3s ease-out;\n }\n .checkmark svg {\n width: 40px;\n height: 40px;\n stroke: white;\n stroke-width: 3;\n }\n h1 {\n margin: 0 0 0.5rem;\n font-size: 1.75rem;\n font-weight: 600;\n }\n p {\n margin: 0;\n color: #a0aec0;\n font-size: 1rem;\n }\n @keyframes pop {\n 0% { transform: scale(0); }\n 50% { transform: scale(1.1); }\n 100% { transform: scale(1); }\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"checkmark\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M5 13l4 4L19 7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n </div>\n <script>setTimeout(() => window.close(), 3000);</script>\n</body>\n</html>`;\n\n/**\n * Starts a local HTTP server to receive the OAuth callback.\n * Uses random port selection with retry logic to handle port conflicts.\n *\n * @returns A promise that resolves with the port number and a function to wait for the callback.\n */\nexport async function startCallbackServer(options: CallbackServerOptions = {}): Promise<{\n callbackUrl: string;\n close: () => Promise<void>;\n port: number;\n waitForCallback: () => Promise<CallbackServerResult>;\n}> {\n const { maxAttempts = MAX_PORT_ATTEMPTS, preferredPort, timeoutMs = DEFAULT_TIMEOUT_MS } = options;\n\n let resolveCallback: (result: CallbackServerResult) => void;\n let rejectCallback: (error: Error) => void;\n // Using an object to hold the timeout ID so it can be set after server creation\n // but still referenced in the close() function\n const timeoutRef: { id?: ReturnType<typeof setTimeout> } = {};\n\n const callbackPromise = new Promise<CallbackServerResult>((resolve, reject) => {\n resolveCallback = resolve;\n rejectCallback = reject;\n });\n\n // Track the actual bound port for use in request handler\n let boundPort: number;\n\n const server = createServer((req, res) => {\n // Ignore favicon requests\n if (req.url === \"/favicon.ico\") {\n res.writeHead(404);\n res.end();\n return;\n }\n\n // Only handle the callback path\n if (!req.url?.startsWith(\"/callback\")) {\n res.writeHead(404);\n res.end(\"Not Found\");\n return;\n }\n\n try {\n const parsedUrl = new URL(req.url, `http://localhost:${boundPort}`);\n const code = parsedUrl.searchParams.get(\"code\");\n const error = parsedUrl.searchParams.get(\"error\");\n const errorDescription = parsedUrl.searchParams.get(\"error_description\");\n const state = parsedUrl.searchParams.get(\"state\");\n\n if (error) {\n logger.error(\"OAuth authorization error\", { error, errorDescription });\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(getErrorHtml(error, errorDescription || undefined));\n clearTimeout(timeoutRef.id);\n rejectCallback(\n new Error(`OAuth authorization failed: ${error}${errorDescription ? ` - ${errorDescription}` : \"\"}`)\n );\n return;\n }\n\n if (!code) {\n logger.error(\"OAuth callback missing authorization code\");\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(getErrorHtml(\"missing_code\", \"No authorization code was provided\"));\n clearTimeout(timeoutRef.id);\n rejectCallback(new Error(\"OAuth callback missing authorization code\"));\n return;\n }\n\n logger.info(\"OAuth authorization code received\", {\n codePrefix: `${code.substring(0, 10)}...`,\n hasState: !!state,\n });\n\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(SUCCESS_HTML);\n\n clearTimeout(timeoutRef.id);\n resolveCallback({ code, state: state || undefined });\n\n // Close server after a short delay to ensure response is sent\n setTimeout(() => server.close(), 1000);\n } catch (err) {\n logger.error(\"Error processing OAuth callback\", { error: String(err) });\n res.writeHead(500);\n res.end(\"Internal Server Error\");\n clearTimeout(timeoutRef.id);\n rejectCallback(err instanceof Error ? err : new Error(String(err)));\n }\n });\n\n // Set up timeout (now that server exists)\n timeoutRef.id = setTimeout(() => {\n rejectCallback(new Error(`OAuth callback timeout after ${timeoutMs}ms`));\n server.close();\n }, timeoutMs);\n\n // Try to bind to a port with retry logic\n const attemptedPorts: number[] = [];\n let lastError: Error | undefined;\n\n while (attemptedPorts.length < maxAttempts) {\n // First attempt uses preferred port if provided, otherwise random\n let portToTry: number;\n if (attemptedPorts.length === 0 && preferredPort !== undefined) {\n portToTry = preferredPort;\n } else {\n // Generate a random port that hasn't been tried yet\n portToTry = getRandomPort();\n while (attemptedPorts.includes(portToTry)) {\n portToTry = getRandomPort();\n }\n }\n attemptedPorts.push(portToTry);\n\n try {\n await tryListenOnPort(server, portToTry);\n boundPort = portToTry;\n const callbackUrl = `http://localhost:${boundPort}/callback`;\n logger.info(\"OAuth callback server started\", { attempt: attemptedPorts.length, callbackUrl, port: boundPort });\n\n return {\n callbackUrl,\n close: () =>\n new Promise<void>((resolve) => {\n clearTimeout(timeoutRef.id);\n server.close(() => resolve());\n }),\n port: boundPort,\n waitForCallback: () => callbackPromise,\n };\n } catch (err) {\n const nodeErr = err as NodeJS.ErrnoException;\n if (nodeErr.code === \"EADDRINUSE\") {\n logger.debug(\"Port in use, trying another port\", {\n attempt: attemptedPorts.length,\n maxAttempts,\n port: portToTry,\n });\n lastError = new Error(`Port ${portToTry} is already in use`);\n } else {\n // For non-EADDRINUSE errors, clean up and throw immediately\n clearTimeout(timeoutRef.id);\n server.close();\n throw err;\n }\n }\n }\n\n // All attempts failed\n clearTimeout(timeoutRef.id);\n server.close();\n throw new Error(\n `Failed to start OAuth callback server after ${maxAttempts} attempts. ` +\n `Tried ports: ${attemptedPorts.join(\", \")}. Last error: ${lastError?.message || \"unknown\"}`\n );\n}\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\n/**\n * HTML page shown to the user when authorization fails.\n */\nfunction getErrorHtml(error: string, description?: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Authorization Failed</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n color: #fff;\n }\n .container {\n text-align: center;\n padding: 2rem;\n max-width: 400px;\n }\n .error-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: #ef4444;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 1.5rem;\n }\n .error-icon svg {\n width: 40px;\n height: 40px;\n stroke: white;\n stroke-width: 3;\n }\n h1 {\n margin: 0 0 0.5rem;\n font-size: 1.75rem;\n font-weight: 600;\n }\n .error-code {\n color: #f87171;\n font-family: monospace;\n margin-bottom: 0.5rem;\n }\n p {\n margin: 0;\n color: #a0aec0;\n font-size: 1rem;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"error-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M6 18L18 6M6 6l12 12\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n <h1>Authorization Failed</h1>\n <p class=\"error-code\">${escapeHtml(error)}</p>\n ${description ? `<p>${escapeHtml(description)}</p>` : \"\"}\n </div>\n</body>\n</html>`;\n}\n","import type { OAuthClientInformationFull, OAuthTokens } from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\nimport { createHash } from \"node:crypto\";\nimport { chmod, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { logger } from \"../lib/logger.js\";\nimport { normalizeUrl } from \"../utils/normalizeUrl.js\";\n\nexport interface OAuthCredentialStoreOptions {\n /**\n * Directory to store OAuth credentials.\n * Defaults to ~/.onyx/mcp-gateway/oauth/\n */\n storageDir: string;\n}\n\n/**\n * Stored credentials structure persisted to disk.\n */\nexport interface StoredCredentials {\n /**\n * DCR client registration information.\n */\n clientInfo?: OAuthClientInformationFull;\n /**\n * Timestamp when the credentials were stored (ISO string).\n */\n createdAt: string;\n /**\n * The original server URL these credentials are for.\n */\n serverUrl: string;\n /**\n * OAuth tokens (access_token, refresh_token, etc.).\n */\n tokens?: OAuthTokens;\n /**\n * Timestamp when tokens were obtained (ISO string).\n * Used to calculate token expiration.\n */\n tokensObtainedAt?: string;\n /**\n * Last update timestamp (ISO string).\n */\n updatedAt: string;\n}\n\n/**\n * File-based credential store for OAuth credentials.\n * Stores credentials in JSON files keyed by server URL hash.\n */\nexport class OAuthCredentialStore {\n private readonly storageDir: string;\n\n constructor(options: OAuthCredentialStoreOptions) {\n this.storageDir = options.storageDir;\n }\n\n /**\n * Deletes stored credentials for a server URL.\n */\n async delete(serverUrl: string): Promise<void> {\n // Normalize URL to ensure consistent key generation\n const normalizedUrl = normalizeUrl(serverUrl);\n const filePath = this.getFilePath(normalizedUrl);\n\n try {\n await rm(filePath);\n logger.debug(\"Deleted stored credentials\", { filePath, serverUrl: normalizedUrl });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n logger.warn(\"Failed to delete stored credentials\", {\n error: String(error),\n filePath,\n serverUrl: normalizedUrl,\n });\n }\n }\n }\n\n /**\n * Checks if stored credentials have a refresh token.\n */\n hasRefreshToken(credentials: StoredCredentials): boolean {\n return !!credentials.tokens?.refresh_token;\n }\n\n /**\n * Checks if stored credentials have a valid (non-expired) access token.\n */\n hasValidAccessToken(credentials: StoredCredentials): boolean {\n return !!credentials.tokens?.access_token && !this.isAccessTokenExpired(credentials);\n }\n\n /**\n * Checks if the stored access token is expired.\n * Returns true if expired or if expiration cannot be determined.\n */\n isAccessTokenExpired(credentials: StoredCredentials): boolean {\n if (!credentials.tokens || !credentials.tokensObtainedAt) {\n return true;\n }\n\n const { expires_in } = credentials.tokens;\n if (!expires_in) {\n // No expiration info - assume not expired\n return false;\n }\n\n const obtainedAt = new Date(credentials.tokensObtainedAt).getTime();\n const expiresAt = obtainedAt + expires_in * 1000;\n const now = Date.now();\n\n // Add 30 second buffer to avoid edge cases\n const isExpired = now >= expiresAt - 30000;\n\n logger.debug(\"Checked access token expiration\", {\n expiresAt: new Date(expiresAt).toISOString(),\n expiresIn: expires_in,\n isExpired,\n now: new Date(now).toISOString(),\n obtainedAt: credentials.tokensObtainedAt,\n });\n\n return isExpired;\n }\n\n /**\n * Loads stored credentials for a server URL.\n * Returns undefined if no credentials exist or if they're invalid.\n */\n async load(serverUrl: string): Promise<StoredCredentials | undefined> {\n // Normalize URL to ensure consistent key generation\n const normalizedUrl = normalizeUrl(serverUrl);\n const filePath = this.getFilePath(normalizedUrl);\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const credentials = JSON.parse(content) as StoredCredentials;\n\n // Validate the stored credentials match the requested server (using normalized URL)\n // Also accept credentials stored with the original URL format for backward compatibility\n const storedUrlNormalized = normalizeUrl(credentials.serverUrl);\n if (storedUrlNormalized !== normalizedUrl) {\n logger.warn(\"Credential file server URL mismatch\", {\n expected: normalizedUrl,\n filePath,\n found: credentials.serverUrl,\n });\n return undefined;\n }\n\n logger.debug(\"Loaded stored credentials\", {\n clientId: credentials.clientInfo?.client_id,\n hasRefreshToken: !!credentials.tokens?.refresh_token,\n hasTokens: !!credentials.tokens,\n serverUrl: normalizedUrl,\n });\n\n return credentials;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n // File doesn't exist - no stored credentials\n logger.debug(\"No stored credentials found\", { serverUrl: normalizedUrl });\n return undefined;\n }\n\n logger.warn(\"Failed to load stored credentials\", {\n error: String(error),\n filePath,\n serverUrl: normalizedUrl,\n });\n return undefined;\n }\n }\n\n /**\n * Saves credentials to disk.\n */\n async save(credentials: StoredCredentials): Promise<void> {\n await this.ensureStorageDir();\n\n const filePath = this.getFilePath(credentials.serverUrl);\n const content = JSON.stringify(credentials, null, 2);\n\n try {\n await writeFile(filePath, content, { encoding: \"utf-8\", mode: 0o600 });\n // Ensure permissions are correct even if file existed\n await chmod(filePath, 0o600);\n\n logger.debug(\"Saved credentials to disk\", {\n clientId: credentials.clientInfo?.client_id,\n filePath,\n hasTokens: !!credentials.tokens,\n serverUrl: credentials.serverUrl,\n });\n } catch (error) {\n logger.error(\"Failed to save credentials\", {\n error: String(error),\n filePath,\n serverUrl: credentials.serverUrl,\n });\n throw error;\n }\n }\n\n /**\n * Updates specific fields in stored credentials.\n * Creates new credentials if none exist.\n */\n async update(\n serverUrl: string,\n updates: Partial<Pick<StoredCredentials, \"clientInfo\" | \"tokens\" | \"tokensObtainedAt\">>\n ): Promise<StoredCredentials> {\n // Normalize URL to ensure consistent key generation and storage\n const normalizedUrl = normalizeUrl(serverUrl);\n const existing = await this.load(normalizedUrl);\n const now = new Date().toISOString();\n\n const credentials: StoredCredentials = existing\n ? {\n ...existing,\n ...updates,\n // Update serverUrl to normalized form for consistency\n serverUrl: normalizedUrl,\n updatedAt: now,\n }\n : {\n createdAt: now,\n serverUrl: normalizedUrl,\n updatedAt: now,\n ...updates,\n };\n\n await this.save(credentials);\n return credentials;\n }\n\n /**\n * Ensures the storage directory exists with proper permissions.\n */\n private async ensureStorageDir(): Promise<void> {\n try {\n await mkdir(this.storageDir, { mode: 0o700, recursive: true });\n } catch (error) {\n // Directory may already exist\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw error;\n }\n }\n }\n\n /**\n * Generates a URL-safe filename from a server URL.\n * Uses SHA-256 hash truncated to 16 chars for brevity.\n */\n private generateKey(serverUrl: string): string {\n const hash = createHash(\"sha256\").update(serverUrl).digest(\"hex\");\n return hash.substring(0, 16);\n }\n\n /**\n * Gets the file path for a server's credentials.\n */\n private getFilePath(serverUrl: string): string {\n const key = this.generateKey(serverUrl);\n return join(this.storageDir, `${key}.json`);\n }\n}\n\n","import type { OAuthProviderConfig } from \"../config/config.js\";\n\nimport { BUILD_TIME_CONFIG } from \"../config/config.generated.js\";\nimport { logger } from \"../lib/logger.js\";\n\n/**\n * Pre-configured OAuth providers loaded from build-time config.\n * These providers have their credentials embedded at build time.\n */\nexport const OAUTH_PROVIDERS: OAuthProviderConfig[] = BUILD_TIME_CONFIG.OAUTH_PROVIDERS;\n\n/**\n * Finds a pre-configured OAuth provider that matches the given server URL.\n *\n * @param serverUrl The MCP server URL to match against provider URL patterns\n * @returns The matching provider config, or undefined if no match found\n */\nexport function findProviderForUrl(serverUrl: string | URL): OAuthProviderConfig | undefined {\n const serverUrlObj = typeof serverUrl === \"string\" ? new URL(serverUrl) : serverUrl;\n const hostname = serverUrlObj.hostname.toLowerCase();\n\n for (const provider of OAUTH_PROVIDERS) {\n for (const pattern of provider.urlPatterns) {\n // Pattern can be a hostname or a hostname with path prefix\n const patternLower = pattern.toLowerCase();\n\n // Check if pattern matches hostname (with or without subdomains)\n if (matchesHostname(hostname, patternLower)) {\n logger.debug(\"Found matching OAuth provider for URL\", {\n hostname,\n pattern,\n providerId: provider.id,\n providerName: provider.name,\n });\n return provider;\n }\n }\n }\n\n logger.debug(\"No pre-configured OAuth provider found for URL\", {\n hostname,\n serverUrl: serverUrlObj.toString(),\n });\n return undefined;\n}\n\n/**\n * Gets a provider by its ID.\n *\n * @param providerId The provider ID (e.g., \"github\")\n * @returns The provider config, or undefined if not found\n */\nexport function getProviderById(providerId: string): OAuthProviderConfig | undefined {\n return OAUTH_PROVIDERS.find((p) => p.id === providerId);\n}\n\n/**\n * Lists all configured OAuth providers.\n * Useful for debugging and admin interfaces.\n *\n * @returns Array of provider summaries (without secrets)\n */\nexport function listProviders(): Array<{\n hasClientSecret: boolean;\n id: string;\n name: string;\n urlPatterns: string[];\n}> {\n return OAUTH_PROVIDERS.map((p) => ({\n hasClientSecret: !!p.clientSecret,\n id: p.id,\n name: p.name,\n urlPatterns: p.urlPatterns,\n }));\n}\n\n/**\n * Checks if a hostname matches a pattern.\n * Supports exact matches and subdomain matches.\n *\n * @param hostname The hostname to check (e.g., \"api.github.com\")\n * @param pattern The pattern to match against (e.g., \"github.com\")\n * @returns true if the hostname matches the pattern\n */\nfunction matchesHostname(hostname: string, pattern: string): boolean {\n // Exact match\n if (hostname === pattern) {\n return true;\n }\n\n // Subdomain match: hostname ends with \".pattern\"\n // e.g., \"api.github.com\" matches \"github.com\"\n if (hostname.endsWith(`.${pattern}`)) {\n return true;\n }\n\n return false;\n}\n","import {\n discoverAuthorizationServerMetadata,\n discoverOAuthProtectedResourceMetadata,\n exchangeAuthorization,\n extractResourceMetadataUrl,\n refreshAuthorization,\n registerClient,\n startAuthorization,\n} from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport type {\n OAuthClientInformationFull,\n OAuthClientMetadata,\n OAuthProtectedResourceMetadata,\n OAuthTokens,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\nimport type { OAuthProviderConfig } from \"../config/config.js\";\nimport { logger } from \"../lib/logger.js\";\nimport { normalizeUrl } from \"../utils/normalizeUrl.js\";\nimport { openBrowser } from \"./browser.js\";\nimport { startCallbackServer } from \"./callbackServer.js\";\nimport type { OAuthCredentialStore, StoredCredentials } from \"./OAuthCredentialStore.js\";\nimport { findProviderForUrl } from \"./providers.js\";\n\nexport interface OAuthFlowOptions {\n /**\n * Custom client metadata for DCR registration.\n * If not provided, default metadata will be used.\n */\n clientMetadata?: Partial<OAuthClientMetadata>;\n /**\n * Credential store for persisting OAuth credentials.\n * If provided, credentials will be loaded from and saved to disk.\n */\n credentialStore?: OAuthCredentialStore;\n /**\n * OAuth scope to request.\n */\n scope?: string;\n /**\n * Timeout in milliseconds for the OAuth callback.\n * Default: 5 minutes\n */\n timeoutMs?: number;\n}\n\nexport interface OAuthFlowResult {\n clientInfo: OAuthClientInformationFull;\n /**\n * Whether the result came from stored credentials (true) or a fresh OAuth flow (false).\n */\n fromCache?: boolean;\n tokens: OAuthTokens;\n}\n\n/**\n * Attempts to detect if a server requires OAuth by making a test request.\n * Returns the resource metadata URL from the WWW-Authenticate header if OAuth is required.\n *\n * @param serverUrl The MCP server URL to check\n * @returns The resource metadata URL if OAuth is required, undefined otherwise\n */\nexport async function detectOAuthRequirement(serverUrl: string | URL): Promise<undefined | URL> {\n // Normalize URL to avoid redirect issues on Windows\n const normalizedUrl = normalizeUrl(serverUrl);\n const serverUrlObj = new URL(normalizedUrl);\n\n try {\n const response = await fetch(serverUrlObj, {\n headers: {\n Accept: \"application/json\",\n },\n method: \"POST\",\n });\n\n if (response.status === 401) {\n // Check for resource metadata URL in response\n const resourceMetadataUrl = extractResourceMetadataUrl(response);\n if (resourceMetadataUrl) {\n logger.debug(\"OAuth required, found resource metadata URL\", {\n resourceMetadataUrl: resourceMetadataUrl.toString(),\n });\n return resourceMetadataUrl;\n }\n\n // Check WWW-Authenticate header for Bearer realm\n const wwwAuth = response.headers.get(\"WWW-Authenticate\");\n if (wwwAuth && wwwAuth.toLowerCase().includes(\"bearer\")) {\n logger.debug(\"OAuth required based on WWW-Authenticate header\", { wwwAuth });\n return undefined; // OAuth required but no metadata URL\n }\n }\n\n return undefined;\n } catch (err) {\n logger.debug(\"Error detecting OAuth requirement\", { error: String(err) });\n return undefined;\n }\n}\n\n/**\n * Performs the complete OAuth 2.0 Authorization Code flow with DCR.\n *\n * This function:\n * 1. Checks for existing valid credentials (if credentialStore provided)\n * 2. Attempts token refresh if access token expired (if credentialStore provided)\n * 3. Discovers OAuth protected resource metadata\n * 4. Discovers authorization server metadata\n * 5. Dynamically registers the client (DCR)\n * 6. Opens browser for user authorization\n * 7. Waits for the callback with authorization code\n * 8. Exchanges the code for tokens\n * 9. Persists credentials (if credentialStore provided)\n *\n * @param serverUrl The MCP server URL that requires OAuth\n * @param options Optional configuration\n * @returns The client info and tokens from successful authorization\n */\nexport async function performOAuthFlow(\n serverUrl: string | URL,\n options: OAuthFlowOptions = {}\n): Promise<OAuthFlowResult> {\n const { credentialStore, scope, timeoutMs } = options;\n // Normalize URL to avoid redirect issues on Windows (POST to GET conversion)\n const serverUrlString = normalizeUrl(serverUrl);\n const serverUrlObj = new URL(serverUrlString);\n\n logger.info(\"Starting OAuth flow\", { serverUrl: serverUrlString });\n\n // Step 0: Check for existing valid credentials\n if (credentialStore) {\n const existingCredentials = await tryLoadExistingCredentials(serverUrlObj, credentialStore);\n if (existingCredentials) {\n logger.info(\"Using existing OAuth credentials\", {\n clientId: existingCredentials.clientInfo.client_id,\n serverUrl: serverUrlString,\n });\n return existingCredentials;\n }\n }\n\n // Step 1: Start the callback server to get a dynamic port\n logger.debug(\"Starting OAuth callback server...\");\n const callbackServer = await startCallbackServer({ timeoutMs });\n const redirectUrl = callbackServer.callbackUrl;\n\n logger.info(\"OAuth callback server started\", {\n callbackUrl: redirectUrl,\n port: callbackServer.port,\n });\n\n try {\n // Step 2: Check for pre-configured OAuth provider (non-DCR servers)\n const preConfiguredProvider = findProviderForUrl(serverUrlObj);\n\n if (preConfiguredProvider) {\n logger.info(\"Using pre-configured OAuth provider\", {\n providerId: preConfiguredProvider.id,\n providerName: preConfiguredProvider.name,\n });\n\n // Use pre-configured provider flow - pass the callback server instance\n return await performPreConfiguredProviderFlow(\n serverUrlObj,\n preConfiguredProvider,\n callbackServer,\n credentialStore,\n scope\n );\n }\n\n // Step 3: Discover OAuth protected resource metadata (DCR flow)\n logger.debug(\"Discovering OAuth protected resource metadata...\");\n let resourceMetadata: OAuthProtectedResourceMetadata | undefined;\n\n try {\n resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrlObj);\n logger.info(\"Discovered protected resource metadata\", {\n authorizationServers: resourceMetadata?.authorization_servers,\n resource: resourceMetadata?.resource,\n scopesSupported: resourceMetadata?.scopes_supported,\n });\n } catch (err) {\n logger.warn(\"Could not discover protected resource metadata, will try direct discovery\", {\n error: String(err),\n });\n }\n\n // Step 4: Determine authorization server URL\n let authServerUrl: URL;\n if (resourceMetadata?.authorization_servers?.length) {\n authServerUrl = new URL(resourceMetadata.authorization_servers[0]);\n logger.debug(\"Using authorization server from resource metadata\", {\n authServerUrl: authServerUrl.toString(),\n });\n } else {\n // Fall back to using the server URL itself\n authServerUrl = serverUrlObj;\n logger.debug(\"Using server URL as authorization server\", {\n authServerUrl: authServerUrl.toString(),\n });\n }\n\n // Step 5: Discover authorization server metadata\n logger.debug(\"Discovering authorization server metadata...\");\n const authMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n\n if (!authMetadata) {\n throw new Error(\n `Could not discover OAuth metadata from ${authServerUrl}. ` +\n `The server may not support OAuth 2.0 or the metadata endpoint is not accessible.`\n );\n }\n\n logger.info(\"Discovered authorization server metadata\", {\n authorizationEndpoint: authMetadata.authorization_endpoint,\n issuer: authMetadata.issuer,\n registrationEndpoint: authMetadata.registration_endpoint,\n tokenEndpoint: authMetadata.token_endpoint,\n });\n\n // Step 6: Check if we have existing client info that can be reused\n let clientInfo: OAuthClientInformationFull;\n const existingCredentials = credentialStore ? await credentialStore.load(serverUrlString) : undefined;\n\n // Check if existing client registration has a matching redirect_uri\n const existingRedirectUris = existingCredentials?.clientInfo?.redirect_uris || [];\n const canReuseClient = existingCredentials?.clientInfo && existingRedirectUris.includes(redirectUrl);\n\n if (canReuseClient && existingCredentials?.clientInfo) {\n // Reuse existing client registration - redirect_uri matches\n logger.info(\"Reusing existing DCR client registration\", {\n clientId: existingCredentials.clientInfo.client_id,\n clientName: existingCredentials.clientInfo.client_name,\n redirectUri: redirectUrl,\n });\n clientInfo = existingCredentials.clientInfo;\n } else {\n // Need to register new client (either no existing client, or redirect_uri doesn't match)\n if (existingCredentials?.clientInfo) {\n logger.info(\"Existing DCR client has different redirect_uri, re-registering\", {\n existingRedirectUris,\n newRedirectUri: redirectUrl,\n });\n }\n\n // Step 6b: Build client metadata for DCR\n const clientMetadata: OAuthClientMetadata = {\n client_name: options.clientMetadata?.client_name || \"MCP Gateway\",\n grant_types: options.clientMetadata?.grant_types || [\"authorization_code\", \"refresh_token\"],\n redirect_uris: [redirectUrl],\n response_types: options.clientMetadata?.response_types || [\"code\"],\n // Public client - no client secret\n token_endpoint_auth_method: options.clientMetadata?.token_endpoint_auth_method || \"none\",\n ...(scope && { scope }),\n };\n\n logger.debug(\"Client metadata for DCR\", { clientMetadata });\n\n // Step 7: Register client via DCR\n if (!authMetadata.registration_endpoint) {\n throw new Error(\n `Authorization server does not support Dynamic Client Registration. ` +\n `No registration_endpoint found in metadata.`\n );\n }\n\n logger.info(\"Registering client via DCR...\", {\n registrationEndpoint: authMetadata.registration_endpoint,\n });\n\n clientInfo = await registerClient(authServerUrl, {\n clientMetadata,\n metadata: authMetadata,\n });\n\n logger.info(\"Client registered successfully via DCR\", {\n clientId: clientInfo.client_id,\n clientName: clientInfo.client_name,\n clientSecretExpiresAt: clientInfo.client_secret_expires_at,\n });\n\n // Persist the client info immediately\n if (credentialStore) {\n await credentialStore.update(serverUrlString, { clientInfo });\n }\n }\n\n // Step 8: RFC 8707 Resource Indicators\n // NOTE: We intentionally do NOT send the resource parameter. Some OAuth servers (e.g., Notion)\n // ignore the client's requested resource and always issue tokens with their configured default\n // audience. When we then present that token to a different path (e.g., /mcp), the resource\n // server rejects it with \"Token audience does not match resource server\".\n // By omitting the resource parameter entirely, we let the OAuth server use its default\n // behavior, which typically issues tokens that work with the resource server.\n // This matches how Linear works (no protected resource metadata = no resource param sent).\n\n // Step 9: Start authorization flow (generates PKCE challenge)\n logger.debug(\"Starting authorization flow...\");\n const { authorizationUrl, codeVerifier } = await startAuthorization(authServerUrl, {\n clientInformation: clientInfo,\n metadata: authMetadata,\n redirectUrl,\n scope,\n });\n\n await openBrowser(authorizationUrl);\n\n // Step 11: Wait for callback with authorization code\n logger.debug(\"Waiting for OAuth callback...\");\n const callbackResult = await callbackServer.waitForCallback();\n\n logger.info(\"Authorization code received\", {\n codePrefix: `${callbackResult.code.substring(0, 10)}...`,\n hasState: !!callbackResult.state,\n });\n\n // Step 12: Exchange authorization code for tokens\n logger.debug(\"Exchanging authorization code for tokens...\");\n const tokens = await exchangeAuthorization(authServerUrl, {\n authorizationCode: callbackResult.code,\n clientInformation: clientInfo,\n codeVerifier,\n metadata: authMetadata,\n redirectUri: redirectUrl,\n });\n\n logger.info(\"OAuth tokens obtained successfully\", {\n expiresIn: tokens.expires_in,\n hasRefreshToken: !!tokens.refresh_token,\n tokenType: tokens.token_type,\n });\n\n // Step 13: Persist tokens\n if (credentialStore) {\n const tokensObtainedAt = new Date().toISOString();\n await credentialStore.update(serverUrlString, {\n tokens,\n tokensObtainedAt,\n });\n logger.info(\"OAuth credentials persisted to disk\", { serverUrl: serverUrlString });\n }\n\n return {\n clientInfo,\n fromCache: false,\n tokens,\n };\n } finally {\n // Always close the callback server\n await callbackServer.close();\n }\n}\n\n/**\n * Exchanges an authorization code for tokens using a pre-configured provider.\n * Handles different token endpoint authentication methods.\n */\nasync function exchangeCodeForTokens(\n provider: OAuthProviderConfig,\n code: string,\n redirectUri: string,\n codeVerifier?: string\n): Promise<OAuthTokens> {\n const tokenEndpoint = provider.tokenEndpoint;\n const authMethod = provider.tokenEndpointAuthMethod || (provider.clientSecret ? \"client_secret_post\" : \"none\");\n\n // Build token request body\n const body = new URLSearchParams();\n body.set(\"grant_type\", \"authorization_code\");\n body.set(\"code\", code);\n body.set(\"redirect_uri\", redirectUri);\n\n // Add PKCE verifier if present\n if (codeVerifier) {\n body.set(\"code_verifier\", codeVerifier);\n }\n\n // Build request headers\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n };\n\n // Add client authentication based on method\n if (authMethod === \"client_secret_basic\" && provider.clientSecret) {\n // HTTP Basic authentication\n const credentials = Buffer.from(`${provider.clientId}:${provider.clientSecret}`).toString(\"base64\");\n headers.Authorization = `Basic ${credentials}`;\n } else if (authMethod === \"client_secret_post\" && provider.clientSecret) {\n // Client credentials in POST body\n body.set(\"client_id\", provider.clientId);\n body.set(\"client_secret\", provider.clientSecret);\n } else {\n // Public client - just client_id\n body.set(\"client_id\", provider.clientId);\n }\n\n logger.debug(\"Sending token request\", {\n authMethod,\n providerId: provider.id,\n tokenEndpoint,\n });\n\n const response = await fetch(tokenEndpoint, {\n body: body.toString(),\n headers,\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n logger.error(\"Token exchange failed\", {\n error: errorText,\n providerId: provider.id,\n status: response.status,\n });\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n // GitHub returns tokens as application/x-www-form-urlencoded by default\n // unless Accept: application/json is set\n const contentType = response.headers.get(\"content-type\") || \"\";\n let tokenData: Record<string, unknown>;\n\n if (contentType.includes(\"application/json\")) {\n tokenData = (await response.json()) as Record<string, unknown>;\n } else if (contentType.includes(\"application/x-www-form-urlencoded\") || contentType.includes(\"text/plain\")) {\n // Parse URL-encoded response (GitHub's default format)\n const text = await response.text();\n const params = new URLSearchParams(text);\n tokenData = Object.fromEntries(params.entries()) as Record<string, unknown>;\n } else {\n // Try JSON first, fall back to URL-encoded\n const text = await response.text();\n try {\n tokenData = JSON.parse(text) as Record<string, unknown>;\n } catch {\n const params = new URLSearchParams(text);\n tokenData = Object.fromEntries(params.entries()) as Record<string, unknown>;\n }\n }\n\n // Convert expires_in to number if it's a string\n if (typeof tokenData.expires_in === \"string\") {\n tokenData.expires_in = parseInt(tokenData.expires_in, 10);\n }\n\n return {\n access_token: tokenData.access_token as string,\n expires_in: tokenData.expires_in as number | undefined,\n refresh_token: tokenData.refresh_token as string | undefined,\n scope: tokenData.scope as string | undefined,\n token_type: (tokenData.token_type as string) || \"bearer\",\n };\n}\n\n/**\n * Performs OAuth flow using a pre-configured provider (non-DCR).\n * This is used for OAuth servers that don't support Dynamic Client Registration.\n */\nasync function performPreConfiguredProviderFlow(\n serverUrl: URL,\n provider: OAuthProviderConfig,\n callbackServerInstance: {\n callbackUrl: string;\n close: () => Promise<void>;\n waitForCallback: () => Promise<{ code: string; state?: string }>;\n },\n credentialStore: OAuthCredentialStore | undefined,\n scope: string | undefined\n): Promise<OAuthFlowResult> {\n const serverUrlString = serverUrl.toString();\n const redirectUrl = callbackServerInstance.callbackUrl;\n\n // Build client info from pre-configured provider\n const clientInfo: OAuthClientInformationFull = {\n client_id: provider.clientId,\n client_name: provider.name,\n client_secret: provider.clientSecret,\n redirect_uris: [redirectUrl],\n token_endpoint_auth_method:\n provider.tokenEndpointAuthMethod || (provider.clientSecret ? \"client_secret_post\" : \"none\"),\n };\n\n logger.info(\"Using pre-configured OAuth client\", {\n clientId: provider.clientId,\n hasClientSecret: !!provider.clientSecret,\n providerId: provider.id,\n tokenEndpointAuthMethod: clientInfo.token_endpoint_auth_method,\n });\n\n // Determine scope to use\n const effectiveScope = scope || provider.scopes?.join(\" \");\n\n // Generate PKCE challenge if enabled for this provider\n const usePkce = provider.usePkce !== false; // Default to true unless explicitly disabled\n let codeVerifier: string | undefined;\n let codeChallenge: string | undefined;\n let codeChallengeMethod: string | undefined;\n\n if (usePkce) {\n // Generate PKCE code verifier and challenge\n const { generateCodeChallenge, generateCodeVerifier } = await import(\"./pkce.js\");\n codeVerifier = generateCodeVerifier();\n codeChallenge = await generateCodeChallenge(codeVerifier);\n codeChallengeMethod = \"S256\";\n }\n\n // Build authorization URL manually\n const authUrl = new URL(provider.authorizationEndpoint);\n authUrl.searchParams.set(\"client_id\", provider.clientId);\n authUrl.searchParams.set(\"redirect_uri\", redirectUrl);\n authUrl.searchParams.set(\"response_type\", \"code\");\n\n if (effectiveScope) {\n authUrl.searchParams.set(\"scope\", effectiveScope);\n }\n\n // Add state for security\n const state = crypto.randomUUID();\n authUrl.searchParams.set(\"state\", state);\n\n // Add PKCE challenge if enabled\n if (codeChallenge && codeChallengeMethod) {\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", codeChallengeMethod);\n }\n\n await openBrowser(authUrl);\n\n // Wait for callback on the existing callback server\n logger.debug(\"Waiting for OAuth callback...\");\n const callbackResult = await callbackServerInstance.waitForCallback();\n\n // Verify state matches\n if (callbackResult.state !== state) {\n throw new Error(\"OAuth state mismatch - possible CSRF attack\");\n }\n\n logger.info(\"Authorization code received\", {\n codePrefix: `${callbackResult.code.substring(0, 10)}...`,\n providerId: provider.id,\n });\n\n // Exchange authorization code for tokens\n logger.debug(\"Exchanging authorization code for tokens...\");\n const tokens = await exchangeCodeForTokens(provider, callbackResult.code, redirectUrl, codeVerifier);\n\n logger.info(\"OAuth tokens obtained successfully\", {\n expiresIn: tokens.expires_in,\n hasRefreshToken: !!tokens.refresh_token,\n providerId: provider.id,\n tokenType: tokens.token_type,\n });\n\n // Persist credentials\n if (credentialStore) {\n const tokensObtainedAt = new Date().toISOString();\n await credentialStore.update(serverUrlString, {\n clientInfo,\n tokens,\n tokensObtainedAt,\n });\n logger.info(\"OAuth credentials persisted to disk\", {\n providerId: provider.id,\n serverUrl: serverUrlString,\n });\n }\n\n return {\n clientInfo,\n fromCache: false,\n tokens,\n };\n}\n\n/**\n * Checks for existing valid credentials and returns them if available.\n * Handles token refresh if the access token is expired but refresh token exists.\n */\nasync function tryLoadExistingCredentials(\n serverUrl: URL,\n credentialStore: OAuthCredentialStore\n): Promise<OAuthFlowResult | undefined> {\n const credentials = await credentialStore.load(serverUrl.toString());\n\n if (!credentials) {\n logger.debug(\"No stored credentials found\", { serverUrl: serverUrl.toString() });\n return undefined;\n }\n\n if (!credentials.clientInfo) {\n logger.debug(\"Stored credentials have no client info\", { serverUrl: serverUrl.toString() });\n return undefined;\n }\n\n // Check if we have a valid access token\n if (credentialStore.hasValidAccessToken(credentials) && credentials.tokens) {\n logger.info(\"Using cached OAuth credentials with valid access token\", {\n clientId: credentials.clientInfo.client_id,\n serverUrl: serverUrl.toString(),\n });\n\n return {\n clientInfo: credentials.clientInfo,\n fromCache: true,\n tokens: credentials.tokens,\n };\n }\n\n // Access token is expired or missing, try to refresh\n if (credentialStore.hasRefreshToken(credentials)) {\n const refreshResult = await tryRefreshToken(serverUrl, credentials, credentialStore);\n if (refreshResult) {\n return refreshResult;\n }\n }\n\n logger.debug(\"Stored credentials are expired and no valid refresh token\", {\n serverUrl: serverUrl.toString(),\n });\n\n return undefined;\n}\n\n/**\n * Attempts to refresh an expired access token using the refresh token.\n * Returns new tokens if successful, undefined if refresh fails.\n */\nasync function tryRefreshToken(\n serverUrl: URL,\n credentials: StoredCredentials,\n credentialStore: OAuthCredentialStore\n): Promise<OAuthFlowResult | undefined> {\n if (!credentials.clientInfo || !credentials.tokens?.refresh_token) {\n return undefined;\n }\n\n logger.info(\"Attempting to refresh expired access token\", {\n clientId: credentials.clientInfo.client_id,\n serverUrl: serverUrl.toString(),\n });\n\n try {\n // Discover authorization server metadata\n let resourceMetadata: OAuthProtectedResourceMetadata | undefined;\n try {\n resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl);\n } catch {\n // Continue without resource metadata\n }\n\n let authServerUrl: URL;\n if (resourceMetadata?.authorization_servers?.length) {\n authServerUrl = new URL(resourceMetadata.authorization_servers[0]);\n } else {\n authServerUrl = serverUrl;\n }\n\n const authMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n // NOTE: We don't send the resource parameter (same reasoning as in performOAuthFlow)\n\n // Attempt token refresh\n const newTokens = await refreshAuthorization(authServerUrl, {\n clientInformation: credentials.clientInfo,\n metadata: authMetadata,\n refreshToken: credentials.tokens.refresh_token,\n });\n\n logger.info(\"Successfully refreshed access token\", {\n clientId: credentials.clientInfo.client_id,\n expiresIn: newTokens.expires_in,\n hasNewRefreshToken: !!newTokens.refresh_token,\n });\n\n // Persist the new tokens\n const tokensObtainedAt = new Date().toISOString();\n await credentialStore.update(serverUrl.toString(), {\n tokens: newTokens,\n tokensObtainedAt,\n });\n\n return {\n clientInfo: credentials.clientInfo,\n fromCache: true,\n tokens: newTokens,\n };\n } catch (error) {\n logger.warn(\"Failed to refresh access token, will require re-authorization\", {\n error: String(error),\n serverUrl: serverUrl.toString(),\n });\n\n // Invalidate the tokens but keep the client info\n await credentialStore.update(serverUrl.toString(), {\n tokens: undefined,\n tokensObtainedAt: undefined,\n });\n\n return undefined;\n }\n}\n","/**\n * MCP Transport Utilities\n *\n * Simple transport creation for remote MCP servers.\n * Transport type is determined by trying Streamable HTTP first,\n * falling back to SSE if the endpoint doesn't support it.\n */\n\nimport type { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n\nimport { logger } from \"./logger.js\";\n\nexport type TransportType = \"sse\" | \"streamable-http\";\n\n/** Order in which transports are tried (per MCP spec: Streamable HTTP first, SSE fallback) */\nconst TRANSPORT_PRIORITY: TransportType[] = [\"streamable-http\", \"sse\"];\n\n/** Result of a successful transport connection */\nexport interface TransportConnectionResult {\n /** The connected client instance */\n client: Client;\n /** The transport type that successfully connected */\n transportType: TransportType;\n}\n\n/**\n * Connects to a remote MCP server, trying transports in order.\n * Per MCP spec: tries Streamable HTTP first, falls back to SSE if not supported (404/405).\n *\n * Creates a fresh client for each transport attempt to avoid state issues.\n * The MCP SDK Client maintains internal connection state that does not properly\n * reset after a failed connect attempt, so reusing the same client instance\n * across transport fallbacks can cause \"already connected\" or similar errors.\n *\n * @param createClient - Factory function to create a new Client instance\n * @param url - The URL of the remote MCP server\n * @param requestInit - Optional request init for the transport (headers, etc.)\n * @returns The connected client and transport type\n * @throws The error from the last transport attempt if none succeed,\n * or immediately if an error is not a \"transport not supported\" error\n */\nexport async function connectWithTransportFallback(\n createClient: () => Client,\n url: string,\n requestInit?: RequestInit\n): Promise<TransportConnectionResult> {\n let lastError: unknown;\n\n for (const transportType of TRANSPORT_PRIORITY) {\n // Create a fresh client for each transport attempt to avoid state issues\n const client = createClient();\n try {\n const transport = createTransport(url, transportType, requestInit);\n await client.connect(transport);\n return { client, transportType };\n } catch (err) {\n lastError = err;\n\n if (isTransportNotSupportedError(err)) {\n logger.info(`${transportType} not supported, trying next transport...`);\n continue;\n }\n\n // Non-transport error (e.g., 401, network error) - propagate immediately\n throw err;\n }\n }\n\n // All transports failed with \"not supported\" errors\n throw lastError ?? new Error(\"No compatible transport found\");\n}\n\n/**\n * Creates a transport instance for the given type.\n */\nexport function createTransport(\n url: string,\n type: TransportType,\n requestInit?: RequestInit\n): SSEClientTransport | StreamableHTTPClientTransport {\n const parsedUrl = new URL(url);\n\n if (type === \"streamable-http\") {\n return new StreamableHTTPClientTransport(parsedUrl, { requestInit });\n }\n\n return new SSEClientTransport(parsedUrl, { requestInit });\n}\n\n/**\n * Checks if an error indicates the transport/endpoint is not supported (404/405).\n * This means we should try a different transport type.\n */\nexport function isTransportNotSupportedError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n\n const status = (err as { status?: number }).status;\n const code = (err as { code?: number }).code;\n const message = (err as { message?: string }).message;\n\n // Check status codes that indicate endpoint doesn't support this transport\n if (status === 404 || status === 405 || code === 404 || code === 405) {\n return true;\n }\n\n // Check error message for these status codes\n if (message && /404|not found|405|method not allowed/i.test(String(message))) {\n return true;\n }\n\n return false;\n}\n","import { Transform } from \"node:stream\";\n\n/**\n * Filters out lines that do not start with '{' from the input stream.\n * We use this to drop anything that is obviously not a JSON-RPC message.\n */\nexport class JSONFilterTransform extends Transform {\n private buffer = \"\";\n\n constructor() {\n super({ objectMode: false });\n }\n\n _flush(callback: (error: Error | null, chunk: Buffer | null) => void) {\n // Handle any remaining data in buffer\n if (this.buffer.trim().startsWith(\"{\")) {\n callback(null, Buffer.from(this.buffer));\n } else {\n callback(null, null);\n }\n }\n\n _transform(chunk: Buffer, _encoding: string, callback: (error: Error | null, chunk: Buffer | null) => void) {\n this.buffer += chunk.toString();\n const lines = this.buffer.split(\"\\n\");\n\n // Keep the last incomplete line in the buffer\n this.buffer = lines.pop() || \"\";\n\n // Filter lines that start with '{' - silently ignore non-JSON lines\n const jsonLines = lines.filter((line) => line.trim().startsWith(\"{\"));\n\n if (jsonLines.length > 0) {\n // Send filtered lines with newlines\n const output = `${jsonLines.join(\"\\n\")}\\n`;\n\n callback(null, Buffer.from(output));\n } else {\n callback(null, null);\n }\n }\n}\n","/**\n * Forked from https://github.com/modelcontextprotocol/typescript-sdk/blob/a1608a6513d18eb965266286904760f830de96fe/src/client/stdio.ts\n */\n\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\n\nimport {\n ReadBuffer,\n serializeMessage,\n} from \"@modelcontextprotocol/sdk/shared/stdio.js\";\nimport { type ChildProcess, type IOType, spawn } from \"node:child_process\";\nimport { PassThrough, type Stream } from \"node:stream\";\n\nimport { JSONFilterTransform } from \"./JSONFilterTransform.js\";\nimport { ProcessSpawnError, TransportError } from \"./lib/errors.js\";\nimport { addBreadcrumbSafe, captureExceptionSafe } from \"./lib/sentry.js\";\n\nexport type StdioServerParameters = {\n\t/**\n\t * Command line arguments to pass to the executable.\n\t */\n\targs?: string[];\n\n\t/**\n\t * The executable to run to start the server.\n\t */\n\tcommand: string;\n\n\t/**\n\t * The working directory to use when spawning the process.\n\t *\n\t * If not specified, the current working directory will be inherited.\n\t */\n\tcwd?: string;\n\n\t/**\n\t * The environment to use when spawning the process.\n\t *\n\t * If not specified, the result of getDefaultEnvironment() will be used.\n\t */\n\tenv: Record<string, string>;\n\n\t/**\n\t * A function to call when an event occurs.\n\t */\n\tonEvent?: (event: TransportEvent) => void;\n\n\t/**\n\t * When true, spawn the child process using the user's shell.\n\t */\n\tshell?: boolean;\n\n\t/**\n\t * How to handle stderr of the child process. This matches the semantics of Node's `child_process.spawn`.\n\t *\n\t * The default is \"inherit\", meaning messages to stderr will be printed to the parent process's stderr.\n\t */\n\tstderr?: IOType | number | Stream;\n};\n\ntype TransportEvent =\n\t| {\n\t\t\tchunk: string;\n\t\t\ttype: \"data\";\n\t }\n\t| {\n\t\t\terror: Error;\n\t\t\ttype: \"error\";\n\t }\n\t| {\n\t\t\tmessage: JSONRPCMessage;\n\t\t\ttype: \"message\";\n\t }\n\t| {\n\t\t\ttype: \"close\";\n\t };\n\n/**\n * Client transport for stdio: this will connect to a server by spawning a process and communicating with it over stdin/stdout.\n *\n * This transport is only available in Node.js environments.\n */\nexport class StdioClientTransport implements Transport {\n\tonclose?: () => void;\n\n\tonerror?: (error: Error) => void;\n\tonmessage?: (message: JSONRPCMessage) => void;\n\t/**\n\t * The child process pid spawned by this transport.\n\t *\n\t * This is only available after the transport has been started.\n\t */\n\tget pid(): null | number {\n\t\treturn this._process?.pid ?? null;\n\t}\n\t/**\n\t * The stderr stream of the child process, if `StdioServerParameters.stderr` was set to \"pipe\" or \"overlapped\".\n\t *\n\t * If stderr piping was requested, a PassThrough stream is returned _immediately_, allowing callers to\n\t * attach listeners before the start method is invoked. This prevents loss of any early\n\t * error output emitted by the child process.\n\t */\n\tget stderr(): null | Stream {\n\t\tif (this._stderrStream) {\n\t\t\treturn this._stderrStream;\n\t\t}\n\n\t\treturn this._process?.stderr ?? null;\n\t}\n\tprivate _abortController: AbortController = new AbortController();\n\tprivate _process?: ChildProcess;\n\tprivate _readBuffer: ReadBuffer = new ReadBuffer();\n\tprivate _serverParams: StdioServerParameters;\n\tprivate _stderrStream: null | PassThrough = null;\n\n\tprivate onEvent?: (event: TransportEvent) => void;\n\n\tconstructor(server: StdioServerParameters) {\n\t\tthis._serverParams = server;\n\t\tif (server.stderr === \"pipe\" || server.stderr === \"overlapped\") {\n\t\t\tthis._stderrStream = new PassThrough();\n\t\t}\n\t\tthis.onEvent = server.onEvent;\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.onEvent?.({\n\t\t\ttype: \"close\",\n\t\t});\n\n\t\tthis._abortController.abort();\n\t\tthis._process = undefined;\n\t\tthis._readBuffer.clear();\n\t}\n\n\tsend(message: JSONRPCMessage): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (!this._process?.stdin) {\n\t\t\t\tthrow new Error(\"Not connected\");\n\t\t\t}\n\n\t\t\tconst json = serializeMessage(message);\n\t\t\tif (this._process.stdin.write(json)) {\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\tthis._process.stdin.once(\"drain\", resolve);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Starts the server process and prepares to communicate with it.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this._process) {\n\t\t\tthrow new Error(\n\t\t\t\t\"StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.\",\n\t\t\t);\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis._process = spawn(\n\t\t\t\tthis._serverParams.command,\n\t\t\t\tthis._serverParams.args ?? [],\n\t\t\t\t{\n\t\t\t\t\tcwd: this._serverParams.cwd,\n\t\t\t\t\tenv: this._serverParams.env,\n\t\t\t\t\tshell: this._serverParams.shell ?? false,\n\t\t\t\t\tsignal: this._abortController.signal,\n\t\t\t\t\tstdio: [\"pipe\", \"pipe\", this._serverParams.stderr ?? \"inherit\"],\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tthis._process.on(\"error\", (error) => {\n\t\t\t\tif (error.name === \"AbortError\") {\n\t\t\t\t\t// Expected when close() is called.\n\t\t\t\t\tthis.onclose?.();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Capture spawn errors with context\n\t\t\t\tconst spawnError = new ProcessSpawnError(\n\t\t\t\t\t`Process spawn error: ${error.message}`,\n\t\t\t\t\tthis._serverParams.command,\n\t\t\t\t\tthis._serverParams.args,\n\t\t\t\t\t{\n\t\t\t\t\t\tcwd: this._serverParams.cwd,\n\t\t\t\t\t\terrorCode: (error as NodeJS.ErrnoException).code,\n\t\t\t\t\t\terrorErrno: (error as NodeJS.ErrnoException).errno,\n\t\t\t\t\t\terrorSyscall: (error as NodeJS.ErrnoException).syscall,\n\t\t\t\t\t\tshell: this._serverParams.shell,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(spawnError);\n\n\t\t\t\treject(error);\n\t\t\t\tthis.onerror?.(error);\n\t\t\t});\n\n\t\t\tthis._process.on(\"spawn\", () => {\n\t\t\t\taddBreadcrumbSafe(\"Process spawned successfully\", \"transport\", \"info\", {\n\t\t\t\t\targCount: this._serverParams.args?.length,\n\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\t\t\tthis._process.on(\"close\", (_code) => {\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\ttype: \"close\",\n\t\t\t\t});\n\n\t\t\t\tthis._process = undefined;\n\t\t\t\tthis.onclose?.();\n\t\t\t});\n\n\t\t\tthis._process.stdin?.on(\"error\", (error) => {\n\t\t\t\tconst transportError = new TransportError(\n\t\t\t\t\t`Stdin error: ${error.message}`,\n\t\t\t\t\t\"stdio\",\n\t\t\t\t\t\"write\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(transportError);\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\terror,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t});\n\n\t\t\t\tthis.onerror?.(error);\n\t\t\t});\n\n\t\t\tconst jsonFilterTransform = new JSONFilterTransform();\n\n\t\t\tthis._process.stdout?.pipe(jsonFilterTransform);\n\n\t\t\tjsonFilterTransform.on(\"data\", (chunk) => {\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\tchunk: chunk.toString(),\n\t\t\t\t\ttype: \"data\",\n\t\t\t\t});\n\n\t\t\t\tthis._readBuffer.append(chunk);\n\t\t\t\tthis.processReadBuffer();\n\t\t\t});\n\n\t\t\tjsonFilterTransform.on(\"error\", (error) => {\n\t\t\t\tconst transportError = new TransportError(\n\t\t\t\t\t`JSON filter error: ${error.message}`,\n\t\t\t\t\t\"stdio\",\n\t\t\t\t\t\"parse\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(transportError);\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\terror,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t});\n\n\t\t\t\tthis.onerror?.(error);\n\t\t\t});\n\n\t\t\tif (this._stderrStream && this._process.stderr) {\n\t\t\t\tthis._process.stderr.pipe(this._stderrStream);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate processReadBuffer() {\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tconst message = this._readBuffer.readMessage();\n\n\t\t\t\tif (message === null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\tmessage,\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t});\n\n\t\t\t\tthis.onmessage?.(message);\n\t\t\t} catch (error) {\n\t\t\t\tlet message: JSONRPCMessage | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tmessage = this._readBuffer.readMessage();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore error when trying to read message for context\n\t\t\t\t}\n\n\t\t\t\tconst transportError = new TransportError(\n\t\t\t\t\t`Message processing error: ${(error as Error).message}`,\n\t\t\t\t\t\"stdio\",\n\t\t\t\t\t\"process\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcommand: this._serverParams.command,\n\t\t\t\t\t\tmessageType: message ? typeof message : \"unknown\",\n\t\t\t\t\t\tpid: this._process?.pid,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tcaptureExceptionSafe(transportError);\n\n\t\t\t\tthis.onEvent?.({\n\t\t\t\t\terror: error as Error,\n\t\t\t\t\ttype: \"error\",\n\t\t\t\t});\n\n\t\t\t\tthis.onerror?.(error as Error);\n\t\t\t}\n\t\t}\n\t}\n}\n","import { setTimeout } from \"node:timers\";\nimport util from \"node:util\";\nimport { UnauthorizedError } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { EventSource } from \"eventsource\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nimport { AccessControlAuthorizer } from \"../accessControl/authorizer.js\";\nimport { AccessControlClient } from \"../accessControl/client.js\";\nimport { OAuthCredentialStore, performOAuthFlow } from \"../auth/index.js\";\nimport { config } from \"../config/env.js\";\nimport { AccessControlBlockError, ConfigurationError, ProxyConnectionError } from \"../lib/errors.js\";\nimport { logger } from \"../lib/logger.js\";\nimport { addBreadcrumbSafe, addGlobalTags, captureExceptionSafe, initSentrySafe } from \"../lib/sentry.js\";\nimport { initializeTrafficMirror } from \"../lib/trafficMirror.js\";\nimport { connectWithTransportFallback, type TransportType } from \"../lib/transportDetection.js\";\nimport { proxyServer } from \"../proxyServer.js\";\nimport { StdioClientTransport } from \"../StdioClientTransport.js\";\nimport { startHTTPServer } from \"../startHTTPServer.js\";\nimport { getSessionData, type RemoteSessionCliArgs, type StdioSessionCliArgs } from \"../utils/getUserData.js\";\nimport { normalizeUrl } from \"../utils/normalizeUrl.js\";\n\nutil.inspect.defaultOptions.depth = 8;\n\nif (!(\"EventSource\" in global)) {\n // @ts-expect-error - figure out how to use --experimental-eventsource with vitest\n global.EventSource = EventSource;\n}\n\n// Export ProxyType for use by other modules\nexport type ProxyType = \"remote\" | \"stdio\";\n\nexport const main = async () => {\n const argv = await yargs(hideBin(process.argv))\n .scriptName(\"mcp-gateway\")\n .command(\"stdio [command] [args...]\", \"Run a local command with stdio transport (default)\", (yargs) => {\n return yargs\n .positional(\"command\", {\n describe: \"The command to run\",\n type: \"string\",\n })\n .positional(\"args\", {\n array: true,\n describe: \"The arguments to pass to the command\",\n type: \"string\",\n });\n })\n .command(\"remote\", \"Connect to a remote MCP server\", (yargs) => {\n return yargs.options({\n headers: {\n describe: \"Headers for remote authentication (format: key:value)\",\n string: true,\n type: \"array\",\n },\n \"remote-transport\": {\n choices: [\"sse\", \"stream\"],\n deprecated: \"Transport is now auto-detected per MCP spec. This option is ignored.\",\n describe: \"(Deprecated) The transport type - now auto-detected\",\n type: \"string\",\n },\n url: {\n demandOption: true,\n describe: \"The URL of the remote MCP server\",\n type: \"string\",\n },\n });\n })\n .command(\"$0 [command] [args...]\", \"Run a command with MCP arguments (backward compatible)\", (yargs) => {\n return yargs\n .positional(\"command\", {\n describe: \"The command to run\",\n type: \"string\",\n })\n .positional(\"args\", {\n array: true,\n describe: \"The arguments to pass to the command\",\n type: \"string\",\n });\n })\n .env(\"MCP_GATEWAY\")\n .parserConfiguration({\n \"populate--\": true,\n })\n .options({\n // NEW: Scanner/Gateway configuration CLI args (these override env vars)\n // Note: These are pre-parsed in mcp-gateway.ts and set as env vars before this file loads\n \"access-control-url\": {\n describe: \"URL of the access control service (overrides MCP_GATEWAY_ACCESS_CONTROL_URL)\",\n type: \"string\",\n },\n debug: {\n default: false,\n describe: \"Enable debug logging\",\n type: \"boolean\",\n },\n env: {\n describe: \"Environment variables to pass to the command (format: KEY=value)\",\n string: true,\n type: \"array\",\n },\n gracefulShutdownTimeout: {\n default: 5000,\n describe: \"The timeout (in milliseconds) for graceful shutdown\",\n type: \"number\",\n },\n port: {\n describe: \"Run as HTTP server on the specified port instead of stdio\",\n type: \"number\",\n },\n \"scanner-api-key\": {\n describe: \"API key for scanner authentication (overrides MCP_GATEWAY_SCANNER_API_KEY)\",\n type: \"string\",\n },\n \"scanner-fail-open\": {\n describe: \"Allow requests when scanner is unavailable (overrides MCP_GATEWAY_SCANNER_FAIL_OPEN)\",\n type: \"boolean\",\n },\n \"scanner-timeout-ms\": {\n describe: \"Scanner request timeout in milliseconds (overrides MCP_GATEWAY_SCANNER_TIMEOUT_MS)\",\n type: \"number\",\n },\n \"scanner-url\": {\n describe: \"URL of the scanner/evaluator service (overrides MCP_GATEWAY_SCANNER_URL)\",\n type: \"string\",\n },\n shell: {\n default: process.platform === \"win32\",\n describe: \"Spawn the server via the user's shell (defaults to true on Windows)\",\n type: \"boolean\",\n },\n })\n .help()\n .parseAsync();\n\n // Determine the proxy type based on the command\n let proxyType: ProxyType;\n let finalCommand: string | undefined;\n let finalArgs: string[] | undefined;\n let remoteUrl: string | undefined;\n // Transport type used for connection (for logging)\n let usedTransport: TransportType | undefined;\n const remoteHeaders: Record<string, string> = {};\n\n // Parse headers if provided\n if (argv.headers) {\n for (const header of argv.headers as string[]) {\n const [key, ...valueParts] = header.split(\":\");\n if (key && valueParts.length > 0) {\n remoteHeaders[key.trim()] = valueParts.join(\":\").trim();\n }\n }\n }\n\n // Initialize traffic scanner from config\n const scannerHeaders = { ...config.scanner.headers };\n\n logger.debug(\"Initializing traffic scanner\", {\n failOpen: config.scanner.failOpen,\n hasApiKey: !!config.scanner.apiKey,\n scannerEnabled: config.scanner.enabled,\n scannerUrl: config.scanner.url,\n timeoutMs: config.scanner.timeoutMs,\n });\n\n // Determine proxy type based on command\n if (argv._[0] === \"remote\") {\n proxyType = \"remote\";\n\n // Warn if deprecated remote-transport argument is used\n if (argv[\"remote-transport\"]) {\n logger.warn(\n \"The --remote-transport option is deprecated and will be ignored. \" +\n \"Transport is now auto-detected per MCP spec (Streamable HTTP with SSE fallback).\",\n { providedTransport: argv[\"remote-transport\"] }\n );\n }\n\n const rawUrl = argv.url as string;\n if (!rawUrl) {\n throw new ConfigurationError(\"Remote URL is required for remote proxy type\", \"url\");\n }\n // Normalize URL to avoid redirect issues on Windows\n // Windows may convert POST to GET on 301/302 redirects (e.g., trailing slash differences)\n remoteUrl = normalizeUrl(rawUrl);\n if (remoteUrl !== rawUrl) {\n logger.debug(\"Normalized remote URL\", { normalizedUrl: remoteUrl, originalUrl: rawUrl });\n }\n } else {\n // Default to stdio for backward compatibility\n proxyType = \"stdio\";\n\n // If -- separator was used, first item in argv[\"--\"] is the command\n if (argv[\"--\"] && (argv[\"--\"] as string[]).length > 0) {\n const dashDashArgs = argv[\"--\"] as string[];\n finalCommand = dashDashArgs[0];\n finalArgs = dashDashArgs.slice(1);\n } else {\n // Use positional arguments\n finalCommand = argv.command as string;\n finalArgs = (argv.args as string[]) || [];\n }\n\n if (!finalCommand) {\n throw new ConfigurationError(\"No command specified for stdio proxy\", \"command\");\n }\n }\n\n // Parse environment variables\n const customEnv: Record<string, string> = {};\n if (argv.env) {\n for (const envVar of argv.env as string[]) {\n const [key, ...valueParts] = envVar.split(\"=\");\n if (key && valueParts.length > 0) {\n customEnv[key.trim()] = valueParts.join(\"=\").trim();\n }\n }\n }\n\n // Build session data and initialize access control client if configured\n let accessControlClient: AccessControlClient | undefined;\n if (config.accessControl.url && config.scanner.apiKey) {\n const sessionDataBase = await getSessionData();\n\n const sessionData =\n proxyType === \"stdio\"\n ? {\n ...sessionDataBase,\n proxyType: \"stdio\" as const,\n stdioCliArgs: {\n args: finalArgs as string[],\n command: finalCommand as string,\n proxyType: \"stdio\" as const,\n },\n }\n : {\n ...sessionDataBase,\n proxyType: \"remote\" as const,\n remoteCliArgs: {\n proxyType: \"remote\" as const,\n url: remoteUrl as string,\n },\n };\n\n accessControlClient = new AccessControlClient({\n apiKey: config.scanner.apiKey,\n headers: config.accessControl.headers,\n sessionData,\n timeoutMs: config.accessControl.timeoutMs,\n url: config.accessControl.url,\n });\n }\n\n // Determine server name for error messages\n const serverName = proxyType === \"remote\" ? remoteUrl : finalCommand;\n\n // Initialize access control authorizer\n const accessControlAuthorizer = new AccessControlAuthorizer({\n client: accessControlClient,\n enabled: config.accessControl.enabled,\n serverName,\n });\n\n // Initialize OAuth credential store for remote connections\n const oauthCredentialStore = new OAuthCredentialStore({\n storageDir: config.oauthDir,\n });\n\n // Factory function to create a new Client instance\n const createClient = (): Client =>\n new Client(\n {\n name: \"mcp-gateway\",\n version: config.appVersion,\n },\n {\n capabilities: {},\n }\n );\n\n const connect = async (client: Client): Promise<Client> => {\n // Check access control (async - may query backend)\n const isAllowed = await accessControlAuthorizer.isAllowed();\n if (!isAllowed) {\n const reason = accessControlAuthorizer.getBlockReason();\n logger.warn(\"MCP server blocked by access control\", {\n proxyType,\n reason,\n });\n\n throw new AccessControlBlockError(reason, \"policy_block\");\n }\n\n logger.info(\"MCP server allowed by access control\");\n\n if (proxyType === \"stdio\") {\n // Merge custom CLI environment variables with process.env for child processes\n // Note: This is for MCP server processes, not the proxy's internal configuration\n const mergedEnv = {\n ...process.env,\n ...customEnv,\n } as Record<string, string>;\n\n const transport = new StdioClientTransport({\n args: finalArgs,\n command: finalCommand as string,\n env: mergedEnv,\n onEvent: (event) => {\n if (argv.debug) {\n logger.debug(\"Transport event\", { event });\n }\n },\n shell: argv.shell,\n // We want to passthrough stderr from the MCP server to enable better debugging\n stderr: \"inherit\",\n });\n\n try {\n await client.connect(transport);\n return client;\n } catch (error) {\n throw new ProxyConnectionError(`Failed to connect to stdio process: ${error}`, undefined, \"stdio\", {\n argCount: finalArgs?.length,\n command: finalCommand,\n });\n }\n } else if (proxyType === \"remote\") {\n // Connect to remote server\n // Strategy: Try Streamable HTTP first, fall back to SSE if not supported (404/405)\n // Handle OAuth (401) separately - if auth fails, get token and retry the whole process\n\n const url = remoteUrl as string;\n\n // Build request init with headers and optional auth token\n const buildRequestInit = (authToken?: string): RequestInit | undefined => {\n const headers: Record<string, string> = { ...remoteHeaders };\n if (authToken) {\n headers.Authorization = `Bearer ${authToken}`;\n }\n return Object.keys(headers).length > 0 ? { headers } : undefined;\n };\n\n // Check if error is a 401 (requires OAuth)\n const is401Error = (err: unknown): boolean => {\n if (err instanceof UnauthorizedError) return true;\n if (err && typeof err === \"object\") {\n const status = (err as { status?: number }).status;\n const code = (err as { code?: number }).code;\n const message = (err as { message?: string }).message;\n if (status === 401 || code === 401) return true;\n if (message && /unauthorized|401/i.test(String(message))) return true;\n }\n return false;\n };\n\n // Get OAuth token (from cache or via flow)\n const getOAuthToken = async (): Promise<string> => {\n const oauthResult = await performOAuthFlow(url, { credentialStore: oauthCredentialStore });\n logger.info(oauthResult.fromCache ? \"Using cached OAuth credentials\" : \"OAuth flow completed successfully\");\n return oauthResult.tokens.access_token;\n };\n\n let connectedClient: Client;\n try {\n // Check for cached OAuth token\n const cached = await oauthCredentialStore.load(url);\n let authToken =\n cached && oauthCredentialStore.hasValidAccessToken(cached) ? cached.tokens?.access_token : undefined;\n let usedFreshOAuth = false; // Track if we've already tried with a fresh token\n\n try {\n // Try connecting with transport fallback (Streamable HTTP → SSE)\n // Uses a client factory to create fresh clients for each transport attempt\n const result = await connectWithTransportFallback(createClient, url, buildRequestInit(authToken));\n connectedClient = result.client;\n usedTransport = result.transportType;\n logger.info(\"Connected to remote server\", { hasAuth: !!authToken, transport: usedTransport });\n } catch (err) {\n // Handle 401 errors - need OAuth authentication or token refresh\n if (is401Error(err)) {\n // If we already tried with a fresh OAuth token, don't retry again (avoid infinite loop)\n if (usedFreshOAuth) {\n throw err;\n }\n\n // If cached token was rejected, invalidate it so performOAuthFlow doesn't return it again\n if (authToken) {\n logger.info(\"Cached OAuth token rejected (possibly revoked), invalidating and re-authorizing...\");\n await oauthCredentialStore.update(url, {\n tokens: undefined,\n tokensObtainedAt: undefined,\n });\n } else {\n logger.info(\"Server requires OAuth, starting authentication flow...\");\n }\n\n authToken = await getOAuthToken();\n usedFreshOAuth = true;\n const result = await connectWithTransportFallback(createClient, url, buildRequestInit(authToken));\n connectedClient = result.client;\n usedTransport = result.transportType;\n logger.info(\"Connected to remote server with OAuth\", { transport: usedTransport });\n } else {\n throw err;\n }\n }\n return connectedClient;\n } catch (error) {\n if (error instanceof ProxyConnectionError) throw error;\n throw new ProxyConnectionError(\n `Failed to connect to remote server: ${error instanceof Error ? error.message : String(error)}`,\n url,\n usedTransport,\n { headerCount: Object.keys(remoteHeaders).length }\n );\n }\n }\n\n // Should never reach here, but satisfy TypeScript\n return client;\n };\n\n const proxy = async () => {\n // If port is specified, start HTTP server mode\n if (argv.port) {\n const cliArgs =\n proxyType === \"stdio\"\n ? ({\n args: finalArgs as string[],\n command: finalCommand as string,\n proxyType,\n } as StdioSessionCliArgs)\n : ({\n proxyType,\n url: remoteUrl as string,\n } as RemoteSessionCliArgs);\n\n // Initialize traffic evaluator with scanner config once for HTTP server mode\n await initializeTrafficMirror({\n cliArgs,\n scanApiKey: config.scanner.apiKey,\n // Scanner config from env vars\n scanEnabled: config.scanner.enabled,\n scanFailOpen: config.scanner.failOpen,\n scanHeaders: scannerHeaders,\n scanTimeoutMs: config.scanner.timeoutMs,\n scanUrl: config.scanner.url || \"\",\n });\n\n let client: Client | undefined;\n const httpServer = await startHTTPServer({\n createServer: async () => {\n // Create initial client, but connect() may return a different instance for remote connections\n const initialClient = createClient();\n client = await connect(initialClient);\n\n const serverVersion = client.getServerVersion() as {\n name: string;\n version: string;\n };\n\n const serverCapabilities = client.getServerCapabilities() as {\n capabilities: Record<string, unknown>;\n };\n\n const server = new Server(serverVersion, {\n capabilities: serverCapabilities,\n });\n\n proxyServer({\n authorizer: accessControlAuthorizer,\n client,\n server,\n serverCapabilities,\n });\n\n return server;\n },\n onClose: async (server) => {\n if (client) {\n await client.close();\n }\n await server.close();\n },\n port: argv.port,\n });\n\n logger.info(\"MCP gateway started as HTTP server\", { port: argv.port, proxyType });\n if (proxyType === \"stdio\") {\n logger.info(\"Running command\", { args: finalArgs?.join(\" \") || \"\", command: finalCommand });\n } else {\n logger.info(\"Remote server configured (connection deferred until client connects)\", { remoteUrl });\n }\n\n return {\n close: () => {\n return httpServer.close();\n },\n };\n }\n\n // Original stdio mode\n // Create initial client, but connect() may return a different instance for remote connections\n const initialClient = createClient();\n const client = await connect(initialClient);\n\n const serverVersion = client.getServerVersion() as {\n name: string;\n version: string;\n };\n\n const serverCapabilities = client.getServerCapabilities() as {\n capabilities: Record<string, unknown>;\n };\n\n // Always create a stdio server for client-facing interface\n const server = new Server(serverVersion, {\n capabilities: serverCapabilities,\n });\n\n proxyServer({\n authorizer: accessControlAuthorizer,\n client,\n server,\n serverCapabilities,\n });\n\n const stdioTransport = new StdioServerTransport();\n\n const cliArgs =\n proxyType === \"stdio\"\n ? ({\n args: finalArgs as string[],\n command: finalCommand as string,\n proxyType,\n } as StdioSessionCliArgs)\n : ({\n proxyType,\n url: remoteUrl as string,\n } as RemoteSessionCliArgs);\n\n // Initialize traffic evaluator with scanner config\n await initializeTrafficMirror({\n cliArgs,\n scanApiKey: config.scanner.apiKey,\n // Scanner config from env vars\n scanEnabled: config.scanner.enabled,\n scanFailOpen: config.scanner.failOpen,\n scanHeaders: scannerHeaders,\n scanTimeoutMs: config.scanner.timeoutMs,\n scanUrl: config.scanner.url || \"\",\n });\n\n await server.connect(stdioTransport);\n\n logger.info(\"MCP gateway started\", { proxyType });\n if (proxyType === \"stdio\") {\n logger.info(\"Running command\", { args: finalArgs?.join(\" \") || \"\", command: finalCommand });\n } else {\n logger.info(\"Connected to remote server\", { remoteUrl, transport: usedTransport });\n }\n\n return {\n close: () => {\n // Close the client connection\n return client.close();\n },\n };\n };\n\n const createGracefulShutdown = ({\n server,\n timeout,\n }: {\n server: { close: () => Promise<void> | void };\n timeout: number;\n }) => {\n const gracefulShutdown = async () => {\n logger.info(\"Received shutdown signal; shutting down\");\n\n try {\n await server.close();\n process.exit(0);\n } catch (error) {\n const errorMessage = `Error during shutdown: ${error}`;\n logger.error(\"Error during shutdown\", { error: errorMessage });\n\n captureExceptionSafe(\n error instanceof Error ? error : new Error(errorMessage),\n {\n shutdownTimeout: timeout,\n },\n {\n feature: \"shutdown\",\n module: \"cli\",\n operation: \"close\",\n }\n );\n\n process.exit(1);\n }\n };\n\n const timeoutHandler = async () => {\n setTimeout(() => {\n logger.error(\"Graceful shutdown timeout exceeded, forcing exit\");\n process.exit(1);\n }, timeout).unref();\n\n await gracefulShutdown();\n };\n\n process.once(\"SIGTERM\", timeoutHandler);\n process.once(\"SIGINT\", timeoutHandler);\n\n return () => {\n return server.close();\n };\n };\n\n // Initialize Sentry before any other operations\n initSentrySafe();\n\n // Add global context for all Sentry events\n addGlobalTags({\n \"proxy.mode\": proxyType === \"stdio\" ? \"local\" : \"remote\",\n \"proxy.transport\": proxyType === \"remote\" ? \"auto-detect\" : \"stdio\",\n \"proxy.type\": proxyType,\n });\n\n addBreadcrumbSafe(\"MCP Gateway starting\", \"lifecycle\", \"info\", {\n command: finalCommand,\n proxyType,\n transport: proxyType === \"remote\" ? \"auto-detect\" : \"stdio\",\n url: remoteUrl,\n });\n\n try {\n const server = await proxy();\n\n createGracefulShutdown({\n server,\n timeout: argv.gracefulShutdownTimeout,\n });\n } catch (error) {\n // Handle access control blocks gracefully\n if (error instanceof AccessControlBlockError) {\n // Show user-visible error message\n console.error(\"\\n❌ ACCESS DENIED\");\n console.error(\"━\".repeat(60));\n console.error(\n \"\\nThis MCP server has been blocked by Onyx because it is not authorized by your organization's access control policy. Please contact your administrator if you believe this is an error.\\n\"\n );\n\n // Also log for debugging\n logger.error(\"MCP server blocked by access control policy\", {\n error: error.message,\n reason: error.reason,\n });\n\n // Give logs time to flush before exiting cleanly\n setTimeout(() => {\n process.exit(1);\n }, 1000);\n return;\n }\n\n // Handle unexpected errors\n const errorMessage = `Could not start the proxy: ${error}`;\n logger.error(\"Unhandled error occurred\", { error: errorMessage });\n\n // Capture the startup error with context\n if (error instanceof Error) {\n captureExceptionSafe(\n error,\n {\n args: finalArgs,\n command: finalCommand,\n proxyType,\n transport: usedTransport,\n url: remoteUrl,\n },\n {\n feature: \"startup\",\n module: \"cli\",\n operation: \"initialize\",\n proxyType,\n transport: proxyType === \"remote\" ? usedTransport || \"unknown\" : \"stdio\",\n }\n );\n } else {\n captureExceptionSafe(\n new Error(errorMessage),\n {\n command: finalCommand,\n originalError: String(error),\n proxyType,\n },\n {\n feature: \"startup\",\n module: \"cli\",\n operation: \"initialize\",\n }\n );\n }\n\n // We give an extra second for logs to flush\n setTimeout(() => {\n process.exit(1);\n }, 1000);\n }\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}