"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildMcpToolkit = buildMcpToolkit;
exports.executeMcpTool = executeMcpTool;
exports.loadMcpToolOptions = loadMcpToolOptions;
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const ai_utilities_1 = require("@n8n/ai-utilities");
const pick_1 = __importDefault(require("lodash/pick"));
const n8n_core_1 = require("n8n-core");
const n8n_workflow_1 = require("n8n-workflow");
const utils_1 = require("../McpClientTool/utils");
const utils_2 = require("./utils");
async function connectAndGetTools(ctx, config) {
    const client = await (0, utils_2.connectMcpClientForCredential)(ctx, {
        authentication: config.authentication,
        serverTransport: config.transport,
        endpointUrl: config.endpointUrl,
        surface: 'MCP Client Tool',
    });
    if (!client.ok) {
        return { client, mcpTools: null, error: client.error };
    }
    try {
        const allTools = await (0, utils_2.getAllTools)(client.result);
        const mcpTools = (0, utils_1.getSelectedTools)({
            tools: allTools,
            mode: config.toolFilter.mode,
            includeTools: config.toolFilter.includeTools,
            excludeTools: config.toolFilter.excludeTools,
        });
        return { client: client.result, mcpTools, error: null };
    }
    catch (error) {
        await client.result.close();
        throw error;
    }
}
async function buildMcpToolkit(ctx, itemIndex, config) {
    const node = ctx.getNode();
    const setError = (error) => {
        ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, itemIndex, error);
        throw error;
    };
    const signal = ctx.getExecutionCancelSignal();
    if (signal?.aborted) {
        return setError(new n8n_workflow_1.NodeOperationError(node, 'Execution was cancelled', { itemIndex }));
    }
    const { client, mcpTools, error } = await connectAndGetTools(ctx, config);
    if (error) {
        ctx.logger.error('MCP client: Failed to connect to MCP Server', { error });
        return setError((0, utils_2.mapToNodeOperationError)(node, error));
    }
    ctx.logger.debug('MCP client: Successfully connected to MCP Server');
    if (!mcpTools?.length) {
        await client.close();
        return setError(new n8n_workflow_1.NodeOperationError(node, 'MCP Server returned no tools', {
            itemIndex,
            description: 'Connected successfully to your MCP server but it returned an empty list of tools.',
        }));
    }
    try {
        const tools = mcpTools.map((tool) => {
            const prefixedName = (0, utils_1.buildMcpToolName)(node.name, tool.name);
            return (0, ai_utilities_1.logWrapper)((0, utils_1.mcpToolToDynamicTool)({ ...tool, name: prefixedName }, (0, utils_1.createCallTool)(tool.name, client, config.timeout, (errorMessage) => {
                const callError = new n8n_workflow_1.NodeOperationError(node, errorMessage, { itemIndex });
                void ctx.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, itemIndex, callError);
                ctx.logger.error(`MCP client: Tool "${tool.name}" failed to execute`, {
                    error: callError,
                });
            }, () => ctx.getExecutionCancelSignal())), ctx);
        });
        ctx.logger.debug(`MCP client: Connected to MCP Server with ${tools.length} tools`);
        const toolkit = new n8n_core_1.StructuredToolkit(tools);
        return { response: toolkit, closeFunction: async () => await client.close() };
    }
    catch (e) {
        await client.close();
        throw e;
    }
}
async function executeMcpTool(ctx, resolveConfig) {
    const node = ctx.getNode();
    const items = ctx.getInputData();
    const returnData = [];
    for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
        const signal = ctx.getExecutionCancelSignal();
        if (signal?.aborted) {
            throw new n8n_workflow_1.NodeOperationError(node, 'Execution was cancelled', { itemIndex });
        }
        const item = items[itemIndex];
        const config = await resolveConfig(itemIndex);
        const { client, mcpTools, error } = await connectAndGetTools(ctx, config);
        if (error) {
            throw new n8n_workflow_1.NodeOperationError(node, error.error, { itemIndex });
        }
        try {
            if (!mcpTools?.length) {
                throw new n8n_workflow_1.NodeOperationError(node, 'MCP Server returned no tools', { itemIndex });
            }
            if (!item.json.tool || typeof item.json.tool !== 'string') {
                throw new n8n_workflow_1.NodeOperationError(node, 'Tool name not found in item.json.tool or item.tool', {
                    itemIndex,
                });
            }
            const toolName = item.json.tool;
            for (const tool of mcpTools) {
                const prefixedName = (0, utils_1.buildMcpToolName)(node.name, tool.name);
                if (toolName !== prefixedName)
                    continue;
                const { tool: _, ...toolArguments } = item.json;
                const schema = tool.inputSchema;
                const sanitizedToolArguments = schema.additionalProperties !== true
                    ? (0, pick_1.default)(toolArguments, Object.keys(schema.properties ?? {}))
                    : toolArguments;
                const result = await client.callTool({ name: tool.name, arguments: sanitizedToolArguments }, types_js_1.CallToolResultSchema, {
                    timeout: config.timeout,
                    signal: ctx.getExecutionCancelSignal(),
                });
                if (node.typeVersion >= 1.3 && result.isError) {
                    const errorMessage = (0, utils_1.getErrorDescriptionFromToolCall)(result) ?? `Tool "${tool.name}" returned an error`;
                    throw new n8n_workflow_1.NodeOperationError(node, errorMessage, { itemIndex });
                }
                returnData.push({
                    json: {
                        response: result.content,
                        ...((0, utils_2.isStructuredContent)(result.structuredContent) && {
                            structuredContent: result.structuredContent,
                        }),
                    },
                    pairedItem: { item: itemIndex },
                });
            }
        }
        finally {
            await client.close();
        }
    }
    return [returnData];
}
async function loadMcpToolOptions(ctx, config) {
    const node = ctx.getNode();
    const client = await (0, utils_2.connectMcpClientForCredential)(ctx, {
        authentication: config.authentication,
        serverTransport: config.transport,
        endpointUrl: config.endpointUrl,
        surface: 'MCP Client Tool',
    });
    if (!client.ok) {
        throw (0, utils_2.mapToNodeOperationError)(node, client.error);
    }
    try {
        const tools = await (0, utils_2.getAllTools)(client.result);
        return tools.map((tool) => ({
            name: tool.name,
            value: tool.name,
            description: tool.description,
            inputSchema: tool.inputSchema,
        }));
    }
    finally {
        await client.result.close();
    }
}
//# sourceMappingURL=runtime.js.map