@mcp-z/client 2.2.3 → 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 +40 -4
- 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 +406 -97
- 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/cjs/utils/path-utils.js +7 -4
- package/dist/cjs/utils/path-utils.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 +34 -4
- 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 +228 -99
- 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/dist/esm/utils/path-utils.js +7 -4
- package/dist/esm/utils/path-utils.js.map +1 -1
- package/package.json +8 -3
- 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/connection/connect-client.ts"],"sourcesContent":["/**\n * connect-mcp-client.ts\n *\n * Helper to connect MCP SDK clients to servers with intelligent transport inference.\n * Automatically detects transport type from URL protocol or type field.\n */\n\nimport type { ClientOptions, Transport, VersionNegotiationOptions } from '@modelcontextprotocol/client';\nimport { Client, SSEClientTransport, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';\nimport { StdioClientTransport } from '@modelcontextprotocol/client/stdio';\nimport getPort from 'get-port';\nimport { probeAuthCapabilities } from '../auth/index.ts';\nimport { DCR_CAPABILTY_DISCOVERY_TIMEOUT } from '../constants.ts';\nimport { DcrAuthenticator, type DcrAuthenticatorOptions } from '../dcr/index.ts';\nimport { normalizeUrl } from '../lib/url-utils.ts';\nimport type { ServerProcess } from '../spawn/spawn-server.ts';\nimport type { ServersConfig } from '../spawn/spawn-servers.ts';\n\n/**\n * Minimal interface for connecting to servers.\n * Only needs config and servers map for connection logic.\n */\ninterface RegistryLike {\n config: ServersConfig;\n servers: Map<string, ServerProcess>;\n}\n\nimport type { McpServerEntry, TransportType } from '../types.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { ExistingProcessTransport } from './existing-process-transport.ts';\nimport { waitForHttpReady } from './wait-for-http-ready.ts';\n\n/**\n * Wrap promise with timeout - throws if promise takes too long\n * Clears timeout when promise completes to prevent hanging event loop\n * @param promise - Promise to wrap\n * @param ms - Timeout in milliseconds\n * @param operation - Description of operation for error message\n * @returns Promise result or timeout error\n */\nasync function withTimeout<T>(promise: Promise<T>, ms: number, operation: string): Promise<T> {\n let timeoutId: NodeJS.Timeout;\n\n return Promise.race([\n promise.finally(() => clearTimeout(timeoutId)),\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Timeout after ${ms}ms: ${operation}`)), ms);\n }),\n ]);\n}\n\n/**\n * Infer transport type from server configuration with validation.\n *\n * Priority:\n * 1. Explicit type field (if present)\n * 2. URL protocol (if URL present): http://, https://\n * 3. Default to 'stdio' (if neither present)\n *\n * @param config - Server configuration\n * @returns Transport type\n * @throws Error if configuration is invalid or has conflicts\n */\nfunction inferTransportType(config: McpServerEntry): TransportType {\n // Priority 1: Explicit type field\n if (config.type) {\n // Validate consistency with URL if both present\n if (config.url) {\n const url = new URL(config.url);\n const protocol = url.protocol;\n\n if ((protocol === 'http:' || protocol === 'https:') && config.type !== 'http' && config.type !== 'sse-ide') {\n throw new Error(`Conflicting transport: URL protocol '${protocol}' requires type 'http', but got '${config.type}'`);\n }\n }\n\n // Return normalized type\n if (config.type === 'http' || config.type === 'sse-ide') return 'http';\n if (config.type === 'stdio') return 'stdio';\n\n throw new Error(`Unsupported transport type: ${config.type}`);\n }\n\n // Priority 2: Infer from URL protocol\n if (config.url) {\n const url = new URL(config.url);\n const protocol = url.protocol;\n\n if (protocol === 'http:' || protocol === 'https:') {\n return 'http';\n }\n throw new Error(`Unsupported URL protocol: ${protocol}`);\n }\n\n // Priority 3: Default to stdio\n return 'stdio';\n}\n\n/**\n * Connect MCP SDK client to server with full readiness handling.\n * @internal - Use registry.connect() instead\n *\n * **Completely handles readiness**: transport availability + MCP protocol handshake.\n *\n * Transport is intelligently inferred and handled:\n * - **Stdio servers**: Direct MCP connect (fast for spawned processes)\n * - **HTTP servers**: Transport polling (/mcp endpoint) + MCP connect\n * - **Registry result**: Handles both spawned and external servers\n *\n * Returns only when server is fully MCP-ready (initialize handshake complete).\n *\n * @param registryOrConfig - Result from createServerRegistry() or servers config object\n * @param serverName - Server name from servers config\n * @param options - Connection options (see below)\n * @param options.dcrAuthenticator - DCR authenticator options\n * @param options.logger - Logger for connection diagnostics\n * @param options.versionNegotiation - SDK protocol version negotiation (protocol revision\n * 2026-07-28 and later). Omitted by default, which keeps the SDK's `'legacy'` mode:\n * the plain 2025 connect sequence. Pass `{ mode: 'auto' }` to probe the server and\n * fall back to 2025 when it cannot serve the modern era, or `{ mode: { pin: '2026-07-28' } }`\n * to require the pinned revision (a server that cannot serve it fails the connect with\n * a typed era-negotiation error).\n * @returns Connected MCP SDK Client (guaranteed ready)\n *\n * @example\n * // Using registry (recommended)\n * const registry = createServerRegistry({ echo: { command: 'node', args: ['server.ts'] } });\n * const client = await registry.connect('echo');\n * // Server is fully ready - transport available + MCP handshake complete\n *\n * @example\n * // HTTP server readiness (waits for /mcp polling + MCP handshake)\n * const registry = createServerRegistry(\n * { http: { type: 'http', url: 'http://localhost:3000/mcp', start: {...} } },\n * { dialects: ['start'] }\n * );\n * const client = await registry.connect('http');\n * // 1. Waits for HTTP server to respond on /mcp\n * // 2. Performs MCP initialize handshake\n * // 3. Returns ready client\n */\nexport async function connectMcpClient(\n registryOrConfig: RegistryLike | ServersConfig,\n serverName: string,\n options?: {\n dcrAuthenticator?: Partial<DcrAuthenticatorOptions>;\n logger?: Logger;\n versionNegotiation?: VersionNegotiationOptions;\n }\n): Promise<Client> {\n // Detect whether we have a RegistryLike instance or just config\n const isRegistry = 'servers' in registryOrConfig && registryOrConfig.servers instanceof Map;\n const serversConfig: ServersConfig = isRegistry ? (registryOrConfig as RegistryLike).config : (registryOrConfig as ServersConfig);\n const registry = isRegistry ? (registryOrConfig as RegistryLike) : undefined;\n const logger = options?.logger ?? defaultLogger;\n\n const serverConfig = serversConfig[serverName];\n\n if (!serverConfig) {\n const available = Object.keys(serversConfig).join(', ');\n throw new Error(`Server '${serverName}' not found in config. Available servers: ${available || 'none'}`);\n }\n\n // Infer transport type with validation\n const transportType = inferTransportType(serverConfig);\n\n // SDK client options for both transports (main + SSE fallback). versionNegotiation is\n // omitted rather than set to undefined so the default stays the SDK's 'legacy' mode —\n // the plain 2025 connect sequence — for callers that do not pass it.\n const clientOptions: ClientOptions = { capabilities: {} };\n if (options?.versionNegotiation !== undefined) {\n clientOptions.versionNegotiation = options.versionNegotiation;\n }\n\n // Create MCP client\n const client = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, clientOptions);\n\n // Connect based on inferred transport\n if (transportType === 'stdio') {\n // Check if we have a spawned process in the registry\n const serverHandle = registry?.servers.get(serverName);\n\n if (serverHandle) {\n // Reuse the already-spawned process\n const transport = new ExistingProcessTransport(serverHandle.process);\n await client.connect(transport);\n } else {\n // No registry or server not in registry - spawn new process directly\n // This is the standard fallback when process management is not used\n if (!serverConfig.command) {\n throw new Error(`Server '${serverName}' has stdio transport but missing 'command' field`);\n }\n\n const transport = new StdioClientTransport({\n command: serverConfig.command,\n args: serverConfig.args || [],\n env: serverConfig.env || {},\n });\n\n // client.connect() performs initialize handshake - when it resolves, server is ready\n await client.connect(transport);\n }\n } else if (transportType === 'http') {\n if (!('url' in serverConfig) || !serverConfig.url) {\n throw new Error(`Server '${serverName}' has http transport but missing 'url' field`);\n }\n\n // Check if this is a freshly spawned HTTP server (from registry)\n // that might not be ready yet - transport readiness check needed\n const isSpawnedHttp = registry?.servers.has(serverName);\n\n if (isSpawnedHttp) {\n logger.debug(`[connectMcpClient] waiting for HTTP server '${serverName}' at ${serverConfig.url}`);\n await waitForHttpReady(serverConfig.url);\n logger.debug(`[connectMcpClient] HTTP server '${serverName}' ready`);\n }\n\n const url = new URL(serverConfig.url);\n\n // Check for DCR support and handle authentication automatically\n // The canonical MCP server URI, path segment and all. Both calls below need\n // the server's identity, not its deployment root: discovery uses the path to\n // find resource-specific metadata (RFC 9728 sub-path), and the authenticator\n // audience-binds tokens to it (RFC 8707). Handing either the `/mcp`-stripped\n // base names a different resource, which authorization servers that validate\n // the `resource` indicator reject as `invalid_target`.\n const mcpServerUrl = normalizeUrl(serverConfig.url);\n const capabilities = await withTimeout(probeAuthCapabilities(mcpServerUrl), DCR_CAPABILTY_DISCOVERY_TIMEOUT, 'DCR capability discovery');\n\n let authToken: string | undefined;\n\n if (capabilities.supportsDcr) {\n logger.debug(`🔐 Server '${serverName}' supports DCR authentication`);\n\n // Get available port and create the exact redirect URI to use\n const port = await getPort();\n const redirectUri = `http://localhost:${port}/callback`;\n\n // Handle authentication using DcrAuthenticator with fully resolved redirectUri\n const authenticator = new DcrAuthenticator({\n headless: false,\n redirectUri,\n logger,\n ...options?.dcrAuthenticator,\n });\n\n // Ensure we have valid tokens (performs DCR + OAuth if needed)\n const tokens = await authenticator.ensureAuthenticated(mcpServerUrl, capabilities);\n authToken = tokens.accessToken;\n\n logger.debug(`✅ Authentication complete for '${serverName}'`);\n } else {\n logger.debug(`ℹ️ Server '${serverName}' does not support DCR - connecting without authentication`);\n }\n\n try {\n // Try modern Streamable HTTP first (protocol version 2025-03-26)\n // Merge static headers from config with DCR auth headers (DCR Authorization takes precedence)\n const staticHeaders = serverConfig.headers || {};\n const dcrHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};\n const mergedHeaders = { ...staticHeaders, ...dcrHeaders };\n\n const transportOptions =\n Object.keys(mergedHeaders).length > 0\n ? {\n requestInit: {\n headers: mergedHeaders,\n },\n }\n : undefined;\n\n const transport = new StreamableHTTPClientTransport(url, transportOptions);\n // Type assertion: SDK transport has sessionId: string | undefined but Transport expects string\n // This is safe at runtime - the undefined is valid per MCP spec\n await withTimeout(client.connect(transport as unknown as Transport), 30000, 'StreamableHTTP connection');\n } catch (error) {\n // Fall back to SSE transport (MCP protocol version 2024-11-05)\n // SSE is a standard MCP transport used by many servers (e.g., FastMCP ecosystem)\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Fast-fail: Don't try SSE if connection was refused (server not running)\n // Check error.cause.code for ECONNREFUSED (fetch errors wrap the actual error in cause)\n const cause = error instanceof Error ? (error as Error & { cause?: { code?: string } }).cause : undefined;\n const isConnectionRefused = cause?.code === 'ECONNREFUSED' || errorMessage.includes('Connection refused');\n\n if (isConnectionRefused) {\n // Clean up client resources before throwing\n await client.close().catch(() => {});\n throw new Error(`Server not running at ${url}`);\n }\n\n // Check for known errors that indicate SSE fallback is needed\n const shouldFallback =\n errorMessage.includes('Missing session ID') || // FastMCP specific\n errorMessage.includes('404') || // Server doesn't have streamable HTTP endpoint\n errorMessage.includes('405'); // Method not allowed\n\n if (shouldFallback) {\n logger.warn(`Streamable HTTP failed (${errorMessage}), falling back to SSE transport`);\n } else {\n logger.warn('Streamable HTTP connection failed, trying SSE transport as fallback');\n }\n\n // Create new client for SSE transport (required per SDK pattern)\n const sseClient = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, clientOptions);\n\n // SSE transport with merged headers (static + DCR auth)\n // Reuse the same header merging logic as Streamable HTTP\n const staticHeaders = serverConfig.headers || {};\n const dcrHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};\n const mergedHeaders = { ...staticHeaders, ...dcrHeaders };\n\n const sseTransportOptions =\n Object.keys(mergedHeaders).length > 0\n ? {\n requestInit: {\n headers: mergedHeaders,\n },\n }\n : undefined;\n\n const sseTransport = new SSEClientTransport(url, sseTransportOptions);\n\n try {\n await withTimeout(sseClient.connect(sseTransport), 30000, 'SSE connection');\n // Return SSE client instead of original\n return sseClient;\n } catch (sseError) {\n // SSE connection failed - clean up both clients before throwing\n await Promise.all([client.close().catch(() => {}), sseClient.close().catch(() => {})]);\n throw sseError;\n }\n }\n }\n\n return client; // Guaranteed ready when returned\n}\n"],"names":["connectMcpClient","withTimeout","promise","ms","operation","timeoutId","Promise","race","finally","clearTimeout","_","reject","setTimeout","Error","inferTransportType","config","type","url","URL","protocol","registryOrConfig","serverName","options","isRegistry","serversConfig","registry","logger","serverConfig","available","transportType","clientOptions","client","serverHandle","transport","isSpawnedHttp","mcpServerUrl","capabilities","authToken","port","redirectUri","authenticator","tokens","staticHeaders","dcrHeaders","mergedHeaders","transportOptions","error","errorMessage","cause","isConnectionRefused","shouldFallback","sseClient","sseTransportOptions","sseTransport","sseError","servers","Map","undefined","defaultLogger","Object","keys","join","versionNegotiation","Client","name","version","get","ExistingProcessTransport","process","connect","command","StdioClientTransport","args","env","has","debug","waitForHttpReady","normalizeUrl","probeAuthCapabilities","DCR_CAPABILTY_DISCOVERY_TIMEOUT","supportsDcr","getPort","DcrAuthenticator","headless","dcrAuthenticator","ensureAuthenticated","accessToken","headers","Authorization","length","requestInit","StreamableHTTPClientTransport","message","String","code","includes","close","catch","warn","SSEClientTransport","all"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/connection/connect-client.ts"],"sourcesContent":["/**\n * connect-mcp-client.ts\n *\n * Helper to connect MCP SDK clients to servers with intelligent transport inference.\n * Automatically detects transport type from URL protocol or type field.\n */\n\nimport type { ClientOptions, Transport, VersionNegotiationOptions } from '@modelcontextprotocol/client';\nimport { Client, SSEClientTransport, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';\nimport { StdioClientTransport } from '@modelcontextprotocol/client/stdio';\nimport getPort from 'get-port';\nimport { probeAuthCapabilities } from '../auth/index.ts';\nimport { DCR_CAPABILTY_DISCOVERY_TIMEOUT } from '../constants.ts';\nimport { DcrAuthenticator, type DcrAuthenticatorOptions } from '../dcr/index.ts';\nimport { normalizeUrl } from '../lib/url-utils.ts';\nimport type { ServerProcess } from '../spawn/spawn-server.ts';\nimport type { ServersConfig } from '../spawn/spawn-servers.ts';\n\n/**\n * Minimal interface for connecting to servers.\n * Only needs config and servers map for connection logic.\n */\ninterface RegistryLike {\n config: ServersConfig;\n servers: Map<string, ServerProcess>;\n}\n\nimport type { McpServerEntry, TransportType } from '../types.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { ExistingProcessTransport } from './existing-process-transport.ts';\nimport { waitForHttpReady } from './wait-for-http-ready.ts';\n\nconst HTTP_CONNECTION_TIMEOUT_MS = 30_000;\nconst CLIENT_CLEANUP_TIMEOUT_MS = 5_000;\n\n/** @internal - Bounds work by aborting the actual request signal at the deadline. */\nexport async function withAbortTimeout<T>(operationFn: (signal: AbortSignal) => Promise<T>, ms: number, operation: string, parentSignal?: AbortSignal): Promise<T> {\n const controller = new AbortController();\n const timeoutError = new Error(`Timeout after ${ms}ms: ${operation}`);\n const abortFromParent = (): void => controller.abort(parentSignal?.reason instanceof Error ? parentSignal.reason : new Error('Connection was cancelled'));\n parentSignal?.addEventListener('abort', abortFromParent, { once: true });\n if (parentSignal?.aborted) abortFromParent();\n const timeoutId = setTimeout(() => controller.abort(timeoutError), ms);\n\n try {\n // Preserve the operation's error unchanged: connection cleanup may have\n // attached failures to the original cancellation or timeout.\n return await operationFn(controller.signal);\n } finally {\n clearTimeout(timeoutId);\n parentSignal?.removeEventListener('abort', abortFromParent);\n }\n}\n\n/** @internal - Connects a transport with a deadline that aborts and cleans up the actual connection. */\nexport async function connectTransportWithTimeout(client: Client, transport: Transport, ms: number, operation: string, parentSignal?: AbortSignal): Promise<void> {\n return withAbortTimeout((signal) => connectTransport(client, transport, signal), ms, operation, parentSignal);\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw signal.reason instanceof Error ? signal.reason : new Error('Connection was cancelled');\n}\n\nasync function closeClientWithinTimeout(client: Client): Promise<void> {\n let timeoutId: NodeJS.Timeout | undefined;\n try {\n await Promise.race([\n client.close(),\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Client cleanup did not complete within ${CLIENT_CLEANUP_TIMEOUT_MS}ms`)), CLIENT_CLEANUP_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n}\n\nasync function rethrowAfterClientCleanup(error: unknown, clients: Client[], message: string): Promise<never> {\n const cleanup = await Promise.allSettled(clients.map((client) => closeClientWithinTimeout(client)));\n const cleanupErrors = cleanup.flatMap((result) => (result.status === 'rejected' ? [result.reason] : []));\n if (cleanupErrors.length > 0) throw new AggregateError([error, ...cleanupErrors], message, { cause: error });\n throw error;\n}\n\n/** @internal - Connects one client/transport pair and reports any failed cleanup. */\nexport async function connectTransport(client: Client, transport: Transport, signal?: AbortSignal): Promise<void> {\n let abortHandler: (() => void) | undefined;\n try {\n throwIfAborted(signal);\n const connecting = client.connect(transport);\n if (signal) {\n const aborted = new Promise<never>((_, reject) => {\n abortHandler = () => reject(signal.reason instanceof Error ? signal.reason : new Error('Connection was cancelled'));\n signal.addEventListener('abort', abortHandler, { once: true });\n });\n await Promise.race([connecting, aborted]);\n } else {\n await connecting;\n }\n } catch (error) {\n await rethrowAfterClientCleanup(error, [client], 'MCP connection failed and client cleanup also failed');\n } finally {\n if (signal && abortHandler) signal.removeEventListener('abort', abortHandler);\n }\n}\n\n/**\n * Infer transport type from server configuration with validation.\n *\n * Priority:\n * 1. Explicit type field (if present)\n * 2. URL protocol (if URL present): http://, https://\n * 3. Default to 'stdio' (if neither present)\n *\n * @param config - Server configuration\n * @returns Transport type\n * @throws Error if configuration is invalid or has conflicts\n */\nfunction inferTransportType(config: McpServerEntry): TransportType {\n // Priority 1: Explicit type field\n if (config.type) {\n // Validate consistency with URL if both present\n if (config.url) {\n const url = new URL(config.url);\n const protocol = url.protocol;\n\n if ((protocol === 'http:' || protocol === 'https:') && config.type !== 'http' && config.type !== 'sse-ide') {\n throw new Error(`Conflicting transport: URL protocol '${protocol}' requires type 'http', but got '${config.type}'`);\n }\n }\n\n // Return normalized type\n if (config.type === 'http' || config.type === 'sse-ide') return 'http';\n if (config.type === 'stdio') return 'stdio';\n\n throw new Error(`Unsupported transport type: ${config.type}`);\n }\n\n // Priority 2: Infer from URL protocol\n if (config.url) {\n const url = new URL(config.url);\n const protocol = url.protocol;\n\n if (protocol === 'http:' || protocol === 'https:') {\n return 'http';\n }\n throw new Error(`Unsupported URL protocol: ${protocol}`);\n }\n\n // Priority 3: Default to stdio\n return 'stdio';\n}\n\n/**\n * Connect MCP SDK client to server with full readiness handling.\n * @internal - Use registry.connect() instead\n *\n * **Completely handles readiness**: transport availability + MCP protocol handshake.\n *\n * Transport is intelligently inferred and handled:\n * - **Stdio servers**: Direct MCP connect (fast for spawned processes)\n * - **HTTP servers**: Transport polling (/mcp endpoint) + MCP connect\n * - **Registry result**: Handles both spawned and external servers\n *\n * Returns only when server is fully MCP-ready (initialize handshake complete).\n *\n * @param registryOrConfig - Result from createServerRegistry() or servers config object\n * @param serverName - Server name from servers config\n * @param options - Connection options (see below)\n * @param options.dcrAuthenticator - DCR authenticator options\n * @param options.logger - Logger for connection diagnostics\n * @param options.versionNegotiation - SDK protocol version negotiation (protocol revision\n * 2026-07-28 and later). Omitted by default, which keeps the SDK's `'legacy'` mode:\n * the plain 2025 connect sequence. Pass `{ mode: 'auto' }` to probe the server and\n * fall back to 2025 when it cannot serve the modern era, or `{ mode: { pin: '2026-07-28' } }`\n * to require the pinned revision (a server that cannot serve it fails the connect with\n * a typed era-negotiation error).\n * @returns Connected MCP SDK Client (guaranteed ready)\n *\n * @example\n * // Using registry (recommended)\n * const registry = createServerRegistry({ echo: { command: 'node', args: ['server.ts'] } });\n * const client = await registry.connect('echo');\n * // Server is fully ready - transport available + MCP handshake complete\n *\n * @example\n * // HTTP server readiness (waits for /mcp polling + MCP handshake)\n * const registry = createServerRegistry(\n * { http: { type: 'http', url: 'http://localhost:3000/mcp', start: {...} } },\n * { dialects: ['start'] }\n * );\n * const client = await registry.connect('http');\n * // 1. Waits for HTTP server to respond on /mcp\n * // 2. Performs MCP initialize handshake\n * // 3. Returns ready client\n */\nexport async function connectMcpClient(\n registryOrConfig: RegistryLike | ServersConfig,\n serverName: string,\n options?: {\n dcrAuthenticator?: Partial<DcrAuthenticatorOptions>;\n logger?: Logger;\n versionNegotiation?: VersionNegotiationOptions;\n /** Cancels readiness, authentication, and transport connection work. */\n signal?: AbortSignal;\n }\n): Promise<Client> {\n // Detect whether we have a RegistryLike instance or just config\n const isRegistry = 'servers' in registryOrConfig && registryOrConfig.servers instanceof Map;\n const serversConfig: ServersConfig = isRegistry ? (registryOrConfig as RegistryLike).config : (registryOrConfig as ServersConfig);\n const registry = isRegistry ? (registryOrConfig as RegistryLike) : undefined;\n const logger = options?.logger ?? defaultLogger;\n throwIfAborted(options?.signal);\n\n const serverConfig = serversConfig[serverName];\n\n if (!serverConfig) {\n const available = Object.keys(serversConfig).join(', ');\n throw new Error(`Server '${serverName}' not found in config. Available servers: ${available || 'none'}`);\n }\n\n // Infer transport type with validation\n const transportType = inferTransportType(serverConfig);\n\n // SDK client options for both transports (main + SSE fallback). versionNegotiation is\n // omitted rather than set to undefined so the default stays the SDK's 'legacy' mode —\n // the plain 2025 connect sequence — for callers that do not pass it.\n const clientOptions: ClientOptions = { capabilities: {} };\n if (options?.versionNegotiation !== undefined) {\n clientOptions.versionNegotiation = options.versionNegotiation;\n }\n\n // Create MCP client\n const client = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, clientOptions);\n\n // Connect based on inferred transport\n if (transportType === 'stdio') {\n // Check if we have a spawned process in the registry\n const serverHandle = registry?.servers.get(serverName);\n\n if (serverHandle) {\n // Reuse the already-spawned process\n const transport = new ExistingProcessTransport(serverHandle.process);\n await connectTransport(client, transport, options?.signal);\n } else {\n // No registry or server not in registry - spawn new process directly\n // This is the standard fallback when process management is not used\n if (!serverConfig.command) {\n throw new Error(`Server '${serverName}' has stdio transport but missing 'command' field`);\n }\n\n const transport = new StdioClientTransport({\n command: serverConfig.command,\n args: serverConfig.args || [],\n env: serverConfig.env || {},\n });\n\n // client.connect() performs initialize handshake - when it resolves, server is ready\n await connectTransport(client, transport, options?.signal);\n }\n } else if (transportType === 'http') {\n if (!('url' in serverConfig) || !serverConfig.url) {\n throw new Error(`Server '${serverName}' has http transport but missing 'url' field`);\n }\n\n // Check if this is a freshly spawned HTTP server (from registry)\n // that might not be ready yet - transport readiness check needed\n const isSpawnedHttp = registry?.servers.has(serverName);\n\n if (isSpawnedHttp) {\n logger.debug(`[connectMcpClient] waiting for HTTP server '${serverName}' at ${serverConfig.url}`);\n await waitForHttpReady(serverConfig.url, 30000, options?.signal);\n logger.debug(`[connectMcpClient] HTTP server '${serverName}' ready`);\n }\n\n const url = new URL(serverConfig.url);\n\n // Check for DCR support and handle authentication automatically\n // The canonical MCP server URI, path segment and all. Both calls below need\n // the server's identity, not its deployment root: discovery uses the path to\n // find resource-specific metadata (RFC 9728 sub-path), and the authenticator\n // audience-binds tokens to it (RFC 8707). Handing either the `/mcp`-stripped\n // base names a different resource, which authorization servers that validate\n // the `resource` indicator reject as `invalid_target`.\n const mcpServerUrl = normalizeUrl(serverConfig.url);\n const capabilities = await withAbortTimeout((signal) => probeAuthCapabilities(mcpServerUrl, { signal }), DCR_CAPABILTY_DISCOVERY_TIMEOUT, 'DCR capability discovery', options?.signal);\n\n let authToken: string | undefined;\n\n if (capabilities.supportsDcr) {\n logger.debug(`🔐 Server '${serverName}' supports DCR authentication`);\n\n // Get available port and create the exact redirect URI to use\n const port = await getPort();\n const redirectUri = `http://localhost:${port}/callback`;\n\n // Handle authentication using DcrAuthenticator with fully resolved redirectUri\n const authenticator = new DcrAuthenticator({\n headless: false,\n redirectUri,\n logger,\n ...options?.dcrAuthenticator,\n });\n\n // Ensure we have valid tokens (performs DCR + OAuth if needed)\n const tokens = await authenticator.ensureAuthenticated(mcpServerUrl, capabilities, options?.signal);\n authToken = tokens.accessToken;\n\n logger.debug(`✅ Authentication complete for '${serverName}'`);\n } else {\n logger.debug(`ℹ️ Server '${serverName}' does not support DCR - connecting without authentication`);\n }\n\n try {\n // Try modern Streamable HTTP first (protocol version 2025-03-26)\n // Merge static headers from config with DCR auth headers (DCR Authorization takes precedence)\n const staticHeaders = serverConfig.headers || {};\n const dcrHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};\n const mergedHeaders = { ...staticHeaders, ...dcrHeaders };\n\n const transportOptions =\n Object.keys(mergedHeaders).length > 0\n ? {\n requestInit: {\n headers: mergedHeaders,\n },\n }\n : undefined;\n\n const transport = new StreamableHTTPClientTransport(url, transportOptions);\n // Type assertion: SDK transport has sessionId: string | undefined but Transport expects string\n // This is safe at runtime - the undefined is valid per MCP spec\n await connectTransportWithTimeout(client, transport as unknown as Transport, HTTP_CONNECTION_TIMEOUT_MS, 'StreamableHTTP connection', options?.signal);\n } catch (error) {\n // Cancellation and cleanup failures must reach the caller; neither can\n // safely be converted into an SSE fallback attempt.\n if (options?.signal?.aborted || error instanceof AggregateError) throw error;\n // Fall back to SSE transport (MCP protocol version 2024-11-05)\n // SSE is a standard MCP transport used by many servers (e.g., FastMCP ecosystem)\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Fast-fail: Don't try SSE if connection was refused (server not running)\n // Check error.cause.code for ECONNREFUSED (fetch errors wrap the actual error in cause)\n const cause = error instanceof Error ? (error as Error & { cause?: { code?: string } }).cause : undefined;\n const isConnectionRefused = cause?.code === 'ECONNREFUSED' || errorMessage.includes('Connection refused');\n\n if (isConnectionRefused) {\n const unavailable = new Error(`Server not running at ${url}`, { cause: error });\n throw unavailable;\n }\n\n // Check for known errors that indicate SSE fallback is needed\n const shouldFallback =\n errorMessage.includes('Missing session ID') || // FastMCP specific\n errorMessage.includes('404') || // Server doesn't have streamable HTTP endpoint\n errorMessage.includes('405'); // Method not allowed\n\n if (shouldFallback) {\n logger.warn(`Streamable HTTP failed (${errorMessage}), falling back to SSE transport`);\n } else {\n logger.warn('Streamable HTTP connection failed, trying SSE transport as fallback');\n }\n\n // Create new client for SSE transport (required per SDK pattern)\n const sseClient = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, clientOptions);\n\n // SSE transport with merged headers (static + DCR auth)\n // Reuse the same header merging logic as Streamable HTTP\n const staticHeaders = serverConfig.headers || {};\n const dcrHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};\n const mergedHeaders = { ...staticHeaders, ...dcrHeaders };\n\n const sseTransportOptions =\n Object.keys(mergedHeaders).length > 0\n ? {\n requestInit: {\n headers: mergedHeaders,\n },\n }\n : undefined;\n\n const sseTransport = new SSEClientTransport(url, sseTransportOptions);\n\n await connectTransportWithTimeout(sseClient, sseTransport, HTTP_CONNECTION_TIMEOUT_MS, 'SSE connection', options?.signal);\n // Return SSE client instead of original\n return sseClient;\n }\n }\n\n return client; // Guaranteed ready when returned\n}\n"],"names":["connectMcpClient","connectTransport","connectTransportWithTimeout","withAbortTimeout","HTTP_CONNECTION_TIMEOUT_MS","CLIENT_CLEANUP_TIMEOUT_MS","operationFn","ms","operation","parentSignal","controller","timeoutError","abortFromParent","timeoutId","AbortController","Error","abort","reason","addEventListener","once","aborted","setTimeout","signal","clearTimeout","removeEventListener","client","transport","throwIfAborted","closeClientWithinTimeout","Promise","race","close","_","reject","rethrowAfterClientCleanup","error","clients","message","cleanup","cleanupErrors","allSettled","map","flatMap","result","status","length","AggregateError","cause","abortHandler","connecting","connect","inferTransportType","config","type","url","URL","protocol","registryOrConfig","serverName","options","isRegistry","serversConfig","registry","logger","serverConfig","available","transportType","clientOptions","serverHandle","isSpawnedHttp","mcpServerUrl","capabilities","authToken","port","redirectUri","authenticator","tokens","staticHeaders","dcrHeaders","mergedHeaders","transportOptions","errorMessage","isConnectionRefused","unavailable","shouldFallback","sseClient","sseTransportOptions","sseTransport","servers","Map","undefined","defaultLogger","Object","keys","join","versionNegotiation","Client","name","version","get","ExistingProcessTransport","process","command","StdioClientTransport","args","env","has","debug","waitForHttpReady","normalizeUrl","probeAuthCapabilities","DCR_CAPABILTY_DISCOVERY_TIMEOUT","supportsDcr","getPort","DcrAuthenticator","headless","dcrAuthenticator","ensureAuthenticated","accessToken","headers","Authorization","requestInit","StreamableHTTPClientTransport","String","code","includes","warn","SSEClientTransport"],"mappings":"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"}
|
|
@@ -2,38 +2,38 @@
|
|
|
2
2
|
* existing-process-transport.ts
|
|
3
3
|
*
|
|
4
4
|
* MCP transport that wraps an existing child process for stdio communication.
|
|
5
|
-
* Used when connecting to
|
|
5
|
+
* Used when connecting to already-started processes.
|
|
6
6
|
*/
|
|
7
7
|
import type { JSONRPCMessage, Transport } from '@modelcontextprotocol/client';
|
|
8
8
|
import type { ChildProcess } from 'child_process';
|
|
9
9
|
/**
|
|
10
10
|
* Transport that communicates with an existing child process via stdio.
|
|
11
|
-
*
|
|
11
|
+
* Closing the transport releases only its listeners; process ownership stays with the registry.
|
|
12
12
|
*/
|
|
13
13
|
export declare class ExistingProcessTransport implements Transport {
|
|
14
|
-
private _process;
|
|
15
|
-
private _readBuffer;
|
|
14
|
+
private readonly _process;
|
|
15
|
+
private readonly _readBuffer;
|
|
16
16
|
private _dataHandler;
|
|
17
|
-
private
|
|
17
|
+
private _stdinErrorHandler;
|
|
18
|
+
private _stdoutErrorHandler;
|
|
19
|
+
private _stdinCloseHandler;
|
|
20
|
+
private _processErrorHandler;
|
|
21
|
+
private _processCloseHandler;
|
|
22
|
+
private _started;
|
|
23
|
+
private _closed;
|
|
24
|
+
private readonly _pendingSends;
|
|
25
|
+
private _pendingWrites;
|
|
26
|
+
private _stdinErrorObserved;
|
|
18
27
|
onclose?: () => void;
|
|
19
28
|
onerror?: (error: Error) => void;
|
|
20
29
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
21
30
|
constructor(process: ChildProcess);
|
|
22
|
-
/**
|
|
23
|
-
* Start the transport - sets up stdio listeners on existing process.
|
|
24
|
-
*/
|
|
25
31
|
start(): Promise<void>;
|
|
26
|
-
/**
|
|
27
|
-
* Process buffered messages from stdout.
|
|
28
|
-
*/
|
|
29
32
|
private processReadBuffer;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
private detachListeners;
|
|
34
|
+
private detachPendingWriteErrorListener;
|
|
35
|
+
private retainStdinErrorListenerUntilClose;
|
|
36
|
+
private finish;
|
|
34
37
|
close(): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* Send a message to the server via stdin.
|
|
37
|
-
*/
|
|
38
38
|
send(message: JSONRPCMessage): Promise<void>;
|
|
39
39
|
}
|
|
@@ -2,38 +2,38 @@
|
|
|
2
2
|
* existing-process-transport.ts
|
|
3
3
|
*
|
|
4
4
|
* MCP transport that wraps an existing child process for stdio communication.
|
|
5
|
-
* Used when connecting to
|
|
5
|
+
* Used when connecting to already-started processes.
|
|
6
6
|
*/
|
|
7
7
|
import type { JSONRPCMessage, Transport } from '@modelcontextprotocol/client';
|
|
8
8
|
import type { ChildProcess } from 'child_process';
|
|
9
9
|
/**
|
|
10
10
|
* Transport that communicates with an existing child process via stdio.
|
|
11
|
-
*
|
|
11
|
+
* Closing the transport releases only its listeners; process ownership stays with the registry.
|
|
12
12
|
*/
|
|
13
13
|
export declare class ExistingProcessTransport implements Transport {
|
|
14
|
-
private _process;
|
|
15
|
-
private _readBuffer;
|
|
14
|
+
private readonly _process;
|
|
15
|
+
private readonly _readBuffer;
|
|
16
16
|
private _dataHandler;
|
|
17
|
-
private
|
|
17
|
+
private _stdinErrorHandler;
|
|
18
|
+
private _stdoutErrorHandler;
|
|
19
|
+
private _stdinCloseHandler;
|
|
20
|
+
private _processErrorHandler;
|
|
21
|
+
private _processCloseHandler;
|
|
22
|
+
private _started;
|
|
23
|
+
private _closed;
|
|
24
|
+
private readonly _pendingSends;
|
|
25
|
+
private _pendingWrites;
|
|
26
|
+
private _stdinErrorObserved;
|
|
18
27
|
onclose?: () => void;
|
|
19
28
|
onerror?: (error: Error) => void;
|
|
20
29
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
21
30
|
constructor(process: ChildProcess);
|
|
22
|
-
/**
|
|
23
|
-
* Start the transport - sets up stdio listeners on existing process.
|
|
24
|
-
*/
|
|
25
31
|
start(): Promise<void>;
|
|
26
|
-
/**
|
|
27
|
-
* Process buffered messages from stdout.
|
|
28
|
-
*/
|
|
29
32
|
private processReadBuffer;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
private detachListeners;
|
|
34
|
+
private detachPendingWriteErrorListener;
|
|
35
|
+
private retainStdinErrorListenerUntilClose;
|
|
36
|
+
private finish;
|
|
34
37
|
close(): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* Send a message to the server via stdin.
|
|
37
|
-
*/
|
|
38
38
|
send(message: JSONRPCMessage): Promise<void>;
|
|
39
39
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* existing-process-transport.ts
|
|
3
3
|
*
|
|
4
4
|
* MCP transport that wraps an existing child process for stdio communication.
|
|
5
|
-
* Used when connecting to
|
|
5
|
+
* Used when connecting to already-started processes.
|
|
6
6
|
*/ "use strict";
|
|
7
7
|
Object.defineProperty(exports, "__esModule", {
|
|
8
8
|
value: true
|
|
@@ -43,6 +43,12 @@ function _async_to_generator(fn) {
|
|
|
43
43
|
function _class_call_check(instance, Constructor) {
|
|
44
44
|
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
|
45
45
|
}
|
|
46
|
+
function _instanceof(left, right) {
|
|
47
|
+
"@swc/helpers - instanceof";
|
|
48
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
49
|
+
return !!right[Symbol.hasInstance](left);
|
|
50
|
+
} else return left instanceof right;
|
|
51
|
+
}
|
|
46
52
|
function _ts_generator(thisArg, body) {
|
|
47
53
|
var f, y, t, _ = {
|
|
48
54
|
label: 0,
|
|
@@ -147,7 +153,16 @@ var ExistingProcessTransport = /*#__PURE__*/ function() {
|
|
|
147
153
|
function ExistingProcessTransport(process) {
|
|
148
154
|
_class_call_check(this, ExistingProcessTransport);
|
|
149
155
|
this._dataHandler = null;
|
|
150
|
-
this.
|
|
156
|
+
this._stdinErrorHandler = null;
|
|
157
|
+
this._stdoutErrorHandler = null;
|
|
158
|
+
this._stdinCloseHandler = null;
|
|
159
|
+
this._processErrorHandler = null;
|
|
160
|
+
this._processCloseHandler = null;
|
|
161
|
+
this._started = false;
|
|
162
|
+
this._closed = false;
|
|
163
|
+
this._pendingSends = new Set();
|
|
164
|
+
this._pendingWrites = 0;
|
|
165
|
+
this._stdinErrorObserved = false;
|
|
151
166
|
if (!process.stdin || !process.stdout) {
|
|
152
167
|
throw new Error('Child process must have stdin and stdout pipes');
|
|
153
168
|
}
|
|
@@ -155,112 +170,202 @@ var ExistingProcessTransport = /*#__PURE__*/ function() {
|
|
|
155
170
|
this._readBuffer = new _client.ReadBuffer();
|
|
156
171
|
}
|
|
157
172
|
var _proto = ExistingProcessTransport.prototype;
|
|
158
|
-
|
|
159
|
-
* Start the transport - sets up stdio listeners on existing process.
|
|
160
|
-
*/ _proto.start = function start() {
|
|
173
|
+
_proto.start = function start() {
|
|
161
174
|
return _async_to_generator(function() {
|
|
162
|
-
var _this;
|
|
175
|
+
var _this, _this__process, stdin, stdout;
|
|
163
176
|
return _ts_generator(this, function(_state) {
|
|
164
177
|
_this = this;
|
|
178
|
+
if (this._closed) throw new Error('Transport is closed');
|
|
179
|
+
if (this._started) return [
|
|
180
|
+
2
|
|
181
|
+
];
|
|
182
|
+
_this__process = this._process, stdin = _this__process.stdin, stdout = _this__process.stdout;
|
|
183
|
+
if (!stdin || !stdout) throw new Error('Child process must have stdin and stdout pipes');
|
|
184
|
+
if (this._process.exitCode !== null || this._process.signalCode !== null) {
|
|
185
|
+
this.finish();
|
|
186
|
+
throw new Error('Cannot start transport because the child process has already exited');
|
|
187
|
+
}
|
|
188
|
+
this._started = true;
|
|
189
|
+
this._dataHandler = function(chunk) {
|
|
190
|
+
_this._readBuffer.append(chunk);
|
|
191
|
+
_this.processReadBuffer();
|
|
192
|
+
};
|
|
193
|
+
this._stdinErrorHandler = function(error) {
|
|
194
|
+
_this._stdinErrorObserved = true;
|
|
195
|
+
_this.retainStdinErrorListenerUntilClose();
|
|
196
|
+
_this.finish(error);
|
|
197
|
+
};
|
|
198
|
+
this._stdoutErrorHandler = function(error) {
|
|
199
|
+
return _this.finish(error);
|
|
200
|
+
};
|
|
201
|
+
this._processErrorHandler = function(error) {
|
|
202
|
+
return _this.finish(error);
|
|
203
|
+
};
|
|
204
|
+
this._processCloseHandler = function() {
|
|
205
|
+
return _this.finish();
|
|
206
|
+
};
|
|
207
|
+
stdout.on('data', this._dataHandler);
|
|
208
|
+
stdout.on('error', this._stdoutErrorHandler);
|
|
209
|
+
stdin.on('error', this._stdinErrorHandler);
|
|
210
|
+
this._process.on('error', this._processErrorHandler);
|
|
211
|
+
this._process.on('close', this._processCloseHandler);
|
|
165
212
|
return [
|
|
166
|
-
2
|
|
167
|
-
new Promise(function(resolve, reject) {
|
|
168
|
-
var // Listen for stdout data (MCP messages)
|
|
169
|
-
_this__process_stdout, _this__process_stdout1, _this__process_stdin;
|
|
170
|
-
// Listen for process events
|
|
171
|
-
_this._process.on('error', function(error) {
|
|
172
|
-
var _this_onerror, _this1;
|
|
173
|
-
(_this_onerror = (_this1 = _this).onerror) === null || _this_onerror === void 0 ? void 0 : _this_onerror.call(_this1, error);
|
|
174
|
-
reject(error);
|
|
175
|
-
});
|
|
176
|
-
_this._process.on('close', function() {
|
|
177
|
-
var _this_onclose, _this1;
|
|
178
|
-
(_this_onclose = (_this1 = _this).onclose) === null || _this_onclose === void 0 ? void 0 : _this_onclose.call(_this1);
|
|
179
|
-
});
|
|
180
|
-
// Create and save data handler for close
|
|
181
|
-
_this._dataHandler = function(chunk) {
|
|
182
|
-
_this._readBuffer.append(chunk);
|
|
183
|
-
_this.processReadBuffer();
|
|
184
|
-
};
|
|
185
|
-
// Create and save error handler for close
|
|
186
|
-
_this._errorHandler = function(error) {
|
|
187
|
-
var _this_onerror, _this1;
|
|
188
|
-
(_this_onerror = (_this1 = _this).onerror) === null || _this_onerror === void 0 ? void 0 : _this_onerror.call(_this1, error);
|
|
189
|
-
};
|
|
190
|
-
(_this__process_stdout = _this._process.stdout) === null || _this__process_stdout === void 0 ? void 0 : _this__process_stdout.on('data', _this._dataHandler);
|
|
191
|
-
(_this__process_stdout1 = _this._process.stdout) === null || _this__process_stdout1 === void 0 ? void 0 : _this__process_stdout1.on('error', _this._errorHandler);
|
|
192
|
-
(_this__process_stdin = _this._process.stdin) === null || _this__process_stdin === void 0 ? void 0 : _this__process_stdin.on('error', _this._errorHandler);
|
|
193
|
-
// Process is already running - resolve immediately
|
|
194
|
-
resolve();
|
|
195
|
-
})
|
|
213
|
+
2
|
|
196
214
|
];
|
|
197
215
|
});
|
|
198
216
|
}).call(this);
|
|
199
217
|
};
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
*/ _proto.processReadBuffer = function processReadBuffer() {
|
|
203
|
-
while(true){
|
|
218
|
+
_proto.processReadBuffer = function processReadBuffer() {
|
|
219
|
+
while(!this._closed){
|
|
204
220
|
try {
|
|
205
221
|
var _this_onmessage, _this;
|
|
206
222
|
var message = this._readBuffer.readMessage();
|
|
207
|
-
if (message === null)
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
223
|
+
if (message === null) return;
|
|
210
224
|
(_this_onmessage = (_this = this).onmessage) === null || _this_onmessage === void 0 ? void 0 : _this_onmessage.call(_this, message);
|
|
211
225
|
} catch (error) {
|
|
212
|
-
|
|
213
|
-
(_this_onerror = (_this1 = this).onerror) === null || _this_onerror === void 0 ? void 0 : _this_onerror.call(_this1, error);
|
|
226
|
+
this.finish(_instanceof(error, Error) ? error : new Error(String(error)));
|
|
214
227
|
}
|
|
215
228
|
}
|
|
216
229
|
};
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
_proto.detachListeners = function detachListeners() {
|
|
231
|
+
var stdout = this._process.stdout;
|
|
232
|
+
if (this._dataHandler) stdout === null || stdout === void 0 ? void 0 : stdout.off('data', this._dataHandler);
|
|
233
|
+
if (this._stdoutErrorHandler) stdout === null || stdout === void 0 ? void 0 : stdout.off('error', this._stdoutErrorHandler);
|
|
234
|
+
this._stdoutErrorHandler = null;
|
|
235
|
+
if (this._processErrorHandler) this._process.off('error', this._processErrorHandler);
|
|
236
|
+
if (this._processCloseHandler) this._process.off('close', this._processCloseHandler);
|
|
237
|
+
this._dataHandler = null;
|
|
238
|
+
this._processErrorHandler = null;
|
|
239
|
+
this._processCloseHandler = null;
|
|
240
|
+
this.detachPendingWriteErrorListener();
|
|
241
|
+
};
|
|
242
|
+
_proto.detachPendingWriteErrorListener = function detachPendingWriteErrorListener() {
|
|
243
|
+
var stdin = this._process.stdin;
|
|
244
|
+
if (!this._closed || this._pendingWrites > 0 || !this._stdinErrorHandler) return;
|
|
245
|
+
if (this._stdinErrorObserved) {
|
|
246
|
+
this.retainStdinErrorListenerUntilClose();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
stdin === null || stdin === void 0 ? void 0 : stdin.off('error', this._stdinErrorHandler);
|
|
250
|
+
if (this._stdinCloseHandler) stdin === null || stdin === void 0 ? void 0 : stdin.off('close', this._stdinCloseHandler);
|
|
251
|
+
this._stdinErrorHandler = null;
|
|
252
|
+
this._stdinCloseHandler = null;
|
|
253
|
+
};
|
|
254
|
+
_proto.retainStdinErrorListenerUntilClose = function retainStdinErrorListenerUntilClose() {
|
|
255
|
+
var _this = this;
|
|
256
|
+
var stdin = this._process.stdin;
|
|
257
|
+
var errorHandler = this._stdinErrorHandler;
|
|
258
|
+
if (!stdin || !errorHandler) return;
|
|
259
|
+
if (stdin.closed) {
|
|
260
|
+
stdin.off('error', errorHandler);
|
|
261
|
+
this._stdinErrorHandler = null;
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (this._stdinCloseHandler) return;
|
|
265
|
+
this._stdinCloseHandler = function() {
|
|
266
|
+
stdin.off('error', errorHandler);
|
|
267
|
+
_this._stdinErrorHandler = null;
|
|
268
|
+
_this._stdinCloseHandler = null;
|
|
269
|
+
};
|
|
270
|
+
stdin.once('close', this._stdinCloseHandler);
|
|
271
|
+
};
|
|
272
|
+
_proto.finish = function finish(error) {
|
|
273
|
+
if (this._closed) return;
|
|
274
|
+
this._closed = true;
|
|
275
|
+
this.detachListeners();
|
|
276
|
+
this._readBuffer.clear();
|
|
277
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
278
|
+
try {
|
|
279
|
+
for(var _iterator = this._pendingSends[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
280
|
+
var reject = _step.value;
|
|
281
|
+
reject(error !== null && error !== void 0 ? error : new Error('Transport is closed'));
|
|
282
|
+
}
|
|
283
|
+
} catch (err) {
|
|
284
|
+
_didIteratorError = true;
|
|
285
|
+
_iteratorError = err;
|
|
286
|
+
} finally{
|
|
287
|
+
try {
|
|
288
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
289
|
+
_iterator.return();
|
|
228
290
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
(_this__process_stdin = this._process.stdin) === null || _this__process_stdin === void 0 ? void 0 : _this__process_stdin.off('error', this._errorHandler);
|
|
233
|
-
this._errorHandler = null;
|
|
291
|
+
} finally{
|
|
292
|
+
if (_didIteratorError) {
|
|
293
|
+
throw _iteratorError;
|
|
234
294
|
}
|
|
235
|
-
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
this._pendingSends.clear();
|
|
298
|
+
try {
|
|
299
|
+
var _this_onerror, _this;
|
|
300
|
+
if (error) (_this_onerror = (_this = this).onerror) === null || _this_onerror === void 0 ? void 0 : _this_onerror.call(_this, error);
|
|
301
|
+
} finally{
|
|
302
|
+
var _this_onclose, _this1;
|
|
303
|
+
(_this_onclose = (_this1 = this).onclose) === null || _this_onclose === void 0 ? void 0 : _this_onclose.call(_this1);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
_proto.close = function close() {
|
|
307
|
+
return _async_to_generator(function() {
|
|
308
|
+
return _ts_generator(this, function(_state) {
|
|
309
|
+
this.finish();
|
|
236
310
|
return [
|
|
237
311
|
2
|
|
238
312
|
];
|
|
239
313
|
});
|
|
240
314
|
}).call(this);
|
|
241
315
|
};
|
|
242
|
-
|
|
243
|
-
* Send a message to the server via stdin.
|
|
244
|
-
*/ _proto.send = function send(message) {
|
|
316
|
+
_proto.send = function send(message) {
|
|
245
317
|
return _async_to_generator(function() {
|
|
246
|
-
var _this;
|
|
318
|
+
var _this, stdin, json;
|
|
247
319
|
return _ts_generator(this, function(_state) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
320
|
+
switch(_state.label){
|
|
321
|
+
case 0:
|
|
322
|
+
_this = this;
|
|
323
|
+
if (this._closed) throw new Error('Transport is closed');
|
|
324
|
+
stdin = this._process.stdin;
|
|
325
|
+
if (!stdin || stdin.destroyed || stdin.writableEnded) throw new Error('Child process stdin is not writable');
|
|
326
|
+
json = (0, _client.serializeMessage)(message);
|
|
327
|
+
return [
|
|
328
|
+
4,
|
|
329
|
+
new Promise(function(resolve, reject) {
|
|
330
|
+
var settled = false;
|
|
331
|
+
var settle = function settle(error) {
|
|
332
|
+
if (settled) return;
|
|
333
|
+
settled = true;
|
|
334
|
+
_this._pendingSends.delete(rejectPending);
|
|
335
|
+
if (error) reject(error);
|
|
336
|
+
else resolve();
|
|
337
|
+
};
|
|
338
|
+
var rejectPending = function rejectPending(error) {
|
|
339
|
+
return settle(error);
|
|
340
|
+
};
|
|
341
|
+
_this._pendingSends.add(rejectPending);
|
|
342
|
+
_this._pendingWrites += 1;
|
|
343
|
+
try {
|
|
344
|
+
stdin.write(json, function(error) {
|
|
345
|
+
_this._pendingWrites -= 1;
|
|
346
|
+
if (error) {
|
|
347
|
+
_this._stdinErrorObserved = true;
|
|
348
|
+
_this.retainStdinErrorListenerUntilClose();
|
|
349
|
+
if (_this._closed) settle(error);
|
|
350
|
+
else _this.finish(error);
|
|
351
|
+
} else {
|
|
352
|
+
settle();
|
|
353
|
+
}
|
|
354
|
+
_this.detachPendingWriteErrorListener();
|
|
355
|
+
});
|
|
356
|
+
} catch (error) {
|
|
357
|
+
_this._pendingWrites -= 1;
|
|
358
|
+
settle(_instanceof(error, Error) ? error : new Error(String(error)));
|
|
359
|
+
_this.detachPendingWriteErrorListener();
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
];
|
|
363
|
+
case 1:
|
|
364
|
+
_state.sent();
|
|
365
|
+
return [
|
|
366
|
+
2
|
|
367
|
+
];
|
|
368
|
+
}
|
|
264
369
|
});
|
|
265
370
|
}).call(this);
|
|
266
371
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/connection/existing-process-transport.ts"],"sourcesContent":["/**\n * existing-process-transport.ts\n *\n * MCP transport that wraps an existing child process for stdio communication.\n * Used when connecting to servers already spawned by initServers().\n */\n\nimport type { JSONRPCMessage, Transport } from '@modelcontextprotocol/client';\nimport { ReadBuffer, serializeMessage } from '@modelcontextprotocol/client';\nimport type { ChildProcess } from 'child_process';\n\n/**\n * Transport that communicates with an existing child process via stdio.\n * Does NOT spawn a new process - uses the one provided.\n */\nexport class ExistingProcessTransport implements Transport {\n private _process: ChildProcess;\n private _readBuffer: ReadBuffer;\n private _dataHandler: ((chunk: Buffer) => void) | null = null;\n private _errorHandler: ((error: Error) => void) | null = null;\n\n // Transport interface callbacks\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(process: ChildProcess) {\n if (!process.stdin || !process.stdout) {\n throw new Error('Child process must have stdin and stdout pipes');\n }\n\n this._process = process;\n this._readBuffer = new ReadBuffer();\n }\n\n /**\n * Start the transport - sets up stdio listeners on existing process.\n */\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n // Listen for process events\n this._process.on('error', (error) => {\n this.onerror?.(error);\n reject(error);\n });\n\n this._process.on('close', () => {\n this.onclose?.();\n });\n\n // Create and save data handler for close\n this._dataHandler = (chunk: Buffer) => {\n this._readBuffer.append(chunk);\n this.processReadBuffer();\n };\n\n // Create and save error handler for close\n this._errorHandler = (error: Error) => {\n this.onerror?.(error);\n };\n\n // Listen for stdout data (MCP messages)\n this._process.stdout?.on('data', this._dataHandler);\n this._process.stdout?.on('error', this._errorHandler);\n this._process.stdin?.on('error', this._errorHandler);\n\n // Process is already running - resolve immediately\n resolve();\n });\n }\n\n /**\n * Process buffered messages from stdout.\n */\n private processReadBuffer(): void {\n while (true) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) {\n break;\n }\n this.onmessage?.(message);\n } catch (error) {\n this.onerror?.(error as Error);\n }\n }\n }\n\n /**\n * Close the transport - close without killing the shared process.\n * The process is managed by the cluster and may have other active connections.\n */\n async close(): Promise<void> {\n if (this._dataHandler) {\n this._process.stdout?.off('data', this._dataHandler);\n this._dataHandler = null;\n }\n\n if (this._errorHandler) {\n this._process.stdout?.off('error', this._errorHandler);\n this._process.stdin?.off('error', this._errorHandler);\n this._errorHandler = null;\n }\n\n this._readBuffer.clear();\n }\n\n /**\n * Send a message to the server via stdin.\n */\n async send(message: JSONRPCMessage): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this._process.stdin) {\n reject(new Error('stdin is not available'));\n return;\n }\n\n const json = serializeMessage(message);\n\n if (this._process.stdin.write(json)) {\n resolve();\n } else {\n this._process.stdin.once('drain', resolve);\n }\n });\n }\n}\n"],"names":["ExistingProcessTransport","process","_dataHandler","_errorHandler","stdin","stdout","Error","_process","_readBuffer","ReadBuffer","start","Promise","resolve","reject","on","error","onerror","onclose","chunk","append","processReadBuffer","message","readMessage","onmessage","close","off","clear","send","json","serializeMessage","write","once"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/connection/existing-process-transport.ts"],"sourcesContent":["/**\n * existing-process-transport.ts\n *\n * MCP transport that wraps an existing child process for stdio communication.\n * Used when connecting to already-started processes.\n */\n\nimport type { JSONRPCMessage, Transport } from '@modelcontextprotocol/client';\nimport { ReadBuffer, serializeMessage } from '@modelcontextprotocol/client';\nimport type { ChildProcess } from 'child_process';\n\n/**\n * Transport that communicates with an existing child process via stdio.\n * Closing the transport releases only its listeners; process ownership stays with the registry.\n */\nexport class ExistingProcessTransport implements Transport {\n private readonly _process: ChildProcess;\n private readonly _readBuffer: ReadBuffer;\n private _dataHandler: ((chunk: Buffer) => void) | null = null;\n private _stdinErrorHandler: ((error: Error) => void) | null = null;\n private _stdoutErrorHandler: ((error: Error) => void) | null = null;\n private _stdinCloseHandler: (() => void) | null = null;\n private _processErrorHandler: ((error: Error) => void) | null = null;\n private _processCloseHandler: (() => void) | null = null;\n private _started = false;\n private _closed = false;\n private readonly _pendingSends = new Set<(error: Error) => void>();\n private _pendingWrites = 0;\n private _stdinErrorObserved = false;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(process: ChildProcess) {\n if (!process.stdin || !process.stdout) {\n throw new Error('Child process must have stdin and stdout pipes');\n }\n\n this._process = process;\n this._readBuffer = new ReadBuffer();\n }\n\n async start(): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n if (this._started) return;\n\n const { stdin, stdout } = this._process;\n if (!stdin || !stdout) throw new Error('Child process must have stdin and stdout pipes');\n if (this._process.exitCode !== null || this._process.signalCode !== null) {\n this.finish();\n throw new Error('Cannot start transport because the child process has already exited');\n }\n\n this._started = true;\n this._dataHandler = (chunk: Buffer) => {\n this._readBuffer.append(chunk);\n this.processReadBuffer();\n };\n this._stdinErrorHandler = (error: Error) => {\n this._stdinErrorObserved = true;\n this.retainStdinErrorListenerUntilClose();\n this.finish(error);\n };\n this._stdoutErrorHandler = (error: Error) => this.finish(error);\n this._processErrorHandler = (error: Error) => this.finish(error);\n this._processCloseHandler = () => this.finish();\n\n stdout.on('data', this._dataHandler);\n stdout.on('error', this._stdoutErrorHandler);\n stdin.on('error', this._stdinErrorHandler);\n this._process.on('error', this._processErrorHandler);\n this._process.on('close', this._processCloseHandler);\n }\n\n private processReadBuffer(): void {\n while (!this._closed) {\n try {\n const message = this._readBuffer.readMessage();\n if (message === null) return;\n this.onmessage?.(message);\n } catch (error) {\n this.finish(error instanceof Error ? error : new Error(String(error)));\n }\n }\n }\n\n private detachListeners(): void {\n const { stdout } = this._process;\n if (this._dataHandler) stdout?.off('data', this._dataHandler);\n if (this._stdoutErrorHandler) stdout?.off('error', this._stdoutErrorHandler);\n this._stdoutErrorHandler = null;\n if (this._processErrorHandler) this._process.off('error', this._processErrorHandler);\n if (this._processCloseHandler) this._process.off('close', this._processCloseHandler);\n this._dataHandler = null;\n this._processErrorHandler = null;\n this._processCloseHandler = null;\n this.detachPendingWriteErrorListener();\n }\n\n private detachPendingWriteErrorListener(): void {\n const stdin = this._process.stdin;\n if (!this._closed || this._pendingWrites > 0 || !this._stdinErrorHandler) return;\n\n if (this._stdinErrorObserved) {\n this.retainStdinErrorListenerUntilClose();\n return;\n }\n\n stdin?.off('error', this._stdinErrorHandler);\n if (this._stdinCloseHandler) stdin?.off('close', this._stdinCloseHandler);\n this._stdinErrorHandler = null;\n this._stdinCloseHandler = null;\n }\n\n private retainStdinErrorListenerUntilClose(): void {\n const stdin = this._process.stdin;\n const errorHandler = this._stdinErrorHandler;\n if (!stdin || !errorHandler) return;\n if (stdin.closed) {\n stdin.off('error', errorHandler);\n this._stdinErrorHandler = null;\n return;\n }\n if (this._stdinCloseHandler) return;\n\n this._stdinCloseHandler = () => {\n stdin.off('error', errorHandler);\n this._stdinErrorHandler = null;\n this._stdinCloseHandler = null;\n };\n stdin.once('close', this._stdinCloseHandler);\n }\n\n private finish(error?: Error): void {\n if (this._closed) return;\n this._closed = true;\n this.detachListeners();\n this._readBuffer.clear();\n\n for (const reject of this._pendingSends) reject(error ?? new Error('Transport is closed'));\n this._pendingSends.clear();\n\n try {\n if (error) this.onerror?.(error);\n } finally {\n this.onclose?.();\n }\n }\n\n async close(): Promise<void> {\n this.finish();\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (this._closed) throw new Error('Transport is closed');\n const stdin = this._process.stdin;\n if (!stdin || stdin.destroyed || stdin.writableEnded) throw new Error('Child process stdin is not writable');\n\n const json = serializeMessage(message);\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const settle = (error?: Error | null) => {\n if (settled) return;\n settled = true;\n this._pendingSends.delete(rejectPending);\n if (error) reject(error);\n else resolve();\n };\n const rejectPending = (error: Error) => settle(error);\n this._pendingSends.add(rejectPending);\n this._pendingWrites += 1;\n\n try {\n stdin.write(json, (error) => {\n this._pendingWrites -= 1;\n if (error) {\n this._stdinErrorObserved = true;\n this.retainStdinErrorListenerUntilClose();\n if (this._closed) settle(error);\n else this.finish(error);\n } else {\n settle();\n }\n this.detachPendingWriteErrorListener();\n });\n } catch (error) {\n this._pendingWrites -= 1;\n settle(error instanceof Error ? error : new Error(String(error)));\n this.detachPendingWriteErrorListener();\n }\n });\n }\n}\n"],"names":["ExistingProcessTransport","process","_dataHandler","_stdinErrorHandler","_stdoutErrorHandler","_stdinCloseHandler","_processErrorHandler","_processCloseHandler","_started","_closed","_pendingSends","Set","_pendingWrites","_stdinErrorObserved","stdin","stdout","Error","_process","_readBuffer","ReadBuffer","start","exitCode","signalCode","finish","chunk","append","processReadBuffer","error","retainStdinErrorListenerUntilClose","on","message","readMessage","onmessage","String","detachListeners","off","detachPendingWriteErrorListener","errorHandler","closed","once","clear","reject","onerror","onclose","close","send","json","destroyed","writableEnded","serializeMessage","Promise","resolve","settled","settle","delete","rejectPending","add","write"],"mappings":"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"}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @returns Promise that resolves when server is ready
|
|
13
13
|
* @throws Error if server doesn't become ready within timeout
|
|
14
14
|
*/
|
|
15
|
-
export declare function waitForHttpReady(url: string, timeoutMs?: number): Promise<void>;
|
|
15
|
+
export declare function waitForHttpReady(url: string, timeoutMs?: number, signal?: AbortSignal): Promise<void>;
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @returns Promise that resolves when server is ready
|
|
13
13
|
* @throws Error if server doesn't become ready within timeout
|
|
14
14
|
*/
|
|
15
|
-
export declare function waitForHttpReady(url: string, timeoutMs?: number): Promise<void>;
|
|
15
|
+
export declare function waitForHttpReady(url: string, timeoutMs?: number, signal?: AbortSignal): Promise<void>;
|