import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
import { execFileSync } from 'child_process';
import { accessSync, constants as fsConstants } from 'fs';
/**
 * Installation instructions for common MCP server dependencies
 */
const COMMAND_INSTALL_HINTS = {
    uvx: `'uvx' is part of the 'uv' Python package manager.

Install uv:
  • macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh
  • Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
  • pip: pip install uv
  • Homebrew: brew install uv

After installation, restart your terminal and try again.`,
    npx: `'npx' is part of Node.js.

Install Node.js from: https://nodejs.org/
Or use a version manager like nvm, fnm, or volta.`,
    node: `'node' is not installed.

Install Node.js from: https://nodejs.org/
Or use a version manager like nvm, fnm, or volta.`,
    python: `'python' is not installed.

Install Python from: https://python.org/downloads/
Or use a version manager like pyenv.`,
    python3: `'python3' is not installed.

Install Python from: https://python.org/downloads/
Or use a version manager like pyenv.`,
};
/**
 * Checks if a command exists in the system PATH or as an executable path.
 * Uses execFileSync with separate arguments to prevent shell injection.
 * Handles both PATH lookups and direct path references (./bin/cmd, /usr/bin/cmd).
 */
function commandExists(command) {
    // Check if command is a path (contains path separators)
    if (command.includes('/') || command.includes('\\')) {
        try {
            // Check if file exists and is executable
            accessSync(command, fsConstants.X_OK);
            return true;
        }
        catch {
            return false;
        }
    }
    // PATH lookup using which/where
    try {
        const checkCmd = process.platform === 'win32' ? 'where' : 'which';
        execFileSync(checkCmd, [command], { stdio: 'ignore' });
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Gets installation hint for a missing command
 */
function getInstallHint(command) {
    return (COMMAND_INSTALL_HINTS[command] ||
        `'${command}' is not installed or not in your PATH.`);
}
/**
 * Factory for creating MCP client transports based on server configuration
 */
export class TransportFactory {
    /**
     * Creates a transport instance for the given MCP server configuration
     */
    static createTransport(server) {
        switch (server.transport) {
            case 'stdio':
                return this.createStdioTransport(server);
            case 'websocket':
                return this.createWebSocketTransport(server);
            case 'http':
                return this.createHTTPTransport(server);
            default: {
                const _exhaustiveCheck = server.transport;
                throw new Error(`Unsupported transport type: ${_exhaustiveCheck}`);
            }
        }
    }
    /**
     * Creates a stdio transport for local MCP servers
     */
    static createStdioTransport(server) {
        if (!server.command) {
            throw new Error(`MCP server "${server.name}" missing command for stdio transport`);
        }
        // For uvx commands, prepend --native-tls to use system certificates
        // This fixes TLS issues in corporate proxy environments (issue #272)
        let args = server.args || [];
        if (server.command === 'uvx' && !args.includes('--native-tls')) {
            args = ['--native-tls', ...args];
        }
        return new StdioClientTransport({
            command: server.command,
            args,
            env: server.env
                ? { ...process.env, ...server.env }
                : undefined,
        });
    }
    /**
     * Creates a WebSocket transport for remote MCP servers
     */
    static createWebSocketTransport(server) {
        if (!server.url) {
            throw new Error(`MCP server "${server.name}" missing URL for websocket transport`);
        }
        const url = new URL(server.url);
        // Validate WebSocket URL
        if (!url.protocol.startsWith('ws')) {
            throw new Error(`Invalid WebSocket URL protocol: ${url.protocol}. Expected ws:// or wss://`);
        }
        return new WebSocketClientTransport(url);
    }
    /**
     * Creates an HTTP transport for remote MCP servers
     */
    static createHTTPTransport(server) {
        if (!server.url) {
            throw new Error(`MCP server "${server.name}" missing URL for http transport`);
        }
        const url = new URL(server.url);
        // Validate HTTP URL
        if (!url.protocol.startsWith('http')) {
            throw new Error(`Invalid HTTP URL protocol: ${url.protocol}. Expected http:// or https://`);
        }
        // Create transport with headers if provided
        const transportOptions = server.headers
            ? { requestInit: { headers: server.headers } }
            : undefined;
        return new StreamableHTTPClientTransport(url, transportOptions);
    }
    /**
     * Validates the server configuration for the given transport type
     */
    static validateServerConfig(server) {
        const errors = [];
        switch (server.transport) {
            case 'stdio':
                if (!server.command) {
                    errors.push('stdio transport requires a command');
                }
                else if (!commandExists(server.command)) {
                    const hint = getInstallHint(server.command);
                    errors.push(`Command '${server.command}' not found.\n\n${hint}`);
                }
                break;
            case 'websocket':
                if (!server.url) {
                    errors.push('websocket transport requires a URL');
                }
                else {
                    try {
                        const url = new URL(server.url);
                        if (!url.protocol.startsWith('ws')) {
                            errors.push('websocket URL must use ws:// or wss:// protocol'); // nosemgrep
                        }
                    }
                    catch {
                        errors.push('websocket URL is invalid');
                    }
                }
                break;
            case 'http':
                if (!server.url) {
                    errors.push('http transport requires a URL');
                }
                else {
                    try {
                        const url = new URL(server.url);
                        if (!url.protocol.startsWith('http')) {
                            errors.push('http URL must use http:// or https:// protocol');
                        }
                    }
                    catch {
                        errors.push('http URL is invalid');
                    }
                }
                // Headers are now supported, so we don't need to warn about them being ignored
                // The actual warning logic has been moved to the createHTTPTransport method
                break;
        }
        return {
            valid: errors.length === 0,
            errors,
        };
    }
    /**
     * Gets transport-specific configuration tips for users
     */
    static getTransportTips(transportType) {
        switch (transportType) {
            case 'stdio':
                return [
                    'Stdio transport spawns a local process',
                    'Requires a command and optional arguments',
                    'Environment variables can be passed to the process',
                    'Best for local MCP servers and tools',
                ];
            case 'websocket':
                return [
                    'WebSocket transport connects to remote MCP servers',
                    'Requires a ws:// or wss:// URL', // nosemgrep
                    'Supports real-time bidirectional communication',
                    'Best for interactive remote services',
                ];
            case 'http':
                return [
                    'HTTP transport connects to remote MCP servers',
                    'Requires an http:// or https:// URL',
                    'Uses the StreamableHTTP protocol from MCP specification',
                    'Best for stateless remote services and APIs',
                    'Custom headers are supported for authentication',
                ];
            default:
                return ['Unknown transport type'];
        }
    }
}
//# sourceMappingURL=transport-factory.js.map