@mcp-z/client 2.2.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/cjs/auth/capability-discovery.d.cts +3 -1
- package/dist/cjs/auth/capability-discovery.d.ts +3 -1
- package/dist/cjs/auth/capability-discovery.js +46 -32
- package/dist/cjs/auth/capability-discovery.js.map +1 -1
- package/dist/cjs/auth/discovery-fetch.d.cts +2 -0
- package/dist/cjs/auth/discovery-fetch.d.ts +2 -0
- package/dist/cjs/auth/discovery-fetch.js +85 -31
- package/dist/cjs/auth/discovery-fetch.js.map +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.d.cts +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.d.ts +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.js +13 -6
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/cjs/auth/oauth-callback-listener.d.cts +1 -1
- package/dist/cjs/auth/oauth-callback-listener.d.ts +1 -1
- package/dist/cjs/auth/oauth-callback-listener.js +38 -2
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
- package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -2
- package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -2
- package/dist/cjs/auth/rfc9728-discovery.js +120 -88
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
- package/dist/cjs/auth/types.d.cts +4 -0
- package/dist/cjs/auth/types.d.ts +4 -0
- package/dist/cjs/auth/types.js.map +1 -1
- package/dist/cjs/connection/connect-client.d.cts +9 -1
- package/dist/cjs/connection/connect-client.d.ts +9 -1
- package/dist/cjs/connection/connect-client.js +282 -75
- package/dist/cjs/connection/connect-client.js.map +1 -1
- package/dist/cjs/connection/existing-process-transport.d.cts +18 -18
- package/dist/cjs/connection/existing-process-transport.d.ts +18 -18
- package/dist/cjs/connection/existing-process-transport.js +187 -82
- package/dist/cjs/connection/existing-process-transport.js.map +1 -1
- package/dist/cjs/connection/wait-for-http-ready.d.cts +1 -1
- package/dist/cjs/connection/wait-for-http-ready.d.ts +1 -1
- package/dist/cjs/connection/wait-for-http-ready.js +30 -5
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
- package/dist/cjs/dcr/dcr-authenticator.d.cts +1 -1
- package/dist/cjs/dcr/dcr-authenticator.d.ts +1 -1
- package/dist/cjs/dcr/dcr-authenticator.js +29 -18
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
- package/dist/cjs/dcr/dynamic-client-registrar.js +2 -1
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/spawn/owned-process-tree.d.cts +12 -0
- package/dist/cjs/spawn/owned-process-tree.d.ts +12 -0
- package/dist/cjs/spawn/owned-process-tree.js +93 -0
- package/dist/cjs/spawn/owned-process-tree.js.map +1 -0
- package/dist/cjs/spawn/spawn-server.d.cts +8 -32
- package/dist/cjs/spawn/spawn-server.d.ts +8 -32
- package/dist/cjs/spawn/spawn-server.js +398 -212
- package/dist/cjs/spawn/spawn-server.js.map +1 -1
- package/dist/cjs/spawn/spawn-servers.d.cts +18 -8
- package/dist/cjs/spawn/spawn-servers.d.ts +18 -8
- package/dist/cjs/spawn/spawn-servers.js +342 -113
- package/dist/cjs/spawn/spawn-servers.js.map +1 -1
- package/dist/cjs/types.d.cts +1 -1
- package/dist/cjs/types.d.ts +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/auth/capability-discovery.d.ts +3 -1
- package/dist/esm/auth/capability-discovery.js +21 -11
- package/dist/esm/auth/capability-discovery.js.map +1 -1
- package/dist/esm/auth/discovery-fetch.d.ts +2 -0
- package/dist/esm/auth/discovery-fetch.js +64 -23
- package/dist/esm/auth/discovery-fetch.js.map +1 -1
- package/dist/esm/auth/interactive-oauth-flow.d.ts +1 -1
- package/dist/esm/auth/interactive-oauth-flow.js +13 -6
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/esm/auth/oauth-callback-listener.d.ts +1 -1
- package/dist/esm/auth/oauth-callback-listener.js +32 -2
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
- package/dist/esm/auth/rfc9728-discovery.d.ts +7 -2
- package/dist/esm/auth/rfc9728-discovery.js +40 -16
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
- package/dist/esm/auth/types.d.ts +4 -0
- package/dist/esm/auth/types.js.map +1 -1
- package/dist/esm/connection/connect-client.d.ts +9 -1
- package/dist/esm/connection/connect-client.js +97 -35
- package/dist/esm/connection/connect-client.js.map +1 -1
- package/dist/esm/connection/existing-process-transport.d.ts +18 -18
- package/dist/esm/connection/existing-process-transport.js +132 -70
- package/dist/esm/connection/existing-process-transport.js.map +1 -1
- package/dist/esm/connection/wait-for-http-ready.d.ts +1 -1
- package/dist/esm/connection/wait-for-http-ready.js +19 -4
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
- package/dist/esm/dcr/dcr-authenticator.d.ts +1 -1
- package/dist/esm/dcr/dcr-authenticator.js +26 -15
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
- package/dist/esm/dcr/dynamic-client-registrar.js +2 -1
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/spawn/owned-process-tree.d.ts +12 -0
- package/dist/esm/spawn/owned-process-tree.js +39 -0
- package/dist/esm/spawn/owned-process-tree.js.map +1 -0
- package/dist/esm/spawn/spawn-server.d.ts +8 -32
- package/dist/esm/spawn/spawn-server.js +224 -130
- package/dist/esm/spawn/spawn-server.js.map +1 -1
- package/dist/esm/spawn/spawn-servers.d.ts +18 -8
- package/dist/esm/spawn/spawn-servers.js +180 -55
- package/dist/esm/spawn/spawn-servers.js.map +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +8 -5
- package/schemas/servers.d.ts +24 -0
- package/schemas/servers.schema.json +31 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/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
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/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 type { VersionNegotiationOptions } from '@modelcontextprotocol/client';\nimport * as fs from 'fs';\nimport * as path from 'path';\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 type { DcrAuthenticatorOptions } from '../dcr/dcr-authenticator.ts';\nimport { buildCapabilityIndex, type CapabilityClient, searchCapabilities as executeCapabilitySearch, type SearchOptions, type SearchResponse } from '../search/index.ts';\nimport type { McpServerEntry, StartConfig, StopCommandConfig, TransportType } from '../types.ts';\nimport { type Logger, 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\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nfunction withCause(message: string, cause: unknown): Error {\n return new Error(message, { cause });\n}\n\nasync function settleWithin<T>(promise: Promise<T>, timeoutMs: number): Promise<{ settled: true; value: T } | { settled: true; error: unknown } | { settled: false }> {\n let timer: NodeJS.Timeout | undefined;\n return Promise.race([\n promise.then(\n (value) => ({ settled: true as const, value }),\n (error: unknown) => ({ settled: true as const, error })\n ),\n new Promise<{ settled: false }>((resolve) => {\n timer = setTimeout(() => resolve({ settled: false }), timeoutMs);\n }),\n ]).finally(() => {\n if (timer) clearTimeout(timer);\n });\n}\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\ntype RegistryConnectOptions = Omit<NonNullable<Parameters<typeof connectMcpClient>[2]>, 'signal'>;\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 owned process exceeded its cooperative shutdown timeout */\n timedOut: boolean;\n /** Number of owned processes that required emergency force termination */\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 cwd?: string;\n stop?: StopCommandConfig;\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\nfunction getStartSpawnConfig(start: StartConfig, transportType: TransportType, baseEnv: Record<string, string | undefined>): SpawnConfig {\n const stop =\n start.stop && (transportType === 'http' || transportType === 'sse-ide')\n ? {\n ...start.stop,\n cwd: start.stop.cwd,\n env: filterEnv({ ...baseEnv, ...start.env, ...start.stop.env }),\n }\n : undefined;\n\n return {\n shouldSpawn: true,\n command: start.command,\n args: start.args || [],\n env: filterEnv({ ...baseEnv, ...start.env }),\n cwd: start.cwd,\n ...(stop && { stop }),\n };\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 if (dialects.length === 0) {\n return { shouldSpawn: false };\n }\n\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 getStartSpawnConfig(entry.start, transportType, baseEnv);\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 getStartSpawnConfig(entry.start, transportType, baseEnv);\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 */\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 * @param options - Authentication, logging, and protocol negotiation options\n * @returns Connected MCP SDK Client\n */\n connect: (name: string, options?: { dcrAuthenticator?: Partial<DcrAuthenticatorOptions>; logger?: Logger; versionNegotiation?: VersionNegotiationOptions }) => Promise<ManagedClient>;\n\n /**\n * Close all clients and servers gracefully.\n * Stops new connections, waits for in-flight connections, closes tracked clients, and then\n * stops only processes owned by this registry. Stdio children receive stdin EOF first\n * and the requested signal on POSIX if they remain open; owned HTTP children may use\n * their configured application-specific stop command. POSIX cleanup tracks the detached\n * process group; Windows can terminate only the direct child and cannot verify descendants.\n * Cleanup failures are reported after every owned resource has been attempted.\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 required emergency force termination\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 const pendingConnects = new Set<Promise<ManagedClient>>();\n const closeAbortController = new AbortController();\n const clientClosePromises = new WeakMap<ManagedClient, Promise<void>>();\n const closeRaceErrors: Error[] = [];\n let lifecycleState: 'open' | 'closing' | 'closed' = 'open';\n let closePromise: Promise<CloseResult> | undefined;\n\n const closeClientOnce = (client: ManagedClient): Promise<void> => {\n let pending = clientClosePromises.get(client);\n if (!pending) {\n pending = Promise.resolve().then(() => client.close());\n clientClosePromises.set(client, pending);\n }\n return pending;\n };\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 const resolvedCwd = spawnConfig.cwd ? path.resolve(cwd, spawnConfig.cwd) : 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 inheritEnv: options?.env === undefined,\n ...(spawnConfig.stop && { stop: { ...spawnConfig.stop, cwd: spawnConfig.stop.cwd ? path.resolve(resolvedCwd, spawnConfig.stop.cwd) : resolvedCwd } }),\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 // Connect internally, then track each returned lease for registry shutdown.\n const connectInternal = 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, signal: closeAbortController.signal });\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 releasePromise: Promise<void> | undefined;\n let lease: ManagedClient;\n\n lease = new Proxy(entry.client, {\n get(target, prop) {\n if (prop === 'close') {\n return () => {\n releasePromise ??= (async () => {\n entry.refs = Math.max(0, entry.refs - 1);\n try {\n if (entry.refs === 0) {\n sharedStdioClients.delete(name);\n await target.close();\n }\n } finally {\n clients.delete(lease);\n }\n })();\n return releasePromise;\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, signal: closeAbortController.signal });\n const decorated = decorateClient(rawClient, { serverName: name });\n clients.add(decorated);\n return decorated;\n };\n\n const connect = (name: string, options?: RegistryConnectOptions): Promise<ManagedClient> => {\n if (lifecycleState !== 'open') {\n return Promise.reject(new Error(`Cannot connect to server '${name}': registry is ${lifecycleState}`));\n }\n\n const pending = connectInternal(name, options).then(async (client) => {\n if (lifecycleState === 'open') return client;\n\n const result = await settleWithin(closeClientOnce(client), DEFAULT_SHUTDOWN_TIMEOUT_MS);\n if (!result.settled) closeRaceErrors.push(new Error(`Client for server '${name}' did not close within ${DEFAULT_SHUTDOWN_TIMEOUT_MS}ms after a connection raced registry shutdown`));\n else if ('error' in result) closeRaceErrors.push(withCause(`Failed to close server '${name}' after a connection raced registry shutdown`, asError(result.error)));\n throw new Error(`Connection to server '${name}' completed while the registry was closing`);\n });\n\n pendingConnects.add(pending);\n void pending.then(\n () => pendingConnects.delete(pending),\n () => pendingConnects.delete(pending)\n );\n return pending;\n };\n\n const closeAll = async (signal: NodeJS.Signals, opts: { timeoutMs?: number }): Promise<CloseResult> => {\n logger.info(`[registry] closing (${signal})`);\n const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n const errors: Error[] = [];\n closeAbortController.abort(new Error('Server registry is closing'));\n\n const pendingResult = await settleWithin(Promise.allSettled(Array.from(pendingConnects)), timeoutMs);\n if (!pendingResult.settled) {\n errors.push(new Error(`In-flight server connections did not settle within ${timeoutMs}ms after cancellation`));\n }\n errors.push(...closeRaceErrors);\n\n // Client close can hang independently; bound it so every owned server still gets cleanup.\n const clientEntries = Array.from(clients);\n const clientResults = await Promise.all(clientEntries.map(async (client) => ({ client, result: await settleWithin(closeClientOnce(client), timeoutMs) })));\n for (const { client, result } of clientResults) {\n if (!result.settled) {\n errors.push(new Error(`Client${client.serverName ? ` for server '${client.serverName}'` : ''} did not close within ${timeoutMs}ms`));\n } else if ('error' in result) {\n errors.push(withCause(`Failed to close client${client.serverName ? ` for server '${client.serverName}'` : ''}: ${asError(result.error).message}`, asError(result.error)));\n }\n }\n clients.clear();\n sharedStdioClients.clear();\n\n // Server teardown runs even when pending connects or client closes failed.\n const serverEntries = Array.from(servers.entries());\n const serverResults = await Promise.all(\n serverEntries.map(async ([name, server]) => {\n try {\n return { name, result: await server.close(signal, opts) };\n } catch (error) {\n return { name, error: asError(error) };\n }\n })\n );\n\n let timedOut = false;\n let killedCount = 0;\n for (const result of serverResults) {\n if ('error' in result && result.error !== undefined) {\n errors.push(withCause(`Failed to close server '${result.name}': ${result.error.message}`, result.error));\n } else if ('result' in result) {\n timedOut ||= result.result.timedOut;\n if (result.result.killed) killedCount += 1;\n }\n }\n\n lifecycleState = 'closed';\n if (errors.length > 0) {\n const details = errors.map((error) => error.message).join('; ');\n throw new AggregateError(errors, `Registry shutdown completed with ${errors.length} cleanup failure(s): ${details}`);\n }\n\n return { timedOut, killedCount };\n };\n\n const close = (signal: NodeJS.Signals = 'SIGINT', opts: { timeoutMs?: number } = {}): Promise<CloseResult> => {\n if (!closePromise) {\n lifecycleState = 'closing';\n closePromise = closeAll(signal, opts);\n }\n return closePromise;\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","DEFAULT_SHUTDOWN_TIMEOUT_MS","asError","error","Error","String","withCause","message","cause","settleWithin","promise","timeoutMs","timer","Promise","race","then","value","settled","resolve","setTimeout","finally","clearTimeout","inferTransportType","config","type","url","URL","protocol","filterEnv","env","filtered","Object","entries","key","undefined","getStartSpawnConfig","start","transportType","baseEnv","stop","cwd","shouldSpawn","command","args","getSpawnConfig","entry","dialects","length","hasServers","includes","hasStart","serversConfig","options","process","fs","existsSync","validation","validateServers","valid","errors","join","warnings","warning","logger","warn","servers","Map","clients","Set","sharedStdioClients","pendingConnects","closeAbortController","AbortController","clientClosePromises","WeakMap","closeRaceErrors","lifecycleState","closePromise","closeClientOnce","client","pending","get","close","set","name","spawnConfig","info","resolvedCwd","path","stdio","handle","spawnProcess","keys","inheritEnv","e","connectInternal","serverEntry","available","releasePromise","lease","registryLike","rawClient","decorated","refs","connecting","connectMcpClient","signal","decorateClient","serverName","catch","delete","Proxy","target","prop","Math","max","Reflect","bind","add","connect","reject","result","push","closeAll","opts","pendingResult","clientEntries","clientResults","serverEntries","serverResults","timedOut","killedCount","details","abort","allSettled","Array","from","all","map","clear","server","killed","AggregateError","searchFromRegistry","query","requestedServers","unknownServers","capabilityClients","failures","ensureClient","failureDetails","index","filter","managed","nativeClient","reason","size","f","buildCapabilityIndex","executeCapabilitySearch","asyncDispose","searchCapabilities","Symbol"],"mappings":"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"}
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Auto-generated from schemas/servers.schema.json
|
|
5
5
|
*/
|
|
6
|
-
export type { MCPServers, McpServerEntry, StartConfig } from '../schemas/servers.d.js';
|
|
6
|
+
export type { MCPServers, McpServerEntry, StartConfig, StopCommandConfig } from '../schemas/servers.d.js';
|
|
7
7
|
/**
|
|
8
8
|
* Transport type for server configuration
|
|
9
9
|
* Standard MCP transport types matching Claude Code's schemas
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Auto-generated from schemas/servers.schema.json
|
|
5
5
|
*/
|
|
6
|
-
export type { MCPServers, McpServerEntry, StartConfig } from '../schemas/servers.d.js';
|
|
6
|
+
export type { MCPServers, McpServerEntry, StartConfig, StopCommandConfig } from '../schemas/servers.d.js';
|
|
7
7
|
/**
|
|
8
8
|
* Transport type for server configuration
|
|
9
9
|
* Standard MCP transport types matching Claude Code's schemas
|
package/dist/cjs/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/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
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/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, StopCommandConfig } 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"}
|
|
@@ -3,4 +3,6 @@
|
|
|
3
3
|
* Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
|
|
4
4
|
*/
|
|
5
5
|
import type { AuthCapabilities } from './types.js';
|
|
6
|
-
export declare function probeAuthCapabilities(baseUrl: string
|
|
6
|
+
export declare function probeAuthCapabilities(baseUrl: string, options?: {
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}): Promise<AuthCapabilities>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OAuth Server Capability Discovery
|
|
3
3
|
* Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
|
|
4
4
|
*/ import { normalizeUrl } from '../lib/url-utils.js';
|
|
5
|
-
import { isLoopbackUrl } from './discovery-fetch.js';
|
|
5
|
+
import { isLoopbackUrl, throwIfAborted } from './discovery-fetch.js';
|
|
6
6
|
import { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.js';
|
|
7
7
|
/**
|
|
8
8
|
* Extract origin (protocol + host) from a URL
|
|
@@ -70,22 +70,27 @@ import { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata,
|
|
|
70
70
|
}
|
|
71
71
|
return capabilities;
|
|
72
72
|
}
|
|
73
|
-
async function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes, allowLoopback, resource) {
|
|
73
|
+
async function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes, allowLoopback, resource, signal) {
|
|
74
74
|
const metadata = await discoverAuthorizationServerMetadata(authServerUrl, {
|
|
75
|
-
allowLoopback
|
|
75
|
+
allowLoopback,
|
|
76
|
+
signal
|
|
76
77
|
});
|
|
77
78
|
if (!metadata) return null;
|
|
78
79
|
return buildCapabilities(metadata, scopes, resource);
|
|
79
80
|
}
|
|
80
|
-
export async function probeAuthCapabilities(baseUrl) {
|
|
81
|
+
export async function probeAuthCapabilities(baseUrl, options = {}) {
|
|
82
|
+
const { signal } = options;
|
|
81
83
|
try {
|
|
84
|
+
throwIfAborted(signal);
|
|
82
85
|
const normalizedBaseUrl = normalizeUrl(baseUrl);
|
|
83
86
|
// Trust signal for every authorization-server fetch below: whether the
|
|
84
87
|
// configured MCP server is itself loopback, never a remote-supplied URL.
|
|
85
88
|
const allowLoopback = isLoopbackUrl(normalizedBaseUrl);
|
|
86
89
|
// Strategy 1: Try RFC 9728 Protected Resource Metadata discovery
|
|
87
90
|
// This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp → todoist.com)
|
|
88
|
-
const resourceMetadata = await discoverProtectedResourceMetadata(normalizedBaseUrl
|
|
91
|
+
const resourceMetadata = await discoverProtectedResourceMetadata(normalizedBaseUrl, {
|
|
92
|
+
signal
|
|
93
|
+
});
|
|
89
94
|
if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {
|
|
90
95
|
// Found protected resource metadata with authorization servers
|
|
91
96
|
// Discover the authorization server's metadata (RFC 8414)
|
|
@@ -96,31 +101,36 @@ export async function probeAuthCapabilities(baseUrl) {
|
|
|
96
101
|
supportsDcr: false
|
|
97
102
|
};
|
|
98
103
|
}
|
|
99
|
-
const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported, allowLoopback, resourceMetadata.resource);
|
|
104
|
+
const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported, allowLoopback, resourceMetadata.resource, signal);
|
|
100
105
|
if (capabilities) {
|
|
101
106
|
return capabilities;
|
|
102
107
|
}
|
|
103
|
-
const issuer = await discoverAuthorizationServerIssuer(baseUrl
|
|
108
|
+
const issuer = await discoverAuthorizationServerIssuer(baseUrl, {
|
|
109
|
+
signal
|
|
110
|
+
});
|
|
104
111
|
if (issuer) {
|
|
105
|
-
const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported, allowLoopback, resourceMetadata.resource);
|
|
112
|
+
const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported, allowLoopback, resourceMetadata.resource, signal);
|
|
106
113
|
if (issuerCapabilities) return issuerCapabilities;
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
|
-
const issuer = await discoverAuthorizationServerIssuer(normalizedBaseUrl
|
|
116
|
+
const issuer = await discoverAuthorizationServerIssuer(normalizedBaseUrl, {
|
|
117
|
+
signal
|
|
118
|
+
});
|
|
110
119
|
if (issuer) {
|
|
111
|
-
const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, undefined, allowLoopback);
|
|
120
|
+
const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, undefined, allowLoopback, undefined, signal);
|
|
112
121
|
if (issuerCapabilities) return issuerCapabilities;
|
|
113
122
|
}
|
|
114
123
|
// Strategy 2: Fall back to direct RFC 8414 discovery at resource origin
|
|
115
124
|
// This handles same-domain OAuth (traditional setup)
|
|
116
125
|
const origin = getOrigin(normalizedBaseUrl);
|
|
117
|
-
const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin, undefined, allowLoopback);
|
|
126
|
+
const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin, undefined, allowLoopback, undefined, signal);
|
|
118
127
|
if (originCapabilities) return originCapabilities;
|
|
119
128
|
// No OAuth metadata found
|
|
120
129
|
return {
|
|
121
130
|
supportsDcr: false
|
|
122
131
|
};
|
|
123
132
|
} catch (_error) {
|
|
133
|
+
throwIfAborted(signal);
|
|
124
134
|
// Network error, invalid JSON, or other fetch failure
|
|
125
135
|
// Gracefully degrade - assume no DCR support
|
|
126
136
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/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 { normalizeUrl } from '../lib/url-utils.ts';\nimport { isLoopbackUrl } from './discovery-fetch.ts';\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[], resource?: string): AuthCapabilities {\n const supportsDcr = !!metadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr, authorizationResponseIssSupported: metadata.authorization_response_iss_parameter_supported === true };\n\n if (metadata.issuer) {\n capabilities.issuer = metadata.issuer;\n }\n\n // Carried from the RFC 9728 document, not from the URL we were given: the\n // resource server names itself, and that name is what RFC 8707 audience-binds\n // a token to.\n if (resource) {\n capabilities.resource = resource;\n }\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[] | undefined, allowLoopback: boolean, resource?: string): Promise<AuthCapabilities | null> {\n const metadata = await discoverAuthorizationServerMetadata(authServerUrl, { allowLoopback });\n if (!metadata) return null;\n return buildCapabilities(metadata, scopes, resource);\n}\n\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n const normalizedBaseUrl = normalizeUrl(baseUrl);\n // Trust signal for every authorization-server fetch below: whether the\n // configured MCP server is itself loopback, never a remote-supplied URL.\n const allowLoopback = isLoopbackUrl(normalizedBaseUrl);\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(normalizedBaseUrl);\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, allowLoopback, resourceMetadata.resource);\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, allowLoopback, resourceMetadata.resource);\n if (issuerCapabilities) return issuerCapabilities;\n }\n }\n\n const issuer = await discoverAuthorizationServerIssuer(normalizedBaseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, undefined, allowLoopback);\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(normalizedBaseUrl);\n const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin, undefined, allowLoopback);\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":["normalizeUrl","isLoopbackUrl","discoverAuthorizationServerIssuer","discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","buildCapabilities","metadata","scopes","resource","supportsDcr","registration_endpoint","capabilities","authorizationResponseIssSupported","authorization_response_iss_parameter_supported","issuer","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","length","scopes_supported","resolveCapabilitiesFromAuthorizationServer","authServerUrl","allowLoopback","probeAuthCapabilities","baseUrl","normalizedBaseUrl","resourceMetadata","authorization_servers","issuerCapabilities","undefined","originCapabilities","_error"],"mappings":"AAAA;;;CAGC,GAED,SAASA,YAAY,QAAQ,sBAAsB;AACnD,SAASC,aAAa,QAAQ,uBAAuB;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/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 { normalizeUrl } from '../lib/url-utils.ts';\nimport { isLoopbackUrl, throwIfAborted } from './discovery-fetch.ts';\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[], resource?: string): AuthCapabilities {\n const supportsDcr = !!metadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr, authorizationResponseIssSupported: metadata.authorization_response_iss_parameter_supported === true };\n\n if (metadata.issuer) {\n capabilities.issuer = metadata.issuer;\n }\n\n // Carried from the RFC 9728 document, not from the URL we were given: the\n // resource server names itself, and that name is what RFC 8707 audience-binds\n // a token to.\n if (resource) {\n capabilities.resource = resource;\n }\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[] | undefined, allowLoopback: boolean, resource?: string, signal?: AbortSignal): Promise<AuthCapabilities | null> {\n const metadata = await discoverAuthorizationServerMetadata(authServerUrl, { allowLoopback, signal });\n if (!metadata) return null;\n return buildCapabilities(metadata, scopes, resource);\n}\n\nexport async function probeAuthCapabilities(baseUrl: string, options: { signal?: AbortSignal } = {}): Promise<AuthCapabilities> {\n const { signal } = options;\n try {\n throwIfAborted(signal);\n const normalizedBaseUrl = normalizeUrl(baseUrl);\n // Trust signal for every authorization-server fetch below: whether the\n // configured MCP server is itself loopback, never a remote-supplied URL.\n const allowLoopback = isLoopbackUrl(normalizedBaseUrl);\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(normalizedBaseUrl, { signal });\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, allowLoopback, resourceMetadata.resource, signal);\n if (capabilities) {\n return capabilities;\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl, { signal });\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported, allowLoopback, resourceMetadata.resource, signal);\n if (issuerCapabilities) return issuerCapabilities;\n }\n }\n\n const issuer = await discoverAuthorizationServerIssuer(normalizedBaseUrl, { signal });\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, undefined, allowLoopback, undefined, signal);\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(normalizedBaseUrl);\n const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin, undefined, allowLoopback, undefined, signal);\n if (originCapabilities) return originCapabilities;\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n throwIfAborted(signal);\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["normalizeUrl","isLoopbackUrl","throwIfAborted","discoverAuthorizationServerIssuer","discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","buildCapabilities","metadata","scopes","resource","supportsDcr","registration_endpoint","capabilities","authorizationResponseIssSupported","authorization_response_iss_parameter_supported","issuer","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","length","scopes_supported","resolveCapabilitiesFromAuthorizationServer","authServerUrl","allowLoopback","signal","probeAuthCapabilities","baseUrl","options","normalizedBaseUrl","resourceMetadata","authorization_servers","issuerCapabilities","undefined","originCapabilities","_error"],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare class DiscoveryFetchError extends Error {
|
|
2
2
|
constructor(message: string);
|
|
3
3
|
}
|
|
4
|
+
/** Preserve cancellation while discovery falls back across endpoints. */
|
|
5
|
+
export declare function throwIfAborted(signal?: AbortSignal | null): void;
|
|
4
6
|
type LookupRecord = {
|
|
5
7
|
address: string;
|
|
6
8
|
family: number;
|
|
@@ -15,6 +15,9 @@ export class DiscoveryFetchError extends Error {
|
|
|
15
15
|
const DEFAULT_TIMEOUT_MS = 10000;
|
|
16
16
|
const MAX_REDIRECTS = 5;
|
|
17
17
|
const MAX_BODY_BYTES = 1000000; // 1 MB - discovery documents are small JSON
|
|
18
|
+
/** Preserve cancellation while discovery falls back across endpoints. */ export function throwIfAborted(signal) {
|
|
19
|
+
signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
|
|
20
|
+
}
|
|
18
21
|
function stripBrackets(hostname) {
|
|
19
22
|
return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;
|
|
20
23
|
}
|
|
@@ -79,32 +82,39 @@ function isLoopbackAddress(address) {
|
|
|
79
82
|
}
|
|
80
83
|
return url;
|
|
81
84
|
}
|
|
82
|
-
function lookupAll(hostname, lookup, timeoutMs) {
|
|
85
|
+
function lookupAll(hostname, lookup, timeoutMs, signal) {
|
|
83
86
|
return new Promise((resolve, reject)=>{
|
|
84
87
|
let settled = false;
|
|
88
|
+
const finish = (complete)=>{
|
|
89
|
+
if (settled) return;
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(deadline);
|
|
92
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
93
|
+
complete();
|
|
94
|
+
};
|
|
95
|
+
const onAbort = ()=>finish(()=>reject(signal === null || signal === void 0 ? void 0 : signal.reason));
|
|
85
96
|
const deadline = setTimeout(()=>{
|
|
86
|
-
|
|
87
|
-
settled = true;
|
|
88
|
-
reject(new Error('DNS resolution timed out'));
|
|
89
|
-
}
|
|
97
|
+
finish(()=>reject(new Error('DNS resolution timed out')));
|
|
90
98
|
}, timeoutMs);
|
|
99
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, {
|
|
100
|
+
once: true
|
|
101
|
+
});
|
|
102
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
103
|
+
onAbort();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
91
106
|
try {
|
|
92
107
|
lookup(hostname, {
|
|
93
108
|
all: true,
|
|
94
109
|
verbatim: true
|
|
95
110
|
}, (error, addresses)=>{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
else resolve(addresses);
|
|
111
|
+
finish(()=>{
|
|
112
|
+
if (error || addresses.length === 0) reject(new Error('DNS resolution failed'));
|
|
113
|
+
else resolve(addresses);
|
|
114
|
+
});
|
|
101
115
|
});
|
|
102
116
|
} catch {
|
|
103
|
-
|
|
104
|
-
settled = true;
|
|
105
|
-
clearTimeout(deadline);
|
|
106
|
-
reject(new Error('DNS resolution failed'));
|
|
107
|
-
}
|
|
117
|
+
finish(()=>reject(new Error('DNS resolution failed')));
|
|
108
118
|
}
|
|
109
119
|
});
|
|
110
120
|
}
|
|
@@ -114,7 +124,8 @@ function lookupAll(hostname, lookup, timeoutMs) {
|
|
|
114
124
|
* request itself (see `requestOnce`), which is what closes the DNS-rebinding
|
|
115
125
|
* TOCTOU: the transport can only dial addresses that were already validated,
|
|
116
126
|
* and it never resolves the hostname a second time.
|
|
117
|
-
*/ async function resolveSafeAddresses(url, context, allowLoopback, lookup, timeoutMs) {
|
|
127
|
+
*/ async function resolveSafeAddresses(url, context, allowLoopback, lookup, timeoutMs, signal) {
|
|
128
|
+
throwIfAborted(signal);
|
|
118
129
|
const host = stripBrackets(url.hostname);
|
|
119
130
|
if (isIP(host)) return [
|
|
120
131
|
{
|
|
@@ -128,8 +139,9 @@ function lookupAll(hostname, lookup, timeoutMs) {
|
|
|
128
139
|
const loopbackGrant = allowLoopback && isLoopbackHost(host);
|
|
129
140
|
let addresses;
|
|
130
141
|
try {
|
|
131
|
-
addresses = await lookupAll(host, lookup, timeoutMs);
|
|
132
|
-
} catch
|
|
142
|
+
addresses = await lookupAll(host, lookup, timeoutMs, signal);
|
|
143
|
+
} catch (_error) {
|
|
144
|
+
throwIfAborted(signal);
|
|
133
145
|
throw new DiscoveryFetchError(`Refusing to fetch ${context}: host could not be resolved`);
|
|
134
146
|
}
|
|
135
147
|
if (loopbackGrant) {
|
|
@@ -211,7 +223,8 @@ function toResponse(status, statusText, headers, body) {
|
|
|
211
223
|
});
|
|
212
224
|
}
|
|
213
225
|
async function requestOnce(url, init, context, addresses, timeoutMs) {
|
|
214
|
-
var _init_method;
|
|
226
|
+
var _init_method, _init_signal;
|
|
227
|
+
throwIfAborted(init.signal);
|
|
215
228
|
let body;
|
|
216
229
|
try {
|
|
217
230
|
body = await toRequestBody(init.body);
|
|
@@ -223,24 +236,50 @@ async function requestOnce(url, init, context, addresses, timeoutMs) {
|
|
|
223
236
|
method: ((_init_method = init.method) !== null && _init_method !== void 0 ? _init_method : 'GET').toUpperCase(),
|
|
224
237
|
headers: toRequestHeaders(init),
|
|
225
238
|
agent: false,
|
|
226
|
-
lookup: pinnedLookup(addresses)
|
|
239
|
+
lookup: pinnedLookup(addresses),
|
|
240
|
+
signal: (_init_signal = init.signal) !== null && _init_signal !== void 0 ? _init_signal : undefined
|
|
227
241
|
};
|
|
228
242
|
return new Promise((resolve, reject)=>{
|
|
243
|
+
var _init_signal;
|
|
229
244
|
let settled = false;
|
|
230
245
|
let req;
|
|
231
246
|
let res;
|
|
247
|
+
const signal = (_init_signal = init.signal) !== null && _init_signal !== void 0 ? _init_signal : undefined;
|
|
248
|
+
const cleanup = ()=>{
|
|
249
|
+
clearTimeout(deadline);
|
|
250
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
251
|
+
};
|
|
232
252
|
const fail = (message)=>{
|
|
233
253
|
if (!settled) {
|
|
234
254
|
settled = true;
|
|
235
|
-
|
|
255
|
+
cleanup();
|
|
236
256
|
reject(new DiscoveryFetchError(message));
|
|
237
257
|
}
|
|
238
258
|
};
|
|
259
|
+
const failWith = (error)=>{
|
|
260
|
+
if (!settled) {
|
|
261
|
+
settled = true;
|
|
262
|
+
cleanup();
|
|
263
|
+
reject(error);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
239
266
|
const deadline = setTimeout(()=>{
|
|
240
267
|
res === null || res === void 0 ? void 0 : res.destroy();
|
|
241
268
|
req === null || req === void 0 ? void 0 : req.destroy();
|
|
242
269
|
fail(`Failed to fetch ${context}`);
|
|
243
270
|
}, timeoutMs);
|
|
271
|
+
const onAbort = ()=>{
|
|
272
|
+
res === null || res === void 0 ? void 0 : res.destroy();
|
|
273
|
+
req === null || req === void 0 ? void 0 : req.destroy();
|
|
274
|
+
failWith(signal === null || signal === void 0 ? void 0 : signal.reason);
|
|
275
|
+
};
|
|
276
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, {
|
|
277
|
+
once: true
|
|
278
|
+
});
|
|
279
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
280
|
+
onAbort();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
244
283
|
try {
|
|
245
284
|
req = transport.request(url, options, (response)=>{
|
|
246
285
|
var _response_statusMessage;
|
|
@@ -267,7 +306,7 @@ async function requestOnce(url, init, context, addresses, timeoutMs) {
|
|
|
267
306
|
response.once('end', ()=>{
|
|
268
307
|
if (settled) return;
|
|
269
308
|
settled = true;
|
|
270
|
-
|
|
309
|
+
cleanup();
|
|
271
310
|
resolve(toResponse(status, statusText, response.headers, Buffer.concat(chunks)));
|
|
272
311
|
});
|
|
273
312
|
});
|
|
@@ -280,7 +319,8 @@ async function requestOnce(url, init, context, addresses, timeoutMs) {
|
|
|
280
319
|
});
|
|
281
320
|
}
|
|
282
321
|
async function fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup) {
|
|
283
|
-
|
|
322
|
+
throwIfAborted(init.signal);
|
|
323
|
+
const addresses = await resolveSafeAddresses(url, context, allowLoopback, lookup, timeoutMs, init.signal);
|
|
284
324
|
return requestOnce(url, init, context, addresses, timeoutMs);
|
|
285
325
|
}
|
|
286
326
|
/**
|
|
@@ -297,6 +337,7 @@ async function fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup) {
|
|
|
297
337
|
* @param context - Short label used only in error messages, never echoing the URL.
|
|
298
338
|
* @param options - See `DiscoveryFetchOptions`.
|
|
299
339
|
*/ export async function discoveryFetch(rawUrl, init = {}, context = 'discovery URL', options = {}) {
|
|
340
|
+
throwIfAborted(init.signal);
|
|
300
341
|
const { allowLoopback = false, timeoutMs = DEFAULT_TIMEOUT_MS, lookup = dns.lookup } = options;
|
|
301
342
|
let url = assertSafeUrl(rawUrl, context, allowLoopback);
|
|
302
343
|
let response = await fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup);
|