import { toErrorMessage } from '../../utils/error.js';
import { generateMCPServerId } from '../../utils/id.js';
import { getRootLogger } from '../../utils/logger.js';
export class MCPServer {
    id;
    config;
    toolProvider;
    resourceProvider;
    transport = null;
    running = false;
    connectedClients = 0;
    startedAt;
    log;
    constructor(config, toolProvider, resourceProvider) {
        this.id = config.id ?? generateMCPServerId();
        this.config = config;
        this.toolProvider = toolProvider;
        this.resourceProvider = resourceProvider;
        this.log = getRootLogger().child({ component: 'MCPServer', serverId: this.id });
    }
    async start(transport) {
        if (this.running) {
            throw new Error(`MCPServer "${this.config.name}" is already running`);
        }
        this.transport = transport;
        transport.onMessage((msg) => this.handleRequest(msg));
        transport.onClose(() => {
            this.connectedClients = Math.max(0, this.connectedClients - 1);
            this.log.info('MCP client disconnected');
        });
        transport.onError((err) => {
            this.log.error('MCP server transport error', { error: err.message });
        });
        await transport.connect();
        this.running = true;
        this.startedAt = Date.now();
        this.log.info(`MCPServer "${this.config.name}" started`);
    }
    async stop() {
        if (!this.running)
            return;
        this.running = false;
        if (this.transport) {
            await this.transport.close();
            this.transport = null;
        }
        this.log.info(`MCPServer "${this.config.name}" stopped`);
    }
    isRunning() {
        return this.running;
    }
    getState() {
        return {
            id: this.id,
            name: this.config.name,
            running: this.running,
            connectedClients: this.connectedClients,
            startedAt: this.startedAt,
        };
    }
    async handleRequest(message) {
        if (message.id === undefined || !message.method)
            return;
        try {
            const result = await this.dispatch(message.method, message.params ?? {});
            await this.respond(message.id, result);
        }
        catch (err) {
            await this.respondError(message.id, -32603, toErrorMessage(err));
        }
    }
    async dispatch(method, params) {
        switch (method) {
            case 'initialize':
                return this.handleInitialize();
            case 'tools/list':
                return this.handleToolsList();
            case 'tools/call':
                return this.handleToolsCall(params);
            case 'resources/list':
                return this.handleResourcesList();
            case 'resources/read':
                return this.handleResourcesRead(params);
            case 'ping':
                return {};
            default:
                throw new Error(`Unknown method: ${method}`);
        }
    }
    handleInitialize() {
        this.connectedClients++;
        const capabilities = {
            tools: { listChanged: false },
            ...this.config.capabilities,
        };
        if (this.resourceProvider) {
            capabilities.resources = { subscribe: false, listChanged: false };
        }
        return {
            protocolVersion: '2024-11-05',
            capabilities,
            serverInfo: {
                name: this.config.name,
                version: this.config.version,
            },
        };
    }
    handleToolsList() {
        return { tools: this.toolProvider.listTools() };
    }
    async handleToolsCall(params) {
        const name = params.name;
        const args = (params.arguments ?? {});
        if (!name) {
            return {
                content: [{ type: 'text', text: 'Missing tool name' }],
                isError: true,
            };
        }
        return this.toolProvider.callTool(name, args);
    }
    handleResourcesList() {
        if (!this.resourceProvider) {
            return { resources: [] };
        }
        return { resources: this.resourceProvider.listResources() };
    }
    async handleResourcesRead(params) {
        const uri = params.uri;
        if (!uri) {
            throw new Error('Missing resource URI');
        }
        if (!this.resourceProvider) {
            throw new Error('Resource provider not configured');
        }
        return { contents: await this.resourceProvider.readResource(uri) };
    }
    async respond(id, result) {
        if (!this.transport)
            return;
        await this.transport.send({
            jsonrpc: '2.0',
            id,
            result,
        });
    }
    async respondError(id, code, message) {
        if (!this.transport)
            return;
        await this.transport.send({
            jsonrpc: '2.0',
            id,
            error: { code, message },
        });
    }
}
//# sourceMappingURL=server.js.map