/**
 * Simple MCP implementation for Pi Agent version
 * Actually tries to connect to MCP servers using the SDK
 */
import { Type } from "@sinclair/typebox";
import { log } from "../utils/logger.js";
import { spawn } from 'node:child_process';
class SimpleMcpClient {
    config;
    nextId = 1;
    child = null;
    pending = new Map();
    constructor(config) {
        this.config = config;
    }
    async connect() {
        return new Promise((resolve, reject) => {
            this.child = spawn(this.config.command, this.config.args || [], {
                cwd: this.config.cwd,
                env: { ...process.env, ...(this.config.env || {}) },
                stdio: ['pipe', 'pipe', 'pipe'],
            });
            let buffer = Buffer.alloc(0);
            let initialized = false;
            const cleanup = () => {
                if (this.child) {
                    this.child.kill();
                    this.child = null;
                }
                for (const waiter of this.pending.values()) {
                    waiter.reject(new Error('MCP server disconnected'));
                }
                this.pending.clear();
            };
            this.child.on('error', (error) => {
                cleanup();
                reject(error);
            });
            this.child.on('exit', () => {
                cleanup();
            });
            this.child.stdout.on('data', (chunk) => {
                buffer = Buffer.concat([buffer, chunk]);
                // Parse JSON-RPC messages
                while (true) {
                    const headerEnd = buffer.indexOf('\r\n\r\n');
                    if (headerEnd === -1)
                        break;
                    const headerText = buffer.slice(0, headerEnd).toString('utf8');
                    const lengthMatch = headerText.match(/Content-Length:\s*(\d+)/i);
                    if (!lengthMatch) {
                        cleanup();
                        break;
                    }
                    const contentLength = Number(lengthMatch[1]);
                    const totalLength = headerEnd + 4 + contentLength;
                    if (buffer.length < totalLength)
                        break;
                    const body = buffer.slice(headerEnd + 4, totalLength).toString('utf8');
                    buffer = buffer.slice(totalLength);
                    try {
                        const message = JSON.parse(body);
                        if (typeof message.id === 'number' && this.pending.has(message.id)) {
                            const waiter = this.pending.get(message.id);
                            this.pending.delete(message.id);
                            if (message.error) {
                                waiter.reject(new Error(message.error.message));
                            }
                            else {
                                waiter.resolve(message.result);
                            }
                        }
                        // Handle initialization response
                        if (message.id === 1 && message.result) { // First request is initialize
                            initialized = true;
                            resolve();
                        }
                    }
                    catch (error) {
                        log.warn(`Failed to parse MCP message: ${error}`);
                    }
                }
            });
            // Send initialize request
            const initRequest = {
                jsonrpc: '2.0',
                id: this.nextId++,
                method: 'initialize',
                params: {
                    protocolVersion: '2024-11-05',
                    capabilities: {},
                    clientInfo: {
                        name: 'clawx',
                        version: '0.9.20',
                    },
                },
            };
            this.sendRequest(initRequest).catch(reject);
        });
    }
    sendRequest(request) {
        return new Promise((resolve, reject) => {
            if (!this.child) {
                reject(new Error('MCP server not connected'));
                return;
            }
            this.pending.set(request.id, { resolve, reject });
            const body = JSON.stringify(request);
            const message = `Content-Length: ${body.length}\r\n\r\n${body}`;
            this.child.stdin.write(message);
        });
    }
    async listTools() {
        const result = await this.sendRequest({
            jsonrpc: '2.0',
            id: this.nextId++,
            method: 'tools/list',
        });
        return result.tools || [];
    }
    async callTool(toolName, args) {
        return this.sendRequest({
            jsonrpc: '2.0',
            id: this.nextId++,
            method: 'tools/call',
            params: {
                name: toolName,
                arguments: args,
            },
        });
    }
    async close() {
        if (this.child) {
            this.child.kill();
            this.child = null;
        }
        this.pending.clear();
    }
}
/**
 * Create actual MCP tools that connect to servers
 */
export async function createMcpTools(config) {
    const tools = [];
    if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) {
        return tools;
    }
    for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
        if (serverConfig.disabled) {
            log.info(`MCP server ${serverName} is disabled`);
            continue;
        }
        try {
            log.info(`Connecting to MCP server: ${serverName}`);
            const client = new SimpleMcpClient(serverConfig);
            await client.connect();
            // Get tools from server
            const serverTools = await client.listTools();
            log.info(`MCP server ${serverName} has ${serverTools.length} tools`);
            for (const tool of serverTools) {
                // Create a tool for each MCP tool
                const mcpTool = {
                    name: `${serverName}_${tool.name}`,
                    description: tool.description || `Tool from MCP server ${serverName}`,
                    label: `MCP: ${serverName}/${tool.name}`,
                    parameters: Type.Object(tool.inputSchema?.properties || {}, tool.inputSchema?.required ? { required: tool.inputSchema.required } : {}),
                    async execute(toolCallId, params, signal) {
                        try {
                            const result = await client.callTool(tool.name, params);
                            // Format the result
                            let text = '';
                            if (result.content) {
                                for (const item of result.content) {
                                    if (item.type === 'text') {
                                        text += item.text + '\n';
                                    }
                                    else if (item.type === 'image') {
                                        text += `[Image: ${item.data.slice(0, 50)}...]\n`;
                                    }
                                }
                            }
                            if (!text.trim()) {
                                text = `Tool ${tool.name} executed successfully`;
                            }
                            return {
                                content: [{
                                        type: 'text',
                                        text: text.trim(),
                                    }],
                                details: {
                                    server: serverName,
                                    tool: tool.name,
                                    success: true,
                                },
                            };
                        }
                        catch (error) {
                            return {
                                content: [{
                                        type: 'text',
                                        text: `Error calling MCP tool ${tool.name}: ${error instanceof Error ? error.message : String(error)}`,
                                    }],
                                details: {
                                    server: serverName,
                                    tool: tool.name,
                                    success: false,
                                    error: error instanceof Error ? error.message : String(error),
                                },
                            };
                        }
                    },
                };
                tools.push(mcpTool);
                log.info(`Registered MCP tool: ${mcpTool.name}`);
            }
            // Note: In a real implementation, we'd need to manage client lifecycle
            // For now, the client will be recreated each time
        }
        catch (error) {
            log.error(`Failed to connect to MCP server ${serverName}: ${error instanceof Error ? error.message : String(error)}`);
            // Create a fallback tool that shows the error
            const errorTool = {
                name: `mcp_${serverName}_error`,
                description: `MCP server ${serverName} - Connection failed`,
                label: `MCP Error: ${serverName}`,
                parameters: Type.Object({}),
                async execute(toolCallId, params, signal) {
                    return {
                        content: [{
                                type: 'text',
                                text: `Cannot connect to MCP server ${serverName}.\n` +
                                    `Error: ${error instanceof Error ? error.message : String(error)}\n` +
                                    `Command: ${serverConfig.command} ${serverConfig.args?.join(' ') || ''}`,
                            }],
                        details: {
                            server: serverName,
                            error: error instanceof Error ? error.message : String(error),
                            connected: false,
                        },
                    };
                },
            };
            tools.push(errorTool);
        }
    }
    return tools;
}
//# sourceMappingURL=mcpToolsSimple.js.map