/**
 * HTTP transport for MCP clients.
 * Supports the VCV Rack MCP server and other HTTP-based MCP servers.
 */
export class McpHttpClient {
    name;
    config;
    baseUrl;
    connected = false;
    sessionId = null;
    constructor(name, config) {
        this.name = name;
        this.config = config;
        this.baseUrl = config.url.replace(/\/$/, '');
    }
    getCandidateUrls() {
        const candidates = [this.baseUrl];
        if (!this.baseUrl.endsWith('/mcp')) {
            candidates.push(`${this.baseUrl}/mcp`);
        }
        return candidates;
    }
    /**
     * Connect to the HTTP MCP server
     */
    async connect() {
        if (this.config.disabled) {
            throw new Error(`MCP server '${this.name}' is disabled`);
        }
        if (!this.config.url) {
            throw new Error(`MCP server '${this.name}' requires a URL for HTTP transport`);
        }
        let lastError = null;
        for (const candidateUrl of this.getCandidateUrls()) {
            try {
                this.baseUrl = candidateUrl;
                this.sessionId = null;
                await this.request('initialize', {
                    protocolVersion: '2024-11-05',
                    capabilities: {},
                    clientInfo: {
                        name: 'clawx-pi-agent',
                        version: '0.9.20',
                    },
                }, true);
                this.connected = true;
                console.log(`[MCP ${this.name}] HTTP connection established to ${this.baseUrl}, session: ${this.sessionId}`);
                return;
            }
            catch (error) {
                lastError = error;
            }
        }
        throw new Error(`Failed to connect to MCP server ${this.name}: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
    }
    /**
     * Disconnect from the server
     */
    async disconnect() {
        this.connected = false;
        // HTTP connections don't need explicit disconnection
    }
    /**
     * List tools from the MCP server
     */
    async listTools() {
        const result = await this.request('tools/list', {});
        return result.tools || [];
    }
    /**
     * Call a tool on the MCP server
     */
    async callTool(toolName, params) {
        return await this.request('tools/call', {
            name: toolName,
            arguments: params,
        });
    }
    /**
     * Check if client is connected
     */
    isConnected() {
        return this.connected;
    }
    /**
     * Send a JSON-RPC request over HTTP
     */
    /**
     * Send a JSON-RPC request over HTTP
     */
    async request(method, params, isInit = false) {
        const id = Math.floor(Math.random() * 1000000);
        const request = {
            jsonrpc: '2.0',
            id,
            method,
            params,
        };
        try {
            // Build headers
            const headers = {
                'Content-Type': 'application/json',
                'Accept': 'application/json,text/event-stream',
            };
            // Add session ID if we have one (for Penpot)
            if (this.sessionId) {
                headers['mcp-session-id'] = this.sessionId;
            }
            const response = await fetch(this.baseUrl, {
                method: 'POST',
                headers,
                body: JSON.stringify(request),
            });
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            // Check if response is SSE
            const contentType = response.headers.get('content-type') || '';
            const responseText = await response.text();
            // Store session ID from response headers (for Penpot)
            const newSessionId = response.headers.get('mcp-session-id');
            if (newSessionId) {
                this.sessionId = newSessionId;
            }
            if (contentType.includes('text/event-stream') || responseText.startsWith('event:')) {
                // Parse SSE response
                const lines = responseText.split('\n');
                for (const line of lines) {
                    if (line.startsWith('data: ')) {
                        try {
                            const data = JSON.parse(line.substring(6));
                            if (data.error) {
                                throw new Error(`MCP error: ${data.error.message}`);
                            }
                            return data.result;
                        }
                        catch (e) {
                            // Not JSON, continue
                        }
                    }
                }
                throw new Error('No valid JSON data found in SSE response');
            }
            else {
                // Regular JSON response
                const data = JSON.parse(responseText);
                if (data.error) {
                    throw new Error(`MCP error: ${data.error.message}`);
                }
                return data.result;
            }
        }
        catch (error) {
            if (error instanceof Error && error.message.includes('Failed to fetch')) {
                this.connected = false;
                throw new Error(`MCP server '${this.name}' is unreachable`);
            }
            throw error;
        }
    }
}
//# sourceMappingURL=http-client.js.map