@akon-labs/nexus-cli 0.1.9 → 0.1.10
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.
- package/CHANGELOG.md +21 -0
- package/LICENSE +1 -0
- package/README.md +3 -2
- package/dist/app-bridge.bundle.js +67 -0
- package/dist/cli.js +20263 -2158
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auto-model.d.ts +1 -1
- package/dist/core/auto-model.d.ts.map +1 -1
- package/dist/core/auto-model.js +1 -1
- package/dist/core/auto-model.js.map +1 -1
- package/dist/core/model-resolver.d.ts +2 -2
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +3 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +15 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +50 -0
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -3
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +5 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/websearch.d.ts.map +1 -1
- package/dist/core/tools/websearch.js +3 -3
- package/dist/core/tools/websearch.js.map +1 -1
- package/dist/extensions/gitnexus/gateway.d.ts +6 -6
- package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
- package/dist/extensions/gitnexus/gateway.js +4 -4
- package/dist/extensions/gitnexus/gateway.js.map +1 -1
- package/dist/extensions/gitnexus/index.d.ts.map +1 -1
- package/dist/extensions/gitnexus/index.js +4 -2
- package/dist/extensions/gitnexus/index.js.map +1 -1
- package/dist/extensions/mcp/abort.d.ts +3 -0
- package/dist/extensions/mcp/abort.d.ts.map +1 -0
- package/dist/extensions/mcp/abort.js +41 -0
- package/dist/extensions/mcp/abort.js.map +1 -0
- package/dist/extensions/mcp/agent-dir.d.ts +6 -0
- package/dist/extensions/mcp/agent-dir.d.ts.map +1 -0
- package/dist/extensions/mcp/agent-dir.js +18 -0
- package/dist/extensions/mcp/agent-dir.js.map +1 -0
- package/dist/extensions/mcp/agent-plugin-loader.d.ts +9 -0
- package/dist/extensions/mcp/agent-plugin-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/agent-plugin-loader.js +384 -0
- package/dist/extensions/mcp/agent-plugin-loader.js.map +1 -0
- package/dist/extensions/mcp/app-bridge.bundle.js +67 -0
- package/dist/extensions/mcp/commands.d.ts +27 -0
- package/dist/extensions/mcp/commands.d.ts.map +1 -0
- package/dist/extensions/mcp/commands.js +629 -0
- package/dist/extensions/mcp/commands.js.map +1 -0
- package/dist/extensions/mcp/config.d.ts +121 -38
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +1030 -70
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/extensions/mcp/consent-manager.d.ts +14 -0
- package/dist/extensions/mcp/consent-manager.d.ts.map +1 -0
- package/dist/extensions/mcp/consent-manager.js +60 -0
- package/dist/extensions/mcp/consent-manager.js.map +1 -0
- package/dist/extensions/mcp/direct-tools.d.ts +11 -0
- package/dist/extensions/mcp/direct-tools.d.ts.map +1 -0
- package/dist/extensions/mcp/direct-tools.js +562 -0
- package/dist/extensions/mcp/direct-tools.js.map +1 -0
- package/dist/extensions/mcp/elicitation-handler.d.ts +18 -0
- package/dist/extensions/mcp/elicitation-handler.d.ts.map +1 -0
- package/dist/extensions/mcp/elicitation-handler.js +320 -0
- package/dist/extensions/mcp/elicitation-handler.js.map +1 -0
- package/dist/extensions/mcp/error-signal.d.ts +16 -0
- package/dist/extensions/mcp/error-signal.d.ts.map +1 -0
- package/dist/extensions/mcp/error-signal.js +22 -0
- package/dist/extensions/mcp/error-signal.js.map +1 -0
- package/dist/extensions/mcp/errors.d.ts +104 -0
- package/dist/extensions/mcp/errors.d.ts.map +1 -0
- package/dist/extensions/mcp/errors.js +171 -0
- package/dist/extensions/mcp/errors.js.map +1 -0
- package/dist/extensions/mcp/glimpse-ui.d.ts +10 -0
- package/dist/extensions/mcp/glimpse-ui.d.ts.map +1 -0
- package/dist/extensions/mcp/glimpse-ui.js +67 -0
- package/dist/extensions/mcp/glimpse-ui.js.map +1 -0
- package/dist/extensions/mcp/host-html-template.d.ts +17 -0
- package/dist/extensions/mcp/host-html-template.d.ts.map +1 -0
- package/dist/extensions/mcp/host-html-template.js +442 -0
- package/dist/extensions/mcp/host-html-template.js.map +1 -0
- package/dist/extensions/mcp/index.d.ts +20 -15
- package/dist/extensions/mcp/index.d.ts.map +1 -1
- package/dist/extensions/mcp/index.js +1064 -82
- package/dist/extensions/mcp/index.js.map +1 -1
- package/dist/extensions/mcp/init.d.ts +28 -0
- package/dist/extensions/mcp/init.d.ts.map +1 -0
- package/dist/extensions/mcp/init.js +631 -0
- package/dist/extensions/mcp/init.js.map +1 -0
- package/dist/extensions/mcp/json-schema-validator.d.ts +3 -0
- package/dist/extensions/mcp/json-schema-validator.d.ts.map +1 -0
- package/dist/extensions/mcp/json-schema-validator.js +53 -0
- package/dist/extensions/mcp/json-schema-validator.js.map +1 -0
- package/dist/extensions/mcp/lifecycle.d.ts +56 -0
- package/dist/extensions/mcp/lifecycle.d.ts.map +1 -0
- package/dist/extensions/mcp/lifecycle.js +395 -0
- package/dist/extensions/mcp/lifecycle.js.map +1 -0
- package/dist/extensions/mcp/logger.d.ts +52 -0
- package/dist/extensions/mcp/logger.d.ts.map +1 -0
- package/dist/extensions/mcp/logger.js +134 -0
- package/dist/extensions/mcp/logger.js.map +1 -0
- package/dist/extensions/mcp/mcp-auth-flow.d.ts +117 -0
- package/dist/extensions/mcp/mcp-auth-flow.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-auth-flow.js +869 -0
- package/dist/extensions/mcp/mcp-auth-flow.js.map +1 -0
- package/dist/extensions/mcp/mcp-auth.d.ts +162 -0
- package/dist/extensions/mcp/mcp-auth.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-auth.js +897 -0
- package/dist/extensions/mcp/mcp-auth.js.map +1 -0
- package/dist/extensions/mcp/mcp-bearer-store.d.ts +25 -0
- package/dist/extensions/mcp/mcp-bearer-store.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-bearer-store.js +350 -0
- package/dist/extensions/mcp/mcp-bearer-store.js.map +1 -0
- package/dist/extensions/mcp/mcp-callback-server.d.ts +54 -0
- package/dist/extensions/mcp/mcp-callback-server.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-callback-server.js +454 -0
- package/dist/extensions/mcp/mcp-callback-server.js.map +1 -0
- package/dist/extensions/mcp/mcp-code.d.ts +48 -0
- package/dist/extensions/mcp/mcp-code.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-code.js +358 -0
- package/dist/extensions/mcp/mcp-code.js.map +1 -0
- package/dist/extensions/mcp/mcp-keyring-helper.cjs +89 -0
- package/dist/extensions/mcp/mcp-oauth-provider.d.ts +135 -0
- package/dist/extensions/mcp/mcp-oauth-provider.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-oauth-provider.js +491 -0
- package/dist/extensions/mcp/mcp-oauth-provider.js.map +1 -0
- package/dist/extensions/mcp/mcp-output-guard.d.ts +59 -0
- package/dist/extensions/mcp/mcp-output-guard.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-output-guard.js +343 -0
- package/dist/extensions/mcp/mcp-output-guard.js.map +1 -0
- package/dist/extensions/mcp/mcp-panel.d.ts +71 -0
- package/dist/extensions/mcp/mcp-panel.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-panel.js +973 -0
- package/dist/extensions/mcp/mcp-panel.js.map +1 -0
- package/dist/extensions/mcp/mcp-probe.d.ts +7 -0
- package/dist/extensions/mcp/mcp-probe.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-probe.js +155 -0
- package/dist/extensions/mcp/mcp-probe.js.map +1 -0
- package/dist/extensions/mcp/mcp-script-worker.mjs +130 -0
- package/dist/extensions/mcp/mcp-setup-panel.d.ts +85 -0
- package/dist/extensions/mcp/mcp-setup-panel.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-setup-panel.js +618 -0
- package/dist/extensions/mcp/mcp-setup-panel.js.map +1 -0
- package/dist/extensions/mcp/mcp-status.d.ts +12 -0
- package/dist/extensions/mcp/mcp-status.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-status.js +98 -0
- package/dist/extensions/mcp/mcp-status.js.map +1 -0
- package/dist/extensions/mcp/mcp-trace.d.ts +95 -0
- package/dist/extensions/mcp/mcp-trace.d.ts.map +1 -0
- package/dist/extensions/mcp/mcp-trace.js +252 -0
- package/dist/extensions/mcp/mcp-trace.js.map +1 -0
- package/dist/extensions/mcp/metadata-cache.d.ts +20 -0
- package/dist/extensions/mcp/metadata-cache.d.ts.map +1 -0
- package/dist/extensions/mcp/metadata-cache.js +318 -0
- package/dist/extensions/mcp/metadata-cache.js.map +1 -0
- package/dist/extensions/mcp/npx-resolver.d.ts +7 -0
- package/dist/extensions/mcp/npx-resolver.d.ts.map +1 -0
- package/dist/extensions/mcp/npx-resolver.js +505 -0
- package/dist/extensions/mcp/npx-resolver.js.map +1 -0
- package/dist/extensions/mcp/oauth.d.ts +21 -0
- package/dist/extensions/mcp/oauth.d.ts.map +1 -0
- package/dist/extensions/mcp/oauth.js +15 -0
- package/dist/extensions/mcp/oauth.js.map +1 -0
- package/dist/extensions/mcp/onboarding-state.d.ts +13 -0
- package/dist/extensions/mcp/onboarding-state.d.ts.map +1 -0
- package/dist/extensions/mcp/onboarding-state.js +65 -0
- package/dist/extensions/mcp/onboarding-state.js.map +1 -0
- package/dist/extensions/mcp/package-mcp-loader.d.ts +3 -0
- package/dist/extensions/mcp/package-mcp-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/package-mcp-loader.js +170 -0
- package/dist/extensions/mcp/package-mcp-loader.js.map +1 -0
- package/dist/extensions/mcp/panel-keys.d.ts +21 -0
- package/dist/extensions/mcp/panel-keys.d.ts.map +1 -0
- package/dist/extensions/mcp/panel-keys.js +29 -0
- package/dist/extensions/mcp/panel-keys.js.map +1 -0
- package/dist/extensions/mcp/prompts.d.ts +63 -0
- package/dist/extensions/mcp/prompts.d.ts.map +1 -0
- package/dist/extensions/mcp/prompts.js +297 -0
- package/dist/extensions/mcp/prompts.js.map +1 -0
- package/dist/extensions/mcp/proxy-modes.d.ts +15 -0
- package/dist/extensions/mcp/proxy-modes.d.ts.map +1 -0
- package/dist/extensions/mcp/proxy-modes.js +1421 -0
- package/dist/extensions/mcp/proxy-modes.js.map +1 -0
- package/dist/extensions/mcp/request-headers-command.d.ts +11 -0
- package/dist/extensions/mcp/request-headers-command.d.ts.map +1 -0
- package/dist/extensions/mcp/request-headers-command.js +334 -0
- package/dist/extensions/mcp/request-headers-command.js.map +1 -0
- package/dist/extensions/mcp/resource-tools.d.ts +2 -0
- package/dist/extensions/mcp/resource-tools.d.ts.map +1 -0
- package/dist/extensions/mcp/resource-tools.js +15 -0
- package/dist/extensions/mcp/resource-tools.js.map +1 -0
- package/dist/extensions/mcp/runtime-owner.d.ts +14 -0
- package/dist/extensions/mcp/runtime-owner.d.ts.map +1 -0
- package/dist/extensions/mcp/runtime-owner.js +84 -0
- package/dist/extensions/mcp/runtime-owner.js.map +1 -0
- package/dist/extensions/mcp/sampling-handler.d.ts +18 -0
- package/dist/extensions/mcp/sampling-handler.d.ts.map +1 -0
- package/dist/extensions/mcp/sampling-handler.js +229 -0
- package/dist/extensions/mcp/sampling-handler.js.map +1 -0
- package/dist/extensions/mcp/search-ranking.d.ts +25 -0
- package/dist/extensions/mcp/search-ranking.d.ts.map +1 -0
- package/dist/extensions/mcp/search-ranking.js +234 -0
- package/dist/extensions/mcp/search-ranking.js.map +1 -0
- package/dist/extensions/mcp/server-manager.d.ts +116 -0
- package/dist/extensions/mcp/server-manager.d.ts.map +1 -0
- package/dist/extensions/mcp/server-manager.js +1052 -0
- package/dist/extensions/mcp/server-manager.js.map +1 -0
- package/dist/extensions/mcp/session-recovery.d.ts +27 -0
- package/dist/extensions/mcp/session-recovery.d.ts.map +1 -0
- package/dist/extensions/mcp/session-recovery.js +140 -0
- package/dist/extensions/mcp/session-recovery.js.map +1 -0
- package/dist/extensions/mcp/skills/mcp-scripting/SKILL.md +38 -0
- package/dist/extensions/mcp/state.d.ts +62 -0
- package/dist/extensions/mcp/state.d.ts.map +1 -0
- package/dist/extensions/mcp/state.js +2 -0
- package/dist/extensions/mcp/state.js.map +1 -0
- package/dist/extensions/mcp/tool-approval.d.ts +11 -0
- package/dist/extensions/mcp/tool-approval.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-approval.js +156 -0
- package/dist/extensions/mcp/tool-approval.js.map +1 -0
- package/dist/extensions/mcp/tool-metadata.d.ts +11 -0
- package/dist/extensions/mcp/tool-metadata.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-metadata.js +264 -0
- package/dist/extensions/mcp/tool-metadata.js.map +1 -0
- package/dist/extensions/mcp/tool-registrar.d.ts +21 -0
- package/dist/extensions/mcp/tool-registrar.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-registrar.js +229 -0
- package/dist/extensions/mcp/tool-registrar.js.map +1 -0
- package/dist/extensions/mcp/tool-result-renderer.d.ts +83 -0
- package/dist/extensions/mcp/tool-result-renderer.d.ts.map +1 -0
- package/dist/extensions/mcp/tool-result-renderer.js +345 -0
- package/dist/extensions/mcp/tool-result-renderer.js.map +1 -0
- package/dist/extensions/mcp/ts-shape.d.ts +3 -0
- package/dist/extensions/mcp/ts-shape.d.ts.map +1 -0
- package/dist/extensions/mcp/ts-shape.js +160 -0
- package/dist/extensions/mcp/ts-shape.js.map +1 -0
- package/dist/extensions/mcp/types.d.ts +521 -0
- package/dist/extensions/mcp/types.d.ts.map +1 -0
- package/dist/extensions/mcp/types.js +294 -0
- package/dist/extensions/mcp/types.js.map +1 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts +7 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.js +37 -0
- package/dist/extensions/mcp/ui-app-bridge-helpers.js.map +1 -0
- package/dist/extensions/mcp/ui-resource-handler.d.ts +18 -0
- package/dist/extensions/mcp/ui-resource-handler.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-resource-handler.js +210 -0
- package/dist/extensions/mcp/ui-resource-handler.js.map +1 -0
- package/dist/extensions/mcp/ui-server.d.ts +52 -0
- package/dist/extensions/mcp/ui-server.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-server.js +740 -0
- package/dist/extensions/mcp/ui-server.js.map +1 -0
- package/dist/extensions/mcp/ui-session.d.ts +40 -0
- package/dist/extensions/mcp/ui-session.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-session.js +477 -0
- package/dist/extensions/mcp/ui-session.js.map +1 -0
- package/dist/extensions/mcp/ui-stream-types.d.ts +105 -0
- package/dist/extensions/mcp/ui-stream-types.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-stream-types.js +61 -0
- package/dist/extensions/mcp/ui-stream-types.js.map +1 -0
- package/dist/extensions/mcp/ui-tool-visibility.d.ts +5 -0
- package/dist/extensions/mcp/ui-tool-visibility.d.ts.map +1 -0
- package/dist/extensions/mcp/ui-tool-visibility.js +27 -0
- package/dist/extensions/mcp/ui-tool-visibility.js.map +1 -0
- package/dist/extensions/mcp/unix-socket-transport.d.ts +15 -0
- package/dist/extensions/mcp/unix-socket-transport.d.ts.map +1 -0
- package/dist/extensions/mcp/unix-socket-transport.js +85 -0
- package/dist/extensions/mcp/unix-socket-transport.js.map +1 -0
- package/dist/extensions/mcp/utils.d.ts +32 -0
- package/dist/extensions/mcp/utils.d.ts.map +1 -0
- package/dist/extensions/mcp/utils.js +336 -0
- package/dist/extensions/mcp/utils.js.map +1 -0
- package/dist/extensions/subagent/index.d.ts +61 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +177 -4
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/web-access.d.ts.map +1 -1
- package/dist/extensions/web-access.js +1 -1
- package/dist/extensions/web-access.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20057 -1947
- package/dist/index.js.map +1 -1
- package/dist/mcp-keyring-helper.cjs +89 -0
- package/dist/mcp-script-worker.mjs +130 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/response-separator.d.ts +1 -1
- package/dist/modes/interactive/components/response-separator.d.ts.map +1 -1
- package/dist/modes/interactive/components/response-separator.js +1 -1
- package/dist/modes/interactive/components/response-separator.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/rpc-entry.js +20263 -2158
- package/dist/skills/mcp-scripting/SKILL.md +38 -0
- package/dist/vendor/ai.js +280 -205
- package/docs/mcp.md +178 -0
- package/docs/pi-subagents.md +75 -0
- package/docs/quickstart.md +1 -1
- package/docs/usage.md +2 -1
- package/examples/extensions/pi-subagents-worker-policy.ts +86 -0
- package/npm-shrinkwrap.json +763 -6
- package/package.json +15 -4
- package/dist/extensions/mcp/client.d.ts +0 -86
- package/dist/extensions/mcp/client.d.ts.map +0 -1
- package/dist/extensions/mcp/client.js +0 -275
- package/dist/extensions/mcp/client.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/extensions/mcp/logger.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Centralized logging for MCP UI operations.\n * Provides structured, contextual logs with levels.\n */\n\nimport { sanitizeTerminalText } from \"./utils.ts\";\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface LogContext {\n\tserver?: string;\n\tsession?: string;\n\ttool?: string;\n\turi?: string;\n\t[key: string]: unknown;\n}\n\nexport interface LogEntry {\n\tlevel: LogLevel;\n\tmessage: string;\n\tcontext?: LogContext;\n\terror?: Error;\n\ttimestamp: Date;\n}\n\ntype LogHandler = (entry: LogEntry) => void;\n\nconst LEVEL_PRIORITY: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\nconst LEVEL_PREFIX: Record<LogLevel, string> = {\n\tdebug: \"[MCP-UI:DEBUG]\",\n\tinfo: \"[MCP-UI]\",\n\twarn: \"[MCP-UI:WARN]\",\n\terror: \"[MCP-UI:ERROR]\",\n};\n\nclass Logger {\n\tprivate minLevel: LogLevel = \"info\";\n\tprivate handlers: LogHandler[] = [];\n\tprivate defaultContext: LogContext = {};\n\n\tsetLevel(level: LogLevel): void {\n\t\tthis.minLevel = level;\n\t}\n\n\tsetDefaultContext(context: LogContext): void {\n\t\tthis.defaultContext = context;\n\t}\n\n\taddHandler(handler: LogHandler): void {\n\t\tthis.handlers.push(handler);\n\t}\n\n\tclearHandlers(): void {\n\t\tthis.handlers = [];\n\t}\n\n\tprivate shouldLog(level: LogLevel): boolean {\n\t\treturn LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.minLevel];\n\t}\n\n\tprivate emit(level: LogLevel, message: string, context?: LogContext, error?: Error): void {\n\t\tif (!this.shouldLog(level)) return;\n\n\t\tconst entry: LogEntry = {\n\t\t\tlevel,\n\t\t\tmessage,\n\t\t\tcontext: { ...this.defaultContext, ...context },\n\t\t\t...(error !== undefined ? { error } : {}),\n\t\t\ttimestamp: new Date(),\n\t\t};\n\n\t\t// Default console output\n\t\tconst prefix = LEVEL_PREFIX[level];\n\t\tconst contextStr = formatContext(entry.context);\n\t\tconst terminalMessage = sanitizeTerminalText(message);\n\t\tconst fullMessage = contextStr ? `${prefix} ${terminalMessage} ${contextStr}` : `${prefix} ${terminalMessage}`;\n\n\t\tif (level === \"error\") {\n\t\t\tconsole.error(fullMessage, error ? sanitizeTerminalText(error.stack ?? error.message) : \"\");\n\t\t} else if (level === \"warn\") {\n\t\t\tconsole.warn(fullMessage);\n\t\t} else if (level === \"debug\") {\n\t\t\tconsole.debug(fullMessage);\n\t\t} else {\n\t\t\tconsole.log(fullMessage);\n\t\t}\n\n\t\t// Custom handlers\n\t\tfor (const handler of this.handlers) {\n\t\t\ttry {\n\t\t\t\thandler(entry);\n\t\t\t} catch {\n\t\t\t\t// Ignore handler errors\n\t\t\t}\n\t\t}\n\t}\n\n\tdebug(message: string, context?: LogContext): void {\n\t\tthis.emit(\"debug\", message, context);\n\t}\n\n\tinfo(message: string, context?: LogContext): void {\n\t\tthis.emit(\"info\", message, context);\n\t}\n\n\twarn(message: string, context?: LogContext): void {\n\t\tthis.emit(\"warn\", message, context);\n\t}\n\n\terror(message: string, error?: Error, context?: LogContext): void {\n\t\tthis.emit(\"error\", message, context, error);\n\t}\n\n\t/**\n\t * Create a child logger with additional default context.\n\t */\n\tchild(context: LogContext): ChildLogger {\n\t\treturn new ChildLogger(this, context);\n\t}\n}\n\nclass ChildLogger {\n\tprivate parent: Logger;\n\tprivate context: LogContext;\n\n\tconstructor(parent: Logger, context: LogContext) {\n\t\tthis.parent = parent;\n\t\tthis.context = context;\n\t}\n\n\tdebug(message: string, context?: LogContext): void {\n\t\tthis.parent.debug(message, { ...this.context, ...context });\n\t}\n\n\tinfo(message: string, context?: LogContext): void {\n\t\tthis.parent.info(message, { ...this.context, ...context });\n\t}\n\n\twarn(message: string, context?: LogContext): void {\n\t\tthis.parent.warn(message, { ...this.context, ...context });\n\t}\n\n\terror(message: string, error?: Error, context?: LogContext): void {\n\t\tthis.parent.error(message, error, { ...this.context, ...context });\n\t}\n\n\tchild(context: LogContext): ChildLogger {\n\t\treturn new ChildLogger(this.parent, { ...this.context, ...context });\n\t}\n}\n\nfunction formatContext(context?: LogContext): string {\n\tif (!context || Object.keys(context).length === 0) return \"\";\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(context)) {\n\t\tif (value !== undefined && value !== null) {\n\t\t\tconst rendered = typeof value === \"string\" ? value : (JSON.stringify(value) ?? String(value));\n\t\t\tparts.push(`${sanitizeTerminalText(key)}=${sanitizeTerminalText(rendered)}`);\n\t\t}\n\t}\n\treturn parts.length > 0 ? `(${parts.join(\", \")})` : \"\";\n}\n\n// Singleton instance\nexport const logger = new Logger();\n\n// Enable debug mode via environment variable\nif (process.env.MCP_UI_DEBUG === \"1\" || process.env.MCP_UI_DEBUG === \"true\") {\n\tlogger.setLevel(\"debug\");\n}\n"]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Auth Flow
|
|
3
|
+
*
|
|
4
|
+
* High-level OAuth flow management using the MCP SDK's built-in auth functions.
|
|
5
|
+
*/
|
|
6
|
+
import { type AuthStorageOptions, type StoredTokens } from "./mcp-auth.ts";
|
|
7
|
+
import { type McpOAuthConfig } from "./mcp-oauth-provider.ts";
|
|
8
|
+
import { type ServerEntry } from "./types.ts";
|
|
9
|
+
/** Auth status for a server */
|
|
10
|
+
export type AuthStatus = "authenticated" | "expired" | "not_authenticated";
|
|
11
|
+
export interface McpOAuthRuntime {
|
|
12
|
+
readonly signal: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
export interface AuthenticateOptions {
|
|
15
|
+
onAuthorizationUrl?: (authorizationUrl: string) => void | Promise<void>;
|
|
16
|
+
onAuthorizationInput?: (authorizationUrl: string, signal: AbortSignal) => Promise<string | undefined>;
|
|
17
|
+
authStorageOptions?: AuthStorageOptions;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
runtime?: McpOAuthRuntime;
|
|
20
|
+
skipIssuerMetadataValidation?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function createOAuthRuntime(signal?: AbortSignal): McpOAuthRuntime;
|
|
23
|
+
export declare function hasPendingAuth(serverName: string, options?: AuthStorageOptions, runtime?: McpOAuthRuntime): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Extract OAuth configuration from a ServerEntry.
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractOAuthConfig(definition: ServerEntry): McpOAuthConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Start OAuth authentication flow for a server.
|
|
30
|
+
* Returns the authorization URL when browser authorization is required.
|
|
31
|
+
*/
|
|
32
|
+
export declare function startAuth(serverName: string, serverUrl: string, definition?: ServerEntry, options?: AuthenticateOptions): Promise<{
|
|
33
|
+
authorizationUrl: string;
|
|
34
|
+
}>;
|
|
35
|
+
/** Authorization code plus the optional RFC 9207 `iss` callback parameter. */
|
|
36
|
+
export interface AuthorizationCodeInput {
|
|
37
|
+
code: string;
|
|
38
|
+
iss?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extract an OAuth authorization code (and the RFC 9207 `iss` parameter, when
|
|
42
|
+
* present) from either a raw code, a query string, or the full localhost
|
|
43
|
+
* redirect URL copied from the browser address bar.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseAuthorizationRedirectInput(input: string, expectedState?: string): AuthorizationCodeInput;
|
|
46
|
+
/**
|
|
47
|
+
* Extract an OAuth authorization code from either a raw code, a query string,
|
|
48
|
+
* or the full localhost redirect URL copied from the browser address bar.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseAuthorizationCodeInput(input: string, expectedState?: string): string;
|
|
51
|
+
type AuthorizationResponse = {
|
|
52
|
+
input: AuthorizationCodeInput;
|
|
53
|
+
source: "callback" | "manual";
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Wait for either the localhost callback or a manually pasted redirect URL.
|
|
57
|
+
* The manual input prompt is dismissed as soon as either path finishes.
|
|
58
|
+
*/
|
|
59
|
+
export declare function waitForAuthorizationResponse(callbackPromise: Promise<AuthorizationCodeInput>, authorizationUrl: string, expectedState: string, onAuthorizationInput?: AuthenticateOptions["onAuthorizationInput"], signal?: AbortSignal): Promise<AuthorizationResponse>;
|
|
60
|
+
/**
|
|
61
|
+
* Complete OAuth authentication from manual user input.
|
|
62
|
+
*/
|
|
63
|
+
export declare function completeAuthFromInput(serverName: string, input: string, options?: AuthenticateOptions): Promise<AuthStatus>;
|
|
64
|
+
/**
|
|
65
|
+
* Complete OAuth authentication with the authorization code.
|
|
66
|
+
*/
|
|
67
|
+
export declare function completeAuth(serverName: string, authorizationCode: string | AuthorizationCodeInput, options?: AuthenticateOptions): Promise<AuthStatus>;
|
|
68
|
+
/**
|
|
69
|
+
* Perform the complete OAuth authentication flow for a server.
|
|
70
|
+
*
|
|
71
|
+
* @param serverName - The name of the MCP server
|
|
72
|
+
* @param serverUrl - The URL of the MCP server
|
|
73
|
+
* @param definition - The server definition (optional)
|
|
74
|
+
* @returns The final auth status
|
|
75
|
+
*/
|
|
76
|
+
export declare function authenticate(serverName: string, serverUrl: string, definition?: ServerEntry, options?: AuthenticateOptions): Promise<AuthStatus>;
|
|
77
|
+
/**
|
|
78
|
+
* Get a valid access token for a server, refreshing if necessary.
|
|
79
|
+
*
|
|
80
|
+
* @param serverName - The name of the MCP server
|
|
81
|
+
* @param serverUrl - The URL of the MCP server
|
|
82
|
+
* @returns The valid tokens or null if not authenticated
|
|
83
|
+
*/
|
|
84
|
+
export declare function getValidToken(serverName: string, serverUrl: string, options?: AuthenticateOptions): Promise<StoredTokens | null>;
|
|
85
|
+
/**
|
|
86
|
+
* Check the authentication status for a server.
|
|
87
|
+
*
|
|
88
|
+
* @param serverName - The name of the MCP server
|
|
89
|
+
* @returns The current auth status
|
|
90
|
+
*/
|
|
91
|
+
export declare function getAuthStatus(serverName: string, options?: AuthenticateOptions): Promise<AuthStatus>;
|
|
92
|
+
/**
|
|
93
|
+
* Remove all OAuth credentials for a server.
|
|
94
|
+
*
|
|
95
|
+
* @param serverName - The name of the MCP server
|
|
96
|
+
*/
|
|
97
|
+
export declare function removeAuth(serverName: string, options?: AuthenticateOptions): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Check if OAuth is supported for a server configuration.
|
|
100
|
+
* OAuth is supported for HTTP servers unless explicitly disabled.
|
|
101
|
+
*
|
|
102
|
+
* @param definition - The server definition
|
|
103
|
+
* @returns True if OAuth is supported
|
|
104
|
+
*/
|
|
105
|
+
export declare function supportsOAuth(definition: ServerEntry): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Initialize the OAuth system on startup.
|
|
108
|
+
* OAuth callback binding is lazy and starts from startAuth() only.
|
|
109
|
+
*/
|
|
110
|
+
export declare function initializeOAuth(runtimeOrSignal?: McpOAuthRuntime | AbortSignal): Promise<McpOAuthRuntime>;
|
|
111
|
+
/**
|
|
112
|
+
* Shutdown one OAuth runtime. The callback server remains process-shared while
|
|
113
|
+
* another runtime has pending/reserved callback state or is still active.
|
|
114
|
+
*/
|
|
115
|
+
export declare function shutdownOAuth(runtime?: McpOAuthRuntime): Promise<void>;
|
|
116
|
+
export {};
|
|
117
|
+
//# sourceMappingURL=mcp-auth-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-auth-flow.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/mcp-auth-flow.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP Auth Flow\n *\n * High-level OAuth flow management using the MCP SDK's built-in auth functions.\n */\n\nimport {\n\ttype AuthOptions,\n\textractWWWAuthenticateParams,\n\tLATEST_PROTOCOL_VERSION,\n\tauth as runSdkAuth,\n\tUnauthorizedError,\n} from \"@modelcontextprotocol/client\";\nimport open from \"open\";\nimport { VERSION } from \"../../config.ts\";\nimport { abortable, throwIfAborted } from \"./abort.ts\";\nimport {\n\ttype AuthStorageOptions,\n\tclearAllCredentials,\n\tclearClientInfo,\n\tclearCodeVerifier,\n\tclearOAuthState,\n\tclearTokens,\n\tgetAuthBaseDir,\n\tgetAuthForUrl,\n\tgetOAuthState,\n\thasStoredTokens,\n\tisTokenExpired,\n\tOAuthCredentialStoreError,\n\ttype StoredTokens,\n} from \"./mcp-auth.ts\";\nimport {\n\tcancelPendingCallback,\n\tensureCallbackServer,\n\treleaseCallbackServer,\n\tstopCallbackServer,\n\tstopCallbackServerIfIdle,\n\twaitForCallback,\n} from \"./mcp-callback-server.ts\";\nimport { type McpOAuthConfig, McpOAuthProvider } from \"./mcp-oauth-provider.ts\";\nimport { combineAbortSignals, isAbortError } from \"./runtime-owner.ts\";\nimport { isServerDisabled, type ServerEntry } from \"./types.ts\";\nimport { formatTerminalError, interpolateEnvRecord, interpolateEnvVars } from \"./utils.ts\";\n\n/** Auth status for a server */\nexport type AuthStatus = \"authenticated\" | \"expired\" | \"not_authenticated\";\n\nexport interface McpOAuthRuntime {\n\treadonly signal: AbortSignal;\n}\n\nexport interface AuthenticateOptions {\n\tonAuthorizationUrl?: (authorizationUrl: string) => void | Promise<void>;\n\tonAuthorizationInput?: (authorizationUrl: string, signal: AbortSignal) => Promise<string | undefined>;\n\tauthStorageOptions?: AuthStorageOptions;\n\tsignal?: AbortSignal;\n\truntime?: McpOAuthRuntime;\n\tskipIssuerMetadataValidation?: boolean;\n}\n\ntype AuthDiscovery = Pick<AuthOptions, \"resourceMetadataUrl\" | \"scope\" | \"skipIssuerMetadataValidation\">;\n\nfunction applyOAuthConfig(discovery: AuthDiscovery, config: McpOAuthConfig): AuthDiscovery {\n\treturn {\n\t\t...discovery,\n\t\t...(config.scope !== undefined ? { scope: config.scope } : {}),\n\t\t...(config.skipIssuerMetadataValidation === true ? { skipIssuerMetadataValidation: true } : {}),\n\t};\n}\n\ntype PendingAuth = {\n\tserverName: string;\n\tauthProvider: McpOAuthProvider;\n\tserverUrl: string;\n\tauthorizationUrl: string;\n\tdiscovery: AuthDiscovery;\n\tauthStorageOptions: AuthStorageOptions;\n};\n\ntype RuntimeState = {\n\tcontroller: AbortController;\n\tgeneration: number;\n\tpendingAuths: Map<string, PendingAuth>;\n\tpendingAuthStates: Map<string, string>;\n\tpendingAuthCleanupTimers: Map<string, ReturnType<typeof setTimeout>>;\n\tpendingAuthentications: Map<string, Promise<AuthStatus>>;\n};\n\nconst runtimeStates = new WeakMap<McpOAuthRuntime, RuntimeState>();\nconst activeRuntimes = new Set<McpOAuthRuntime>();\n\nexport function createOAuthRuntime(signal?: AbortSignal): McpOAuthRuntime {\n\tconst controller = new AbortController();\n\tconst runtime = { signal: combineAbortSignals(signal, controller.signal)! } satisfies McpOAuthRuntime;\n\truntimeStates.set(runtime, {\n\t\tcontroller,\n\t\tgeneration: 0,\n\t\tpendingAuths: new Map(),\n\t\tpendingAuthStates: new Map(),\n\t\tpendingAuthCleanupTimers: new Map(),\n\t\tpendingAuthentications: new Map(),\n\t});\n\tactiveRuntimes.add(runtime);\n\treturn runtime;\n}\n\nlet legacyRuntime = createOAuthRuntime();\nactiveRuntimes.delete(legacyRuntime);\n\nfunction getRuntime(options?: AuthenticateOptions): McpOAuthRuntime {\n\tif (options?.runtime) {\n\t\toptions.runtime.signal.throwIfAborted();\n\t\tactiveRuntimes.add(options.runtime);\n\t\treturn options.runtime;\n\t}\n\tif (legacyRuntime.signal.aborted) legacyRuntime = createOAuthRuntime();\n\tactiveRuntimes.add(legacyRuntime);\n\treturn legacyRuntime;\n}\n\nfunction getRuntimeState(runtime: McpOAuthRuntime): RuntimeState {\n\tconst state = runtimeStates.get(runtime);\n\tif (!state) throw new Error(\"Unknown OAuth runtime\");\n\treturn state;\n}\n\nfunction getPendingAuthKey(serverName: string, options: AuthStorageOptions): string {\n\treturn `${serverName}|${getAuthBaseDir(options)}`;\n}\n\nexport function hasPendingAuth(serverName: string, options?: AuthStorageOptions, runtime?: McpOAuthRuntime): boolean {\n\tconst state = getRuntimeState(runtime ?? legacyRuntime);\n\tif (options) {\n\t\treturn state.pendingAuths.has(getPendingAuthKey(serverName, options));\n\t}\n\treturn Array.from(state.pendingAuths.values()).some((pendingAuth) => pendingAuth.serverName === serverName);\n}\n\n/** Timeout for manual auth completion (5 minutes) */\nconst MANUAL_AUTH_TIMEOUT_MS = 5 * 60 * 1000;\n\n/**\n * Generate a cryptographically secure random state parameter.\n */\nfunction generateState(): string {\n\treturn Array.from(crypto.getRandomValues(new Uint8Array(32)))\n\t\t.map((b) => b.toString(16).padStart(2, \"0\"))\n\t\t.join(\"\");\n}\n\n/**\n * Extract OAuth configuration from a ServerEntry.\n */\nexport function extractOAuthConfig(definition: ServerEntry): McpOAuthConfig {\n\tif (definition.oauth === false) {\n\t\treturn {};\n\t}\n\n\tconst config: McpOAuthConfig = {};\n\tif (definition.oauth?.grantType !== undefined) config.grantType = definition.oauth.grantType;\n\tif (definition.oauth?.clientId !== undefined) {\n\t\tif (typeof definition.oauth.clientId !== \"string\") throw new Error(\"OAuth clientId must be a string\");\n\t\tconfig.clientId = interpolateEnvVars(definition.oauth.clientId);\n\t}\n\tif (definition.oauth?.clientSecret !== undefined) {\n\t\tif (typeof definition.oauth.clientSecret !== \"string\") throw new Error(\"OAuth clientSecret must be a string\");\n\t\t// Preserve command expressions for the provider; interpolation remains eager for ordinary values.\n\t\tconfig.clientSecret = definition.oauth.clientSecret.startsWith(\"!\")\n\t\t\t? definition.oauth.clientSecret\n\t\t\t: interpolateEnvVars(definition.oauth.clientSecret);\n\t}\n\tif (definition.oauth?.scope !== undefined) {\n\t\tif (typeof definition.oauth.scope !== \"string\") throw new Error(\"OAuth scope must be a string\");\n\t\tconfig.scope = interpolateEnvVars(definition.oauth.scope);\n\t}\n\tif (definition.oauth?.authorizationParams !== undefined) {\n\t\tconst params = definition.oauth.authorizationParams;\n\t\tif (!params || typeof params !== \"object\" || Array.isArray(params)) {\n\t\t\tthrow new Error(\"OAuth authorizationParams must be an object\");\n\t\t}\n\t\tconfig.authorizationParams = {};\n\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\tif (!key) throw new Error(\"OAuth authorizationParams keys must not be empty\");\n\t\t\tif (typeof value !== \"string\") throw new Error(`OAuth authorizationParams.${key} must be a string`);\n\t\t\tconfig.authorizationParams[key] = interpolateEnvVars(value);\n\t\t}\n\t}\n\tif (definition.oauth?.redirectUri !== undefined) {\n\t\tif (typeof definition.oauth.redirectUri !== \"string\") {\n\t\t\tthrow new Error(\"OAuth redirectUri must be a string\");\n\t\t}\n\t\tconst redirectUri = interpolateEnvVars(definition.oauth.redirectUri).trim();\n\t\tif (!redirectUri) {\n\t\t\tthrow new Error(\"OAuth redirectUri must not be empty\");\n\t\t}\n\t\tconfig.redirectUri = redirectUri;\n\t}\n\tif (definition.oauth?.clientName !== undefined) {\n\t\tif (typeof definition.oauth.clientName !== \"string\") {\n\t\t\tthrow new Error(\"OAuth clientName must be a string\");\n\t\t}\n\t\tconst clientName = interpolateEnvVars(definition.oauth.clientName).trim();\n\t\tif (!clientName) {\n\t\t\tthrow new Error(\"OAuth clientName must not be empty\");\n\t\t}\n\t\tconfig.clientName = clientName;\n\t}\n\tif (definition.oauth?.clientUri !== undefined) {\n\t\tif (typeof definition.oauth.clientUri !== \"string\") {\n\t\t\tthrow new Error(\"OAuth clientUri must be a string\");\n\t\t}\n\t\tconst clientUri = interpolateEnvVars(definition.oauth.clientUri).trim();\n\t\tif (!clientUri) {\n\t\t\tthrow new Error(\"OAuth clientUri must not be empty\");\n\t\t}\n\t\tconfig.clientUri = clientUri;\n\t}\n\tif (definition.oauth?.logoUri !== undefined) {\n\t\tif (typeof definition.oauth.logoUri !== \"string\") {\n\t\t\tthrow new Error(\"OAuth logoUri must be a string\");\n\t\t}\n\t\tconst logoUri = interpolateEnvVars(definition.oauth.logoUri).trim();\n\t\tif (!logoUri) {\n\t\t\tthrow new Error(\"OAuth logoUri must not be empty\");\n\t\t}\n\t\t// Consent screens fetch this server-side, so a local path silently renders\n\t\t// nothing. Fail here instead, where the message can say why.\n\t\tlet parsed: URL;\n\t\ttry {\n\t\t\tparsed = new URL(logoUri);\n\t\t} catch {\n\t\t\tthrow new Error(\"OAuth logoUri must be an absolute http(s) URL\");\n\t\t}\n\t\tif (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n\t\t\tthrow new Error(\"OAuth logoUri must be an absolute http(s) URL\");\n\t\t}\n\t\tconfig.logoUri = logoUri;\n\t}\n\tif (definition.oauth?.skipIssuerMetadataValidation !== undefined) {\n\t\tif (typeof definition.oauth.skipIssuerMetadataValidation !== \"boolean\") {\n\t\t\tthrow new Error(\"OAuth skipIssuerMetadataValidation must be a boolean\");\n\t\t}\n\t\tconfig.skipIssuerMetadataValidation = definition.oauth.skipIssuerMetadataValidation;\n\t}\n\treturn config;\n}\n\nasync function probeAuthDiscovery(\n\tserverUrl: string,\n\tdefinition?: ServerEntry,\n\tsignal?: AbortSignal,\n): Promise<AuthDiscovery> {\n\t// Discovery must not execute config commands or send their source text.\n\tconst discoveryHeaders = definition?.headers\n\t\t? Object.fromEntries(\n\t\t\t\tObject.entries(definition.headers).filter(([, value]) => !value.startsWith(\"!\") || value.startsWith(\"!!\")),\n\t\t\t)\n\t\t: undefined;\n\tconst headers = new Headers(interpolateEnvRecord(discoveryHeaders));\n\theaders.set(\"content-type\", \"application/json\");\n\n\tconst controller = new AbortController();\n\tconst discoverySignal = combineAbortSignals(signal, controller.signal);\n\tconst timer = setTimeout(() => controller.abort(), 5000);\n\n\ttry {\n\t\theaders.set(\"accept\", \"application/json, text/event-stream\");\n\n\t\tconst response = await fetch(new URL(serverUrl), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tid: 0,\n\t\t\t\tmethod: \"initialize\",\n\t\t\t\tparams: {\n\t\t\t\t\tprotocolVersion: LATEST_PROTOCOL_VERSION,\n\t\t\t\t\tcapabilities: {},\n\t\t\t\t\tclientInfo: { name: \"nexus-cli\", version: VERSION },\n\t\t\t\t},\n\t\t\t}),\n\t\t\t...(discoverySignal ? { signal: discoverySignal } : {}),\n\t\t});\n\t\tconst { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn { ...(resourceMetadataUrl ? { resourceMetadataUrl } : {}), ...(scope ? { scope } : {}) };\n\t} catch (_error) {\n\t\tif (signal?.aborted) throwIfAborted(signal);\n\t\treturn {};\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\nfunction parseOAuthRedirectUri(redirectUri: string): { port: number; callbackHost: string; callbackPath: string } {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(redirectUri);\n\t} catch (error) {\n\t\tthrow new Error(`Invalid OAuth redirectUri: ${redirectUri}`, { cause: error });\n\t}\n\n\tconst hostname = url.hostname.toLowerCase();\n\tconst isLocalhost =\n\t\thostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"[::1]\" || hostname === \"::1\";\n\tif (url.protocol !== \"http:\" || !isLocalhost) {\n\t\tthrow new Error(\"OAuth redirectUri must be an http:// localhost or loopback URI\");\n\t}\n\n\tif (url.username || url.password) {\n\t\tthrow new Error(\"OAuth redirectUri must not include username or password\");\n\t}\n\n\tif (url.hash) {\n\t\tthrow new Error(\"OAuth redirectUri must not include a fragment\");\n\t}\n\n\tif (!url.port) {\n\t\tthrow new Error(\"OAuth redirectUri must include an explicit numeric port\");\n\t}\n\n\tconst port = Number.parseInt(url.port, 10);\n\tif (!Number.isInteger(port) || port <= 0 || port > 65535) {\n\t\tthrow new Error(\"OAuth redirectUri must include an explicit numeric port\");\n\t}\n\n\tconst callbackHost = hostname === \"[::1]\" ? \"::1\" : hostname;\n\treturn { port, callbackHost, callbackPath: url.pathname };\n}\n\n/**\n * Start OAuth authentication flow for a server.\n * Returns the authorization URL when browser authorization is required.\n */\nexport async function startAuth(\n\tserverName: string,\n\tserverUrl: string,\n\tdefinition?: ServerEntry,\n\toptions: AuthenticateOptions = {},\n): Promise<{ authorizationUrl: string }> {\n\tif (isServerDisabled(definition)) throw new Error(`MCP server \"${serverName}\" is disabled`);\n\tconst runtime = getRuntime(options);\n\tconst runtimeState = getRuntimeState(runtime);\n\tconst config = definition ? extractOAuthConfig(definition) : {};\n\tconst authStorageOptions = options.authStorageOptions ?? {};\n\tconst signal = combineAbortSignals(runtime.signal, options.signal);\n\tconst generation = runtimeState.generation;\n\tthrowIfAborted(signal);\n\n\tif (config.grantType === \"client_credentials\") {\n\t\tconst storedAuth = await getAuthForUrl(serverName, serverUrl, authStorageOptions);\n\t\tif (storedAuth?.clientInfo && !storedAuth.tokens && !config.clientId) {\n\t\t\tclearClientInfo(serverName, authStorageOptions);\n\t\t\tclearCodeVerifier(serverName, authStorageOptions);\n\t\t\tawait clearOAuthState(serverName, authStorageOptions);\n\t\t}\n\n\t\tconst authProvider = new McpOAuthProvider(\n\t\t\tserverName,\n\t\t\tserverUrl,\n\t\t\tconfig,\n\t\t\t{\n\t\t\t\tonRedirect: async () => {\n\t\t\t\t\tthrow new Error(\"Browser redirect is not used for client_credentials flow\");\n\t\t\t\t},\n\t\t\t},\n\t\t\tauthStorageOptions,\n\t\t\truntime.signal,\n\t\t);\n\t\ttry {\n\t\t\tconst discovery = applyOAuthConfig(await probeAuthDiscovery(serverUrl, definition, signal), config);\n\t\t\tthrowIfAborted(signal);\n\t\t\tconst result = await abortable(runSdkAuth(authProvider, { serverUrl, ...discovery }), signal);\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (result !== \"AUTHORIZED\") {\n\t\t\t\tthrow new UnauthorizedError(\"Failed to authorize\");\n\t\t\t}\n\t\t\treturn { authorizationUrl: \"\" };\n\t\t} finally {\n\t\t\tauthProvider.deactivate();\n\t\t}\n\t}\n\n\tconst existingPendingAuth = runtimeState.pendingAuths.get(getPendingAuthKey(serverName, authStorageOptions));\n\tif (existingPendingAuth?.serverUrl === serverUrl) {\n\t\treturn { authorizationUrl: existingPendingAuth.authorizationUrl };\n\t}\n\n\tconst redirectCallback = config.redirectUri !== undefined ? parseOAuthRedirectUri(config.redirectUri) : undefined;\n\tconst oauthState = generateState();\n\n\ttry {\n\t\tawait ensureCallbackServer({\n\t\t\tstrictPort: Boolean(config.clientId) || config.redirectUri !== undefined,\n\t\t\toauthState,\n\t\t\treserveState: true,\n\t\t\t...(redirectCallback\n\t\t\t\t? {\n\t\t\t\t\t\tport: redirectCallback.port,\n\t\t\t\t\t\tcallbackHost: redirectCallback.callbackHost,\n\t\t\t\t\t\tcallbackPath: redirectCallback.callbackPath,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t\tthrowIfAborted(signal);\n\t} catch (error) {\n\t\treleaseCallbackServer(oauthState);\n\t\ttry {\n\t\t\tawait cleanupAndReleaseCallbackServerIfIdle(() => clearOAuthState(serverName, authStorageOptions));\n\t\t} catch (cleanupError) {\n\t\t\tthrow new AggregateError([error, cleanupError], \"OAuth startup cleanup failed\");\n\t\t}\n\t\tthrow error;\n\t}\n\n\tlet capturedUrl: URL | undefined;\n\tconst authProvider = new McpOAuthProvider(\n\t\tserverName,\n\t\tserverUrl,\n\t\tconfig,\n\t\t{\n\t\t\tonRedirect: async (url) => {\n\t\t\t\tcapturedUrl = url;\n\t\t\t},\n\t\t},\n\t\tauthStorageOptions,\n\t\truntime.signal,\n\t\toauthState,\n\t);\n\n\ttry {\n\t\tconst storedAuth = await getAuthForUrl(serverName, serverUrl, authStorageOptions);\n\t\tif (storedAuth?.clientInfo && !config.clientId) {\n\t\t\tif (!storedAuth.tokens) {\n\t\t\t\tclearClientInfo(serverName, authStorageOptions);\n\t\t\t\tclearCodeVerifier(serverName, authStorageOptions);\n\t\t\t\tawait clearOAuthState(serverName, authStorageOptions);\n\t\t\t} else {\n\t\t\t\tconst redirectUris = storedAuth.clientInfo.redirectUris;\n\t\t\t\tif (!Array.isArray(redirectUris) || !redirectUris.includes(authProvider.redirectUrl ?? \"\")) {\n\t\t\t\t\tclearClientInfo(serverName, authStorageOptions);\n\t\t\t\t\tclearTokens(serverName, authStorageOptions);\n\t\t\t\t\tclearCodeVerifier(serverName, authStorageOptions);\n\t\t\t\t\tawait clearOAuthState(serverName, authStorageOptions);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrowIfAborted(signal);\n\n\t\tconst discovery = applyOAuthConfig(await probeAuthDiscovery(serverUrl, definition, signal), config);\n\t\tthrowIfAborted(signal);\n\t\tconst result = await abortable(runSdkAuth(authProvider, { serverUrl, ...discovery }), signal);\n\t\tthrowIfAborted(signal);\n\t\tif (result === \"AUTHORIZED\") {\n\t\t\tauthProvider.deactivate();\n\t\t\treleaseCallbackServer(oauthState);\n\t\t\tawait clearOAuthState(serverName, authStorageOptions);\n\t\t\tawait stopCallbackServerIfIdle();\n\t\t\treturn { authorizationUrl: \"\" };\n\t\t}\n\t\tif (!capturedUrl) {\n\t\t\tthrow new UnauthorizedError(\"OAuth authorization URL was not provided\");\n\t\t}\n\t\tawait setPendingAuth(\n\t\t\truntime,\n\t\t\tserverName,\n\t\t\t{\n\t\t\t\tserverName,\n\t\t\t\tauthProvider,\n\t\t\t\tserverUrl,\n\t\t\t\tauthorizationUrl: capturedUrl.toString(),\n\t\t\t\tdiscovery,\n\t\t\t\tauthStorageOptions,\n\t\t\t},\n\t\t\toauthState,\n\t\t\tsignal,\n\t\t\tgeneration,\n\t\t);\n\t\treturn { authorizationUrl: capturedUrl.toString() };\n\t} catch (error) {\n\t\tauthProvider.deactivate();\n\t\ttry {\n\t\t\tawait clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions);\n\t\t} catch (cleanupError) {\n\t\t\tthrow new AggregateError([error, cleanupError], \"OAuth startup cleanup failed\");\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nasync function setPendingAuth(\n\truntime: McpOAuthRuntime,\n\tserverName: string,\n\tpendingAuth: PendingAuth,\n\toauthState: string,\n\tsignal?: AbortSignal,\n\tgeneration = getRuntimeState(runtime).generation,\n): Promise<void> {\n\tconst state = getRuntimeState(runtime);\n\tconst key = getPendingAuthKey(serverName, pendingAuth.authStorageOptions);\n\tawait clearPendingAuth(runtime, serverName, undefined, pendingAuth.authStorageOptions);\n\tthrowIfAborted(signal);\n\tif (generation !== state.generation) throw new Error(\"OAuth runtime stopped\");\n\tstate.pendingAuths.set(key, pendingAuth);\n\tstate.pendingAuthStates.set(key, oauthState);\n\tconst cleanupTimer = setTimeout(() => {\n\t\tvoid clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, pendingAuth.authStorageOptions).catch(\n\t\t\t(error) => {\n\t\t\t\tconsole.error(`MCP Auth: Timed-out flow cleanup failed: ${formatTerminalError(error)}`);\n\t\t\t},\n\t\t);\n\t}, MANUAL_AUTH_TIMEOUT_MS);\n\tcleanupTimer.unref?.();\n\tstate.pendingAuthCleanupTimers.set(key, cleanupTimer);\n}\n\nasync function clearPendingAuth(\n\truntime: McpOAuthRuntime,\n\tserverName: string,\n\toauthState?: string,\n\tfallbackStorageOptions: AuthStorageOptions = {},\n): Promise<void> {\n\tconst state = getRuntimeState(runtime);\n\tconst key = getPendingAuthKey(serverName, fallbackStorageOptions);\n\tconst pendingAuth = state.pendingAuths.get(key);\n\tconst authStorageOptions = pendingAuth?.authStorageOptions ?? fallbackStorageOptions;\n\tconst pendingState = state.pendingAuthStates.get(key);\n\tif (oauthState && pendingState && pendingState !== oauthState) return;\n\n\tconst timer = state.pendingAuthCleanupTimers.get(key);\n\tif (timer) {\n\t\tclearTimeout(timer);\n\t\tstate.pendingAuthCleanupTimers.delete(key);\n\t}\n\n\tpendingAuth?.authProvider.deactivate();\n\tstate.pendingAuths.delete(key);\n\tstate.pendingAuthStates.delete(key);\n\tconst stateToRelease = pendingState ?? oauthState;\n\tif (stateToRelease) {\n\t\tcancelPendingCallback(stateToRelease);\n\t\tconst storedState = await getOAuthState(serverName, authStorageOptions);\n\t\tif (storedState === stateToRelease) {\n\t\t\tawait clearOAuthState(serverName, authStorageOptions);\n\t\t}\n\t}\n}\n\nasync function clearPendingAuthAndReleaseIfIdle(\n\truntime: McpOAuthRuntime,\n\tserverName: string,\n\toauthState: string | undefined,\n\tfallbackStorageOptions: AuthStorageOptions = {},\n): Promise<void> {\n\tawait cleanupAndReleaseCallbackServerIfIdle(() =>\n\t\tclearPendingAuth(runtime, serverName, oauthState, fallbackStorageOptions),\n\t);\n}\n\nasync function cleanupAndReleaseCallbackServerIfIdle(cleanup: () => void | Promise<void>): Promise<void> {\n\tlet cleanupFailure: { error: unknown } | undefined;\n\ttry {\n\t\tawait cleanup();\n\t} catch (error) {\n\t\tcleanupFailure = { error };\n\t}\n\n\ttry {\n\t\tawait stopCallbackServerIfIdle();\n\t} catch (releaseError) {\n\t\tif (cleanupFailure) {\n\t\t\tthrow new AggregateError([cleanupFailure.error, releaseError], \"OAuth callback cleanup failed\");\n\t\t}\n\t\tthrow releaseError;\n\t}\n\n\tif (cleanupFailure) throw cleanupFailure.error;\n}\n\nfunction getSearchParamsFromInput(input: string): URLSearchParams | undefined {\n\ttry {\n\t\tconst url = new URL(input);\n\t\tconst params = new URLSearchParams(url.search);\n\t\tif (url.hash) {\n\t\t\tconst hash = url.hash.startsWith(\"#\") ? url.hash.slice(1) : url.hash;\n\t\t\tconst hashParams = new URLSearchParams(hash);\n\t\t\tfor (const [key, value] of hashParams) {\n\t\t\t\tif (!params.has(key)) params.set(key, value);\n\t\t\t}\n\t\t}\n\t\treturn params;\n\t} catch {\n\t\tconst query = input.includes(\"?\") ? input.slice(input.indexOf(\"?\") + 1) : input;\n\t\tconst params = new URLSearchParams(query.startsWith(\"#\") ? query.slice(1) : query);\n\t\treturn params.has(\"code\") || params.has(\"state\") || params.has(\"error\") ? params : undefined;\n\t}\n}\n\n/** Authorization code plus the optional RFC 9207 `iss` callback parameter. */\nexport interface AuthorizationCodeInput {\n\tcode: string;\n\tiss?: string;\n}\n\n/**\n * Extract an OAuth authorization code (and the RFC 9207 `iss` parameter, when\n * present) from either a raw code, a query string, or the full localhost\n * redirect URL copied from the browser address bar.\n */\nexport function parseAuthorizationRedirectInput(input: string, expectedState?: string): AuthorizationCodeInput {\n\tconst trimmed = input.trim();\n\tif (!trimmed) {\n\t\tthrow new Error(\"Authorization code or redirect URL is required\");\n\t}\n\n\tconst params = getSearchParamsFromInput(trimmed);\n\tif (params) {\n\t\tconst error = params.get(\"error\");\n\t\tif (error) {\n\t\t\tconst description = params.get(\"error_description\");\n\t\t\tthrow new Error(description ? `${error}: ${description}` : error);\n\t\t}\n\n\t\tconst state = params.get(\"state\");\n\t\tif (expectedState && !state) {\n\t\t\tthrow new Error(\"OAuth state missing from redirect URL\");\n\t\t}\n\t\tif (expectedState && state !== expectedState) {\n\t\t\tthrow new Error(\"OAuth state mismatch - potential CSRF attack\");\n\t\t}\n\n\t\tconst code = params.get(\"code\");\n\t\tif (code) {\n\t\t\tconst iss = params.get(\"iss\");\n\t\t\treturn { code, ...(iss !== null ? { iss } : {}) };\n\t\t}\n\t}\n\n\tif (/^[A-Za-z0-9._~+/=-]+$/.test(trimmed)) {\n\t\treturn { code: trimmed };\n\t}\n\n\tthrow new Error(\"Could not find an OAuth authorization code in the provided input\");\n}\n\n/**\n * Extract an OAuth authorization code from either a raw code, a query string,\n * or the full localhost redirect URL copied from the browser address bar.\n */\nexport function parseAuthorizationCodeInput(input: string, expectedState?: string): string {\n\treturn parseAuthorizationRedirectInput(input, expectedState).code;\n}\n\ntype AuthorizationResponse = {\n\tinput: AuthorizationCodeInput;\n\tsource: \"callback\" | \"manual\";\n};\n\n/**\n * Wait for either the localhost callback or a manually pasted redirect URL.\n * The manual input prompt is dismissed as soon as either path finishes.\n */\nexport async function waitForAuthorizationResponse(\n\tcallbackPromise: Promise<AuthorizationCodeInput>,\n\tauthorizationUrl: string,\n\texpectedState: string,\n\tonAuthorizationInput?: AuthenticateOptions[\"onAuthorizationInput\"],\n\tsignal?: AbortSignal,\n): Promise<AuthorizationResponse> {\n\tif (!onAuthorizationInput) {\n\t\treturn {\n\t\t\tinput: await abortable(callbackPromise, signal),\n\t\t\tsource: \"callback\",\n\t\t};\n\t}\n\n\tconst inputController = new AbortController();\n\ttry {\n\t\tconst response = await abortable(\n\t\t\tPromise.race([\n\t\t\t\tcallbackPromise.then((input) => ({ input, source: \"callback\" as const })),\n\t\t\t\tonAuthorizationInput(authorizationUrl, inputController.signal).then((input) => ({\n\t\t\t\t\tinput,\n\t\t\t\t\tsource: \"manual\" as const,\n\t\t\t\t})),\n\t\t\t]),\n\t\t\tsignal,\n\t\t);\n\n\t\tif (response.source === \"callback\") return response;\n\t\tif (!response.input?.trim()) throw new Error(\"OAuth authentication cancelled\");\n\t\tif (!getSearchParamsFromInput(response.input.trim())) {\n\t\t\tthrow new Error(\"Paste the full OAuth callback URL, including its code and state parameters\");\n\t\t}\n\t\treturn {\n\t\t\tinput: parseAuthorizationRedirectInput(response.input, expectedState),\n\t\t\tsource: \"manual\",\n\t\t};\n\t} finally {\n\t\tinputController.abort();\n\t}\n}\n\n/**\n * Complete OAuth authentication from manual user input.\n */\nexport async function completeAuthFromInput(\n\tserverName: string,\n\tinput: string,\n\toptions: AuthenticateOptions = {},\n): Promise<AuthStatus> {\n\tconst runtime = getRuntime(options);\n\tconst runtimeState = getRuntimeState(runtime);\n\tconst fallbackAuthStorageOptions = options.authStorageOptions ?? {};\n\tconst signal = combineAbortSignals(runtime.signal, options.signal);\n\tthrowIfAborted(signal);\n\tconst key = getPendingAuthKey(serverName, fallbackAuthStorageOptions);\n\tconst oauthState = runtimeState.pendingAuthStates.get(key);\n\tthrowIfAborted(signal);\n\tconst parsed = parseAuthorizationRedirectInput(input, oauthState);\n\treturn completeAuth(serverName, parsed, options);\n}\n\n/**\n * Complete OAuth authentication with the authorization code.\n */\nexport async function completeAuth(\n\tserverName: string,\n\tauthorizationCode: string | AuthorizationCodeInput,\n\toptions: AuthenticateOptions = {},\n): Promise<AuthStatus> {\n\tconst runtime = getRuntime(options);\n\tconst runtimeState = getRuntimeState(runtime);\n\tconst { code, iss } =\n\t\ttypeof authorizationCode === \"string\" ? { code: authorizationCode, iss: undefined } : authorizationCode;\n\tconst fallbackAuthStorageOptions = options.authStorageOptions ?? {};\n\tconst signal = combineAbortSignals(runtime.signal, options.signal);\n\tthrowIfAborted(signal);\n\tconst key = getPendingAuthKey(serverName, fallbackAuthStorageOptions);\n\tconst pendingAuth = runtimeState.pendingAuths.get(key);\n\tconst authStorageOptions = pendingAuth?.authStorageOptions ?? fallbackAuthStorageOptions;\n\tif (!pendingAuth) {\n\t\tthrow new Error(`No pending OAuth flow for server: ${serverName}`);\n\t}\n\n\tconst oauthState = runtimeState.pendingAuthStates.get(key);\n\tthrowIfAborted(signal);\n\n\tlet keepPendingForRetry = false;\n\ttry {\n\t\tconst discoveryState = await pendingAuth.authProvider.discoveryState();\n\t\tconst metadata = discoveryState?.authorizationServerMetadata;\n\t\tconst expectedIssuer = metadata?.issuer ?? discoveryState?.authorizationServerUrl;\n\t\tconst requiresIssuer =\n\t\t\t(metadata as { authorization_response_iss_parameter_supported?: unknown } | undefined)\n\t\t\t\t?.authorization_response_iss_parameter_supported === true;\n\t\tif (expectedIssuer !== undefined && iss === undefined && requiresIssuer) {\n\t\t\tkeepPendingForRetry = true;\n\t\t\tthrow new Error(\n\t\t\t\t`The authorization server for ${serverName} requires the RFC 9207 \"iss\" parameter. ` +\n\t\t\t\t\t\"Paste the full redirect URL from the browser address bar (not just the authorization code).\",\n\t\t\t);\n\t\t}\n\t\tif (expectedIssuer !== undefined && iss !== undefined && iss !== expectedIssuer) {\n\t\t\tthrow new Error(\n\t\t\t\t`The OAuth authorization response issuer does not match the discovered issuer for ${serverName}.`,\n\t\t\t);\n\t\t}\n\n\t\tconst result = await abortable(\n\t\t\trunSdkAuth(pendingAuth.authProvider, {\n\t\t\t\tserverUrl: pendingAuth.serverUrl,\n\t\t\t\tauthorizationCode: code,\n\t\t\t\t...(iss !== undefined ? { iss } : {}),\n\t\t\t\t...pendingAuth.discovery,\n\t\t\t}),\n\t\t\tsignal,\n\t\t);\n\t\tthrowIfAborted(signal);\n\t\tif (result !== \"AUTHORIZED\") {\n\t\t\tthrow new UnauthorizedError(\"Failed to authorize\");\n\t\t}\n\t} catch (error) {\n\t\tif (!keepPendingForRetry) {\n\t\t\ttry {\n\t\t\t\tawait clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions);\n\t\t\t} catch (cleanupError) {\n\t\t\t\tthrow new AggregateError([error, cleanupError], \"OAuth completion cleanup failed\");\n\t\t\t}\n\t\t}\n\t\tthrow error;\n\t}\n\n\tawait clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions);\n\treturn \"authenticated\";\n}\n\n/**\n * Perform the complete OAuth authentication flow for a server.\n *\n * @param serverName - The name of the MCP server\n * @param serverUrl - The URL of the MCP server\n * @param definition - The server definition (optional)\n * @returns The final auth status\n */\nexport async function authenticate(\n\tserverName: string,\n\tserverUrl: string,\n\tdefinition?: ServerEntry,\n\toptions: AuthenticateOptions = {},\n): Promise<AuthStatus> {\n\tif (isServerDisabled(definition)) throw new Error(`MCP server \"${serverName}\" is disabled`);\n\tconst runtime = getRuntime(options);\n\tconst runtimeState = getRuntimeState(runtime);\n\tconst authStorageOptions = options.authStorageOptions ?? {};\n\tconst signal = combineAbortSignals(runtime.signal, options.signal);\n\tthrowIfAborted(signal);\n\tconst authKey = `${serverName}|${serverUrl}|${getAuthBaseDir(authStorageOptions)}`;\n\tconst inFlight = runtimeState.pendingAuthentications.get(authKey);\n\tif (inFlight) {\n\t\treturn inFlight;\n\t}\n\n\tconst operation = (async (): Promise<AuthStatus> => {\n\t\tconst { authorizationUrl } = await startAuth(serverName, serverUrl, definition, {\n\t\t\t...options,\n\t\t\t...(signal ? { signal } : {}),\n\t\t\truntime,\n\t\t});\n\n\t\tif (!authorizationUrl) {\n\t\t\treturn \"authenticated\";\n\t\t}\n\n\t\tlet oauthState: string | undefined;\n\t\ttry {\n\t\t\t// Get the state that was already generated and stored in startAuth().\n\t\t\t// Keep this lookup and its abort check inside the cleanup boundary because\n\t\t\t// startAuth has already reserved callback state at this point.\n\t\t\toauthState = runtimeState.pendingAuthStates.get(getPendingAuthKey(serverName, authStorageOptions));\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (!oauthState) {\n\t\t\t\tthrow new Error(\"OAuth state not found - this should not happen\");\n\t\t\t}\n\n\t\t\t// Register the callback BEFORE opening the browser.\n\t\t\tconst callbackPromise = waitForCallback(oauthState);\n\t\t\tvoid callbackPromise.catch(() => {});\n\n\t\t\t// Open browser. Always surface the URL first so remote/headless users can copy it\n\t\t\t// even when the OS browser handoff is unavailable or invisible.\n\t\t\tif (options.onAuthorizationUrl) {\n\t\t\t\tawait abortable(Promise.resolve(options.onAuthorizationUrl(authorizationUrl)), signal);\n\t\t\t} else {\n\t\t\t\tconsole.log(`MCP Auth: Open this URL to authenticate ${serverName}:\\n${authorizationUrl}`);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait abortable(open(authorizationUrl), signal);\n\t\t\t} catch (error) {\n\t\t\t\tif (isAbortError(error, signal)) throw error;\n\t\t\t\tconsole.warn(`MCP Auth: Failed to open browser for ${serverName}; waiting for manual callback`, { error });\n\t\t\t}\n\n\t\t\tconst authorizationResponse = await waitForAuthorizationResponse(\n\t\t\t\tcallbackPromise,\n\t\t\t\tauthorizationUrl,\n\t\t\t\toauthState,\n\t\t\t\toptions.onAuthorizationInput,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (authorizationResponse.source === \"manual\") {\n\t\t\t\tcancelPendingCallback(oauthState);\n\t\t\t}\n\n\t\t\t// The callback server accepted only the flow-local reserved state. Manual\n\t\t\t// input is checked against the same state before token exchange.\n\t\t\tthrowIfAborted(signal);\n\n\t\t\treturn await completeAuth(serverName, authorizationResponse.input, {\n\t\t\t\t...options,\n\t\t\t\t...(signal ? { signal } : {}),\n\t\t\t\truntime,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (oauthState) cancelPendingCallback(oauthState);\n\t\t\ttry {\n\t\t\t\tawait clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions);\n\t\t\t} catch (cleanupError) {\n\t\t\t\tthrow new AggregateError([error, cleanupError], \"OAuth cancellation cleanup failed\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t})();\n\n\truntimeState.pendingAuthentications.set(authKey, operation);\n\n\ttry {\n\t\treturn await operation;\n\t} finally {\n\t\tif (runtimeState.pendingAuthentications.get(authKey) === operation) {\n\t\t\truntimeState.pendingAuthentications.delete(authKey);\n\t\t}\n\t}\n}\n\n/**\n * Get a valid access token for a server, refreshing if necessary.\n *\n * @param serverName - The name of the MCP server\n * @param serverUrl - The URL of the MCP server\n * @returns The valid tokens or null if not authenticated\n */\nexport async function getValidToken(\n\tserverName: string,\n\tserverUrl: string,\n\toptions: AuthenticateOptions = {},\n): Promise<StoredTokens | null> {\n\tconst runtime = getRuntime(options);\n\tconst authStorageOptions = options.authStorageOptions ?? {};\n\tconst signal = combineAbortSignals(runtime.signal, options.signal);\n\tthrowIfAborted(signal);\n\tconst entry = await getAuthForUrl(serverName, serverUrl, authStorageOptions);\n\tthrowIfAborted(signal);\n\tif (!entry?.tokens) {\n\t\treturn null;\n\t}\n\n\tconst expired = await isTokenExpired(serverName, authStorageOptions);\n\tif (expired === false) {\n\t\treturn entry.tokens;\n\t}\n\n\tif (expired === true && entry.tokens.refreshToken) {\n\t\tconsole.log(`MCP Auth: Token expired for ${serverName}, attempting refresh`);\n\n\t\ttry {\n\t\t\tconst authProvider = new McpOAuthProvider(\n\t\t\t\tserverName,\n\t\t\t\tserverUrl,\n\t\t\t\t{},\n\t\t\t\t{\n\t\t\t\t\tonRedirect: async () => {},\n\t\t\t\t},\n\t\t\t\tauthStorageOptions,\n\t\t\t\truntime.signal,\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tconst clientInfo = await authProvider.clientInformation();\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tif (!clientInfo) {\n\t\t\t\t\tconsole.log(`MCP Auth: No client info for refresh for ${serverName}`);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tconst discovery = await probeAuthDiscovery(serverUrl, undefined, signal);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tconst result = await abortable(\n\t\t\t\t\trunSdkAuth(authProvider, {\n\t\t\t\t\t\tserverUrl,\n\t\t\t\t\t\t...discovery,\n\t\t\t\t\t\t...(options.skipIssuerMetadataValidation === true ? { skipIssuerMetadataValidation: true } : {}),\n\t\t\t\t\t}),\n\t\t\t\t\tsignal,\n\t\t\t\t);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tif (result !== \"AUTHORIZED\") {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst refreshed = await getAuthForUrl(serverName, serverUrl, authStorageOptions);\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\treturn refreshed?.tokens ?? null;\n\t\t\t} finally {\n\t\t\t\tauthProvider.deactivate();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isAbortError(error, signal) || error instanceof OAuthCredentialStoreError) throw error;\n\t\t\tconsole.error(`MCP Auth: Token refresh failed for ${serverName}`, { error });\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// No expiration info or no refresh token, assume valid\n\treturn entry.tokens;\n}\n\n/**\n * Check the authentication status for a server.\n *\n * @param serverName - The name of the MCP server\n * @returns The current auth status\n */\nexport async function getAuthStatus(serverName: string, options: AuthenticateOptions = {}): Promise<AuthStatus> {\n\tgetRuntime(options);\n\tconst authStorageOptions = options.authStorageOptions ?? {};\n\tconst hasTokens = await hasStoredTokens(serverName, authStorageOptions);\n\tif (!hasTokens) return \"not_authenticated\";\n\n\tconst expired = await isTokenExpired(serverName, authStorageOptions);\n\treturn expired ? \"expired\" : \"authenticated\";\n}\n\n/**\n * Remove all OAuth credentials for a server.\n *\n * @param serverName - The name of the MCP server\n */\nexport async function removeAuth(serverName: string, options: AuthenticateOptions = {}): Promise<void> {\n\tconst runtime = getRuntime(options);\n\tconst signal = combineAbortSignals(runtime.signal, options.signal);\n\tthrowIfAborted(signal);\n\tconst authStorageOptions = options.authStorageOptions ?? {};\n\tconst oauthState = await getOAuthState(serverName, authStorageOptions);\n\tthrowIfAborted(signal);\n\tif (oauthState) {\n\t\tcancelPendingCallback(oauthState);\n\t}\n\tawait clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions);\n\tthrowIfAborted(signal);\n\tclearAllCredentials(serverName, authStorageOptions);\n\tawait clearOAuthState(serverName, authStorageOptions);\n\tthrowIfAborted(signal);\n\tconsole.log(`MCP Auth: Removed credentials for ${serverName}`);\n}\n\n/**\n * Check if OAuth is supported for a server configuration.\n * OAuth is supported for HTTP servers unless explicitly disabled.\n *\n * @param definition - The server definition\n * @returns True if OAuth is supported\n */\nexport function supportsOAuth(definition: ServerEntry): boolean {\n\t// OAuth requires a URL\n\tif (!definition.url) return false;\n\n\t// Explicitly disabled via auth: false or oauth: false\n\tif (definition.auth === false) return false;\n\tif (definition.oauth === false) return false;\n\tif (definition.auth === \"oauth\") return true;\n\n\t// Configured custom headers take precedence over implicit OAuth auto-detection.\n\tif (definition.headers && Object.keys(definition.headers).length > 0) return false;\n\n\t// OAuth is enabled when auth is not specified (auto-detect)\n\treturn definition.auth === undefined;\n}\n\n/**\n * Initialize the OAuth system on startup.\n * OAuth callback binding is lazy and starts from startAuth() only.\n */\nexport async function initializeOAuth(runtimeOrSignal?: McpOAuthRuntime | AbortSignal): Promise<McpOAuthRuntime> {\n\tif (runtimeOrSignal && \"signal\" in runtimeOrSignal) {\n\t\truntimeOrSignal.signal.throwIfAborted();\n\t\tactiveRuntimes.add(runtimeOrSignal);\n\t\treturn runtimeOrSignal;\n\t}\n\n\tawait shutdownOAuth(legacyRuntime);\n\tlegacyRuntime = createOAuthRuntime(runtimeOrSignal as AbortSignal | undefined);\n\treturn legacyRuntime;\n}\n\n/**\n * Shutdown one OAuth runtime. The callback server remains process-shared while\n * another runtime has pending/reserved callback state or is still active.\n */\nexport async function shutdownOAuth(runtime: McpOAuthRuntime = legacyRuntime): Promise<void> {\n\tconst state = getRuntimeState(runtime);\n\tif (state.controller.signal.aborted) return;\n\tstate.generation += 1;\n\tstate.controller.abort(new Error(\"OAuth runtime stopped\"));\n\tfor (const callbackState of Array.from(state.pendingAuthStates.values())) cancelPendingCallback(callbackState);\n\tfor (const pendingAuth of Array.from(state.pendingAuths.values())) {\n\t\tawait clearPendingAuth(runtime, pendingAuth.serverName, undefined, pendingAuth.authStorageOptions);\n\t}\n\tstate.pendingAuthentications.clear();\n\tactiveRuntimes.delete(runtime);\n\n\tif (activeRuntimes.size === 0) {\n\t\tawait stopCallbackServer();\n\t}\n}\n"]}
|