/**
 * MCP Server Manager.
 *
 * Manages lifecycle of multiple MCP server connections:
 *   - Auto-connect on startup from config
 *   - Tool/resource aggregation across servers
 *   - Clean shutdown on exit
 *   - Provider interface for MCPTool
 */
import { MCPClient } from './client.js';
import { loadMCPConfig } from './config.js';
export class MCPManager {
    clients = new Map();
    states = new Map();
    /** Connect to all configured MCP servers. Returns summary of results. */
    async connectAll(cwd) {
        const configs = loadMCPConfig(cwd);
        if (configs.size === 0)
            return [];
        const results = [];
        const promises = [];
        for (const [name, config] of configs) {
            promises.push(this.connectOne(name, config).then((state) => { results.push(state); }));
        }
        await Promise.allSettled(promises);
        return results;
    }
    /** Connect to a single MCP server by name and config. */
    async connectOne(name, config) {
        // Disconnect existing if reconnecting
        await this.disconnectOne(name);
        const state = {
            name,
            config,
            status: 'connecting',
            tools: [],
            resources: [],
        };
        this.states.set(name, state);
        const client = new MCPClient(name, config);
        this.clients.set(name, client);
        // Capture stderr for diagnostics
        client.on('stderr', (data) => {
            const trimmed = data.trim();
            if (trimmed)
                state.error = (state.error ?? '') + trimmed + '\n';
        });
        client.on('exit', () => {
            state.status = 'disconnected';
        });
        try {
            const initResult = await client.connect();
            state.status = 'connected';
            state.capabilities = initResult.capabilities;
            state.serverInfo = initResult.serverInfo;
            // Pre-fetch tools and resources
            if (initResult.capabilities.tools) {
                try {
                    state.tools = await client.listTools();
                }
                catch {
                    // Server may not support listing
                }
            }
            if (initResult.capabilities.resources) {
                try {
                    state.resources = await client.listResources();
                }
                catch {
                    // Server may not support listing
                }
            }
        }
        catch (err) {
            state.status = 'error';
            state.error = err instanceof Error ? err.message : String(err);
        }
        return state;
    }
    /** Disconnect a single server. */
    async disconnectOne(name) {
        const client = this.clients.get(name);
        if (client) {
            await client.disconnect();
            this.clients.delete(name);
        }
        this.states.delete(name);
    }
    /** Disconnect all servers. Call on app exit. */
    async disconnectAll() {
        const promises = Array.from(this.clients.keys()).map((n) => this.disconnectOne(n));
        await Promise.allSettled(promises);
    }
    // ─── MCPClientProvider interface ──────────────────────────────────
    isConnected(serverName) {
        return this.states.get(serverName)?.status === 'connected';
    }
    async callTool(serverName, toolName, args) {
        const client = this.clients.get(serverName);
        if (!client?.connected) {
            return { content: `MCP server "${serverName}" not connected.`, isError: true };
        }
        const result = await client.callTool(toolName, args);
        // Flatten content to text
        const text = result.content
            .map((c) => {
            if (c.type === 'text')
                return c.text ?? '';
            if (c.type === 'resource' && c.resource?.text)
                return c.resource.text;
            return `[${c.type} content]`;
        })
            .join('\n');
        return { content: text, isError: result.isError };
    }
    // ─── MCPResourceProvider interface ──────────────────────────────
    async listResources(serverName) {
        const state = this.states.get(serverName);
        if (state?.status !== 'connected')
            return [];
        return state.resources.map((r) => ({ uri: r.uri, name: r.name, description: r.description }));
    }
    // ─── MCPReadResourceProvider interface (readResource below) ────
    // ─── Query methods ────────────────────────────────────────────────
    /** Get state of all servers. */
    getServers() {
        return Array.from(this.states.values());
    }
    /** Get state of a specific server. */
    getServer(name) {
        return this.states.get(name);
    }
    /** Get all tools across all connected servers, prefixed with server name. */
    getAllTools() {
        const result = [];
        for (const state of this.states.values()) {
            if (state.status !== 'connected')
                continue;
            for (const tool of state.tools) {
                result.push({ server: state.name, tool });
            }
        }
        return result;
    }
    /** Get all resources across all connected servers. */
    getAllResources() {
        const result = [];
        for (const state of this.states.values()) {
            if (state.status !== 'connected')
                continue;
            for (const resource of state.resources) {
                result.push({ server: state.name, resource });
            }
        }
        return result;
    }
    /** Read a resource from a specific server. */
    async readResource(serverName, uri) {
        const client = this.clients.get(serverName);
        if (!client?.connected)
            throw new Error(`MCP server "${serverName}" not connected`);
        const contents = await client.readResource(uri);
        const text = contents.map((c) => c.text ?? '[binary]').join('\n');
        const mimeType = contents[0]?.mimeType;
        return { content: text, mimeType };
    }
    /** Get server count by status. */
    summary() {
        let connected = 0;
        let error = 0;
        for (const state of this.states.values()) {
            if (state.status === 'connected')
                connected++;
            else if (state.status === 'error')
                error++;
        }
        return { total: this.states.size, connected, error };
    }
}
//# sourceMappingURL=manager.js.map