@mcp-z/client 1.0.1 → 1.0.3

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.
Files changed (73) hide show
  1. package/dist/cjs/auth/capability-discovery.d.cts +0 -19
  2. package/dist/cjs/auth/capability-discovery.d.ts +0 -19
  3. package/dist/cjs/auth/capability-discovery.js +123 -52
  4. package/dist/cjs/auth/capability-discovery.js.map +1 -1
  5. package/dist/cjs/auth/index.js.map +1 -1
  6. package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
  7. package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
  8. package/dist/cjs/auth/pkce.js.map +1 -1
  9. package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -0
  10. package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -0
  11. package/dist/cjs/auth/rfc9728-discovery.js +208 -46
  12. package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
  13. package/dist/cjs/auth/types.js.map +1 -1
  14. package/dist/cjs/client-helpers.js.map +1 -1
  15. package/dist/cjs/config/server-loader.js.map +1 -1
  16. package/dist/cjs/config/validate-config.js +3 -7
  17. package/dist/cjs/config/validate-config.js.map +1 -1
  18. package/dist/cjs/connection/connect-client.js.map +1 -1
  19. package/dist/cjs/connection/existing-process-transport.js.map +1 -1
  20. package/dist/cjs/connection/types.js.map +1 -1
  21. package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
  22. package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
  23. package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
  24. package/dist/cjs/dcr/index.js.map +1 -1
  25. package/dist/cjs/index.js.map +1 -1
  26. package/dist/cjs/monkey-patches.js.map +1 -1
  27. package/dist/cjs/response-wrappers.js.map +1 -1
  28. package/dist/cjs/search/index.js.map +1 -1
  29. package/dist/cjs/search/search.js.map +1 -1
  30. package/dist/cjs/search/types.js.map +1 -1
  31. package/dist/cjs/spawn/spawn-server.js +4 -0
  32. package/dist/cjs/spawn/spawn-server.js.map +1 -1
  33. package/dist/cjs/spawn/spawn-servers.js.map +1 -1
  34. package/dist/cjs/types.js.map +1 -1
  35. package/dist/cjs/utils/logger.js.map +1 -1
  36. package/dist/cjs/utils/path-utils.js.map +1 -1
  37. package/dist/cjs/utils/sanitizer.js.map +1 -1
  38. package/dist/esm/auth/capability-discovery.d.ts +0 -19
  39. package/dist/esm/auth/capability-discovery.js +43 -41
  40. package/dist/esm/auth/capability-discovery.js.map +1 -1
  41. package/dist/esm/auth/index.js.map +1 -1
  42. package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
  43. package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
  44. package/dist/esm/auth/pkce.js.map +1 -1
  45. package/dist/esm/auth/rfc9728-discovery.d.ts +7 -0
  46. package/dist/esm/auth/rfc9728-discovery.js +67 -0
  47. package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
  48. package/dist/esm/auth/types.js.map +1 -1
  49. package/dist/esm/client-helpers.js.map +1 -1
  50. package/dist/esm/config/server-loader.js.map +1 -1
  51. package/dist/esm/config/validate-config.js +3 -7
  52. package/dist/esm/config/validate-config.js.map +1 -1
  53. package/dist/esm/connection/connect-client.js.map +1 -1
  54. package/dist/esm/connection/existing-process-transport.js.map +1 -1
  55. package/dist/esm/connection/types.js.map +1 -1
  56. package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
  57. package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
  58. package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
  59. package/dist/esm/dcr/index.js.map +1 -1
  60. package/dist/esm/index.js.map +1 -1
  61. package/dist/esm/monkey-patches.js.map +1 -1
  62. package/dist/esm/response-wrappers.js.map +1 -1
  63. package/dist/esm/search/index.js.map +1 -1
  64. package/dist/esm/search/search.js.map +1 -1
  65. package/dist/esm/search/types.js.map +1 -1
  66. package/dist/esm/spawn/spawn-server.js +4 -0
  67. package/dist/esm/spawn/spawn-server.js.map +1 -1
  68. package/dist/esm/spawn/spawn-servers.js.map +1 -1
  69. package/dist/esm/types.js.map +1 -1
  70. package/dist/esm/utils/logger.js.map +1 -1
  71. package/dist/esm/utils/path-utils.js.map +1 -1
  72. package/dist/esm/utils/sanitizer.js.map +1 -1
  73. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/spawn/spawn-server.ts"],"sourcesContent":["/**\n * Low-level single server spawning utilities.\n * Provides core process spawning with path resolution, environment management, and lifecycle control.\n */\n\nimport { type ChildProcess, type SpawnOptions, type StdioOptions, spawn } from 'child_process';\nimport * as process from 'process';\nimport { logger } from '../utils/logger.ts';\nimport { resolveArgsPaths } from '../utils/path-utils.ts';\n\n/**\n * Options for spawning a single server process.\n * @internal\n */\nexport interface SpawnProcessOptions {\n /** Server name for logging */\n name: string;\n /** Command to execute (e.g., 'node', 'npx') */\n command: string;\n /** Command arguments (paths will be resolved relative to cwd) */\n args?: string[];\n /** Working directory (must be absolute path) */\n cwd?: string;\n /** Additional environment variables (merged with process.env) */\n env?: Record<string, string>;\n /** Standard I/O configuration */\n stdio?: StdioOptions;\n /** Use shell for command execution (default: false, true on Windows) */\n shell?: boolean;\n}\n\n/**\n * Handle to a spawned server process.\n * Provides access to the process, resolved config, and lifecycle control.\n * @hidden\n */\nexport interface ServerProcess {\n /**\n * The resolved server configuration that was actually used.\n * Useful for debugging and understanding what was spawned.\n */\n config: {\n name: string;\n command: string;\n args: string[];\n cwd: string;\n env: Record<string, string>;\n stdio: StdioOptions;\n shell: boolean;\n };\n\n /**\n * The spawned child process.\n */\n process: ChildProcess;\n\n /**\n * Close the server gracefully.\n * Sends the specified signal (default: SIGINT), then SIGKILL after timeout.\n *\n * @param signal - Signal to send (default: SIGINT)\n * @param opts - Options including timeout\n * @returns Promise resolving to whether the process timed out and was force-killed\n */\n close: (signal?: NodeJS.Signals, opts?: { timeoutMs?: number }) => Promise<{ timedOut: boolean; killed: boolean }>;\n}\n\n/**\n * Normalize environment variables by merging with process.env and filtering undefined values.\n */\nfunction normalizeEnv(env?: Record<string, string>): Record<string, string> {\n const merged = { ...process.env, ...(env || {}) };\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(merged)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Spawn a single server process with path resolution and environment management.\n *\n * @internal\n * @param opts - Server spawn options\n * @returns ServerProcess handle with resolved config, process, and stop function\n *\n * @example\n * const handle = spawnProcess({\n * name: 'echo',\n * command: 'node',\n * args: ['./bin/server.js', '--port', '3000'],\n * cwd: '/home/user/project/test/lib/servers/echo',\n * env: { LOG_LEVEL: 'error' }\n * });\n *\n * // Later...\n * await handle.close();\n */\nexport function spawnProcess(opts: SpawnProcessOptions): ServerProcess {\n const name = opts.name;\n const command = opts.command;\n const cwd = opts.cwd ?? process.cwd();\n const stdio = opts.stdio ?? 'inherit';\n const shell = opts.shell ?? process.platform === 'win32';\n\n // Resolve paths in args relative to the working directory\n const args = opts.args ? resolveArgsPaths(opts.args, cwd) : [];\n\n // Merge environment variables\n const env = normalizeEnv(opts.env);\n\n // Create resolved config for return value\n const resolvedConfig = {\n name,\n command,\n args,\n cwd,\n env,\n stdio,\n shell,\n };\n\n // Log spawn operation\n logger.info(`[${name}] → ${command} ${args.join(' ')}`);\n\n // Spawn the process\n const spawnOpts: SpawnOptions = { cwd, env, stdio, shell };\n const child = spawn(command, args, spawnOpts);\n\n // Attach lifecycle logging\n child.on('exit', (code, sig) => logger.info(`[${name}] exited (code=${code}, signal=${sig || 'none'})`));\n child.on('error', (err) => logger.info(`[${name}] process error: ${err.message}`));\n\n // Create stop function with graceful shutdown\n const stop = async (signal: NodeJS.Signals = 'SIGINT', opts: { timeoutMs?: number } = {}): Promise<{ timedOut: boolean; killed: boolean }> => {\n // If already exited, return immediately\n if (child.exitCode !== null || child.signalCode !== null) {\n return { timedOut: false, killed: false };\n }\n\n const timeoutMs = opts.timeoutMs ?? 500;\n\n // Wait for 'close' event (process exit + stdio streams closed)\n // This is better than 'exit' because it ensures stdio is fully cleaned up\n const closePromise = new Promise<{ timedOut: boolean; killed: boolean }>((resolve) => {\n let isResolved = false;\n let wasKilled = false;\n\n const resolveOnce = (timedOut: boolean) => {\n if (isResolved) return;\n isResolved = true;\n clearTimeout(timeout);\n resolve({ timedOut, killed: wasKilled });\n };\n\n // Set timeout for forceful kill\n const timeout = setTimeout(() => {\n try {\n // Check again before SIGKILL\n if (child.exitCode === null && !child.killed) {\n child.kill('SIGKILL');\n wasKilled = true;\n }\n } catch (_) {}\n // Even if kill fails, resolve\n resolveOnce(true);\n }, timeoutMs);\n\n // Listen for 'close' event (not 'exit') to wait for stdio close\n child.once('close', () => {\n resolveOnce(false);\n });\n\n // Also listen for 'error' event in case spawn failed\n // This prevents promise from hanging forever if process never started\n child.once('error', () => {\n resolveOnce(false);\n });\n\n // Send graceful shutdown signal\n try {\n // Check one more time before killing\n if (child.exitCode !== null) {\n resolveOnce(false);\n return;\n }\n\n const killed = child.kill(signal);\n // If kill returned false, process already exited\n if (!killed) {\n resolveOnce(false);\n }\n } catch (_err) {\n // If kill throws, process is gone or unreachable\n resolveOnce(false);\n }\n });\n\n return closePromise;\n };\n\n return {\n config: resolvedConfig,\n process: child,\n close: stop,\n };\n}\n"],"names":["spawnProcess","normalizeEnv","env","merged","process","result","Object","entries","key","value","undefined","opts","name","command","cwd","stdio","shell","platform","args","resolveArgsPaths","resolvedConfig","logger","info","join","spawnOpts","child","spawn","on","code","sig","err","message","stop","signal","timeoutMs","closePromise","exitCode","signalCode","timedOut","killed","Promise","resolve","isResolved","wasKilled","resolveOnce","clearTimeout","timeout","setTimeout","kill","_","once","_err","config","close"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/spawn/spawn-server.ts"],"sourcesContent":["/**\n * Low-level single server spawning utilities.\n * Provides core process spawning with path resolution, environment management, and lifecycle control.\n */\n\nimport { type ChildProcess, type SpawnOptions, type StdioOptions, spawn } from 'child_process';\nimport * as process from 'process';\nimport { logger } from '../utils/logger.ts';\nimport { resolveArgsPaths } from '../utils/path-utils.ts';\n\n/**\n * Options for spawning a single server process.\n * @internal\n */\nexport interface SpawnProcessOptions {\n /** Server name for logging */\n name: string;\n /** Command to execute (e.g., 'node', 'npx') */\n command: string;\n /** Command arguments (paths will be resolved relative to cwd) */\n args?: string[];\n /** Working directory (must be absolute path) */\n cwd?: string;\n /** Additional environment variables (merged with process.env) */\n env?: Record<string, string>;\n /** Standard I/O configuration */\n stdio?: StdioOptions;\n /** Use shell for command execution (default: false, true on Windows) */\n shell?: boolean;\n}\n\n/**\n * Handle to a spawned server process.\n * Provides access to the process, resolved config, and lifecycle control.\n * @hidden\n */\nexport interface ServerProcess {\n /**\n * The resolved server configuration that was actually used.\n * Useful for debugging and understanding what was spawned.\n */\n config: {\n name: string;\n command: string;\n args: string[];\n cwd: string;\n env: Record<string, string>;\n stdio: StdioOptions;\n shell: boolean;\n };\n\n /**\n * The spawned child process.\n */\n process: ChildProcess;\n\n /**\n * Close the server gracefully.\n * Sends the specified signal (default: SIGINT), then SIGKILL after timeout.\n *\n * @param signal - Signal to send (default: SIGINT)\n * @param opts - Options including timeout\n * @returns Promise resolving to whether the process timed out and was force-killed\n */\n close: (signal?: NodeJS.Signals, opts?: { timeoutMs?: number }) => Promise<{ timedOut: boolean; killed: boolean }>;\n}\n\n/**\n * Normalize environment variables by merging with process.env and filtering undefined values.\n */\nfunction normalizeEnv(env?: Record<string, string>): Record<string, string> {\n const merged = { ...process.env, ...(env || {}) };\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(merged)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Spawn a single server process with path resolution and environment management.\n *\n * @internal\n * @param opts - Server spawn options\n * @returns ServerProcess handle with resolved config, process, and stop function\n *\n * @example\n * const handle = spawnProcess({\n * name: 'echo',\n * command: 'node',\n * args: ['./bin/server.js', '--port', '3000'],\n * cwd: '/home/user/project/test/lib/servers/echo',\n * env: { LOG_LEVEL: 'error' }\n * });\n *\n * // Later...\n * await handle.close();\n */\nexport function spawnProcess(opts: SpawnProcessOptions): ServerProcess {\n const name = opts.name;\n const command = opts.command;\n const cwd = opts.cwd ?? process.cwd();\n const stdio = opts.stdio ?? 'inherit';\n const shell = opts.shell ?? process.platform === 'win32';\n\n // Resolve paths in args relative to the working directory\n const args = opts.args ? resolveArgsPaths(opts.args, cwd) : [];\n\n // Merge environment variables\n const env = normalizeEnv(opts.env);\n\n // Create resolved config for return value\n const resolvedConfig = {\n name,\n command,\n args,\n cwd,\n env,\n stdio,\n shell,\n };\n\n // Log spawn operation\n logger.info(`[${name}] → ${command} ${args.join(' ')}`);\n\n // Spawn the process\n const spawnOpts: SpawnOptions = { cwd, env, stdio, shell };\n const child = spawn(command, args, spawnOpts);\n\n // Pipe stdio if not inherited\n if (child.stderr)\n child.stderr.on('data', (chunk) => {\n process.stderr.write(chunk);\n });\n\n // Attach lifecycle logging\n child.on('exit', (code, sig) => logger.info(`[${name}] exited (code=${code}, signal=${sig || 'none'})`));\n child.on('error', (err) => logger.info(`[${name}] process error: ${err.message}`));\n\n // Create stop function with graceful shutdown\n const stop = async (signal: NodeJS.Signals = 'SIGINT', opts: { timeoutMs?: number } = {}): Promise<{ timedOut: boolean; killed: boolean }> => {\n // If already exited, return immediately\n if (child.exitCode !== null || child.signalCode !== null) {\n return { timedOut: false, killed: false };\n }\n\n const timeoutMs = opts.timeoutMs ?? 500;\n\n // Wait for 'close' event (process exit + stdio streams closed)\n // This is better than 'exit' because it ensures stdio is fully cleaned up\n const closePromise = new Promise<{ timedOut: boolean; killed: boolean }>((resolve) => {\n let isResolved = false;\n let wasKilled = false;\n\n const resolveOnce = (timedOut: boolean) => {\n if (isResolved) return;\n isResolved = true;\n clearTimeout(timeout);\n resolve({ timedOut, killed: wasKilled });\n };\n\n // Set timeout for forceful kill\n const timeout = setTimeout(() => {\n try {\n // Check again before SIGKILL\n if (child.exitCode === null && !child.killed) {\n child.kill('SIGKILL');\n wasKilled = true;\n }\n } catch (_) {}\n // Even if kill fails, resolve\n resolveOnce(true);\n }, timeoutMs);\n\n // Listen for 'close' event (not 'exit') to wait for stdio close\n child.once('close', () => {\n resolveOnce(false);\n });\n\n // Also listen for 'error' event in case spawn failed\n // This prevents promise from hanging forever if process never started\n child.once('error', () => {\n resolveOnce(false);\n });\n\n // Send graceful shutdown signal\n try {\n // Check one more time before killing\n if (child.exitCode !== null) {\n resolveOnce(false);\n return;\n }\n\n const killed = child.kill(signal);\n // If kill returned false, process already exited\n if (!killed) {\n resolveOnce(false);\n }\n } catch (_err) {\n // If kill throws, process is gone or unreachable\n resolveOnce(false);\n }\n });\n\n return closePromise;\n };\n\n return {\n config: resolvedConfig,\n process: child,\n close: stop,\n };\n}\n"],"names":["spawnProcess","normalizeEnv","env","merged","process","result","Object","entries","key","value","undefined","opts","name","command","cwd","stdio","shell","platform","args","resolveArgsPaths","resolvedConfig","logger","info","join","spawnOpts","child","spawn","stderr","on","chunk","write","code","sig","err","message","stop","signal","timeoutMs","closePromise","exitCode","signalCode","timedOut","killed","Promise","resolve","isResolved","wasKilled","resolveOnce","clearTimeout","timeout","setTimeout","kill","_","once","_err","config","close"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/spawn/spawn-servers.ts"],"sourcesContent":["/**\n * High-level multi-server registry management.\n * Starts multiple servers from a servers configuration object.\n * Supports stdio, http, and ws transports.\n * Implements Claude Code-compatible configuration with start extension support.\n */\n\nimport * as fs from 'fs';\nimport * as process from 'process';\nimport { decorateClient, type ManagedClient } from '../client-helpers.ts';\nimport { validateServers } from '../config/validate-config.ts';\nimport { connectMcpClient } from '../connection/connect-client.ts';\nimport { buildCapabilityIndex, type CapabilityClient, searchCapabilities as executeCapabilitySearch, type SearchOptions, type SearchResponse } from '../search/index.ts';\nimport type { McpServerEntry, TransportType } from '../types.ts';\nimport { logger } from '../utils/logger.ts';\nimport { type ServerProcess, spawnProcess } from './spawn-server.ts';\n\n/**\n * Servers configuration type - a map of server names to their configurations.\n */\nexport type ServersConfig = Record<string, McpServerEntry>;\n\n/**\n * Dialect for server spawning.\n *\n * - 'servers': Spawn stdio servers (Claude Code compatible)\n * - 'start': Spawn HTTP servers with start blocks\n */\nexport type Dialect = 'servers' | 'start';\n\n/**\n * Options for creating a server registry.\n */\nexport interface CreateServerRegistryOptions {\n /** Working directory for spawned processes (default: process.cwd()) */\n cwd?: string;\n\n /**\n * Base environment for all servers.\n * If provided, process.env is NOT included (caller has full control).\n * If omitted, process.env is used as the base (default behavior).\n */\n env?: Record<string, string>;\n\n /**\n * Dialects controlling which servers to spawn.\n * - ['servers']: Spawn stdio servers only (default, Claude Code compatible)\n * - ['start']: Only spawn servers with start blocks (HTTP servers)\n * - ['servers', 'start']: Spawn both stdio servers and start blocks\n * @default ['servers']\n */\n dialects?: Dialect[];\n}\n\n/**\n * Result of closing the registry.\n */\nexport interface CloseResult {\n /** Whether any process timed out during shutdown */\n timedOut: boolean;\n /** Number of processes that were force-killed */\n killedCount: number;\n}\n\n/**\n * Infer transport type: explicit type > URL protocol > default 'stdio'\n */\nfunction inferTransportType(config: { type?: TransportType; url?: string }): TransportType {\n if (config.type) {\n return config.type;\n }\n\n if (config.url) {\n const url = new URL(config.url);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n return 'http';\n }\n }\n\n return 'stdio';\n}\n\n/**\n * Spawn configuration result (internal)\n */\ninterface SpawnConfig {\n shouldSpawn: boolean;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n}\n\n/**\n * Helper to filter undefined values from environment\n */\nfunction filterEnv(env: Record<string, string | undefined>): Record<string, string> {\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (value !== undefined) {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n\n/**\n * Determine spawn behavior based on dialects and config structure.\n *\n * Dialects:\n * - ['servers'] (default): Claude Code compatible - ignores start blocks, stdio only\n * - ['start']: Only spawn servers with start blocks (HTTP server testing)\n * - ['servers', 'start']: Spawn both start blocks and stdio servers (full MCP-Z extension support)\n *\n * Environment merging (when baseEnv provided):\n * - HTTP servers (start block): { ...baseEnv, ...entry.start.env }\n * - Stdio servers: { ...baseEnv, ...entry.env }\n *\n * When baseEnv is not provided, process.env is used as the base.\n */\nfunction getSpawnConfig(entry: McpServerEntry, dialects: Dialect[], baseEnv: Record<string, string | undefined>): SpawnConfig {\n const transportType = inferTransportType(entry);\n const hasServers = dialects.includes('servers');\n const hasStart = dialects.includes('start');\n\n // If only 'servers' dialect: Claude Code compatible (ignore start blocks, stdio only)\n if (hasServers && !hasStart) {\n if (transportType === 'stdio' && entry.command) {\n return {\n shouldSpawn: true,\n command: entry.command,\n args: entry.args || [],\n env: filterEnv({ ...baseEnv, ...entry.env }),\n };\n }\n return { shouldSpawn: false };\n }\n\n // If only 'start' dialect: Only spawn servers with start blocks\n if (hasStart && !hasServers) {\n if (entry.start) {\n return {\n shouldSpawn: true,\n command: entry.start.command,\n args: entry.start.args || [],\n env: filterEnv({ ...baseEnv, ...entry.start.env }),\n };\n }\n return { shouldSpawn: false };\n }\n\n // Both dialects: Spawn both start blocks and stdio servers\n // Priority: start blocks first, then stdio\n if (entry.start) {\n return {\n shouldSpawn: true,\n command: entry.start.command,\n args: entry.start.args || [],\n env: filterEnv({ ...baseEnv, ...entry.start.env }),\n };\n }\n\n if (transportType === 'stdio' && entry.command) {\n return {\n shouldSpawn: true,\n command: entry.command,\n args: entry.args || [],\n env: filterEnv({ ...baseEnv, ...entry.env }),\n };\n }\n\n return { shouldSpawn: false };\n}\n\n/**\n * A registry of spawned MCP servers with connection management.\n * Provides access to individual server handles, connection management, and collection-wide close.\n */\ntype RegistryConnectOptions = Parameters<typeof connectMcpClient>[2];\n\nexport interface ServerRegistry {\n /**\n * The resolved servers configuration that was used.\n * Useful for debugging and understanding what was started.\n */\n config: ServersConfig;\n\n /**\n * Map of server name to server process handle.\n * @hidden\n */\n servers: Map<string, ServerProcess>;\n\n /**\n * Set of connected clients tracked by this registry.\n * Automatically populated when using registry.connect().\n */\n clients: Set<ManagedClient>;\n\n /**\n * Connect to a server by name.\n * The connected client is automatically tracked for close.\n *\n * @param name - Server name from configuration\n * @returns Connected MCP SDK Client\n */\n connect: (name: string, options?: RegistryConnectOptions) => Promise<ManagedClient>;\n\n /**\n * Close all clients and servers gracefully.\n * First closes all tracked clients, then sends the specified signal to all server processes.\n *\n * @param signal - Signal to send to processes (default: SIGINT)\n * @param opts - Options including timeout\n * @returns Promise resolving to whether any process timed out and how many were force-killed\n */\n close: (signal?: NodeJS.Signals, opts?: { timeoutMs?: number }) => Promise<CloseResult>;\n\n /**\n * Search indexed capabilities across all currently connected clients.\n * Requires at least one connected client; respects SearchOptions filters.\n */\n searchCapabilities: (query: string, options?: SearchOptions) => Promise<SearchResponse>;\n\n /**\n * Support for `await using` pattern (automatic close).\n */\n [Symbol.asyncDispose]: () => Promise<void>;\n}\n\n/**\n * Create a registry of MCP servers from configuration.\n *\n * **Fast start**: Returns immediately after processes are created.\n * Use `registry.connect()` for lazy MCP connection.\n *\n * @param serversConfig - Map of server names to their configurations\n * @param options - Options for registry creation\n * @param options.cwd - Working directory for spawned processes (default: process.cwd())\n * @param options.env - Base environment for all servers. If provided, process.env is NOT included.\n * If omitted, process.env is used as the base (default behavior).\n * @param options.dialects - Dialects controlling which servers to spawn (default: ['servers'])\n * - ['servers']: Spawn stdio servers only (Claude Code compatible)\n * - ['start']: Only spawn servers with start blocks (HTTP servers)\n * - ['servers', 'start']: Spawn both stdio servers and start blocks\n * @returns ServerRegistry instance with config, server map, connect method, and close function\n *\n * @example\n * // Fast server start (does NOT wait for readiness)\n * const registry = createServerRegistry({\n * 'echo': { command: 'node', args: ['server.ts'] },\n * });\n *\n * // Connect when needed (waits for MCP handshake)\n * const client = await registry.connect('echo');\n *\n * // Cleanup (closes all clients AND processes)\n * await registry.close();\n *\n * @example\n * // Start HTTP servers with start blocks\n * const registry = createServerRegistry(\n * {\n * 'http-server': {\n * url: 'http://localhost:8080/mcp',\n * start: { command: 'node', args: ['http.ts', '--port', '8080'] }\n * },\n * },\n * { dialects: ['start'] }\n * );\n *\n * @example\n * // Using await using for automatic close\n * await using registry = createServerRegistry(config);\n * const client = await registry.connect('server');\n * // Auto-disposed when scope exits\n */\nexport function createServerRegistry(serversConfig: ServersConfig, options?: CreateServerRegistryOptions): ServerRegistry {\n const cwd = options?.cwd ?? process.cwd();\n const dialects = options?.dialects ?? ['servers'];\n\n // Determine base environment:\n // - If options.env provided, use it (process.env NOT included)\n // - If options.env omitted, use process.env as base\n const baseEnv: Record<string, string | undefined> = options?.env ?? process.env;\n\n // Validate working directory exists (fail fast for configuration errors)\n if (!fs.existsSync(cwd)) {\n throw new Error(`Cannot start servers: working directory '${cwd}' does not exist`);\n }\n\n // Validate configuration (fail fast with clear errors)\n const validation = validateServers(serversConfig);\n if (!validation.valid) {\n throw new Error(`Invalid servers configuration:\\n${validation.errors?.join('\\n') ?? 'Unknown validation error'}`);\n }\n\n // Log validation warnings (non-blocking)\n if (validation.warnings && validation.warnings.length > 0) {\n for (const warning of validation.warnings) {\n logger.warn(warning);\n }\n }\n\n const servers = new Map<string, ServerProcess>();\n const clients = new Set<ManagedClient>();\n const sharedStdioClients = new Map<string, { client?: ManagedClient; connecting?: Promise<ManagedClient>; refs: number }>();\n\n // Start each server in the configuration\n for (const [name, entry] of Object.entries(serversConfig)) {\n // Infer transport type from config\n const transportType = inferTransportType(entry);\n\n // Determine spawn behavior based on dialects\n const spawnConfig = getSpawnConfig(entry, dialects, baseEnv);\n\n // Check if we should spawn this server\n if (!spawnConfig.shouldSpawn) {\n // External server - just log, no spawn needed\n if (entry.url) {\n logger.info(`[${name}] external ${transportType} server (url: ${entry.url})`);\n } else {\n logger.warn(`[${name}] skipping: no spawn configuration (missing start or command) and no url for external server`);\n }\n continue;\n }\n\n try {\n // Validate spawn config\n if (!spawnConfig.command) {\n throw new Error(`Server \"${name}\" missing command field`);\n }\n\n // All servers use the same working directory (cwd from options)\n const resolvedCwd = cwd;\n\n // Start the server\n logger.info(`[${name}] starting ${transportType} server (${spawnConfig.command} ${(spawnConfig.args || []).join(' ')})`);\n\n // stdio servers need 'pipe' for MCP communication over stdin/stdout\n // network servers use 'inherit' so we see their logs\n const stdio = transportType === 'stdio' ? 'pipe' : 'inherit';\n\n const handle = spawnProcess({\n name,\n command: spawnConfig.command,\n ...(spawnConfig.args !== undefined && { args: spawnConfig.args }),\n cwd: resolvedCwd,\n ...(spawnConfig.env && Object.keys(spawnConfig.env).length > 0 && { env: spawnConfig.env }),\n stdio,\n });\n\n // Add server to registry (starting is fast, readiness is lazy)\n servers.set(name, handle);\n logger.info(`[${name}] started successfully`);\n } catch (e) {\n logger.info(`[${name}] start ERROR: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n // Create connect function that tracks clients\n const connect = async (name: string, options?: RegistryConnectOptions): Promise<ManagedClient> => {\n const serverEntry = serversConfig[name];\n if (!serverEntry) {\n const available = Object.keys(serversConfig).join(', ');\n throw new Error(`Server '${name}' not found in config. Available servers: ${available || 'none'}`);\n }\n\n const transportType = inferTransportType(serverEntry);\n\n if (transportType === 'stdio') {\n // Stdio is a single logical connection; reuse one client and lease references.\n let entry = sharedStdioClients.get(name);\n if (!entry) {\n entry = { refs: 0 };\n sharedStdioClients.set(name, entry);\n }\n\n if (!entry.client) {\n if (!entry.connecting) {\n entry.connecting = (async () => {\n // Pass minimal RegistryLike object to connectMcpClient\n const registryLike = { config: serversConfig, servers };\n const rawClient = await connectMcpClient(registryLike, name, options);\n const decorated = decorateClient(rawClient, { serverName: name });\n entry.client = decorated;\n entry.connecting = undefined;\n return decorated;\n })().catch((error) => {\n sharedStdioClients.delete(name);\n throw error;\n });\n }\n\n await entry.connecting;\n }\n\n if (!entry.client) {\n throw new Error(`Failed to connect to stdio server '${name}'`);\n }\n\n entry.refs += 1;\n let released = false;\n let lease: ManagedClient;\n\n lease = new Proxy(entry.client, {\n get(target, prop) {\n if (prop === 'close') {\n return async () => {\n if (released) return;\n released = true;\n clients.delete(lease);\n entry.refs = Math.max(0, entry.refs - 1);\n if (entry.refs === 0) {\n sharedStdioClients.delete(name);\n await target.close();\n }\n };\n }\n\n const value = Reflect.get(target, prop, target) as unknown;\n if (typeof value === 'function') {\n return (value as (...args: unknown[]) => unknown).bind(target);\n }\n return value;\n },\n }) as ManagedClient;\n\n clients.add(lease);\n return lease;\n }\n\n // Pass minimal RegistryLike object to connectMcpClient\n const registryLike = { config: serversConfig, servers };\n const rawClient = await connectMcpClient(registryLike, name, options);\n const decorated = decorateClient(rawClient, { serverName: name });\n clients.add(decorated);\n return decorated;\n };\n\n // Create close function that stops all clients and servers\n const close = async (signal: NodeJS.Signals = 'SIGINT', opts: { timeoutMs?: number } = {}): Promise<CloseResult> => {\n logger.info(`[registry] closing (${signal})`);\n\n // First, close all tracked clients\n const clientClosePromises = Array.from(clients).map(async (client) => {\n try {\n await client.close();\n } catch {\n // Ignore errors during client close\n }\n });\n await Promise.all(clientClosePromises);\n clients.clear();\n\n // Then close all server processes\n if (servers.size === 0) {\n return { timedOut: false, killedCount: 0 };\n }\n\n // Close all servers in parallel\n const closeResults = await Promise.all(Array.from(servers.values()).map((server) => server.close(signal, opts)));\n\n // Check if any timed out and count how many were force-killed\n const timedOut = closeResults.some((result) => result.timedOut);\n const killedCount = closeResults.filter((result) => result.killed).length;\n\n return { timedOut, killedCount };\n };\n\n const searchFromRegistry = async (query: string, options: SearchOptions = {}): Promise<SearchResponse> => {\n const requestedServers = options.servers ?? Object.keys(serversConfig);\n if (requestedServers.length === 0) {\n throw new Error('Cannot search capabilities: registry has no configured servers');\n }\n\n const unknownServers = requestedServers.filter((name) => !(name in serversConfig));\n if (unknownServers.length > 0) {\n throw new Error(`Cannot search capabilities: unknown server(s) [${unknownServers.join(', ')}]`);\n }\n\n const capabilityClients = new Map<string, CapabilityClient>();\n const failures: Array<{ server: string; reason: string }> = [];\n\n const ensureClient = async (serverName: string): Promise<ManagedClient> => {\n for (const client of clients) {\n if (client.serverName === serverName) {\n return client;\n }\n }\n return connect(serverName);\n };\n\n await Promise.all(\n requestedServers.map(async (serverName) => {\n try {\n const managed = await ensureClient(serverName);\n capabilityClients.set(serverName, managed.nativeClient);\n } catch (error) {\n failures.push({\n server: serverName,\n reason: error instanceof Error ? error.message : String(error),\n });\n }\n })\n );\n\n if (capabilityClients.size === 0) {\n const failureDetails = failures.length > 0 ? ` Connection failures: ${failures.map((f) => `${f.server} (${f.reason})`).join('; ')}` : '';\n throw new Error(`Cannot search capabilities: unable to connect to any requested servers.${failureDetails}`);\n }\n\n if (failures.length > 0) {\n throw new Error(`Cannot search capabilities: failed to connect to server(s) [${failures.map((f) => f.server).join(', ')}]. Reasons: ${failures.map((f) => `${f.server}: ${f.reason}`).join('; ')}`);\n }\n\n const index = await buildCapabilityIndex(capabilityClients);\n return executeCapabilitySearch(index, query, options);\n };\n\n // Async dispose for `await using` pattern\n const asyncDispose = async (): Promise<void> => {\n await close();\n };\n\n return {\n config: serversConfig,\n servers,\n clients,\n connect,\n close,\n searchCapabilities: searchFromRegistry,\n [Symbol.asyncDispose]: asyncDispose,\n };\n}\n"],"names":["createServerRegistry","inferTransportType","config","type","url","URL","protocol","filterEnv","env","filtered","Object","entries","key","value","undefined","getSpawnConfig","entry","dialects","baseEnv","transportType","hasServers","includes","hasStart","command","shouldSpawn","args","start","serversConfig","options","cwd","process","fs","existsSync","Error","validation","validateServers","valid","errors","join","warnings","length","warning","logger","warn","servers","Map","clients","Set","sharedStdioClients","name","spawnConfig","info","resolvedCwd","stdio","handle","spawnProcess","keys","set","e","message","String","connect","serverEntry","available","released","lease","registryLike","rawClient","decorated","get","refs","client","connecting","connectMcpClient","decorateClient","serverName","catch","error","delete","Proxy","target","prop","Math","max","close","Reflect","bind","add","signal","opts","clientClosePromises","closeResults","timedOut","killedCount","Array","from","map","Promise","all","clear","size","values","server","some","result","filter","killed","searchFromRegistry","query","requestedServers","unknownServers","capabilityClients","failures","ensureClient","failureDetails","index","managed","nativeClient","push","reason","f","buildCapabilityIndex","executeCapabilitySearch","asyncDispose","searchCapabilities","Symbol"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/spawn/spawn-servers.ts"],"sourcesContent":["/**\n * High-level multi-server registry management.\n * Starts multiple servers from a servers configuration object.\n * Supports stdio, http, and ws transports.\n * Implements Claude Code-compatible configuration with start extension support.\n */\n\nimport * as fs from 'fs';\nimport * as process from 'process';\nimport { decorateClient, type ManagedClient } from '../client-helpers.ts';\nimport { validateServers } from '../config/validate-config.ts';\nimport { connectMcpClient } from '../connection/connect-client.ts';\nimport { buildCapabilityIndex, type CapabilityClient, searchCapabilities as executeCapabilitySearch, type SearchOptions, type SearchResponse } from '../search/index.ts';\nimport type { McpServerEntry, TransportType } from '../types.ts';\nimport { logger } from '../utils/logger.ts';\nimport { type ServerProcess, spawnProcess } from './spawn-server.ts';\n\n/**\n * Servers configuration type - a map of server names to their configurations.\n */\nexport type ServersConfig = Record<string, McpServerEntry>;\n\n/**\n * Dialect for server spawning.\n *\n * - 'servers': Spawn stdio servers (Claude Code compatible)\n * - 'start': Spawn HTTP servers with start blocks\n */\nexport type Dialect = 'servers' | 'start';\n\n/**\n * Options for creating a server registry.\n */\nexport interface CreateServerRegistryOptions {\n /** Working directory for spawned processes (default: process.cwd()) */\n cwd?: string;\n\n /**\n * Base environment for all servers.\n * If provided, process.env is NOT included (caller has full control).\n * If omitted, process.env is used as the base (default behavior).\n */\n env?: Record<string, string>;\n\n /**\n * Dialects controlling which servers to spawn.\n * - ['servers']: Spawn stdio servers only (default, Claude Code compatible)\n * - ['start']: Only spawn servers with start blocks (HTTP servers)\n * - ['servers', 'start']: Spawn both stdio servers and start blocks\n * @default ['servers']\n */\n dialects?: Dialect[];\n}\n\n/**\n * Result of closing the registry.\n */\nexport interface CloseResult {\n /** Whether any process timed out during shutdown */\n timedOut: boolean;\n /** Number of processes that were force-killed */\n killedCount: number;\n}\n\n/**\n * Infer transport type: explicit type > URL protocol > default 'stdio'\n */\nfunction inferTransportType(config: { type?: TransportType; url?: string }): TransportType {\n if (config.type) {\n return config.type;\n }\n\n if (config.url) {\n const url = new URL(config.url);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n return 'http';\n }\n }\n\n return 'stdio';\n}\n\n/**\n * Spawn configuration result (internal)\n */\ninterface SpawnConfig {\n shouldSpawn: boolean;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n}\n\n/**\n * Helper to filter undefined values from environment\n */\nfunction filterEnv(env: Record<string, string | undefined>): Record<string, string> {\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (value !== undefined) {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n\n/**\n * Determine spawn behavior based on dialects and config structure.\n *\n * Dialects:\n * - ['servers'] (default): Claude Code compatible - ignores start blocks, stdio only\n * - ['start']: Only spawn servers with start blocks (HTTP server testing)\n * - ['servers', 'start']: Spawn both start blocks and stdio servers (full MCP-Z extension support)\n *\n * Environment merging (when baseEnv provided):\n * - HTTP servers (start block): { ...baseEnv, ...entry.start.env }\n * - Stdio servers: { ...baseEnv, ...entry.env }\n *\n * When baseEnv is not provided, process.env is used as the base.\n */\nfunction getSpawnConfig(entry: McpServerEntry, dialects: Dialect[], baseEnv: Record<string, string | undefined>): SpawnConfig {\n const transportType = inferTransportType(entry);\n const hasServers = dialects.includes('servers');\n const hasStart = dialects.includes('start');\n\n // If only 'servers' dialect: Claude Code compatible (ignore start blocks, stdio only)\n if (hasServers && !hasStart) {\n if (transportType === 'stdio' && entry.command) {\n return {\n shouldSpawn: true,\n command: entry.command,\n args: entry.args || [],\n env: filterEnv({ ...baseEnv, ...entry.env }),\n };\n }\n return { shouldSpawn: false };\n }\n\n // If only 'start' dialect: Only spawn servers with start blocks\n if (hasStart && !hasServers) {\n if (entry.start) {\n return {\n shouldSpawn: true,\n command: entry.start.command,\n args: entry.start.args || [],\n env: filterEnv({ ...baseEnv, ...entry.start.env }),\n };\n }\n return { shouldSpawn: false };\n }\n\n // Both dialects: Spawn both start blocks and stdio servers\n // Priority: start blocks first, then stdio\n if (entry.start) {\n return {\n shouldSpawn: true,\n command: entry.start.command,\n args: entry.start.args || [],\n env: filterEnv({ ...baseEnv, ...entry.start.env }),\n };\n }\n\n if (transportType === 'stdio' && entry.command) {\n return {\n shouldSpawn: true,\n command: entry.command,\n args: entry.args || [],\n env: filterEnv({ ...baseEnv, ...entry.env }),\n };\n }\n\n return { shouldSpawn: false };\n}\n\n/**\n * A registry of spawned MCP servers with connection management.\n * Provides access to individual server handles, connection management, and collection-wide close.\n */\ntype RegistryConnectOptions = Parameters<typeof connectMcpClient>[2];\n\nexport interface ServerRegistry {\n /**\n * The resolved servers configuration that was used.\n * Useful for debugging and understanding what was started.\n */\n config: ServersConfig;\n\n /**\n * Map of server name to server process handle.\n * @hidden\n */\n servers: Map<string, ServerProcess>;\n\n /**\n * Set of connected clients tracked by this registry.\n * Automatically populated when using registry.connect().\n */\n clients: Set<ManagedClient>;\n\n /**\n * Connect to a server by name.\n * The connected client is automatically tracked for close.\n *\n * @param name - Server name from configuration\n * @returns Connected MCP SDK Client\n */\n connect: (name: string, options?: RegistryConnectOptions) => Promise<ManagedClient>;\n\n /**\n * Close all clients and servers gracefully.\n * First closes all tracked clients, then sends the specified signal to all server processes.\n *\n * @param signal - Signal to send to processes (default: SIGINT)\n * @param opts - Options including timeout\n * @returns Promise resolving to whether any process timed out and how many were force-killed\n */\n close: (signal?: NodeJS.Signals, opts?: { timeoutMs?: number }) => Promise<CloseResult>;\n\n /**\n * Search indexed capabilities across all currently connected clients.\n * Requires at least one connected client; respects SearchOptions filters.\n */\n searchCapabilities: (query: string, options?: SearchOptions) => Promise<SearchResponse>;\n\n /**\n * Support for `await using` pattern (automatic close).\n */\n [Symbol.asyncDispose]: () => Promise<void>;\n}\n\n/**\n * Create a registry of MCP servers from configuration.\n *\n * **Fast start**: Returns immediately after processes are created.\n * Use `registry.connect()` for lazy MCP connection.\n *\n * @param serversConfig - Map of server names to their configurations\n * @param options - Options for registry creation\n * @param options.cwd - Working directory for spawned processes (default: process.cwd())\n * @param options.env - Base environment for all servers. If provided, process.env is NOT included.\n * If omitted, process.env is used as the base (default behavior).\n * @param options.dialects - Dialects controlling which servers to spawn (default: ['servers'])\n * - ['servers']: Spawn stdio servers only (Claude Code compatible)\n * - ['start']: Only spawn servers with start blocks (HTTP servers)\n * - ['servers', 'start']: Spawn both stdio servers and start blocks\n * @returns ServerRegistry instance with config, server map, connect method, and close function\n *\n * @example\n * // Fast server start (does NOT wait for readiness)\n * const registry = createServerRegistry({\n * 'echo': { command: 'node', args: ['server.ts'] },\n * });\n *\n * // Connect when needed (waits for MCP handshake)\n * const client = await registry.connect('echo');\n *\n * // Cleanup (closes all clients AND processes)\n * await registry.close();\n *\n * @example\n * // Start HTTP servers with start blocks\n * const registry = createServerRegistry(\n * {\n * 'http-server': {\n * url: 'http://localhost:8080/mcp',\n * start: { command: 'node', args: ['http.ts', '--port', '8080'] }\n * },\n * },\n * { dialects: ['start'] }\n * );\n *\n * @example\n * // Using await using for automatic close\n * await using registry = createServerRegistry(config);\n * const client = await registry.connect('server');\n * // Auto-disposed when scope exits\n */\nexport function createServerRegistry(serversConfig: ServersConfig, options?: CreateServerRegistryOptions): ServerRegistry {\n const cwd = options?.cwd ?? process.cwd();\n const dialects = options?.dialects ?? ['servers'];\n\n // Determine base environment:\n // - If options.env provided, use it (process.env NOT included)\n // - If options.env omitted, use process.env as base\n const baseEnv: Record<string, string | undefined> = options?.env ?? process.env;\n\n // Validate working directory exists (fail fast for configuration errors)\n if (!fs.existsSync(cwd)) {\n throw new Error(`Cannot start servers: working directory '${cwd}' does not exist`);\n }\n\n // Validate configuration (fail fast with clear errors)\n const validation = validateServers(serversConfig);\n if (!validation.valid) {\n throw new Error(`Invalid servers configuration:\\n${validation.errors?.join('\\n') ?? 'Unknown validation error'}`);\n }\n\n // Log validation warnings (non-blocking)\n if (validation.warnings && validation.warnings.length > 0) {\n for (const warning of validation.warnings) {\n logger.warn(warning);\n }\n }\n\n const servers = new Map<string, ServerProcess>();\n const clients = new Set<ManagedClient>();\n const sharedStdioClients = new Map<string, { client?: ManagedClient; connecting?: Promise<ManagedClient>; refs: number }>();\n\n // Start each server in the configuration\n for (const [name, entry] of Object.entries(serversConfig)) {\n // Infer transport type from config\n const transportType = inferTransportType(entry);\n\n // Determine spawn behavior based on dialects\n const spawnConfig = getSpawnConfig(entry, dialects, baseEnv);\n\n // Check if we should spawn this server\n if (!spawnConfig.shouldSpawn) {\n // External server - just log, no spawn needed\n if (entry.url) {\n logger.info(`[${name}] external ${transportType} server (url: ${entry.url})`);\n } else {\n logger.warn(`[${name}] skipping: no spawn configuration (missing start or command) and no url for external server`);\n }\n continue;\n }\n\n try {\n // Validate spawn config\n if (!spawnConfig.command) {\n throw new Error(`Server \"${name}\" missing command field`);\n }\n\n // All servers use the same working directory (cwd from options)\n const resolvedCwd = cwd;\n\n // Start the server\n logger.info(`[${name}] starting ${transportType} server (${spawnConfig.command} ${(spawnConfig.args || []).join(' ')})`);\n\n // stdio servers need 'pipe' for MCP communication over stdin/stdout\n // network servers use 'inherit' so we see their logs\n const stdio = transportType === 'stdio' ? 'pipe' : 'inherit';\n\n const handle = spawnProcess({\n name,\n command: spawnConfig.command,\n ...(spawnConfig.args !== undefined && { args: spawnConfig.args }),\n cwd: resolvedCwd,\n ...(spawnConfig.env && Object.keys(spawnConfig.env).length > 0 && { env: spawnConfig.env }),\n stdio,\n });\n\n // Add server to registry (starting is fast, readiness is lazy)\n servers.set(name, handle);\n logger.info(`[${name}] started successfully`);\n } catch (e) {\n logger.info(`[${name}] start ERROR: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n // Create connect function that tracks clients\n const connect = async (name: string, options?: RegistryConnectOptions): Promise<ManagedClient> => {\n const serverEntry = serversConfig[name];\n if (!serverEntry) {\n const available = Object.keys(serversConfig).join(', ');\n throw new Error(`Server '${name}' not found in config. Available servers: ${available || 'none'}`);\n }\n\n const transportType = inferTransportType(serverEntry);\n\n if (transportType === 'stdio') {\n // Stdio is a single logical connection; reuse one client and lease references.\n let entry = sharedStdioClients.get(name);\n if (!entry) {\n entry = { refs: 0 };\n sharedStdioClients.set(name, entry);\n }\n\n if (!entry.client) {\n if (!entry.connecting) {\n entry.connecting = (async () => {\n // Pass minimal RegistryLike object to connectMcpClient\n const registryLike = { config: serversConfig, servers };\n const rawClient = await connectMcpClient(registryLike, name, options);\n const decorated = decorateClient(rawClient, { serverName: name });\n entry.client = decorated;\n entry.connecting = undefined;\n return decorated;\n })().catch((error) => {\n sharedStdioClients.delete(name);\n throw error;\n });\n }\n\n await entry.connecting;\n }\n\n if (!entry.client) {\n throw new Error(`Failed to connect to stdio server '${name}'`);\n }\n\n entry.refs += 1;\n let released = false;\n let lease: ManagedClient;\n\n lease = new Proxy(entry.client, {\n get(target, prop) {\n if (prop === 'close') {\n return async () => {\n if (released) return;\n released = true;\n clients.delete(lease);\n entry.refs = Math.max(0, entry.refs - 1);\n if (entry.refs === 0) {\n sharedStdioClients.delete(name);\n await target.close();\n }\n };\n }\n\n const value = Reflect.get(target, prop, target) as unknown;\n if (typeof value === 'function') {\n return (value as (...args: unknown[]) => unknown).bind(target);\n }\n return value;\n },\n }) as ManagedClient;\n\n clients.add(lease);\n return lease;\n }\n\n // Pass minimal RegistryLike object to connectMcpClient\n const registryLike = { config: serversConfig, servers };\n const rawClient = await connectMcpClient(registryLike, name, options);\n const decorated = decorateClient(rawClient, { serverName: name });\n clients.add(decorated);\n return decorated;\n };\n\n // Create close function that stops all clients and servers\n const close = async (signal: NodeJS.Signals = 'SIGINT', opts: { timeoutMs?: number } = {}): Promise<CloseResult> => {\n logger.info(`[registry] closing (${signal})`);\n\n // First, close all tracked clients\n const clientClosePromises = Array.from(clients).map(async (client) => {\n try {\n await client.close();\n } catch {\n // Ignore errors during client close\n }\n });\n await Promise.all(clientClosePromises);\n clients.clear();\n\n // Then close all server processes\n if (servers.size === 0) {\n return { timedOut: false, killedCount: 0 };\n }\n\n // Close all servers in parallel\n const closeResults = await Promise.all(Array.from(servers.values()).map((server) => server.close(signal, opts)));\n\n // Check if any timed out and count how many were force-killed\n const timedOut = closeResults.some((result) => result.timedOut);\n const killedCount = closeResults.filter((result) => result.killed).length;\n\n return { timedOut, killedCount };\n };\n\n const searchFromRegistry = async (query: string, options: SearchOptions = {}): Promise<SearchResponse> => {\n const requestedServers = options.servers ?? Object.keys(serversConfig);\n if (requestedServers.length === 0) {\n throw new Error('Cannot search capabilities: registry has no configured servers');\n }\n\n const unknownServers = requestedServers.filter((name) => !(name in serversConfig));\n if (unknownServers.length > 0) {\n throw new Error(`Cannot search capabilities: unknown server(s) [${unknownServers.join(', ')}]`);\n }\n\n const capabilityClients = new Map<string, CapabilityClient>();\n const failures: Array<{ server: string; reason: string }> = [];\n\n const ensureClient = async (serverName: string): Promise<ManagedClient> => {\n for (const client of clients) {\n if (client.serverName === serverName) {\n return client;\n }\n }\n return connect(serverName);\n };\n\n await Promise.all(\n requestedServers.map(async (serverName) => {\n try {\n const managed = await ensureClient(serverName);\n capabilityClients.set(serverName, managed.nativeClient);\n } catch (error) {\n failures.push({\n server: serverName,\n reason: error instanceof Error ? error.message : String(error),\n });\n }\n })\n );\n\n if (capabilityClients.size === 0) {\n const failureDetails = failures.length > 0 ? ` Connection failures: ${failures.map((f) => `${f.server} (${f.reason})`).join('; ')}` : '';\n throw new Error(`Cannot search capabilities: unable to connect to any requested servers.${failureDetails}`);\n }\n\n if (failures.length > 0) {\n throw new Error(`Cannot search capabilities: failed to connect to server(s) [${failures.map((f) => f.server).join(', ')}]. Reasons: ${failures.map((f) => `${f.server}: ${f.reason}`).join('; ')}`);\n }\n\n const index = await buildCapabilityIndex(capabilityClients);\n return executeCapabilitySearch(index, query, options);\n };\n\n // Async dispose for `await using` pattern\n const asyncDispose = async (): Promise<void> => {\n await close();\n };\n\n return {\n config: serversConfig,\n servers,\n clients,\n connect,\n close,\n searchCapabilities: searchFromRegistry,\n [Symbol.asyncDispose]: asyncDispose,\n };\n}\n"],"names":["createServerRegistry","inferTransportType","config","type","url","URL","protocol","filterEnv","env","filtered","Object","entries","key","value","undefined","getSpawnConfig","entry","dialects","baseEnv","transportType","hasServers","includes","hasStart","command","shouldSpawn","args","start","serversConfig","options","cwd","process","fs","existsSync","Error","validation","validateServers","valid","errors","join","warnings","length","warning","logger","warn","servers","Map","clients","Set","sharedStdioClients","name","spawnConfig","info","resolvedCwd","stdio","handle","spawnProcess","keys","set","e","message","String","connect","serverEntry","available","released","lease","registryLike","rawClient","decorated","get","refs","client","connecting","connectMcpClient","decorateClient","serverName","catch","error","delete","Proxy","target","prop","Math","max","close","Reflect","bind","add","signal","opts","clientClosePromises","closeResults","timedOut","killedCount","Array","from","map","Promise","all","clear","size","values","server","some","result","filter","killed","searchFromRegistry","query","requestedServers","unknownServers","capabilityClients","failures","ensureClient","failureDetails","index","managed","nativeClient","push","reason","f","buildCapabilityIndex","executeCapabilitySearch","asyncDispose","searchCapabilities","Symbol"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/types.ts"],"sourcesContent":["/**\n * MCP Configuration Types\n *\n * Auto-generated from schemas/servers.schema.json\n */\n\n// Re-export all generated types\nexport type { MCPServers, McpServerEntry, StartConfig } from '../schemas/servers.d.ts';\n\n/**\n * Transport type for server configuration\n * Standard MCP transport types matching Claude Code's schemas\n */\nexport type TransportType = 'stdio' | 'http' | 'sse-ide' | 'ws-ide' | 'sdk';\n"],"names":[],"mappings":"AAAA;;;;CAIC,GAED,gCAAgC"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/types.ts"],"sourcesContent":["/**\n * MCP Configuration Types\n *\n * Auto-generated from schemas/servers.schema.json\n */\n\n// Re-export all generated types\nexport type { MCPServers, McpServerEntry, StartConfig } from '../schemas/servers.d.ts';\n\n/**\n * Transport type for server configuration\n * Standard MCP transport types matching Claude Code's schemas\n */\nexport type TransportType = 'stdio' | 'http' | 'sse-ide' | 'ws-ide' | 'sdk';\n"],"names":[],"mappings":"AAAA;;;;CAIC,GAED,gCAAgC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/utils/logger.ts"],"sourcesContent":["/**\n * Sanitized logger with log level filtering\n * Provides credential redaction and verbosity control for all log output\n */\n\nimport type { SpawnMetadata } from '../connection/types.ts';\nimport { sanitizeForLogging } from './sanitizer.ts';\n\n/**\n * Log level for filtering output\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\n/**\n * Logger interface - subset of Console\n */\nexport type Logger = Pick<Console, 'info' | 'error' | 'warn' | 'debug'>;\n\n// Log level hierarchy (higher number = more important)\nconst levels: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\n// Default to 'warn' - quiet library, only show warnings and errors\nlet currentLevel: LogLevel = (process.env.LOG_LEVEL as LogLevel) || 'warn';\n\n/**\n * Check if a log should be output based on current level\n */\nfunction shouldLog(level: keyof Omit<typeof levels, 'silent'>): boolean {\n return levels[level] >= levels[currentLevel];\n}\n\n/**\n * Set the global log level\n * Users can call this to control library verbosity\n *\n * @param level - Log level: 'debug' (all), 'info', 'warn', 'error', 'silent' (none)\n */\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\n/**\n * Get current log level\n */\nexport function getLogLevel(): LogLevel {\n return currentLevel;\n}\n\n/**\n * Create a sanitized logger with log level filtering\n */\nfunction createSanitizedLogger(): Logger {\n const createLogMethod = (consoleMethod: 'info' | 'warn' | 'error' | 'debug') => {\n return (message: string, ...args: unknown[]) => {\n // Check if this log level should be output\n if (!shouldLog(consoleMethod)) {\n return;\n }\n\n const metadata = args.length > 0 && typeof args[0] === 'object' ? (args[0] as SpawnMetadata) : {};\n const { message: clean, meta: cleanMeta } = sanitizeForLogging(message, metadata);\n if (Object.keys(cleanMeta).length > 0) {\n console[consoleMethod](clean, cleanMeta);\n } else {\n console[consoleMethod](clean);\n }\n };\n };\n\n return {\n info: createLogMethod('info'),\n warn: createLogMethod('warn'),\n error: createLogMethod('error'),\n debug: createLogMethod('debug'),\n };\n}\n\n// Singleton instance - shared across all operations\nexport const logger = createSanitizedLogger();\n"],"names":["getLogLevel","logger","setLogLevel","levels","debug","info","warn","error","silent","currentLevel","process","env","LOG_LEVEL","shouldLog","level","createSanitizedLogger","createLogMethod","consoleMethod","message","args","metadata","length","sanitizeForLogging","clean","meta","cleanMeta","Object","keys","console"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QA+CeA;eAAAA;;QAkCHC;eAAAA;;QAzCGC;eAAAA;;;2BArCmB;;;;;AAYnC,uDAAuD;AACvD,IAAMC,SAAmC;IACvCC,OAAO;IACPC,MAAM;IACNC,MAAM;IACNC,OAAO;IACPC,QAAQ;AACV;AAEA,mEAAmE;AACnE,IAAIC,eAAyB,AAACC,QAAQC,GAAG,CAACC,SAAS,IAAiB;AAEpE;;CAEC,GACD,SAASC,UAAUC,KAA0C;IAC3D,OAAOX,MAAM,CAACW,MAAM,IAAIX,MAAM,CAACM,aAAa;AAC9C;AAQO,SAASP,YAAYY,KAAe;IACzCL,eAAeK;AACjB;AAKO,SAASd;IACd,OAAOS;AACT;AAEA;;CAEC,GACD,SAASM;IACP,IAAMC,kBAAkB,SAACC;QACvB,OAAO,SAACC;6CAAoBC;gBAAAA;;YAC1B,2CAA2C;YAC3C,IAAI,CAACN,UAAUI,gBAAgB;gBAC7B;YACF;YAEA,IAAMG,WAAWD,KAAKE,MAAM,GAAG,KAAK,SAAOF,IAAI,CAAC,EAAE,MAAK,WAAYA,IAAI,CAAC,EAAE,GAAqB,CAAC;YAChG,IAA4CG,sBAAAA,IAAAA,+BAAkB,EAACJ,SAASE,WAAhEF,AAASK,QAA2BD,oBAApCJ,SAAgBM,AAAMC,YAAcH,oBAApBE;YACxB,IAAIE,OAAOC,IAAI,CAACF,WAAWJ,MAAM,GAAG,GAAG;gBACrCO,OAAO,CAACX,cAAc,CAACM,OAAOE;YAChC,OAAO;gBACLG,OAAO,CAACX,cAAc,CAACM;YACzB;QACF;IACF;IAEA,OAAO;QACLlB,MAAMW,gBAAgB;QACtBV,MAAMU,gBAAgB;QACtBT,OAAOS,gBAAgB;QACvBZ,OAAOY,gBAAgB;IACzB;AACF;AAGO,IAAMf,SAASc"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/utils/logger.ts"],"sourcesContent":["/**\n * Sanitized logger with log level filtering\n * Provides credential redaction and verbosity control for all log output\n */\n\nimport type { SpawnMetadata } from '../connection/types.ts';\nimport { sanitizeForLogging } from './sanitizer.ts';\n\n/**\n * Log level for filtering output\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\n/**\n * Logger interface - subset of Console\n */\nexport type Logger = Pick<Console, 'info' | 'error' | 'warn' | 'debug'>;\n\n// Log level hierarchy (higher number = more important)\nconst levels: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\n// Default to 'warn' - quiet library, only show warnings and errors\nlet currentLevel: LogLevel = (process.env.LOG_LEVEL as LogLevel) || 'warn';\n\n/**\n * Check if a log should be output based on current level\n */\nfunction shouldLog(level: keyof Omit<typeof levels, 'silent'>): boolean {\n return levels[level] >= levels[currentLevel];\n}\n\n/**\n * Set the global log level\n * Users can call this to control library verbosity\n *\n * @param level - Log level: 'debug' (all), 'info', 'warn', 'error', 'silent' (none)\n */\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level;\n}\n\n/**\n * Get current log level\n */\nexport function getLogLevel(): LogLevel {\n return currentLevel;\n}\n\n/**\n * Create a sanitized logger with log level filtering\n */\nfunction createSanitizedLogger(): Logger {\n const createLogMethod = (consoleMethod: 'info' | 'warn' | 'error' | 'debug') => {\n return (message: string, ...args: unknown[]) => {\n // Check if this log level should be output\n if (!shouldLog(consoleMethod)) {\n return;\n }\n\n const metadata = args.length > 0 && typeof args[0] === 'object' ? (args[0] as SpawnMetadata) : {};\n const { message: clean, meta: cleanMeta } = sanitizeForLogging(message, metadata);\n if (Object.keys(cleanMeta).length > 0) {\n console[consoleMethod](clean, cleanMeta);\n } else {\n console[consoleMethod](clean);\n }\n };\n };\n\n return {\n info: createLogMethod('info'),\n warn: createLogMethod('warn'),\n error: createLogMethod('error'),\n debug: createLogMethod('debug'),\n };\n}\n\n// Singleton instance - shared across all operations\nexport const logger = createSanitizedLogger();\n"],"names":["getLogLevel","logger","setLogLevel","levels","debug","info","warn","error","silent","currentLevel","process","env","LOG_LEVEL","shouldLog","level","createSanitizedLogger","createLogMethod","consoleMethod","message","args","metadata","length","sanitizeForLogging","clean","meta","cleanMeta","Object","keys","console"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QA+CeA;eAAAA;;QAkCHC;eAAAA;;QAzCGC;eAAAA;;;2BArCmB;;;;;AAYnC,uDAAuD;AACvD,IAAMC,SAAmC;IACvCC,OAAO;IACPC,MAAM;IACNC,MAAM;IACNC,OAAO;IACPC,QAAQ;AACV;AAEA,mEAAmE;AACnE,IAAIC,eAAyB,AAACC,QAAQC,GAAG,CAACC,SAAS,IAAiB;AAEpE;;CAEC,GACD,SAASC,UAAUC,KAA0C;IAC3D,OAAOX,MAAM,CAACW,MAAM,IAAIX,MAAM,CAACM,aAAa;AAC9C;AAQO,SAASP,YAAYY,KAAe;IACzCL,eAAeK;AACjB;AAKO,SAASd;IACd,OAAOS;AACT;AAEA;;CAEC,GACD,SAASM;IACP,IAAMC,kBAAkB,SAACC;QACvB,OAAO,SAACC;6CAAoBC;gBAAAA;;YAC1B,2CAA2C;YAC3C,IAAI,CAACN,UAAUI,gBAAgB;gBAC7B;YACF;YAEA,IAAMG,WAAWD,KAAKE,MAAM,GAAG,KAAK,SAAOF,IAAI,CAAC,EAAE,MAAK,WAAYA,IAAI,CAAC,EAAE,GAAqB,CAAC;YAChG,IAA4CG,sBAAAA,IAAAA,+BAAkB,EAACJ,SAASE,WAAhEF,AAASK,QAA2BD,oBAApCJ,SAAgBM,AAAMC,YAAcH,oBAApBE;YACxB,IAAIE,OAAOC,IAAI,CAACF,WAAWJ,MAAM,GAAG,GAAG;gBACrCO,OAAO,CAACX,cAAc,CAACM,OAAOE;YAChC,OAAO;gBACLG,OAAO,CAACX,cAAc,CAACM;YACzB;QACF;IACF;IAEA,OAAO;QACLlB,MAAMW,gBAAgB;QACtBV,MAAMU,gBAAgB;QACtBT,OAAOS,gBAAgB;QACvBZ,OAAOY,gBAAgB;IACzB;AACF;AAGO,IAAMf,SAASc"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/utils/path-utils.ts"],"sourcesContent":["/**\n * Path resolution utilities for cluster spawning.\n * Handles ~, relative paths, absolute paths, and special cases (URLs, npm packages, flags).\n */\n\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Resolve a file path relative to a working directory.\n *\n * Handles three cases:\n * - `~` or `~/path` - Expands to home directory\n * - Absolute paths - Returns as-is\n * - Relative paths - Resolves relative to cwd\n *\n * @param filePath - The path to resolve\n * @param cwd - The working directory for resolving relative paths\n * @returns The resolved absolute path\n *\n * @example\n * resolvePath('~/config.json', '/unused') // → '/home/user/config.json'\n * resolvePath('/absolute/path', '/unused') // → '/absolute/path'\n * resolvePath('./relative', '/base') // → '/base/relative'\n */\nexport function resolvePath(filePath: string, cwd: string): string {\n // Expand ~ to home directory\n if (filePath === '~' || filePath.startsWith('~/')) {\n filePath = filePath.replace(/^~/, os.homedir());\n }\n\n // If absolute, return as-is\n if (path.isAbsolute(filePath)) {\n return filePath;\n }\n\n // Otherwise resolve relative to cwd\n return path.resolve(cwd, filePath);\n}\n\n/**\n * Resolve paths in command arguments array.\n *\n * Intelligently handles different argument types:\n * - Flags with paths: `--env-file=./path` → `--env-file=/absolute/path`\n * - Flags without paths: `--port=3000` → unchanged\n * - Command flags: `--verbose` → unchanged\n * - URLs: `http://example.com` → unchanged\n * - npm packages: `@scope/package` or `package-name` → unchanged\n * - Regular paths: `./script.js` → `/absolute/script.js`\n *\n * @param args - Array of command arguments\n * @param cwd - Working directory for resolving relative paths\n * @returns Array with paths resolved\n *\n * @example\n * resolveArgsPaths(['./bin/server.js', '--env-file=./config.env', '--port=3000'], '/home/user')\n * // → ['/home/user/bin/server.js', '--env-file=/home/user/config.env', '--port=3000']\n *\n * resolveArgsPaths(['@scope/package', 'https://example.com'], '/unused')\n * // → ['@scope/package', 'https://example.com'] (unchanged)\n */\nexport function resolveArgsPaths(args: string[], cwd: string): string[] {\n return args.map((arg) => {\n if (typeof arg !== 'string') {\n return arg;\n }\n\n // Check for flags with path values like --env-file=path, --config=path, etc.\n const flagMatch = arg.match(/^(--.+?)=(.+)$/);\n if (flagMatch) {\n const [, flag, value] = flagMatch;\n // Only resolve if the value looks like a path (contains ./ or ../ or / )\n if (value && value.includes('/')) {\n // Skip URLs in flag values\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(value)) {\n return arg;\n }\n return `${flag}=${resolvePath(value, cwd)}`;\n }\n return arg; // Return as-is for non-path flag values like --port=3000\n }\n\n // Skip command-line flags, only resolve actual paths\n if (arg.startsWith('-')) {\n return arg; // Don't resolve command-line flags as paths\n }\n\n // Skip URLs (http://, https://, etc.)\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(arg)) {\n return arg; // Return URLs as-is\n }\n\n // Skip npm package names (starting with @ or containing no path separators)\n if (arg.startsWith('@') || (!arg.includes('/') && !arg.includes('\\\\'))) {\n return arg; // Return npm package names as-is\n }\n\n // Regular arguments get resolved as paths\n return resolvePath(arg, cwd);\n });\n}\n"],"names":["resolveArgsPaths","resolvePath","filePath","cwd","startsWith","replace","os","homedir","path","isAbsolute","resolve","args","map","arg","flagMatch","match","flag","value","includes","test"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QA2DeA;eAAAA;;QArCAC;eAAAA;;;0DApBI;4DACE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBf,SAASA,YAAYC,QAAgB,EAAEC,GAAW;IACvD,6BAA6B;IAC7B,IAAID,aAAa,OAAOA,SAASE,UAAU,CAAC,OAAO;QACjDF,WAAWA,SAASG,OAAO,CAAC,MAAMC,IAAGC,OAAO;IAC9C;IAEA,4BAA4B;IAC5B,IAAIC,MAAKC,UAAU,CAACP,WAAW;QAC7B,OAAOA;IACT;IAEA,oCAAoC;IACpC,OAAOM,MAAKE,OAAO,CAACP,KAAKD;AAC3B;AAwBO,SAASF,iBAAiBW,IAAc,EAAER,GAAW;IAC1D,OAAOQ,KAAKC,GAAG,CAAC,SAACC;QACf,IAAI,OAAOA,QAAQ,UAAU;YAC3B,OAAOA;QACT;QAEA,6EAA6E;QAC7E,IAAMC,YAAYD,IAAIE,KAAK,CAAC;QAC5B,IAAID,WAAW;YACb,IAAwBA,8BAAAA,eAAfE,OAAeF,eAATG,QAASH;YACxB,yEAAyE;YACzE,IAAIG,SAASA,MAAMC,QAAQ,CAAC,MAAM;gBAChC,2BAA2B;gBAC3B,IAAI,gCAAgCC,IAAI,CAACF,QAAQ;oBAC/C,OAAOJ;gBACT;gBACA,OAAO,AAAC,GAAUZ,OAARe,MAAK,KAA2B,OAAxBf,YAAYgB,OAAOd;YACvC;YACA,OAAOU,KAAK,yDAAyD;QACvE;QAEA,qDAAqD;QACrD,IAAIA,IAAIT,UAAU,CAAC,MAAM;YACvB,OAAOS,KAAK,4CAA4C;QAC1D;QAEA,sCAAsC;QACtC,IAAI,gCAAgCM,IAAI,CAACN,MAAM;YAC7C,OAAOA,KAAK,oBAAoB;QAClC;QAEA,4EAA4E;QAC5E,IAAIA,IAAIT,UAAU,CAAC,QAAS,CAACS,IAAIK,QAAQ,CAAC,QAAQ,CAACL,IAAIK,QAAQ,CAAC,OAAQ;YACtE,OAAOL,KAAK,iCAAiC;QAC/C;QAEA,0CAA0C;QAC1C,OAAOZ,YAAYY,KAAKV;IAC1B;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/utils/path-utils.ts"],"sourcesContent":["/**\n * Path resolution utilities for cluster spawning.\n * Handles ~, relative paths, absolute paths, and special cases (URLs, npm packages, flags).\n */\n\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Resolve a file path relative to a working directory.\n *\n * Handles three cases:\n * - `~` or `~/path` - Expands to home directory\n * - Absolute paths - Returns as-is\n * - Relative paths - Resolves relative to cwd\n *\n * @param filePath - The path to resolve\n * @param cwd - The working directory for resolving relative paths\n * @returns The resolved absolute path\n *\n * @example\n * resolvePath('~/config.json', '/unused') // → '/home/user/config.json'\n * resolvePath('/absolute/path', '/unused') // → '/absolute/path'\n * resolvePath('./relative', '/base') // → '/base/relative'\n */\nexport function resolvePath(filePath: string, cwd: string): string {\n // Expand ~ to home directory\n if (filePath === '~' || filePath.startsWith('~/')) {\n filePath = filePath.replace(/^~/, os.homedir());\n }\n\n // If absolute, return as-is\n if (path.isAbsolute(filePath)) {\n return filePath;\n }\n\n // Otherwise resolve relative to cwd\n return path.resolve(cwd, filePath);\n}\n\n/**\n * Resolve paths in command arguments array.\n *\n * Intelligently handles different argument types:\n * - Flags with paths: `--env-file=./path` → `--env-file=/absolute/path`\n * - Flags without paths: `--port=3000` → unchanged\n * - Command flags: `--verbose` → unchanged\n * - URLs: `http://example.com` → unchanged\n * - npm packages: `@scope/package` or `package-name` → unchanged\n * - Regular paths: `./script.js` → `/absolute/script.js`\n *\n * @param args - Array of command arguments\n * @param cwd - Working directory for resolving relative paths\n * @returns Array with paths resolved\n *\n * @example\n * resolveArgsPaths(['./bin/server.js', '--env-file=./config.env', '--port=3000'], '/home/user')\n * // → ['/home/user/bin/server.js', '--env-file=/home/user/config.env', '--port=3000']\n *\n * resolveArgsPaths(['@scope/package', 'https://example.com'], '/unused')\n * // → ['@scope/package', 'https://example.com'] (unchanged)\n */\nexport function resolveArgsPaths(args: string[], cwd: string): string[] {\n return args.map((arg) => {\n if (typeof arg !== 'string') {\n return arg;\n }\n\n // Check for flags with path values like --env-file=path, --config=path, etc.\n const flagMatch = arg.match(/^(--.+?)=(.+)$/);\n if (flagMatch) {\n const [, flag, value] = flagMatch;\n // Only resolve if the value looks like a path (contains ./ or ../ or / )\n if (value && value.includes('/')) {\n // Skip URLs in flag values\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(value)) {\n return arg;\n }\n return `${flag}=${resolvePath(value, cwd)}`;\n }\n return arg; // Return as-is for non-path flag values like --port=3000\n }\n\n // Skip command-line flags, only resolve actual paths\n if (arg.startsWith('-')) {\n return arg; // Don't resolve command-line flags as paths\n }\n\n // Skip URLs (http://, https://, etc.)\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(arg)) {\n return arg; // Return URLs as-is\n }\n\n // Skip npm package names (starting with @ or containing no path separators)\n if (arg.startsWith('@') || (!arg.includes('/') && !arg.includes('\\\\'))) {\n return arg; // Return npm package names as-is\n }\n\n // Regular arguments get resolved as paths\n return resolvePath(arg, cwd);\n });\n}\n"],"names":["resolveArgsPaths","resolvePath","filePath","cwd","startsWith","replace","os","homedir","path","isAbsolute","resolve","args","map","arg","flagMatch","match","flag","value","includes","test"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QA2DeA;eAAAA;;QArCAC;eAAAA;;;0DApBI;4DACE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBf,SAASA,YAAYC,QAAgB,EAAEC,GAAW;IACvD,6BAA6B;IAC7B,IAAID,aAAa,OAAOA,SAASE,UAAU,CAAC,OAAO;QACjDF,WAAWA,SAASG,OAAO,CAAC,MAAMC,IAAGC,OAAO;IAC9C;IAEA,4BAA4B;IAC5B,IAAIC,MAAKC,UAAU,CAACP,WAAW;QAC7B,OAAOA;IACT;IAEA,oCAAoC;IACpC,OAAOM,MAAKE,OAAO,CAACP,KAAKD;AAC3B;AAwBO,SAASF,iBAAiBW,IAAc,EAAER,GAAW;IAC1D,OAAOQ,KAAKC,GAAG,CAAC,SAACC;QACf,IAAI,OAAOA,QAAQ,UAAU;YAC3B,OAAOA;QACT;QAEA,6EAA6E;QAC7E,IAAMC,YAAYD,IAAIE,KAAK,CAAC;QAC5B,IAAID,WAAW;YACb,IAAwBA,8BAAAA,eAAfE,OAAeF,eAATG,QAASH;YACxB,yEAAyE;YACzE,IAAIG,SAASA,MAAMC,QAAQ,CAAC,MAAM;gBAChC,2BAA2B;gBAC3B,IAAI,gCAAgCC,IAAI,CAACF,QAAQ;oBAC/C,OAAOJ;gBACT;gBACA,OAAO,AAAC,GAAUZ,OAARe,MAAK,KAA2B,OAAxBf,YAAYgB,OAAOd;YACvC;YACA,OAAOU,KAAK,yDAAyD;QACvE;QAEA,qDAAqD;QACrD,IAAIA,IAAIT,UAAU,CAAC,MAAM;YACvB,OAAOS,KAAK,4CAA4C;QAC1D;QAEA,sCAAsC;QACtC,IAAI,gCAAgCM,IAAI,CAACN,MAAM;YAC7C,OAAOA,KAAK,oBAAoB;QAClC;QAEA,4EAA4E;QAC5E,IAAIA,IAAIT,UAAU,CAAC,QAAS,CAACS,IAAIK,QAAQ,CAAC,QAAQ,CAACL,IAAIK,QAAQ,CAAC,OAAQ;YACtE,OAAOL,KAAK,iCAAiC;QAC/C;QAEA,0CAA0C;QAC1C,OAAOZ,YAAYY,KAAKV;IAC1B;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/utils/sanitizer.ts"],"sourcesContent":["/**\n * Minimal log sanitization for spawn operations\n * Redacts credentials from environment variables and command arguments\n */\n\nimport type { SpawnMetadata } from '../connection/types.ts';\n\n/**\n * Sanitize log messages and metadata to prevent credential leakage\n *\n * Redacts common credential patterns:\n * - key=value, secret=value, token=value, password=value\n * - Environment variables with sensitive names\n *\n * @param message - Log message to sanitize\n * @param obj - Metadata object to sanitize\n * @returns Sanitized message and metadata\n */\nexport function sanitizeForLogging(message: string, obj: SpawnMetadata): { message: string; meta: SpawnMetadata } {\n // Redact common credential patterns in message\n const cleanMessage = message\n .replace(/key[=:]\\S+/gi, 'key=[REDACTED]')\n .replace(/secret[=:]\\S+/gi, 'secret=[REDACTED]')\n .replace(/token[=:]\\S+/gi, 'token=[REDACTED]')\n .replace(/password[=:]\\S+/gi, 'password=[REDACTED]')\n .replace(/auth[=:]\\S+/gi, 'auth=[REDACTED]');\n\n // Deep clone and redact sensitive env var keys\n const cleanMeta = JSON.parse(JSON.stringify(obj));\n\n // Redact sensitive environment variable values\n if (cleanMeta.env && typeof cleanMeta.env === 'object') {\n for (const envKey of Object.keys(cleanMeta.env)) {\n if (/key|secret|token|password|auth|credential/i.test(envKey)) {\n cleanMeta.env[envKey] = '[REDACTED]';\n }\n }\n }\n\n return { message: cleanMessage, meta: cleanMeta };\n}\n\nexport function sanitizeForLoggingFormatter() {\n return {\n log: (obj: SpawnMetadata) => {\n const message = (obj.msg || obj.message || '') as string;\n const { message: clean, meta } = sanitizeForLogging(message, obj);\n return { ...meta, msg: clean };\n },\n };\n}\n"],"names":["sanitizeForLogging","sanitizeForLoggingFormatter","message","obj","cleanMessage","replace","cleanMeta","JSON","parse","stringify","env","Object","keys","envKey","test","meta","log","msg","clean"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QAeeA;eAAAA;;QAwBAC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxBT,SAASD,mBAAmBE,OAAe,EAAEC,GAAkB;IACpE,+CAA+C;IAC/C,IAAMC,eAAeF,QAClBG,OAAO,CAAC,gBAAgB,kBACxBA,OAAO,CAAC,mBAAmB,qBAC3BA,OAAO,CAAC,kBAAkB,oBAC1BA,OAAO,CAAC,qBAAqB,uBAC7BA,OAAO,CAAC,iBAAiB;IAE5B,+CAA+C;IAC/C,IAAMC,YAAYC,KAAKC,KAAK,CAACD,KAAKE,SAAS,CAACN;IAE5C,+CAA+C;IAC/C,IAAIG,UAAUI,GAAG,IAAI,SAAOJ,UAAUI,GAAG,MAAK,UAAU;YACjD,kCAAA,2BAAA;;YAAL,QAAK,YAAgBC,OAAOC,IAAI,CAACN,UAAUI,GAAG,sBAAzC,SAAA,6BAAA,QAAA,yBAAA,iCAA4C;gBAA5C,IAAMG,SAAN;gBACH,IAAI,6CAA6CC,IAAI,CAACD,SAAS;oBAC7DP,UAAUI,GAAG,CAACG,OAAO,GAAG;gBAC1B;YACF;;YAJK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAKP;IAEA,OAAO;QAAEX,SAASE;QAAcW,MAAMT;IAAU;AAClD;AAEO,SAASL;IACd,OAAO;QACLe,KAAK,SAACb;YACJ,IAAMD,UAAWC,IAAIc,GAAG,IAAId,IAAID,OAAO,IAAI;YAC3C,IAAiCF,sBAAAA,mBAAmBE,SAASC,MAArDD,AAASgB,QAAgBlB,oBAAzBE,SAAgBa,OAASf,oBAATe;YACxB,OAAO,wCAAKA;gBAAME,KAAKC;;QACzB;IACF;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/utils/sanitizer.ts"],"sourcesContent":["/**\n * Minimal log sanitization for spawn operations\n * Redacts credentials from environment variables and command arguments\n */\n\nimport type { SpawnMetadata } from '../connection/types.ts';\n\n/**\n * Sanitize log messages and metadata to prevent credential leakage\n *\n * Redacts common credential patterns:\n * - key=value, secret=value, token=value, password=value\n * - Environment variables with sensitive names\n *\n * @param message - Log message to sanitize\n * @param obj - Metadata object to sanitize\n * @returns Sanitized message and metadata\n */\nexport function sanitizeForLogging(message: string, obj: SpawnMetadata): { message: string; meta: SpawnMetadata } {\n // Redact common credential patterns in message\n const cleanMessage = message\n .replace(/key[=:]\\S+/gi, 'key=[REDACTED]')\n .replace(/secret[=:]\\S+/gi, 'secret=[REDACTED]')\n .replace(/token[=:]\\S+/gi, 'token=[REDACTED]')\n .replace(/password[=:]\\S+/gi, 'password=[REDACTED]')\n .replace(/auth[=:]\\S+/gi, 'auth=[REDACTED]');\n\n // Deep clone and redact sensitive env var keys\n const cleanMeta = JSON.parse(JSON.stringify(obj));\n\n // Redact sensitive environment variable values\n if (cleanMeta.env && typeof cleanMeta.env === 'object') {\n for (const envKey of Object.keys(cleanMeta.env)) {\n if (/key|secret|token|password|auth|credential/i.test(envKey)) {\n cleanMeta.env[envKey] = '[REDACTED]';\n }\n }\n }\n\n return { message: cleanMessage, meta: cleanMeta };\n}\n\nexport function sanitizeForLoggingFormatter() {\n return {\n log: (obj: SpawnMetadata) => {\n const message = (obj.msg || obj.message || '') as string;\n const { message: clean, meta } = sanitizeForLogging(message, obj);\n return { ...meta, msg: clean };\n },\n };\n}\n"],"names":["sanitizeForLogging","sanitizeForLoggingFormatter","message","obj","cleanMessage","replace","cleanMeta","JSON","parse","stringify","env","Object","keys","envKey","test","meta","log","msg","clean"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QAeeA;eAAAA;;QAwBAC;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxBT,SAASD,mBAAmBE,OAAe,EAAEC,GAAkB;IACpE,+CAA+C;IAC/C,IAAMC,eAAeF,QAClBG,OAAO,CAAC,gBAAgB,kBACxBA,OAAO,CAAC,mBAAmB,qBAC3BA,OAAO,CAAC,kBAAkB,oBAC1BA,OAAO,CAAC,qBAAqB,uBAC7BA,OAAO,CAAC,iBAAiB;IAE5B,+CAA+C;IAC/C,IAAMC,YAAYC,KAAKC,KAAK,CAACD,KAAKE,SAAS,CAACN;IAE5C,+CAA+C;IAC/C,IAAIG,UAAUI,GAAG,IAAI,SAAOJ,UAAUI,GAAG,MAAK,UAAU;YACjD,kCAAA,2BAAA;;YAAL,QAAK,YAAgBC,OAAOC,IAAI,CAACN,UAAUI,GAAG,sBAAzC,SAAA,6BAAA,QAAA,yBAAA,iCAA4C;gBAA5C,IAAMG,SAAN;gBACH,IAAI,6CAA6CC,IAAI,CAACD,SAAS;oBAC7DP,UAAUI,GAAG,CAACG,OAAO,GAAG;gBAC1B;YACF;;YAJK;YAAA;;;qBAAA,6BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAKP;IAEA,OAAO;QAAEX,SAASE;QAAcW,MAAMT;IAAU;AAClD;AAEO,SAASL;IACd,OAAO;QACLe,KAAK,SAACb;YACJ,IAAMD,UAAWC,IAAIc,GAAG,IAAId,IAAID,OAAO,IAAI;YAC3C,IAAiCF,sBAAAA,mBAAmBE,SAASC,MAArDD,AAASgB,QAAgBlB,oBAAzBE,SAAgBa,OAASf,oBAATe;YACxB,OAAO,wCAAKA;gBAAME,KAAKC;;QACzB;IACF;AACF"}
@@ -3,23 +3,4 @@
3
3
  * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
4
4
  */
5
5
  import type { AuthCapabilities } from './types.js';
6
- /**
7
- * Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain
8
- * Returns capabilities including DCR support detection
9
- *
10
- * Discovery Strategy:
11
- * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)
12
- * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata
13
- * 3. Fall back to direct RFC 8414 discovery at resource origin
14
- *
15
- * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)
16
- * @returns AuthCapabilities object with discovered endpoints and features
17
- *
18
- * @example
19
- * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com
20
- * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');
21
- * if (caps.supportsDcr) {
22
- * console.log('Registration endpoint:', caps.registrationEndpoint);
23
- * }
24
- */
25
6
  export declare function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities>;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OAuth Server Capability Discovery
3
3
  * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
4
- */ import { discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.js';
4
+ */ import { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.js';
5
5
  /**
6
6
  * Extract origin (protocol + host) from a URL
7
7
  * @param url - Full URL that may include a path
@@ -36,7 +36,34 @@
36
36
  * if (caps.supportsDcr) {
37
37
  * console.log('Registration endpoint:', caps.registrationEndpoint);
38
38
  * }
39
- */ export async function probeAuthCapabilities(baseUrl) {
39
+ */ function buildCapabilities(metadata, scopes) {
40
+ const supportsDcr = !!metadata.registration_endpoint;
41
+ const capabilities = {
42
+ supportsDcr
43
+ };
44
+ if (metadata.registration_endpoint) {
45
+ capabilities.registrationEndpoint = metadata.registration_endpoint;
46
+ }
47
+ if (metadata.authorization_endpoint) {
48
+ capabilities.authorizationEndpoint = metadata.authorization_endpoint;
49
+ }
50
+ if (metadata.token_endpoint) capabilities.tokenEndpoint = metadata.token_endpoint;
51
+ if (metadata.introspection_endpoint) {
52
+ capabilities.introspectionEndpoint = metadata.introspection_endpoint;
53
+ }
54
+ if (scopes && scopes.length > 0) {
55
+ capabilities.scopes = scopes;
56
+ } else if (metadata.scopes_supported) {
57
+ capabilities.scopes = metadata.scopes_supported;
58
+ }
59
+ return capabilities;
60
+ }
61
+ async function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes) {
62
+ const metadata = await discoverAuthorizationServerMetadata(authServerUrl);
63
+ if (!metadata) return null;
64
+ return buildCapabilities(metadata, scopes);
65
+ }
66
+ export async function probeAuthCapabilities(baseUrl) {
40
67
  try {
41
68
  // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery
42
69
  // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)
@@ -51,51 +78,26 @@
51
78
  supportsDcr: false
52
79
  };
53
80
  }
54
- const authServerMetadata = await discoverAuthorizationServerMetadata(authServerUrl);
55
- if (authServerMetadata) {
56
- // Successfully discovered full OAuth metadata via RFC 9728 → RFC 8414 chain
57
- const supportsDcr = !!authServerMetadata.registration_endpoint;
58
- const capabilities = {
59
- supportsDcr
60
- };
61
- if (authServerMetadata.registration_endpoint) {
62
- capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;
63
- }
64
- if (authServerMetadata.authorization_endpoint) {
65
- capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;
66
- }
67
- if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;
68
- if (authServerMetadata.introspection_endpoint) {
69
- capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;
70
- }
71
- // Prefer resource scopes over auth server scopes
72
- const scopes = resourceMetadata.scopes_supported || authServerMetadata.scopes_supported;
73
- if (scopes) capabilities.scopes = scopes;
81
+ const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported);
82
+ if (capabilities) {
74
83
  return capabilities;
75
84
  }
85
+ const issuer = await discoverAuthorizationServerIssuer(baseUrl);
86
+ if (issuer) {
87
+ const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported);
88
+ if (issuerCapabilities) return issuerCapabilities;
89
+ }
90
+ }
91
+ const issuer = await discoverAuthorizationServerIssuer(baseUrl);
92
+ if (issuer) {
93
+ const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer);
94
+ if (issuerCapabilities) return issuerCapabilities;
76
95
  }
77
96
  // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin
78
97
  // This handles same-domain OAuth (traditional setup)
79
98
  const origin = getOrigin(baseUrl);
80
- const authServerMetadata = await discoverAuthorizationServerMetadata(origin);
81
- if (authServerMetadata) {
82
- const supportsDcr = !!authServerMetadata.registration_endpoint;
83
- const capabilities = {
84
- supportsDcr
85
- };
86
- if (authServerMetadata.registration_endpoint) {
87
- capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;
88
- }
89
- if (authServerMetadata.authorization_endpoint) {
90
- capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;
91
- }
92
- if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;
93
- if (authServerMetadata.introspection_endpoint) {
94
- capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;
95
- }
96
- if (authServerMetadata.scopes_supported) capabilities.scopes = authServerMetadata.scopes_supported;
97
- return capabilities;
98
- }
99
+ const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin);
100
+ if (originCapabilities) return originCapabilities;
99
101
  // No OAuth metadata found
100
102
  return {
101
103
  supportsDcr: false
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(baseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const authServerMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n\n if (authServerMetadata) {\n // Successfully discovered full OAuth metadata via RFC 9728 → RFC 8414 chain\n const supportsDcr = !!authServerMetadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (authServerMetadata.registration_endpoint) {\n capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;\n }\n if (authServerMetadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;\n }\n if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;\n if (authServerMetadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;\n }\n\n // Prefer resource scopes over auth server scopes\n const scopes = resourceMetadata.scopes_supported || authServerMetadata.scopes_supported;\n if (scopes) capabilities.scopes = scopes;\n\n return capabilities;\n }\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(baseUrl);\n const authServerMetadata = await discoverAuthorizationServerMetadata(origin);\n\n if (authServerMetadata) {\n const supportsDcr = !!authServerMetadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (authServerMetadata.registration_endpoint) {\n capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;\n }\n if (authServerMetadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;\n }\n if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;\n if (authServerMetadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;\n }\n if (authServerMetadata.scopes_supported) capabilities.scopes = authServerMetadata.scopes_supported;\n\n return capabilities;\n }\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","probeAuthCapabilities","baseUrl","resourceMetadata","authorization_servers","length","authServerUrl","supportsDcr","authServerMetadata","registration_endpoint","capabilities","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","scopes","scopes_supported","_error"],"mappings":"AAAA;;;CAGC,GAED,SAASA,mCAAmC,EAAEC,iCAAiC,QAAQ,yBAAyB;AAGhH;;;;;;;;CAQC,GACD,SAASC,UAAUC,GAAW;IAC5B,IAAI;QACF,OAAO,IAAIC,IAAID,KAAKE,MAAM;IAC5B,EAAE,OAAM;QACN,sDAAsD;QACtD,OAAOF;IACT;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,eAAeG,sBAAsBC,OAAe;IACzD,IAAI;QACF,iEAAiE;QACjE,oFAAoF;QACpF,MAAMC,mBAAmB,MAAMP,kCAAkCM;QAEjE,IAAIC,oBAAoBA,iBAAiBC,qBAAqB,CAACC,MAAM,GAAG,GAAG;YACzE,+DAA+D;YAC/D,0DAA0D;YAC1D,MAAMC,gBAAgBH,iBAAiBC,qBAAqB,CAAC,EAAE;YAC/D,IAAI,CAACE,eAAe;gBAClB,4EAA4E;gBAC5E,OAAO;oBAAEC,aAAa;gBAAM;YAC9B;YACA,MAAMC,qBAAqB,MAAMb,oCAAoCW;YAErE,IAAIE,oBAAoB;gBACtB,4EAA4E;gBAC5E,MAAMD,cAAc,CAAC,CAACC,mBAAmBC,qBAAqB;gBAC9D,MAAMC,eAAiC;oBAAEH;gBAAY;gBAErD,IAAIC,mBAAmBC,qBAAqB,EAAE;oBAC5CC,aAAaC,oBAAoB,GAAGH,mBAAmBC,qBAAqB;gBAC9E;gBACA,IAAID,mBAAmBI,sBAAsB,EAAE;oBAC7CF,aAAaG,qBAAqB,GAAGL,mBAAmBI,sBAAsB;gBAChF;gBACA,IAAIJ,mBAAmBM,cAAc,EAAEJ,aAAaK,aAAa,GAAGP,mBAAmBM,cAAc;gBACrG,IAAIN,mBAAmBQ,sBAAsB,EAAE;oBAC7CN,aAAaO,qBAAqB,GAAGT,mBAAmBQ,sBAAsB;gBAChF;gBAEA,iDAAiD;gBACjD,MAAME,SAASf,iBAAiBgB,gBAAgB,IAAIX,mBAAmBW,gBAAgB;gBACvF,IAAID,QAAQR,aAAaQ,MAAM,GAAGA;gBAElC,OAAOR;YACT;QACF;QAEA,wEAAwE;QACxE,qDAAqD;QACrD,MAAMV,SAASH,UAAUK;QACzB,MAAMM,qBAAqB,MAAMb,oCAAoCK;QAErE,IAAIQ,oBAAoB;YACtB,MAAMD,cAAc,CAAC,CAACC,mBAAmBC,qBAAqB;YAC9D,MAAMC,eAAiC;gBAAEH;YAAY;YAErD,IAAIC,mBAAmBC,qBAAqB,EAAE;gBAC5CC,aAAaC,oBAAoB,GAAGH,mBAAmBC,qBAAqB;YAC9E;YACA,IAAID,mBAAmBI,sBAAsB,EAAE;gBAC7CF,aAAaG,qBAAqB,GAAGL,mBAAmBI,sBAAsB;YAChF;YACA,IAAIJ,mBAAmBM,cAAc,EAAEJ,aAAaK,aAAa,GAAGP,mBAAmBM,cAAc;YACrG,IAAIN,mBAAmBQ,sBAAsB,EAAE;gBAC7CN,aAAaO,qBAAqB,GAAGT,mBAAmBQ,sBAAsB;YAChF;YACA,IAAIR,mBAAmBW,gBAAgB,EAAET,aAAaQ,MAAM,GAAGV,mBAAmBW,gBAAgB;YAElG,OAAOT;QACT;QAEA,0BAA0B;QAC1B,OAAO;YAAEH,aAAa;QAAM;IAC9B,EAAE,OAAOa,QAAQ;QACf,sDAAsD;QACtD,6CAA6C;QAC7C,OAAO;YAAEb,aAAa;QAAM;IAC9B;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities, AuthorizationServerMetadata } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 → RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nfunction buildCapabilities(metadata: AuthorizationServerMetadata, scopes?: string[]): AuthCapabilities {\n const supportsDcr = !!metadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (metadata.registration_endpoint) {\n capabilities.registrationEndpoint = metadata.registration_endpoint;\n }\n if (metadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = metadata.authorization_endpoint;\n }\n if (metadata.token_endpoint) capabilities.tokenEndpoint = metadata.token_endpoint;\n if (metadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = metadata.introspection_endpoint;\n }\n\n if (scopes && scopes.length > 0) {\n capabilities.scopes = scopes;\n } else if (metadata.scopes_supported) {\n capabilities.scopes = metadata.scopes_supported;\n }\n\n return capabilities;\n}\n\nasync function resolveCapabilitiesFromAuthorizationServer(authServerUrl: string, scopes?: string[]): Promise<AuthCapabilities | null> {\n const metadata = await discoverAuthorizationServerMetadata(authServerUrl);\n if (!metadata) return null;\n return buildCapabilities(metadata, scopes);\n}\n\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(baseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported);\n if (capabilities) {\n return capabilities;\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported);\n if (issuerCapabilities) return issuerCapabilities;\n }\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer);\n if (issuerCapabilities) return issuerCapabilities;\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(baseUrl);\n const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin);\n if (originCapabilities) return originCapabilities;\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["discoverAuthorizationServerIssuer","discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","buildCapabilities","metadata","scopes","supportsDcr","registration_endpoint","capabilities","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","length","scopes_supported","resolveCapabilitiesFromAuthorizationServer","authServerUrl","probeAuthCapabilities","baseUrl","resourceMetadata","authorization_servers","issuer","issuerCapabilities","originCapabilities","_error"],"mappings":"AAAA;;;CAGC,GAED,SAASA,iCAAiC,EAAEC,mCAAmC,EAAEC,iCAAiC,QAAQ,yBAAyB;AAGnJ;;;;;;;;CAQC,GACD,SAASC,UAAUC,GAAW;IAC5B,IAAI;QACF,OAAO,IAAIC,IAAID,KAAKE,MAAM;IAC5B,EAAE,OAAM;QACN,sDAAsD;QACtD,OAAOF;IACT;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,SAASG,kBAAkBC,QAAqC,EAAEC,MAAiB;IACjF,MAAMC,cAAc,CAAC,CAACF,SAASG,qBAAqB;IACpD,MAAMC,eAAiC;QAAEF;IAAY;IAErD,IAAIF,SAASG,qBAAqB,EAAE;QAClCC,aAAaC,oBAAoB,GAAGL,SAASG,qBAAqB;IACpE;IACA,IAAIH,SAASM,sBAAsB,EAAE;QACnCF,aAAaG,qBAAqB,GAAGP,SAASM,sBAAsB;IACtE;IACA,IAAIN,SAASQ,cAAc,EAAEJ,aAAaK,aAAa,GAAGT,SAASQ,cAAc;IACjF,IAAIR,SAASU,sBAAsB,EAAE;QACnCN,aAAaO,qBAAqB,GAAGX,SAASU,sBAAsB;IACtE;IAEA,IAAIT,UAAUA,OAAOW,MAAM,GAAG,GAAG;QAC/BR,aAAaH,MAAM,GAAGA;IACxB,OAAO,IAAID,SAASa,gBAAgB,EAAE;QACpCT,aAAaH,MAAM,GAAGD,SAASa,gBAAgB;IACjD;IAEA,OAAOT;AACT;AAEA,eAAeU,2CAA2CC,aAAqB,EAAEd,MAAiB;IAChG,MAAMD,WAAW,MAAMP,oCAAoCsB;IAC3D,IAAI,CAACf,UAAU,OAAO;IACtB,OAAOD,kBAAkBC,UAAUC;AACrC;AAEA,OAAO,eAAee,sBAAsBC,OAAe;IACzD,IAAI;QACF,iEAAiE;QACjE,oFAAoF;QACpF,MAAMC,mBAAmB,MAAMxB,kCAAkCuB;QAEjE,IAAIC,oBAAoBA,iBAAiBC,qBAAqB,CAACP,MAAM,GAAG,GAAG;YACzE,+DAA+D;YAC/D,0DAA0D;YAC1D,MAAMG,gBAAgBG,iBAAiBC,qBAAqB,CAAC,EAAE;YAC/D,IAAI,CAACJ,eAAe;gBAClB,4EAA4E;gBAC5E,OAAO;oBAAEb,aAAa;gBAAM;YAC9B;YACA,MAAME,eAAe,MAAMU,2CAA2CC,eAAeG,iBAAiBL,gBAAgB;YACtH,IAAIT,cAAc;gBAChB,OAAOA;YACT;YAEA,MAAMgB,SAAS,MAAM5B,kCAAkCyB;YACvD,IAAIG,QAAQ;gBACV,MAAMC,qBAAqB,MAAMP,2CAA2CM,QAAQF,iBAAiBL,gBAAgB;gBACrH,IAAIQ,oBAAoB,OAAOA;YACjC;QACF;QAEA,MAAMD,SAAS,MAAM5B,kCAAkCyB;QACvD,IAAIG,QAAQ;YACV,MAAMC,qBAAqB,MAAMP,2CAA2CM;YAC5E,IAAIC,oBAAoB,OAAOA;QACjC;QAEA,wEAAwE;QACxE,qDAAqD;QACrD,MAAMvB,SAASH,UAAUsB;QACzB,MAAMK,qBAAqB,MAAMR,2CAA2ChB;QAC5E,IAAIwB,oBAAoB,OAAOA;QAE/B,0BAA0B;QAC1B,OAAO;YAAEpB,aAAa;QAAM;IAC9B,EAAE,OAAOqB,QAAQ;QACf,sDAAsD;QACtD,6CAA6C;QAC7C,OAAO;YAAErB,aAAa;QAAM;IAC9B;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/index.ts"],"sourcesContent":["/**\n * Authentication Module\n * Exports public API for OAuth authentication only (DCR moved to ../dcr/)\n */\n\nexport { probeAuthCapabilities } from './capability-discovery.ts';\nexport { InteractiveOAuthFlow } from './interactive-oauth-flow.ts';\nexport type { OAuthCallbackListenerOptions } from './oauth-callback-listener.ts';\nexport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthFlowOptions, TokenSet } from './types.ts';\n"],"names":["probeAuthCapabilities","InteractiveOAuthFlow","OAuthCallbackListener"],"mappings":"AAAA;;;CAGC,GAED,SAASA,qBAAqB,QAAQ,4BAA4B;AAClE,SAASC,oBAAoB,QAAQ,8BAA8B;AAEnE,SAASC,qBAAqB,QAAQ,+BAA+B"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/index.ts"],"sourcesContent":["/**\n * Authentication Module\n * Exports public API for OAuth authentication only (DCR moved to ../dcr/)\n */\n\nexport { probeAuthCapabilities } from './capability-discovery.ts';\nexport { InteractiveOAuthFlow } from './interactive-oauth-flow.ts';\nexport type { OAuthCallbackListenerOptions } from './oauth-callback-listener.ts';\nexport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthFlowOptions, TokenSet } from './types.ts';\n"],"names":["probeAuthCapabilities","InteractiveOAuthFlow","OAuthCallbackListener"],"mappings":"AAAA;;;CAGC,GAED,SAASA,qBAAqB,QAAQ,4BAA4B;AAClE,SAASC,oBAAoB,QAAQ,8BAA8B;AAEnE,SAASC,qBAAqB,QAAQ,+BAA+B"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger } from '../utils/logger.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('🔐 Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Add resource parameter if specified (RFC 8707)\n if (options.resource) {\n authUrl.searchParams.set('resource', options.resource);\n }\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('🔗 Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('🌐 Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, pkce?.codeVerifier);\n\n return tokens;\n } catch (error) {\n logger.error('❌ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Exchange authorization code for access and refresh tokens\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636)\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refresh access token using refresh token\n *\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param refreshToken - Refresh token from previous token set\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @returns New token set with refreshed access token\n *\n * @throws Error if refresh fails\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<TokenSet> {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n"],"names":["child_process","logger","defaultLogger","OAuthCallbackListener","generatePkce","InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","callbackListener","port","pkce","debug","start","redirectUri","authUrl","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","timeout","result","waitForCallback","tokens","exchangeCodeForTokens","code","codeVerifier","error","Error","message","String","stop","params","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","response","fetch","method","headers","Accept","Connection","body","ok","errorText","text","status","data","json","access_token","tokenSet","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","process","command","args","child","spawn","detached","stdio","unref"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger } from '../utils/logger.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('🔐 Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Add resource parameter if specified (RFC 8707)\n if (options.resource) {\n authUrl.searchParams.set('resource', options.resource);\n }\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('🔗 Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('🌐 Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, pkce?.codeVerifier);\n\n return tokens;\n } catch (error) {\n logger.error('❌ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Exchange authorization code for access and refresh tokens\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636)\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refresh access token using refresh token\n *\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param refreshToken - Refresh token from previous token set\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @returns New token set with refreshed access token\n *\n * @throws Error if refresh fails\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<TokenSet> {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n"],"names":["child_process","logger","defaultLogger","OAuthCallbackListener","generatePkce","InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","callbackListener","port","pkce","debug","start","redirectUri","authUrl","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","timeout","result","waitForCallback","tokens","exchangeCodeForTokens","code","codeVerifier","error","Error","message","String","stop","params","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","response","fetch","method","headers","Accept","Connection","body","ok","errorText","text","status","data","json","access_token","tokenSet","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","process","command","args","child","spawn","detached","stdio","unref"],"mappings":"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"}