@mcp-z/client 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/auth/capability-discovery.d.cts +0 -19
- package/dist/cjs/auth/capability-discovery.d.ts +0 -19
- package/dist/cjs/auth/capability-discovery.js +123 -52
- package/dist/cjs/auth/capability-discovery.js.map +1 -1
- package/dist/cjs/auth/index.js.map +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
- package/dist/cjs/auth/pkce.js.map +1 -1
- package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -0
- package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -0
- package/dist/cjs/auth/rfc9728-discovery.js +208 -46
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
- package/dist/cjs/auth/types.js.map +1 -1
- package/dist/cjs/client-helpers.js.map +1 -1
- package/dist/cjs/config/server-loader.js.map +1 -1
- package/dist/cjs/config/validate-config.js +3 -7
- package/dist/cjs/config/validate-config.js.map +1 -1
- package/dist/cjs/connection/connect-client.js.map +1 -1
- package/dist/cjs/connection/existing-process-transport.js.map +1 -1
- package/dist/cjs/connection/types.js.map +1 -1
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/cjs/dcr/index.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/monkey-patches.js.map +1 -1
- package/dist/cjs/response-wrappers.js.map +1 -1
- package/dist/cjs/search/index.js.map +1 -1
- package/dist/cjs/search/search.js.map +1 -1
- package/dist/cjs/search/types.js.map +1 -1
- package/dist/cjs/spawn/spawn-server.js +4 -0
- package/dist/cjs/spawn/spawn-server.js.map +1 -1
- package/dist/cjs/spawn/spawn-servers.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils/logger.js.map +1 -1
- package/dist/cjs/utils/path-utils.js.map +1 -1
- package/dist/cjs/utils/sanitizer.js.map +1 -1
- package/dist/esm/auth/capability-discovery.d.ts +0 -19
- package/dist/esm/auth/capability-discovery.js +43 -41
- package/dist/esm/auth/capability-discovery.js.map +1 -1
- package/dist/esm/auth/index.js.map +1 -1
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
- package/dist/esm/auth/pkce.js.map +1 -1
- package/dist/esm/auth/rfc9728-discovery.d.ts +7 -0
- package/dist/esm/auth/rfc9728-discovery.js +67 -0
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
- package/dist/esm/auth/types.js.map +1 -1
- package/dist/esm/client-helpers.js.map +1 -1
- package/dist/esm/config/server-loader.js.map +1 -1
- package/dist/esm/config/validate-config.js +3 -7
- package/dist/esm/config/validate-config.js.map +1 -1
- package/dist/esm/connection/connect-client.js.map +1 -1
- package/dist/esm/connection/existing-process-transport.js.map +1 -1
- package/dist/esm/connection/types.js.map +1 -1
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/esm/dcr/index.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/monkey-patches.js.map +1 -1
- package/dist/esm/response-wrappers.js.map +1 -1
- package/dist/esm/search/index.js.map +1 -1
- package/dist/esm/search/search.js.map +1 -1
- package/dist/esm/search/types.js.map +1 -1
- package/dist/esm/spawn/spawn-server.js +4 -0
- package/dist/esm/spawn/spawn-server.js.map +1 -1
- package/dist/esm/spawn/spawn-servers.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/logger.js.map +1 -1
- package/dist/esm/utils/path-utils.js.map +1 -1
- package/dist/esm/utils/sanitizer.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/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 '../monkey-patches.ts';\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport getPort from 'get-port';\nimport { probeAuthCapabilities } from '../auth/index.ts';\nimport { DcrAuthenticator, type DcrAuthenticatorOptions } from '../dcr/index.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 * Extract base URL from MCP server URL\n * @param mcpUrl - Full MCP endpoint URL (e.g., https://example.com/mcp)\n * @returns Base URL (e.g., https://example.com)\n */\nfunction extractBaseUrl(mcpUrl: string): string {\n const url = new URL(mcpUrl);\n return `${url.protocol}//${url.host}`;\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 * @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 }\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 = isRegistry ? (registryOrConfig as RegistryLike).config : registryOrConfig;\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 // Create MCP client\n const client = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, { capabilities: {} });\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 const baseUrl = extractBaseUrl(serverConfig.url);\n const capabilities = await withTimeout(probeAuthCapabilities(baseUrl), 5000, '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(baseUrl, 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' }, { capabilities: {} });\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","extractBaseUrl","mcpUrl","url","URL","protocol","host","inferTransportType","config","type","registryOrConfig","serverName","options","isRegistry","serversConfig","registry","logger","serverConfig","available","transportType","client","serverHandle","transport","isSpawnedHttp","baseUrl","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","Client","name","version","get","ExistingProcessTransport","process","connect","command","StdioClientTransport","args","env","has","debug","waitForHttpReady","probeAuthCapabilities","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/Projects/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 '../monkey-patches.ts';\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport getPort from 'get-port';\nimport { probeAuthCapabilities } from '../auth/index.ts';\nimport { DcrAuthenticator, type DcrAuthenticatorOptions } from '../dcr/index.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 * Extract base URL from MCP server URL\n * @param mcpUrl - Full MCP endpoint URL (e.g., https://example.com/mcp)\n * @returns Base URL (e.g., https://example.com)\n */\nfunction extractBaseUrl(mcpUrl: string): string {\n const url = new URL(mcpUrl);\n return `${url.protocol}//${url.host}`;\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 * @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 }\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 = isRegistry ? (registryOrConfig as RegistryLike).config : registryOrConfig;\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 // Create MCP client\n const client = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, { capabilities: {} });\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 const baseUrl = extractBaseUrl(serverConfig.url);\n const capabilities = await withTimeout(probeAuthCapabilities(baseUrl), 5000, '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(baseUrl, 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' }, { capabilities: {} });\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","extractBaseUrl","mcpUrl","url","URL","protocol","host","inferTransportType","config","type","registryOrConfig","serverName","options","isRegistry","serversConfig","registry","logger","serverConfig","available","transportType","client","serverHandle","transport","isSpawnedHttp","baseUrl","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","Client","name","version","get","ExistingProcessTransport","process","connect","command","StdioClientTransport","args","env","has","debug","waitForHttpReady","probeAuthCapabilities","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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/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 { ReadBuffer, serializeMessage } from '@modelcontextprotocol/sdk/shared/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/connection/types.ts"],"sourcesContent":["/**\n * Type definitions and type guards for CLI\n * Per project convention, all types and runtime validators in single file\n */\n\n/**\n * JSON-serializable value type per MCP spec\n * Used for tool arguments and responses\n */\nexport type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };\n\n/**\n * Tool arguments must be JSON-serializable\n */\nexport type ToolArguments = Record<string, JsonValue>;\n\n/**\n * Spawn metadata for logging and sanitization\n */\nexport interface SpawnMetadata {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n pid?: number;\n [key: string]: JsonValue;\n}\n\n/**\n * MCP prompt argument structure\n * Explicit undefined for exactOptionalPropertyTypes compatibility\n */\nexport interface PromptArgument {\n name: string;\n description?: string | undefined;\n required?: boolean | undefined;\n}\n\n/**\n * Parsed MCP response structure\n */\nexport interface ParsedResponse {\n result?: JsonValue;\n}\n\n/**\n * Result object structure for error detection\n */\nexport interface ResultObject {\n type?: string;\n error?: string;\n message?: string;\n}\n\n// Type Guards (runtime validation functions)\n\n/**\n * Type guard for JSON-serializable values\n */\nexport function isJsonValue(value: unknown): value is JsonValue {\n if (value === null) return true;\n if (typeof value === 'string') return true;\n if (typeof value === 'number') return true;\n if (typeof value === 'boolean') return true;\n\n if (Array.isArray(value)) {\n return value.every(isJsonValue);\n }\n\n if (typeof value === 'object') {\n return Object.values(value).every(isJsonValue);\n }\n\n return false;\n}\n\n/**\n * Type guard for parsed MCP response\n */\nexport function isParsedResponse(value: unknown): value is ParsedResponse {\n return typeof value === 'object' && value !== null && 'result' in value;\n}\n\n/**\n * Type guard for result object\n */\nexport function isResultObject(value: unknown): value is ResultObject {\n if (typeof value !== 'object' || value === null) return false;\n const obj = value as Record<string, unknown>;\n return (obj.type === undefined || typeof obj.type === 'string') && (obj.error === undefined || typeof obj.error === 'string') && (obj.message === undefined || typeof obj.message === 'string');\n}\n"],"names":["isJsonValue","isParsedResponse","isResultObject","value","Array","isArray","every","Object","values","obj","type","undefined","error","message"],"mappings":"AAAA;;;CAGC,GAED;;;CAGC;;;;;;;;;;;QAmDeA;eAAAA;;QAoBAC;eAAAA;;QAOAC;eAAAA;;;;;;;AA3BT,SAASF,YAAYG,KAAc;IACxC,IAAIA,UAAU,MAAM,OAAO;IAC3B,IAAI,OAAOA,UAAU,UAAU,OAAO;IACtC,IAAI,OAAOA,UAAU,UAAU,OAAO;IACtC,IAAI,OAAOA,UAAU,WAAW,OAAO;IAEvC,IAAIC,MAAMC,OAAO,CAACF,QAAQ;QACxB,OAAOA,MAAMG,KAAK,CAACN;IACrB;IAEA,IAAI,CAAA,OAAOG,sCAAP,SAAOA,MAAI,MAAM,UAAU;QAC7B,OAAOI,OAAOC,MAAM,CAACL,OAAOG,KAAK,CAACN;IACpC;IAEA,OAAO;AACT;AAKO,SAASC,iBAAiBE,KAAc;IAC7C,OAAO,CAAA,OAAOA,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQ,YAAYA;AACpE;AAKO,SAASD,eAAeC,KAAc;IAC3C,IAAI,CAAA,OAAOA,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,MAAM,OAAO;IACxD,IAAMM,MAAMN;IACZ,OAAO,AAACM,CAAAA,IAAIC,IAAI,KAAKC,aAAa,OAAOF,IAAIC,IAAI,KAAK,QAAO,KAAOD,CAAAA,IAAIG,KAAK,KAAKD,aAAa,OAAOF,IAAIG,KAAK,KAAK,QAAO,KAAOH,CAAAA,IAAII,OAAO,KAAKF,aAAa,OAAOF,IAAII,OAAO,KAAK,QAAO;AAC/L"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/connection/wait-for-http-ready.ts"],"sourcesContent":["/**\n * wait-for-http-ready.ts\n *\n * Utility for waiting for HTTP servers to become ready.\n */\n\n/**\n * Wait for HTTP server to be ready by checking endpoint accessibility.\n * Used to handle HTTP servers that need time to start listening.\n *\n * @param url - URL to check for server readiness\n * @param timeoutMs - Maximum time to wait in milliseconds (default: 30000)\n * @returns Promise that resolves when server is ready\n * @throws Error if server doesn't become ready within timeout\n */\nexport async function waitForHttpReady(url: string, timeoutMs = 30000): Promise<void> {\n const start = Date.now();\n const maxRetries = Math.ceil(timeoutMs / 100); // Check every 100ms\n\n for (let i = 0; i < maxRetries; i++) {\n try {\n // Use HEAD request to check server is responding\n const response = await fetch(url, {\n method: 'HEAD',\n headers: { Connection: 'close' },\n signal: AbortSignal.timeout(500), // 500ms per attempt\n });\n\n // Server is responding if we get any HTTP status\n if (response.status >= 200 && response.status < 500) {\n return;\n }\n\n // Server error (5xx) - keep trying as it might still be starting\n } catch (_error) {\n // Connection refused, timeout, or network error\n // Server not ready yet, continue polling\n if (i === maxRetries - 1) {\n const elapsed = Date.now() - start;\n throw new Error(`HTTP server ${url} not ready after ${elapsed}ms`);\n }\n }\n\n // Wait before retry\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n throw new Error(`HTTP server ${url} not ready after ${timeoutMs}ms`);\n}\n"],"names":["waitForHttpReady","url","timeoutMs","start","maxRetries","i","response","_error","elapsed","Date","now","Math","ceil","fetch","method","headers","Connection","signal","AbortSignal","timeout","status","Error","Promise","resolve","setTimeout"],"mappings":"AAAA;;;;CAIC,GAED;;;;;;;;CAQC;;;;+BACqBA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAf,SAAeA,iBAAiBC,GAAW;QAAEC,YAAAA,iEAAY;;YACxDC,OACAC,YAEGC,GAGCC,UAYCC,QAICC;;;;oBAtBNL,QAAQM,KAAKC,GAAG;oBAChBN,aAAaO,KAAKC,IAAI,CAACV,YAAY,MAAM,oBAAoB;oBAE1DG,IAAI;;;yBAAGA,CAAAA,IAAID,UAAS;;;;;;;;;;;;oBAGR;;wBAAMS,MAAMZ,KAAK;4BAChCa,QAAQ;4BACRC,SAAS;gCAAEC,YAAY;4BAAQ;4BAC/BC,QAAQC,YAAYC,OAAO,CAAC;wBAC9B;;;oBAJMb,WAAW;oBAMjB,iDAAiD;oBACjD,IAAIA,SAASc,MAAM,IAAI,OAAOd,SAASc,MAAM,GAAG,KAAK;wBACnD;;;oBACF;;;;;;oBAGOb;oBACP,gDAAgD;oBAChD,yCAAyC;oBACzC,IAAIF,MAAMD,aAAa,GAAG;wBAClBI,UAAUC,KAAKC,GAAG,KAAKP;wBAC7B,MAAM,IAAIkB,MAAM,AAAC,eAAqCb,OAAvBP,KAAI,qBAA2B,OAARO,SAAQ;oBAChE;;;;;;oBAGF,oBAAoB;oBACpB;;wBAAM,IAAIc,QAAQ,SAACC;mCAAYC,WAAWD,SAAS;;;;oBAAnD;;;oBAzB8BlB;;;;;;oBA4BhC,MAAM,IAAIgB,MAAM,AAAC,eAAqCnB,OAAvBD,KAAI,qBAA6B,OAAVC,WAAU;;;IAClE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/dcr/dcr-authenticator.ts"],"sourcesContent":["/**\n * DCR Authenticator\n * Consolidates DCR and OAuth flow logic for MCP HTTP servers\n */\n\nimport path from 'node:path';\nimport * as fs from 'fs';\nimport Keyv from 'keyv';\nimport { KeyvFile } from 'keyv-file';\nimport { InteractiveOAuthFlow } from '../auth/interactive-oauth-flow.ts';\nimport type { AuthCapabilities, TokenSet } from '../auth/types.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { DynamicClientRegistrar } from './dynamic-client-registrar.ts';\n\n/**\n * DcrAuthenticator configuration options\n */\nexport interface DcrAuthenticatorOptions {\n /** Custom Keyv store (for testing) - if not provided, uses default ~/.mcpeasy/tokens.json */\n tokenStore?: Keyv;\n /** Headless mode (don't open browser) */\n headless?: boolean;\n /** Required redirect URI for OAuth callback */\n redirectUri: string;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * Buffer time before token expiry to trigger proactive refresh (5 minutes)\n */\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n/**\n * DcrAuthenticator manages authentication for MCP HTTP servers\n * Handles DCR registration, OAuth flows, and token management\n */\nexport class DcrAuthenticator {\n private tokenStore: Keyv;\n private dcrClient: DynamicClientRegistrar;\n private oauthFlow: InteractiveOAuthFlow;\n private headless: boolean;\n private redirectUri: string;\n private logger: Logger;\n\n constructor(options: DcrAuthenticatorOptions) {\n if (options.tokenStore) {\n this.tokenStore = options.tokenStore;\n } else {\n // Default CLI store in .mcp-z directory (per-project)\n const storePath = path.join(process.cwd(), '.mcp-z', 'tokens.json');\n\n // Ensure directory exists before creating store\n fs.mkdirSync(path.dirname(storePath), { recursive: true });\n\n this.tokenStore = new Keyv({\n store: new KeyvFile({ filename: storePath }),\n });\n }\n this.dcrClient = new DynamicClientRegistrar();\n this.oauthFlow = new InteractiveOAuthFlow();\n this.headless = options.headless || false;\n this.redirectUri = options.redirectUri;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Detect if server is self-hosted DCR (vs external OAuth provider)\n * Self-hosted servers have their own OAuth endpoints and manage token storage\n */\n private async detectSelfHostedMode(baseUrl: string): Promise<boolean> {\n try {\n // Self-hosted DCR servers typically run their own OAuth server\n // Check if this is a self-hosted instance by testing OAuth metadata\n // For now, assume self-hosted if baseUrl matches common localhost patterns\n // TODO: Implement proper self-hosted detection logic\n return baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1');\n } catch (_error) {\n return false; // Assume external mode if detection fails\n }\n }\n\n /**\n * Ensure server is authenticated, performing DCR and OAuth if needed\n * Proactively refreshes tokens if they're within 5 minutes of expiry\n *\n * @param baseUrl - Base URL of the server (e.g., https://example.com)\n * @param capabilities - Auth capabilities from .well-known endpoint\n * @returns Valid token set ready to use\n *\n * @throws Error if authentication fails\n *\n * @example\n * const authenticator = new DcrAuthenticator({ redirectUri: 'http://localhost:3000/callback' });\n * const tokens = await authenticator.ensureAuthenticated(\n * 'https://example.com',\n * capabilities\n * );\n */\n async ensureAuthenticated(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // Auto-detect server mode\n const isSelfHosted = await this.detectSelfHostedMode(baseUrl);\n\n if (isSelfHosted) {\n return this.ensureAuthenticatedSelfHosted(baseUrl, capabilities);\n }\n return this.ensureAuthenticatedExternal(baseUrl, capabilities);\n }\n\n /**\n * Handle authentication for self-hosted DCR servers\n * Self-hosted servers manage their own token storage via /oauth/verify\n */\n private async ensureAuthenticatedSelfHosted(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n const dcrTokenKey = `dcr-tokens:${baseUrl}`;\n\n // 1. Check for existing DCR tokens (different from external tokens)\n let tokens = (await this.tokenStore.get(dcrTokenKey)) as TokenSet | undefined;\n\n if (tokens) {\n // 2. Verify token is still valid by calling /oauth/verify\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (verifyResponse.ok) {\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token === tokens.accessToken) {\n // Token is still valid with the self-hosted server\n return tokens;\n }\n }\n } catch (_error) {\n // Token verification failed - need to re-authenticate\n }\n\n // Token is expired or invalid\n await this.tokenStore.delete(dcrTokenKey);\n tokens = undefined;\n }\n\n // 3. No valid tokens - perform full DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting self-hosted DCR authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client with self-hosted server...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions: { port: number; headless: boolean; scopes?: string[]; redirectUri: string; pkce: boolean; logger: Logger } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // For self-hosted mode, verify the token works with /oauth/verify immediately\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (!verifyResponse.ok) {\n throw new Error(`DCR token verification failed after authentication: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token !== tokens.accessToken) {\n throw new Error('DCR server returned different token in verification');\n }\n\n this.logger.debug('✅ DCR token verified with self-hosted server');\n } catch (error) {\n this.logger.error('❌ DCR token verification failed:', error instanceof Error ? error.message : String(error));\n throw new Error('Self-hosted DCR authentication completed but token verification failed');\n }\n\n // Save tokens for future use\n await this.tokenStore.set(dcrTokenKey, tokens);\n this.logger.debug('✅ Self-hosted DCR authentication successful, tokens saved');\n\n return tokens;\n }\n\n /**\n * Handle authentication for external OAuth providers (original implementation)\n */\n private async ensureAuthenticatedExternal(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n const tokenKey = `tokens:${baseUrl}`;\n\n // 1. Check for existing tokens\n let tokens = (await this.tokenStore.get(tokenKey)) as TokenSet | undefined;\n\n if (tokens) {\n // 2. Proactive refresh if token expires within 5 minutes\n if (tokens.expiresAt < Date.now() + REFRESH_BUFFER_MS) {\n this.logger.debug('🔄 Refreshing access token...');\n\n try {\n tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint);\n await this.tokenStore.set(tokenKey, tokens);\n this.logger.debug('✅ Token refreshed successfully');\n } catch (_error) {\n // Refresh failed - clear tokens and re-authenticate\n this.logger.warn('⚠️ Token refresh failed, re-authenticating...');\n await this.tokenStore.delete(tokenKey);\n tokens = undefined;\n }\n }\n\n if (tokens) {\n return tokens;\n }\n }\n\n // 3. No valid tokens - perform DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting external OAuth authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions: { port: number; headless: boolean; scopes?: string[]; redirectUri: string; pkce: boolean; logger: Logger } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // Save tokens for future use\n await this.tokenStore.set(tokenKey, tokens);\n this.logger.debug('✅ Authentication successful, tokens saved');\n\n return tokens;\n }\n\n /**\n * Refresh access token using refresh token\n */\n private async refreshTokens(tokens: TokenSet, tokenEndpoint?: string): Promise<TokenSet> {\n if (!tokenEndpoint) {\n throw new Error('Token endpoint not available for refresh');\n }\n\n if (!tokens.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n if (!tokens.clientId || !tokens.clientSecret) {\n throw new Error('Client credentials not available for refresh');\n }\n\n return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret);\n }\n\n /**\n * Delete stored tokens for a server\n */\n async deleteTokens(baseUrl: string): Promise<void> {\n const tokenKey = `tokens:${baseUrl}`;\n await this.tokenStore.delete(tokenKey);\n this.logger.debug(`🗑️ Deleted tokens for ${baseUrl}`);\n }\n}\n"],"names":["DcrAuthenticator","REFRESH_BUFFER_MS","options","tokenStore","storePath","path","join","process","cwd","fs","mkdirSync","dirname","recursive","Keyv","store","KeyvFile","filename","dcrClient","DynamicClientRegistrar","oauthFlow","InteractiveOAuthFlow","headless","redirectUri","logger","defaultLogger","detectSelfHostedMode","baseUrl","includes","_error","ensureAuthenticated","capabilities","isSelfHosted","ensureAuthenticatedSelfHosted","ensureAuthenticatedExternal","dcrTokenKey","tokens","verifyUrl","verifyResponse","verifyData","port","client","flowOptions","error","get","fetch","headers","Authorization","accessToken","Connection","ok","json","token","delete","undefined","registrationEndpoint","authorizationEndpoint","tokenEndpoint","Error","debug","parseInt","URL","startsWith","registerClient","pkce","scopes","performAuthFlow","clientId","clientSecret","status","message","String","set","tokenKey","expiresAt","Date","now","refreshTokens","warn","refreshToken","deleteTokens"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/dcr/dcr-authenticator.ts"],"sourcesContent":["/**\n * DCR Authenticator\n * Consolidates DCR and OAuth flow logic for MCP HTTP servers\n */\n\nimport path from 'node:path';\nimport * as fs from 'fs';\nimport Keyv from 'keyv';\nimport { KeyvFile } from 'keyv-file';\nimport { InteractiveOAuthFlow } from '../auth/interactive-oauth-flow.ts';\nimport type { AuthCapabilities, TokenSet } from '../auth/types.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { DynamicClientRegistrar } from './dynamic-client-registrar.ts';\n\n/**\n * DcrAuthenticator configuration options\n */\nexport interface DcrAuthenticatorOptions {\n /** Custom Keyv store (for testing) - if not provided, uses default ~/.mcpeasy/tokens.json */\n tokenStore?: Keyv;\n /** Headless mode (don't open browser) */\n headless?: boolean;\n /** Required redirect URI for OAuth callback */\n redirectUri: string;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * Buffer time before token expiry to trigger proactive refresh (5 minutes)\n */\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n/**\n * DcrAuthenticator manages authentication for MCP HTTP servers\n * Handles DCR registration, OAuth flows, and token management\n */\nexport class DcrAuthenticator {\n private tokenStore: Keyv;\n private dcrClient: DynamicClientRegistrar;\n private oauthFlow: InteractiveOAuthFlow;\n private headless: boolean;\n private redirectUri: string;\n private logger: Logger;\n\n constructor(options: DcrAuthenticatorOptions) {\n if (options.tokenStore) {\n this.tokenStore = options.tokenStore;\n } else {\n // Default CLI store in .mcp-z directory (per-project)\n const storePath = path.join(process.cwd(), '.mcp-z', 'tokens.json');\n\n // Ensure directory exists before creating store\n fs.mkdirSync(path.dirname(storePath), { recursive: true });\n\n this.tokenStore = new Keyv({\n store: new KeyvFile({ filename: storePath }),\n });\n }\n this.dcrClient = new DynamicClientRegistrar();\n this.oauthFlow = new InteractiveOAuthFlow();\n this.headless = options.headless || false;\n this.redirectUri = options.redirectUri;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Detect if server is self-hosted DCR (vs external OAuth provider)\n * Self-hosted servers have their own OAuth endpoints and manage token storage\n */\n private async detectSelfHostedMode(baseUrl: string): Promise<boolean> {\n try {\n // Self-hosted DCR servers typically run their own OAuth server\n // Check if this is a self-hosted instance by testing OAuth metadata\n // For now, assume self-hosted if baseUrl matches common localhost patterns\n // TODO: Implement proper self-hosted detection logic\n return baseUrl.includes('localhost') || baseUrl.includes('127.0.0.1');\n } catch (_error) {\n return false; // Assume external mode if detection fails\n }\n }\n\n /**\n * Ensure server is authenticated, performing DCR and OAuth if needed\n * Proactively refreshes tokens if they're within 5 minutes of expiry\n *\n * @param baseUrl - Base URL of the server (e.g., https://example.com)\n * @param capabilities - Auth capabilities from .well-known endpoint\n * @returns Valid token set ready to use\n *\n * @throws Error if authentication fails\n *\n * @example\n * const authenticator = new DcrAuthenticator({ redirectUri: 'http://localhost:3000/callback' });\n * const tokens = await authenticator.ensureAuthenticated(\n * 'https://example.com',\n * capabilities\n * );\n */\n async ensureAuthenticated(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n // Auto-detect server mode\n const isSelfHosted = await this.detectSelfHostedMode(baseUrl);\n\n if (isSelfHosted) {\n return this.ensureAuthenticatedSelfHosted(baseUrl, capabilities);\n }\n return this.ensureAuthenticatedExternal(baseUrl, capabilities);\n }\n\n /**\n * Handle authentication for self-hosted DCR servers\n * Self-hosted servers manage their own token storage via /oauth/verify\n */\n private async ensureAuthenticatedSelfHosted(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n const dcrTokenKey = `dcr-tokens:${baseUrl}`;\n\n // 1. Check for existing DCR tokens (different from external tokens)\n let tokens = (await this.tokenStore.get(dcrTokenKey)) as TokenSet | undefined;\n\n if (tokens) {\n // 2. Verify token is still valid by calling /oauth/verify\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (verifyResponse.ok) {\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token === tokens.accessToken) {\n // Token is still valid with the self-hosted server\n return tokens;\n }\n }\n } catch (_error) {\n // Token verification failed - need to re-authenticate\n }\n\n // Token is expired or invalid\n await this.tokenStore.delete(dcrTokenKey);\n tokens = undefined;\n }\n\n // 3. No valid tokens - perform full DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting self-hosted DCR authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client with self-hosted server...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions: { port: number; headless: boolean; scopes?: string[]; redirectUri: string; pkce: boolean; logger: Logger } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // For self-hosted mode, verify the token works with /oauth/verify immediately\n try {\n const verifyUrl = `${baseUrl}/oauth/verify`;\n const verifyResponse = await fetch(verifyUrl, {\n headers: { Authorization: `Bearer ${tokens.accessToken}`, Connection: 'close' },\n });\n\n if (!verifyResponse.ok) {\n throw new Error(`DCR token verification failed after authentication: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as { token?: string };\n if (verifyData.token !== tokens.accessToken) {\n throw new Error('DCR server returned different token in verification');\n }\n\n this.logger.debug('✅ DCR token verified with self-hosted server');\n } catch (error) {\n this.logger.error('❌ DCR token verification failed:', error instanceof Error ? error.message : String(error));\n throw new Error('Self-hosted DCR authentication completed but token verification failed');\n }\n\n // Save tokens for future use\n await this.tokenStore.set(dcrTokenKey, tokens);\n this.logger.debug('✅ Self-hosted DCR authentication successful, tokens saved');\n\n return tokens;\n }\n\n /**\n * Handle authentication for external OAuth providers (original implementation)\n */\n private async ensureAuthenticatedExternal(baseUrl: string, capabilities: AuthCapabilities): Promise<TokenSet> {\n const tokenKey = `tokens:${baseUrl}`;\n\n // 1. Check for existing tokens\n let tokens = (await this.tokenStore.get(tokenKey)) as TokenSet | undefined;\n\n if (tokens) {\n // 2. Proactive refresh if token expires within 5 minutes\n if (tokens.expiresAt < Date.now() + REFRESH_BUFFER_MS) {\n this.logger.debug('🔄 Refreshing access token...');\n\n try {\n tokens = await this.refreshTokens(tokens, capabilities.tokenEndpoint);\n await this.tokenStore.set(tokenKey, tokens);\n this.logger.debug('✅ Token refreshed successfully');\n } catch (_error) {\n // Refresh failed - clear tokens and re-authenticate\n this.logger.warn('⚠️ Token refresh failed, re-authenticating...');\n await this.tokenStore.delete(tokenKey);\n tokens = undefined;\n }\n }\n\n if (tokens) {\n return tokens;\n }\n }\n\n // 3. No valid tokens - perform DCR + OAuth flow\n if (!capabilities.registrationEndpoint || !capabilities.authorizationEndpoint || !capabilities.tokenEndpoint) {\n throw new Error('Server does not provide required OAuth endpoints');\n }\n\n this.logger.debug('🔐 No valid tokens found, starting external OAuth authentication...');\n\n // Extract port from pre-resolved redirectUri\n const port = parseInt(new URL(this.redirectUri).port, 10) || (this.redirectUri.startsWith('https:') ? 443 : 80);\n\n // Register OAuth client via DCR\n this.logger.debug('📝 Registering OAuth client...');\n const client = await this.dcrClient.registerClient(capabilities.registrationEndpoint, {\n redirectUri: this.redirectUri,\n });\n\n // Perform OAuth authorization flow with PKCE (RFC 7636)\n const flowOptions: { port: number; headless: boolean; scopes?: string[]; redirectUri: string; pkce: boolean; logger: Logger } = {\n port,\n headless: this.headless,\n redirectUri: this.redirectUri,\n pkce: true,\n logger: this.logger,\n };\n if (capabilities.scopes) {\n flowOptions.scopes = capabilities.scopes;\n }\n\n tokens = await this.oauthFlow.performAuthFlow(capabilities.authorizationEndpoint, capabilities.tokenEndpoint, client.clientId, client.clientSecret, flowOptions);\n\n // Save tokens for future use\n await this.tokenStore.set(tokenKey, tokens);\n this.logger.debug('✅ Authentication successful, tokens saved');\n\n return tokens;\n }\n\n /**\n * Refresh access token using refresh token\n */\n private async refreshTokens(tokens: TokenSet, tokenEndpoint?: string): Promise<TokenSet> {\n if (!tokenEndpoint) {\n throw new Error('Token endpoint not available for refresh');\n }\n\n if (!tokens.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n if (!tokens.clientId || !tokens.clientSecret) {\n throw new Error('Client credentials not available for refresh');\n }\n\n return await this.oauthFlow.refreshTokens(tokenEndpoint, tokens.refreshToken, tokens.clientId, tokens.clientSecret);\n }\n\n /**\n * Delete stored tokens for a server\n */\n async deleteTokens(baseUrl: string): Promise<void> {\n const tokenKey = `tokens:${baseUrl}`;\n await this.tokenStore.delete(tokenKey);\n this.logger.debug(`🗑️ Deleted tokens for ${baseUrl}`);\n }\n}\n"],"names":["DcrAuthenticator","REFRESH_BUFFER_MS","options","tokenStore","storePath","path","join","process","cwd","fs","mkdirSync","dirname","recursive","Keyv","store","KeyvFile","filename","dcrClient","DynamicClientRegistrar","oauthFlow","InteractiveOAuthFlow","headless","redirectUri","logger","defaultLogger","detectSelfHostedMode","baseUrl","includes","_error","ensureAuthenticated","capabilities","isSelfHosted","ensureAuthenticatedSelfHosted","ensureAuthenticatedExternal","dcrTokenKey","tokens","verifyUrl","verifyResponse","verifyData","port","client","flowOptions","error","get","fetch","headers","Authorization","accessToken","Connection","ok","json","token","delete","undefined","registrationEndpoint","authorizationEndpoint","tokenEndpoint","Error","debug","parseInt","URL","startsWith","registerClient","pkce","scopes","performAuthFlow","clientId","clientSecret","status","message","String","set","tokenKey","expiresAt","Date","now","refreshTokens","warn","refreshToken","deleteTokens"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/dcr/dynamic-client-registrar.ts"],"sourcesContent":["/**\n * Dynamic Client Registration (DCR) Client\n * Implements RFC 7591 for OAuth client registration\n */\n\nimport type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.ts';\n\n/**\n * DCR Registration Request (RFC 7591)\n */\ninterface DcrRegistrationRequest {\n client_name?: string;\n redirect_uris?: string[];\n grant_types?: string[];\n response_types?: string[];\n token_endpoint_auth_method?: string;\n}\n\n/**\n * DCR Registration Response (RFC 7591)\n */\ninterface DcrRegistrationResponse {\n client_id: string;\n client_secret?: string;\n client_id_issued_at?: number;\n client_secret_expires_at?: number;\n}\n\n/**\n * DynamicClientRegistrar handles Dynamic Client Registration with OAuth servers\n */\nexport class DynamicClientRegistrar {\n /**\n * Register a new OAuth client with the authorization server\n *\n * @param registrationEndpoint - DCR registration endpoint URL\n * @param options - Registration options (client name, redirect URI)\n * @returns Client credentials (client ID and secret)\n *\n * @throws Error if registration fails or server returns error\n *\n * @example\n * const registrar = new DynamicClientRegistrar();\n * const creds = await registrar.registerClient(\n * 'https://example.com/oauth/register',\n * { clientName: '@mcp-z/client', redirectUri: 'http://localhost:3000/callback' }\n * );\n * console.log('Client ID:', creds.clientId);\n */\n async registerClient(registrationEndpoint: string, options: DcrRegistrationOptions = {}): Promise<ClientCredentials> {\n const requestBody: DcrRegistrationRequest = {\n client_name: options.clientName || '@mcp-z/client',\n redirect_uris: options.redirectUri ? [options.redirectUri] : ['http://localhost:3000/callback'],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'client_secret_basic',\n };\n\n const response = await fetch(registrationEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`DCR registration failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as DcrRegistrationResponse;\n\n if (!data.client_id) {\n throw new Error('DCR registration response missing client_id');\n }\n\n const credentials: ClientCredentials = {\n clientId: data.client_id,\n clientSecret: data.client_secret || '',\n };\n\n if (data.client_id_issued_at) {\n credentials.issuedAt = data.client_id_issued_at;\n }\n\n return credentials;\n }\n}\n"],"names":["DynamicClientRegistrar","registerClient","registrationEndpoint","options","requestBody","response","errorText","data","credentials","client_name","clientName","redirect_uris","redirectUri","grant_types","response_types","token_endpoint_auth_method","fetch","method","headers","Accept","Connection","body","JSON","stringify","ok","text","Error","status","json","client_id","clientId","clientSecret","client_secret","client_id_issued_at","issuedAt"],"mappings":"AAAA;;;CAGC;;;;+BA4BYA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAN,IAAA,AAAMA,uCAAN;;aAAMA;gCAAAA;;iBAAAA;IACX;;;;;;;;;;;;;;;;GAgBC,GACD,OAAMC,cAwCL,GAxCD,SAAMA;4CAAeC,oBAA4B;gBAAEC,SAC3CC,aAQAC,UAWEC,WAIFC,MAMAC;;;;;wBA9B2CL,UAAAA,oEAAkC,CAAC;wBAC9EC,cAAsC;4BAC1CK,aAAaN,QAAQO,UAAU,IAAI;4BACnCC,eAAeR,QAAQS,WAAW;gCAAIT,QAAQS,WAAW;;gCAAK;;4BAC9DC,WAAW;gCAAG;gCAAsB;;4BACpCC,cAAc;gCAAG;;4BACjBC,4BAA4B;wBAC9B;wBAEiB;;4BAAMC,MAAMd,sBAAsB;gCACjDe,QAAQ;gCACRC,SAAS;oCACP,gBAAgB;oCAChBC,QAAQ;oCACRC,YAAY;gCACd;gCACAC,MAAMC,KAAKC,SAAS,CAACnB;4BACvB;;;wBARMC,WAAW;6BAUb,CAACA,SAASmB,EAAE,EAAZ;;;;wBACgB;;4BAAMnB,SAASoB,IAAI;;;wBAA/BnB,YAAY;wBAClB,MAAM,IAAIoB,MAAM,AAAC,4BAAgDpB,OAArBD,SAASsB,MAAM,EAAC,OAAe,OAAVrB;;wBAGrD;;4BAAMD,SAASuB,IAAI;;;wBAA3BrB,OAAQ;wBAEd,IAAI,CAACA,KAAKsB,SAAS,EAAE;4BACnB,MAAM,IAAIH,MAAM;wBAClB;wBAEMlB,cAAiC;4BACrCsB,UAAUvB,KAAKsB,SAAS;4BACxBE,cAAcxB,KAAKyB,aAAa,IAAI;wBACtC;wBAEA,IAAIzB,KAAK0B,mBAAmB,EAAE;4BAC5BzB,YAAY0B,QAAQ,GAAG3B,KAAK0B,mBAAmB;wBACjD;wBAEA;;4BAAOzB;;;;QACT;;WA1DWR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/dcr/index.ts"],"sourcesContent":["/**\n * DCR (Dynamic Client Registration) Module\n * Exports public API for DCR authentication\n */\n\nexport type { ClientCredentials, DcrRegistrationOptions } from '../auth/types.ts';\nexport type { DcrAuthenticatorOptions } from './dcr-authenticator.ts';\nexport { DcrAuthenticator } from './dcr-authenticator.ts';\nexport { DynamicClientRegistrar } from './dynamic-client-registrar.ts';\n"],"names":["DcrAuthenticator","DynamicClientRegistrar"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QAIQA;eAAAA,oCAAgB;;QAChBC;eAAAA,gDAAsB;;;kCADE;wCACM"}
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/index.ts"],"sourcesContent":["/**\n * @mcp-z/client - MCP Client Library\n */\n\n// Config types (from schema)\nexport type { McpServerEntry, StartConfig } from '../schemas/servers.d.ts';\n// Auth - OAuth utilities\nexport { probeAuthCapabilities } from './auth/capability-discovery.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthCallbackListenerOptions, OAuthFlowOptions, TokenSet } from './auth/index.ts';\nexport { InteractiveOAuthFlow } from './auth/interactive-oauth-flow.ts';\nexport { OAuthCallbackListener } from './auth/oauth-callback-listener.ts';\n// Client helpers and lightweight overloads\nexport { decorateClient, type ManagedClient, type PromptArguments, type WrappedCallToolReturn, type WrappedGetPromptReturn, type WrappedReadResourceReturn } from './client-helpers.ts';\n// Config - Configuration validation\nexport { type ValidationResult, validateServers } from './config/validate-config.ts';\n// Connection - MCP client connection utilities (internal helpers exposed for advanced use)\nexport type { JsonValue, PromptArgument, ToolArguments } from './connection/types.ts';\n// DCR - Dynamic Client Registration utilities\nexport { DcrAuthenticator } from './dcr/dcr-authenticator.ts';\nexport { DynamicClientRegistrar } from './dcr/dynamic-client-registrar.ts';\nexport type { ClientCredentials, DcrAuthenticatorOptions, DcrRegistrationOptions } from './dcr/index.ts';\nexport {\n type JsonValidator,\n type NativeCallToolResponse,\n type NativeGetPromptResponse,\n type NativeReadResourceResponse,\n PromptResponseError,\n PromptResponseWrapper,\n ResourceResponseError,\n ResourceResponseWrapper,\n ToolResponseError,\n ToolResponseWrapper,\n} from './response-wrappers.ts';\nexport type { CapabilityClient, CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult } from './search/index.ts';\n// Search - Capability discovery\nexport { buildCapabilityIndex, search, searchCapabilities } from './search/index.ts';\n// Spawn - Server registry (v3 API)\nexport { type CloseResult, type CreateServerRegistryOptions, createServerRegistry, type Dialect, type ServerRegistry, type ServersConfig } from './spawn/spawn-servers.ts';\nexport type { TransportType } from './types.ts';\n// Utils - Shared utilities\nexport { getLogLevel, type Logger, type LogLevel, logger, setLogLevel } from './utils/logger.ts';\nexport { resolveArgsPaths, resolvePath } from './utils/path-utils.ts';\n"],"names":["DcrAuthenticator","DynamicClientRegistrar","InteractiveOAuthFlow","OAuthCallbackListener","PromptResponseError","PromptResponseWrapper","ResourceResponseError","ResourceResponseWrapper","ToolResponseError","ToolResponseWrapper","buildCapabilityIndex","createServerRegistry","decorateClient","getLogLevel","logger","probeAuthCapabilities","resolveArgsPaths","resolvePath","search","searchCapabilities","setLogLevel","validateServers"],"mappings":"AAAA;;CAEC,GAED,6BAA6B;;;;;;;;;;;;QAcpBA;eAAAA,oCAAgB;;QAChBC;eAAAA,gDAAsB;;QAVtBC;eAAAA,4CAAoB;;QACpBC;eAAAA,8CAAqB;;QAgB5BC;eAAAA,uCAAmB;;QACnBC;eAAAA,yCAAqB;;QACrBC;eAAAA,yCAAqB;;QACrBC;eAAAA,2CAAuB;;QACvBC;eAAAA,qCAAiB;;QACjBC;eAAAA,uCAAmB;;QAIZC;eAAAA,6BAAoB;;QAEgCC;eAAAA,oCAAoB;;QAzBxEC;eAAAA,+BAAc;;QA4BdC;eAAAA,qBAAW;;QAA8BC;eAAAA,gBAAM;;QAjC/CC;eAAAA,4CAAqB;;QAkCrBC;eAAAA,6BAAgB;;QAAEC;eAAAA,wBAAW;;QANPC;eAAAA,eAAM;;QAAEC;eAAAA,2BAAkB;;QAKCC;eAAAA,qBAAW;;QA1BrCC;eAAAA,iCAAe;;;qCAPT;sCAED;uCACC;+BAE4H;gCAE3G;kCAItB;wCACM;kCAahC;uBAG0D;8BAE+E;wBAGnE;2BAC/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/monkey-patches.ts"],"sourcesContent":["/**\n * Monkey patches for MCP SDK bugs\n *\n * These patches fix bugs in dependencies that haven't been fixed upstream yet.\n */\n\nimport { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';\n\n/**\n * FIX: Protocol.close() doesn't clear pending timeouts\n *\n * BUG: The MCP SDK's Protocol.close() only closes the transport but does NOT\n * clear pending timeouts from the internal _timeoutInfo Map. This causes Node.js\n * to hang until timeouts fire (default 60 seconds).\n *\n * PATCH: Wrap Protocol.close() to clear all pending timeouts before closing.\n *\n * TO TEST IF STILL NEEDED:\n * 1. Comment out this patch\n * 2. Run: npm run test:unit\n * 3. If tests hang ~60 seconds after completing, bug still exists\n * 4. If tests exit promptly, SDK is fixed and this can be removed\n *\n * UPSTREAM: https://github.com/modelcontextprotocol/typescript-sdk/issues/XXX\n */\nconst originalClose = Protocol.prototype.close;\nProtocol.prototype.close = async function () {\n const self = this as unknown as { _timeoutInfo?: Map<unknown, { timeoutId: ReturnType<typeof setTimeout> }> };\n if (self._timeoutInfo) {\n for (const [, info] of self._timeoutInfo) {\n clearTimeout(info.timeoutId);\n }\n self._timeoutInfo.clear();\n }\n return originalClose.call(this);\n};\n"],"names":["originalClose","Protocol","prototype","close","self","info","_timeoutInfo","clearTimeout","timeoutId","clear","call"],"mappings":"AAAA;;;;CAIC;;;;wBAEwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEzB;;;;;;;;;;;;;;;;CAgBC,GACD,IAAMA,gBAAgBC,kBAAQ,CAACC,SAAS,CAACC,KAAK;AAC9CF,kBAAQ,CAACC,SAAS,CAACC,KAAK,GAAG;;YACnBC,MAEC,2BAAA,mBAAA,gBAAA,WAAA,oBAASC;;YAFVD,OAAO,IAAI;YACjB,IAAIA,KAAKE,YAAY,EAAE;gBAChB,kCAAA,2BAAA;;oBAAL,IAAK,YAAkBF,KAAKE,YAAY,uBAAnC,6BAAA,QAAA,yBAAA,iCAAqC;uDAArC,iBAASD;wBACZE,aAAaF,KAAKG,SAAS;oBAC7B;;oBAFK;oBAAA;;;6BAAA,6BAAA;4BAAA;;;4BAAA;kCAAA;;;;gBAGLJ,KAAKE,YAAY,CAACG,KAAK;YACzB;YACA;;gBAAOT,cAAcU,IAAI,CAAC,IAAI;;;IAChC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/response-wrappers.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { ContentBlock, PromptMessage, TextContent } from '@modelcontextprotocol/sdk/types.js';\n\nexport type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;\nexport type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;\nexport type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;\n\nexport type JsonValidator<T> = (value: unknown) => asserts value is T;\n\nexport class ToolResponseError extends Error {\n readonly response: NativeCallToolResponse;\n\n constructor(message: string, response: NativeCallToolResponse) {\n super(message);\n this.name = 'ToolResponseError';\n this.response = response;\n }\n}\n\nexport class ToolResponseWrapper {\n private readonly payload: NativeCallToolResponse;\n\n constructor(payload: NativeCallToolResponse) {\n this.payload = payload;\n }\n\n raw(): NativeCallToolResponse {\n return this.payload;\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const value = this.resolveJsonPayload();\n if (validator) {\n validator(value);\n }\n return value as T;\n }\n\n text(): string {\n if (isCompatibilityResult(this.payload)) {\n if (typeof this.payload.toolResult === 'string') {\n return this.payload.toolResult;\n }\n throw new ToolResponseError('Compatibility tool result is not text', this.payload);\n }\n\n this.throwIfError();\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include text content', this.payload);\n }\n\n return textBlock.text;\n }\n\n private resolveJsonPayload(): unknown {\n if (isCompatibilityResult(this.payload)) {\n return this.payload.toolResult;\n }\n\n this.throwIfError();\n\n if (hasStructuredContent(this.payload)) {\n return this.payload.structuredContent;\n }\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include structuredContent or text content', this.payload);\n }\n\n try {\n return JSON.parse(textBlock.text) as unknown;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ToolResponseError(`Failed to parse tool text content as JSON: ${reason}`, this.payload);\n }\n }\n\n private throwIfError(): void {\n if ('isError' in this.payload && this.payload.isError) {\n let detail = typeof this.payload.error === 'object' && this.payload.error && 'message' in this.payload.error ? String((this.payload.error as { message?: unknown }).message ?? '') : '';\n if (!detail) {\n const textBlock = findFirstTextBlock((this.payload.content ?? []) as ContentBlock[]);\n if (textBlock?.text) {\n detail = textBlock.text;\n }\n }\n const message = detail ? `Tool invocation returned an error result: ${detail}` : 'Tool invocation returned an error result';\n throw new ToolResponseError(message, this.payload);\n }\n }\n}\n\nexport class PromptResponseError extends Error {\n readonly response: NativeGetPromptResponse;\n\n constructor(message: string, response: NativeGetPromptResponse) {\n super(message);\n this.name = 'PromptResponseError';\n this.response = response;\n }\n}\n\nexport class PromptResponseWrapper {\n private readonly payload: NativeGetPromptResponse;\n\n constructor(payload: NativeGetPromptResponse) {\n this.payload = payload;\n }\n\n raw(): NativeGetPromptResponse {\n return this.payload;\n }\n\n text(): string {\n const segments = collectPromptText(this.payload.messages);\n if (!segments.length) {\n throw new PromptResponseError('Prompt response did not include text content', this.payload);\n }\n return segments.join('\\n\\n');\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n validator(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new PromptResponseError(`Failed to parse prompt text as JSON: ${reason}`, this.payload);\n }\n }\n}\n\nexport class ResourceResponseError extends Error {\n readonly response: NativeReadResourceResponse;\n\n constructor(message: string, response: NativeReadResourceResponse) {\n super(message);\n this.name = 'ResourceResponseError';\n this.response = response;\n }\n}\n\nexport class ResourceResponseWrapper {\n private readonly payload: NativeReadResourceResponse;\n\n constructor(payload: NativeReadResourceResponse) {\n this.payload = payload;\n }\n\n raw(): NativeReadResourceResponse {\n return this.payload;\n }\n\n text(): string {\n const entry = this.firstEntry();\n if ('text' in entry && typeof entry.text === 'string') {\n return entry.text;\n }\n if ('blob' in entry && typeof entry.blob === 'string') {\n try {\n return Buffer.from(entry.blob, 'base64').toString('utf8');\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to decode resource blob as UTF-8 text: ${reason}`, this.payload);\n }\n }\n throw new ResourceResponseError('Resource content does not include text or blob data', this.payload);\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n validator(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to parse resource text as JSON: ${reason}`, this.payload);\n }\n }\n\n private firstEntry(): NativeReadResourceResponse['contents'][number] {\n const [entry] = this.payload.contents ?? [];\n if (!entry) {\n throw new ResourceResponseError('Resource response did not include any contents', this.payload);\n }\n return entry;\n }\n}\n\nfunction hasStructuredContent(response: NativeCallToolResponse): response is NativeCallToolResponse & { structuredContent: Record<string, unknown> } {\n return Boolean((response as { structuredContent?: unknown }).structuredContent);\n}\n\nfunction isCompatibilityResult(response: NativeCallToolResponse): response is NativeCallToolResponse & { toolResult: unknown } {\n return hasOwn(response, 'toolResult');\n}\n\nfunction findFirstTextBlock(blocks: ContentBlock[] | undefined): TextContent | undefined {\n if (!Array.isArray(blocks)) {\n return undefined;\n }\n return blocks.find(isTextContent);\n}\n\nfunction collectPromptText(messages: PromptMessage[]): string[] {\n const segments: string[] = [];\n for (const message of messages) {\n const textBlock = isTextContent(message.content) ? message.content : undefined;\n if (textBlock) {\n segments.push(textBlock.text);\n }\n }\n return segments;\n}\n\nfunction isTextContent(block: unknown): block is TextContent {\n return Boolean(block) && typeof block === 'object' && (block as { type?: string }).type === 'text';\n}\n\nconst protoHasOwn = Object.prototype.hasOwnProperty;\n\nfunction hasOwn(target: object, key: PropertyKey): boolean {\n return protoHasOwn.call(target, key);\n}\n\nfunction formatErrorReason(error: unknown): string {\n if (error instanceof Error && typeof error.message === 'string') {\n return error.message;\n }\n if (typeof error === 'string') {\n return error;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n"],"names":["PromptResponseError","PromptResponseWrapper","ResourceResponseError","ResourceResponseWrapper","ToolResponseError","ToolResponseWrapper","message","response","name","Error","payload","raw","json","validator","value","resolveJsonPayload","text","isCompatibilityResult","toolResult","throwIfError","textBlock","findFirstTextBlock","content","hasStructuredContent","structuredContent","JSON","parse","error","reason","formatErrorReason","isError","detail","String","segments","collectPromptText","messages","length","join","textValue","parsed","entry","firstEntry","blob","Buffer","from","toString","contents","Boolean","hasOwn","blocks","Array","isArray","undefined","find","isTextContent","push","block","type","protoHasOwn","Object","prototype","hasOwnProperty","target","key","call","stringify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/response-wrappers.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { ContentBlock, PromptMessage, TextContent } from '@modelcontextprotocol/sdk/types.js';\n\nexport type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;\nexport type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;\nexport type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;\n\nexport type JsonValidator<T> = (value: unknown) => asserts value is T;\n\nexport class ToolResponseError extends Error {\n readonly response: NativeCallToolResponse;\n\n constructor(message: string, response: NativeCallToolResponse) {\n super(message);\n this.name = 'ToolResponseError';\n this.response = response;\n }\n}\n\nexport class ToolResponseWrapper {\n private readonly payload: NativeCallToolResponse;\n\n constructor(payload: NativeCallToolResponse) {\n this.payload = payload;\n }\n\n raw(): NativeCallToolResponse {\n return this.payload;\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const value = this.resolveJsonPayload();\n if (validator) {\n validator(value);\n }\n return value as T;\n }\n\n text(): string {\n if (isCompatibilityResult(this.payload)) {\n if (typeof this.payload.toolResult === 'string') {\n return this.payload.toolResult;\n }\n throw new ToolResponseError('Compatibility tool result is not text', this.payload);\n }\n\n this.throwIfError();\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include text content', this.payload);\n }\n\n return textBlock.text;\n }\n\n private resolveJsonPayload(): unknown {\n if (isCompatibilityResult(this.payload)) {\n return this.payload.toolResult;\n }\n\n this.throwIfError();\n\n if (hasStructuredContent(this.payload)) {\n return this.payload.structuredContent;\n }\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include structuredContent or text content', this.payload);\n }\n\n try {\n return JSON.parse(textBlock.text) as unknown;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ToolResponseError(`Failed to parse tool text content as JSON: ${reason}`, this.payload);\n }\n }\n\n private throwIfError(): void {\n if ('isError' in this.payload && this.payload.isError) {\n let detail = typeof this.payload.error === 'object' && this.payload.error && 'message' in this.payload.error ? String((this.payload.error as { message?: unknown }).message ?? '') : '';\n if (!detail) {\n const textBlock = findFirstTextBlock((this.payload.content ?? []) as ContentBlock[]);\n if (textBlock?.text) {\n detail = textBlock.text;\n }\n }\n const message = detail ? `Tool invocation returned an error result: ${detail}` : 'Tool invocation returned an error result';\n throw new ToolResponseError(message, this.payload);\n }\n }\n}\n\nexport class PromptResponseError extends Error {\n readonly response: NativeGetPromptResponse;\n\n constructor(message: string, response: NativeGetPromptResponse) {\n super(message);\n this.name = 'PromptResponseError';\n this.response = response;\n }\n}\n\nexport class PromptResponseWrapper {\n private readonly payload: NativeGetPromptResponse;\n\n constructor(payload: NativeGetPromptResponse) {\n this.payload = payload;\n }\n\n raw(): NativeGetPromptResponse {\n return this.payload;\n }\n\n text(): string {\n const segments = collectPromptText(this.payload.messages);\n if (!segments.length) {\n throw new PromptResponseError('Prompt response did not include text content', this.payload);\n }\n return segments.join('\\n\\n');\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n validator(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new PromptResponseError(`Failed to parse prompt text as JSON: ${reason}`, this.payload);\n }\n }\n}\n\nexport class ResourceResponseError extends Error {\n readonly response: NativeReadResourceResponse;\n\n constructor(message: string, response: NativeReadResourceResponse) {\n super(message);\n this.name = 'ResourceResponseError';\n this.response = response;\n }\n}\n\nexport class ResourceResponseWrapper {\n private readonly payload: NativeReadResourceResponse;\n\n constructor(payload: NativeReadResourceResponse) {\n this.payload = payload;\n }\n\n raw(): NativeReadResourceResponse {\n return this.payload;\n }\n\n text(): string {\n const entry = this.firstEntry();\n if ('text' in entry && typeof entry.text === 'string') {\n return entry.text;\n }\n if ('blob' in entry && typeof entry.blob === 'string') {\n try {\n return Buffer.from(entry.blob, 'base64').toString('utf8');\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to decode resource blob as UTF-8 text: ${reason}`, this.payload);\n }\n }\n throw new ResourceResponseError('Resource content does not include text or blob data', this.payload);\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n validator(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to parse resource text as JSON: ${reason}`, this.payload);\n }\n }\n\n private firstEntry(): NativeReadResourceResponse['contents'][number] {\n const [entry] = this.payload.contents ?? [];\n if (!entry) {\n throw new ResourceResponseError('Resource response did not include any contents', this.payload);\n }\n return entry;\n }\n}\n\nfunction hasStructuredContent(response: NativeCallToolResponse): response is NativeCallToolResponse & { structuredContent: Record<string, unknown> } {\n return Boolean((response as { structuredContent?: unknown }).structuredContent);\n}\n\nfunction isCompatibilityResult(response: NativeCallToolResponse): response is NativeCallToolResponse & { toolResult: unknown } {\n return hasOwn(response, 'toolResult');\n}\n\nfunction findFirstTextBlock(blocks: ContentBlock[] | undefined): TextContent | undefined {\n if (!Array.isArray(blocks)) {\n return undefined;\n }\n return blocks.find(isTextContent);\n}\n\nfunction collectPromptText(messages: PromptMessage[]): string[] {\n const segments: string[] = [];\n for (const message of messages) {\n const textBlock = isTextContent(message.content) ? message.content : undefined;\n if (textBlock) {\n segments.push(textBlock.text);\n }\n }\n return segments;\n}\n\nfunction isTextContent(block: unknown): block is TextContent {\n return Boolean(block) && typeof block === 'object' && (block as { type?: string }).type === 'text';\n}\n\nconst protoHasOwn = Object.prototype.hasOwnProperty;\n\nfunction hasOwn(target: object, key: PropertyKey): boolean {\n return protoHasOwn.call(target, key);\n}\n\nfunction formatErrorReason(error: unknown): string {\n if (error instanceof Error && typeof error.message === 'string') {\n return error.message;\n }\n if (typeof error === 'string') {\n return error;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n"],"names":["PromptResponseError","PromptResponseWrapper","ResourceResponseError","ResourceResponseWrapper","ToolResponseError","ToolResponseWrapper","message","response","name","Error","payload","raw","json","validator","value","resolveJsonPayload","text","isCompatibilityResult","toolResult","throwIfError","textBlock","findFirstTextBlock","content","hasStructuredContent","structuredContent","JSON","parse","error","reason","formatErrorReason","isError","detail","String","segments","collectPromptText","messages","length","join","textValue","parsed","entry","firstEntry","blob","Buffer","from","toString","contents","Boolean","hasOwn","blocks","Array","isArray","undefined","find","isTextContent","push","block","type","protoHasOwn","Object","prototype","hasOwnProperty","target","key","call","stringify"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/search/index.ts"],"sourcesContent":["/**\n * Search module for MCP capability discovery\n */\n\nexport type { CapabilityClient } from './search.ts';\nexport { buildCapabilityIndex, search, searchCapabilities } from './search.ts';\nexport type {\n CapabilityIndex,\n CapabilityType,\n IndexedCapability,\n IndexedPrompt,\n IndexedResource,\n IndexedTool,\n SearchField,\n SearchOptions,\n SearchResponse,\n SearchResult,\n} from './types.ts';\n"],"names":["buildCapabilityIndex","search","searchCapabilities"],"mappings":"AAAA;;CAEC;;;;;;;;;;;QAGQA;eAAAA,8BAAoB;;QAAEC;eAAAA,gBAAM;;QAAEC;eAAAA,4BAAkB;;;wBAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/search/search.ts"],"sourcesContent":["/**\n * Search implementation for MCP capability discovery\n *\n * Provides text-based search across tools, prompts, and resources\n * from connected MCP servers.\n */\n\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { PromptArgument } from '../connection/types.ts';\nimport type { CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult } from './types.ts';\n\nexport type CapabilityClient = Pick<Client, 'listTools' | 'listPrompts' | 'listResources'>;\n\nconst DEFAULT_LIMIT = 20;\nconst DEFAULT_THRESHOLD = 0;\nconst DEFAULT_TYPES: CapabilityType[] = ['tool', 'prompt', 'resource'];\nconst DEFAULT_SEARCH_FIELDS: SearchField[] = ['name', 'description', 'schema'];\n\n/**\n * Extract searchable text from a JSON Schema's property descriptions\n */\nfunction extractSchemaText(inputSchema: unknown): string {\n if (!inputSchema || typeof inputSchema !== 'object') {\n return '';\n }\n\n const schema = inputSchema as {\n properties?: Record<string, { description?: string; name?: string }>;\n description?: string;\n };\n\n const parts: string[] = [];\n\n // Add schema-level description if present\n if (schema.description) {\n parts.push(schema.description);\n }\n\n // Add property names and descriptions\n if (schema.properties) {\n for (const [propName, prop] of Object.entries(schema.properties)) {\n parts.push(propName);\n if (prop && typeof prop === 'object' && prop.description) {\n parts.push(prop.description);\n }\n }\n }\n\n return parts.join(' ');\n}\n\n/**\n * Extract searchable text from prompt arguments\n */\nfunction extractArgumentsText(args: PromptArgument[] | undefined): string {\n if (!args || !Array.isArray(args)) {\n return '';\n }\n\n return args\n .map((arg) => {\n const parts = [arg.name];\n if (arg.description) {\n parts.push(arg.description);\n }\n return parts.join(' ');\n })\n .join(' ');\n}\n\n/**\n * Build an index of capabilities from connected MCP clients\n */\nexport async function buildCapabilityIndex(clients: Map<string, CapabilityClient>): Promise<CapabilityIndex> {\n const capabilities: IndexedCapability[] = [];\n const servers: string[] = [];\n\n for (const [serverName, client] of clients) {\n servers.push(serverName);\n\n // Fetch all capabilities in parallel, handling errors gracefully\n const [toolsResult, promptsResult, resourcesResult] = await Promise.all([client.listTools().catch(() => null), client.listPrompts().catch(() => null), client.listResources().catch(() => null)]);\n\n // Index tools\n if (toolsResult?.tools) {\n for (const tool of toolsResult.tools) {\n capabilities.push({\n type: 'tool',\n server: serverName,\n name: tool.name,\n description: tool.description,\n schemaText: extractSchemaText(tool.inputSchema),\n } satisfies IndexedTool);\n }\n }\n\n // Index prompts\n if (promptsResult?.prompts) {\n for (const prompt of promptsResult.prompts) {\n capabilities.push({\n type: 'prompt',\n server: serverName,\n name: prompt.name,\n description: prompt.description,\n argumentsText: extractArgumentsText(prompt.arguments as PromptArgument[] | undefined),\n arguments: prompt.arguments as PromptArgument[] | undefined,\n } satisfies IndexedPrompt);\n }\n }\n\n // Index resources\n if (resourcesResult?.resources) {\n for (const resource of resourcesResult.resources) {\n capabilities.push({\n type: 'resource',\n server: serverName,\n name: resource.name,\n description: resource.description,\n uri: resource.uri,\n mimeType: resource.mimeType,\n } satisfies IndexedResource);\n }\n }\n }\n\n return {\n capabilities,\n servers,\n indexedAt: new Date(),\n };\n}\n\n/**\n * Calculate relevance score and matched fields for a capability against a query\n */\nfunction scoreCapability(capability: IndexedCapability, queryTerms: string[], searchFields: SearchField[]): { score: number; matchedOn: string[] } {\n const matchedOn: string[] = [];\n let totalScore = 0;\n\n // Weights for different match types\n const EXACT_NAME_WEIGHT = 1.0;\n const PARTIAL_NAME_WEIGHT = 0.8;\n const DESCRIPTION_WEIGHT = 0.6;\n const SCHEMA_WEIGHT = 0.4;\n const SERVER_WEIGHT = 0.3;\n\n const nameLower = capability.name.toLowerCase();\n const descLower = (capability.description || '').toLowerCase();\n const serverLower = capability.server.toLowerCase();\n\n // Get schema/arguments text based on type\n let schemaTextLower = '';\n if (capability.type === 'tool') {\n schemaTextLower = capability.schemaText.toLowerCase();\n } else if (capability.type === 'prompt') {\n schemaTextLower = capability.argumentsText.toLowerCase();\n } else if (capability.type === 'resource') {\n // For resources, include URI and mimeType in searchable text\n schemaTextLower = `${capability.uri} ${capability.mimeType || ''}`.toLowerCase();\n }\n\n for (const term of queryTerms) {\n const termLower = term.toLowerCase();\n\n // Check name matches\n if (searchFields.includes('name')) {\n if (nameLower === termLower) {\n totalScore += EXACT_NAME_WEIGHT;\n if (!matchedOn.includes('name')) matchedOn.push('name');\n } else if (nameLower.includes(termLower)) {\n totalScore += PARTIAL_NAME_WEIGHT;\n if (!matchedOn.includes('name')) matchedOn.push('name');\n }\n }\n\n // Check description matches\n if (searchFields.includes('description') && descLower.includes(termLower)) {\n totalScore += DESCRIPTION_WEIGHT;\n if (!matchedOn.includes('description')) matchedOn.push('description');\n }\n\n // Check schema/arguments matches\n if (searchFields.includes('schema') && schemaTextLower.includes(termLower)) {\n totalScore += SCHEMA_WEIGHT;\n const fieldName = capability.type === 'tool' ? 'inputSchema' : capability.type === 'prompt' ? 'arguments' : 'uri';\n if (!matchedOn.includes(fieldName)) matchedOn.push(fieldName);\n }\n\n // Check server name matches\n if (searchFields.includes('server') && serverLower.includes(termLower)) {\n totalScore += SERVER_WEIGHT;\n if (!matchedOn.includes('server')) matchedOn.push('server');\n }\n }\n\n // Normalize score to 0-1 range based on number of terms\n const normalizedScore = queryTerms.length > 0 ? Math.min(1, totalScore / queryTerms.length) : 0;\n\n return { score: normalizedScore, matchedOn };\n}\n\n/**\n * Search for capabilities matching a query string\n */\nexport function searchCapabilities(index: CapabilityIndex, query: string, options: SearchOptions = {}): SearchResponse {\n const { types = DEFAULT_TYPES, servers, searchFields = DEFAULT_SEARCH_FIELDS, limit = DEFAULT_LIMIT, threshold = DEFAULT_THRESHOLD } = options;\n\n // Tokenize query into search terms\n const queryTerms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((term) => term.length > 0);\n\n // If empty query, return empty results\n if (queryTerms.length === 0) {\n return { query, results: [], total: 0 };\n }\n\n // Filter and score capabilities\n const scoredResults: Array<{ capability: IndexedCapability; score: number; matchedOn: string[] }> = [];\n\n for (const capability of index.capabilities) {\n // Filter by type\n if (!types.includes(capability.type)) {\n continue;\n }\n\n // Filter by server\n if (servers && servers.length > 0 && !servers.includes(capability.server)) {\n continue;\n }\n\n // Score the capability\n const { score, matchedOn } = scoreCapability(capability, queryTerms, searchFields);\n\n // Apply threshold filter\n if (score >= threshold && matchedOn.length > 0) {\n scoredResults.push({ capability, score, matchedOn });\n }\n }\n\n // Sort by score descending\n scoredResults.sort((a, b) => b.score - a.score);\n\n // Get total before limiting\n const total = scoredResults.length;\n\n // Apply limit and transform to SearchResult\n const results: SearchResult[] = scoredResults.slice(0, limit).map(({ capability, score, matchedOn }) => ({\n type: capability.type,\n server: capability.server,\n name: capability.name,\n description: capability.description,\n matchedOn,\n score,\n }));\n\n return { query, results, total };\n}\n\n/**\n * Convenience function to search directly from connected clients\n * Builds index and performs search in one call\n */\nexport async function search(clients: Map<string, CapabilityClient>, query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n const index = await buildCapabilityIndex(clients);\n return searchCapabilities(index, query, options);\n}\n"],"names":["buildCapabilityIndex","search","searchCapabilities","DEFAULT_LIMIT","DEFAULT_THRESHOLD","DEFAULT_TYPES","DEFAULT_SEARCH_FIELDS","extractSchemaText","inputSchema","schema","parts","description","push","properties","Object","entries","propName","prop","join","extractArgumentsText","args","Array","isArray","map","arg","name","clients","capabilities","servers","serverName","client","toolsResult","promptsResult","resourcesResult","tool","prompt","resource","Promise","all","listTools","catch","listPrompts","listResources","tools","type","server","schemaText","prompts","argumentsText","arguments","resources","uri","mimeType","indexedAt","Date","scoreCapability","capability","queryTerms","searchFields","matchedOn","totalScore","EXACT_NAME_WEIGHT","PARTIAL_NAME_WEIGHT","DESCRIPTION_WEIGHT","SCHEMA_WEIGHT","SERVER_WEIGHT","nameLower","toLowerCase","descLower","serverLower","schemaTextLower","term","termLower","includes","fieldName","normalizedScore","length","Math","min","score","index","query","options","types","limit","threshold","split","filter","results","total","scoredResults","sort","a","b","slice"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/search/search.ts"],"sourcesContent":["/**\n * Search implementation for MCP capability discovery\n *\n * Provides text-based search across tools, prompts, and resources\n * from connected MCP servers.\n */\n\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { PromptArgument } from '../connection/types.ts';\nimport type { CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult } from './types.ts';\n\nexport type CapabilityClient = Pick<Client, 'listTools' | 'listPrompts' | 'listResources'>;\n\nconst DEFAULT_LIMIT = 20;\nconst DEFAULT_THRESHOLD = 0;\nconst DEFAULT_TYPES: CapabilityType[] = ['tool', 'prompt', 'resource'];\nconst DEFAULT_SEARCH_FIELDS: SearchField[] = ['name', 'description', 'schema'];\n\n/**\n * Extract searchable text from a JSON Schema's property descriptions\n */\nfunction extractSchemaText(inputSchema: unknown): string {\n if (!inputSchema || typeof inputSchema !== 'object') {\n return '';\n }\n\n const schema = inputSchema as {\n properties?: Record<string, { description?: string; name?: string }>;\n description?: string;\n };\n\n const parts: string[] = [];\n\n // Add schema-level description if present\n if (schema.description) {\n parts.push(schema.description);\n }\n\n // Add property names and descriptions\n if (schema.properties) {\n for (const [propName, prop] of Object.entries(schema.properties)) {\n parts.push(propName);\n if (prop && typeof prop === 'object' && prop.description) {\n parts.push(prop.description);\n }\n }\n }\n\n return parts.join(' ');\n}\n\n/**\n * Extract searchable text from prompt arguments\n */\nfunction extractArgumentsText(args: PromptArgument[] | undefined): string {\n if (!args || !Array.isArray(args)) {\n return '';\n }\n\n return args\n .map((arg) => {\n const parts = [arg.name];\n if (arg.description) {\n parts.push(arg.description);\n }\n return parts.join(' ');\n })\n .join(' ');\n}\n\n/**\n * Build an index of capabilities from connected MCP clients\n */\nexport async function buildCapabilityIndex(clients: Map<string, CapabilityClient>): Promise<CapabilityIndex> {\n const capabilities: IndexedCapability[] = [];\n const servers: string[] = [];\n\n for (const [serverName, client] of clients) {\n servers.push(serverName);\n\n // Fetch all capabilities in parallel, handling errors gracefully\n const [toolsResult, promptsResult, resourcesResult] = await Promise.all([client.listTools().catch(() => null), client.listPrompts().catch(() => null), client.listResources().catch(() => null)]);\n\n // Index tools\n if (toolsResult?.tools) {\n for (const tool of toolsResult.tools) {\n capabilities.push({\n type: 'tool',\n server: serverName,\n name: tool.name,\n description: tool.description,\n schemaText: extractSchemaText(tool.inputSchema),\n } satisfies IndexedTool);\n }\n }\n\n // Index prompts\n if (promptsResult?.prompts) {\n for (const prompt of promptsResult.prompts) {\n capabilities.push({\n type: 'prompt',\n server: serverName,\n name: prompt.name,\n description: prompt.description,\n argumentsText: extractArgumentsText(prompt.arguments as PromptArgument[] | undefined),\n arguments: prompt.arguments as PromptArgument[] | undefined,\n } satisfies IndexedPrompt);\n }\n }\n\n // Index resources\n if (resourcesResult?.resources) {\n for (const resource of resourcesResult.resources) {\n capabilities.push({\n type: 'resource',\n server: serverName,\n name: resource.name,\n description: resource.description,\n uri: resource.uri,\n mimeType: resource.mimeType,\n } satisfies IndexedResource);\n }\n }\n }\n\n return {\n capabilities,\n servers,\n indexedAt: new Date(),\n };\n}\n\n/**\n * Calculate relevance score and matched fields for a capability against a query\n */\nfunction scoreCapability(capability: IndexedCapability, queryTerms: string[], searchFields: SearchField[]): { score: number; matchedOn: string[] } {\n const matchedOn: string[] = [];\n let totalScore = 0;\n\n // Weights for different match types\n const EXACT_NAME_WEIGHT = 1.0;\n const PARTIAL_NAME_WEIGHT = 0.8;\n const DESCRIPTION_WEIGHT = 0.6;\n const SCHEMA_WEIGHT = 0.4;\n const SERVER_WEIGHT = 0.3;\n\n const nameLower = capability.name.toLowerCase();\n const descLower = (capability.description || '').toLowerCase();\n const serverLower = capability.server.toLowerCase();\n\n // Get schema/arguments text based on type\n let schemaTextLower = '';\n if (capability.type === 'tool') {\n schemaTextLower = capability.schemaText.toLowerCase();\n } else if (capability.type === 'prompt') {\n schemaTextLower = capability.argumentsText.toLowerCase();\n } else if (capability.type === 'resource') {\n // For resources, include URI and mimeType in searchable text\n schemaTextLower = `${capability.uri} ${capability.mimeType || ''}`.toLowerCase();\n }\n\n for (const term of queryTerms) {\n const termLower = term.toLowerCase();\n\n // Check name matches\n if (searchFields.includes('name')) {\n if (nameLower === termLower) {\n totalScore += EXACT_NAME_WEIGHT;\n if (!matchedOn.includes('name')) matchedOn.push('name');\n } else if (nameLower.includes(termLower)) {\n totalScore += PARTIAL_NAME_WEIGHT;\n if (!matchedOn.includes('name')) matchedOn.push('name');\n }\n }\n\n // Check description matches\n if (searchFields.includes('description') && descLower.includes(termLower)) {\n totalScore += DESCRIPTION_WEIGHT;\n if (!matchedOn.includes('description')) matchedOn.push('description');\n }\n\n // Check schema/arguments matches\n if (searchFields.includes('schema') && schemaTextLower.includes(termLower)) {\n totalScore += SCHEMA_WEIGHT;\n const fieldName = capability.type === 'tool' ? 'inputSchema' : capability.type === 'prompt' ? 'arguments' : 'uri';\n if (!matchedOn.includes(fieldName)) matchedOn.push(fieldName);\n }\n\n // Check server name matches\n if (searchFields.includes('server') && serverLower.includes(termLower)) {\n totalScore += SERVER_WEIGHT;\n if (!matchedOn.includes('server')) matchedOn.push('server');\n }\n }\n\n // Normalize score to 0-1 range based on number of terms\n const normalizedScore = queryTerms.length > 0 ? Math.min(1, totalScore / queryTerms.length) : 0;\n\n return { score: normalizedScore, matchedOn };\n}\n\n/**\n * Search for capabilities matching a query string\n */\nexport function searchCapabilities(index: CapabilityIndex, query: string, options: SearchOptions = {}): SearchResponse {\n const { types = DEFAULT_TYPES, servers, searchFields = DEFAULT_SEARCH_FIELDS, limit = DEFAULT_LIMIT, threshold = DEFAULT_THRESHOLD } = options;\n\n // Tokenize query into search terms\n const queryTerms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((term) => term.length > 0);\n\n // If empty query, return empty results\n if (queryTerms.length === 0) {\n return { query, results: [], total: 0 };\n }\n\n // Filter and score capabilities\n const scoredResults: Array<{ capability: IndexedCapability; score: number; matchedOn: string[] }> = [];\n\n for (const capability of index.capabilities) {\n // Filter by type\n if (!types.includes(capability.type)) {\n continue;\n }\n\n // Filter by server\n if (servers && servers.length > 0 && !servers.includes(capability.server)) {\n continue;\n }\n\n // Score the capability\n const { score, matchedOn } = scoreCapability(capability, queryTerms, searchFields);\n\n // Apply threshold filter\n if (score >= threshold && matchedOn.length > 0) {\n scoredResults.push({ capability, score, matchedOn });\n }\n }\n\n // Sort by score descending\n scoredResults.sort((a, b) => b.score - a.score);\n\n // Get total before limiting\n const total = scoredResults.length;\n\n // Apply limit and transform to SearchResult\n const results: SearchResult[] = scoredResults.slice(0, limit).map(({ capability, score, matchedOn }) => ({\n type: capability.type,\n server: capability.server,\n name: capability.name,\n description: capability.description,\n matchedOn,\n score,\n }));\n\n return { query, results, total };\n}\n\n/**\n * Convenience function to search directly from connected clients\n * Builds index and performs search in one call\n */\nexport async function search(clients: Map<string, CapabilityClient>, query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n const index = await buildCapabilityIndex(clients);\n return searchCapabilities(index, query, options);\n}\n"],"names":["buildCapabilityIndex","search","searchCapabilities","DEFAULT_LIMIT","DEFAULT_THRESHOLD","DEFAULT_TYPES","DEFAULT_SEARCH_FIELDS","extractSchemaText","inputSchema","schema","parts","description","push","properties","Object","entries","propName","prop","join","extractArgumentsText","args","Array","isArray","map","arg","name","clients","capabilities","servers","serverName","client","toolsResult","promptsResult","resourcesResult","tool","prompt","resource","Promise","all","listTools","catch","listPrompts","listResources","tools","type","server","schemaText","prompts","argumentsText","arguments","resources","uri","mimeType","indexedAt","Date","scoreCapability","capability","queryTerms","searchFields","matchedOn","totalScore","EXACT_NAME_WEIGHT","PARTIAL_NAME_WEIGHT","DESCRIPTION_WEIGHT","SCHEMA_WEIGHT","SERVER_WEIGHT","nameLower","toLowerCase","descLower","serverLower","schemaTextLower","term","termLower","includes","fieldName","normalizedScore","length","Math","min","score","index","query","options","types","limit","threshold","split","filter","results","total","scoredResults","sort","a","b","slice"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/search/types.ts"],"sourcesContent":["/**\n * Search types for MCP capability discovery\n *\n * Enables agents to discover tools, prompts, and resources without\n * loading full schemas into context.\n */\n\nimport type { PromptArgument } from '../connection/types.ts';\n\n/**\n * Types of MCP capabilities that can be searched\n */\nexport type CapabilityType = 'tool' | 'prompt' | 'resource';\n\n/**\n * Fields that can be searched within capabilities\n */\nexport type SearchField = 'name' | 'description' | 'schema' | 'server';\n\n/**\n * Options for configuring search behavior\n */\nexport interface SearchOptions {\n /**\n * Filter to specific capability types\n * @default ['tool', 'prompt', 'resource']\n */\n types?: CapabilityType[];\n\n /**\n * Filter to specific servers by name\n * @default all servers in config\n */\n servers?: string[];\n\n /**\n * Which fields to search within\n * @default ['name', 'description', 'schema']\n */\n searchFields?: SearchField[];\n\n /**\n * Maximum number of results to return\n * @default 20\n */\n limit?: number;\n\n /**\n * Minimum relevance score (0-1) for results\n * @default 0\n */\n threshold?: number;\n}\n\n/**\n * A single search result representing a matched capability\n */\nexport interface SearchResult {\n /** The type of capability */\n type: CapabilityType;\n\n /** The server that provides this capability */\n server: string;\n\n /** The name of the capability */\n name: string;\n\n /** Human-readable description (may be truncated) */\n description: string | undefined;\n\n /** Which fields matched the search query */\n matchedOn: string[];\n\n /** Relevance score from 0 (low) to 1 (high) */\n score: number;\n}\n\n/**\n * Complete search response\n */\nexport interface SearchResponse {\n /** The original search query */\n query: string;\n\n /** Matching results, sorted by relevance */\n results: SearchResult[];\n\n /** Total number of matches before limit was applied */\n total: number;\n}\n\n/**\n * Internal representation of a tool for indexing\n */\nexport interface IndexedTool {\n type: 'tool';\n server: string;\n name: string;\n description: string | undefined;\n /** Flattened searchable text from inputSchema property descriptions */\n schemaText: string;\n}\n\n/**\n * Internal representation of a prompt for indexing\n */\nexport interface IndexedPrompt {\n type: 'prompt';\n server: string;\n name: string;\n description: string | undefined;\n /** Flattened searchable text from arguments */\n argumentsText: string;\n arguments: PromptArgument[] | undefined;\n}\n\n/**\n * Internal representation of a resource for indexing\n */\nexport interface IndexedResource {\n type: 'resource';\n server: string;\n name: string;\n description: string | undefined;\n uri: string;\n mimeType: string | undefined;\n}\n\n/**\n * Union of all indexed capability types\n */\nexport type IndexedCapability = IndexedTool | IndexedPrompt | IndexedResource;\n\n/**\n * Index containing all capabilities from connected servers\n */\nexport interface CapabilityIndex {\n /** All indexed capabilities */\n capabilities: IndexedCapability[];\n\n /** Servers that were indexed */\n servers: string[];\n\n /** When the index was created */\n indexedAt: Date;\n}\n"],"names":[],"mappings":"AAAA;;;;;CAKC"}
|
|
@@ -318,6 +318,10 @@ function spawnProcess(opts) {
|
|
|
318
318
|
shell: shell
|
|
319
319
|
};
|
|
320
320
|
var child = (0, _child_process.spawn)(command, args, spawnOpts);
|
|
321
|
+
// Pipe stdio if not inherited
|
|
322
|
+
if (child.stderr) child.stderr.on('data', function(chunk) {
|
|
323
|
+
_process.stderr.write(chunk);
|
|
324
|
+
});
|
|
321
325
|
// Attach lifecycle logging
|
|
322
326
|
child.on('exit', function(code, sig) {
|
|
323
327
|
return _loggerts.logger.info("[".concat(name, "] exited (code=").concat(code, ", signal=").concat(sig || 'none', ")"));
|