n8n
Version:
n8n Workflow Automation Tool
140 lines • 6.57 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSearchExecutionsTool = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const zod_1 = __importDefault(require("zod"));
const mcp_constants_1 = require("../mcp.constants");
const mcp_errors_1 = require("../mcp.errors");
const schemas_1 = require("./schemas");
const workflow_validation_utils_1 = require("./workflow-validation.utils");
const MAX_RESULTS = 200;
const inputSchema = {
workflowId: zod_1.default.string().optional().describe('Filter executions by workflow ID'),
status: zod_1.default
.array(zod_1.default.enum(n8n_workflow_1.ExecutionStatusList))
.optional()
.describe('Filter by execution status(es)'),
startedAfter: zod_1.default
.string()
.datetime({ offset: true })
.optional()
.describe('ISO 8601 timestamp — only return executions that started after this time'),
startedBefore: zod_1.default
.string()
.datetime({ offset: true })
.optional()
.describe('ISO 8601 timestamp — only return executions that started before this time'),
limit: (0, schemas_1.createLimitSchema)(MAX_RESULTS),
lastId: zod_1.default
.string()
.optional()
.describe('Cursor for pagination — pass the last execution ID from the previous page'),
};
const outputSchema = {
data: zod_1.default
.array(zod_1.default.object({
id: zod_1.default.string().describe('The unique identifier of the execution'),
workflowId: zod_1.default.string().describe('The workflow this execution belongs to'),
status: zod_1.default.enum(n8n_workflow_1.ExecutionStatusList).describe('The execution status'),
mode: zod_1.default.enum(n8n_workflow_1.WorkflowExecuteModeList).describe('How the execution was triggered'),
startedAt: zod_1.default.string().nullable().describe('ISO timestamp when the execution started'),
stoppedAt: zod_1.default.string().nullable().describe('ISO timestamp when the execution stopped'),
waitTill: zod_1.default
.string()
.nullable()
.describe('ISO timestamp until when the execution is waiting'),
}))
.describe('List of executions matching the query'),
count: zod_1.default
.union([zod_1.default.literal(-1), zod_1.default.number().int().min(0)])
.describe('Total matching executions, or -1 if the count is unavailable'),
estimated: zod_1.default.boolean().describe('Whether the count is an estimate (for large datasets)'),
error: zod_1.default.string().optional().describe('Error message if the query failed'),
};
const createSearchExecutionsTool = (user, executionService, workflowFinderService, telemetry) => ({
name: 'search_executions',
config: {
description: 'Search for workflow executions with optional filters. Returns execution metadata including status, timing, and workflow ID.',
inputSchema,
outputSchema,
annotations: {
title: 'Search Executions',
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
handler: async ({ workflowId, status, startedAfter, startedBefore, limit = MAX_RESULTS, lastId, }) => {
const parameters = { workflowId, status, startedAfter, startedBefore, limit, lastId };
const telemetryPayload = {
user_id: user.id,
tool_name: 'search_executions',
parameters,
};
try {
if (workflowId) {
await (0, workflow_validation_utils_1.getMcpWorkflow)(workflowId, user, ['workflow:read'], workflowFinderService);
}
const safeLimit = Math.min(Math.max(1, limit), MAX_RESULTS);
const sharingOptions = await executionService.buildSharingOptions('workflow:read');
const query = {
kind: 'range',
user,
sharingOptions,
range: {
limit: safeLimit,
...(lastId ? { lastId } : {}),
},
order: { startedAt: 'DESC' },
...(workflowId ? { workflowId } : {}),
...(status?.length ? { status } : {}),
...(startedAfter ? { startedAfter } : {}),
...(startedBefore ? { startedBefore } : {}),
isArchived: false,
workflowBooleanSettings: [{ key: 'availableInMCP', value: true }],
};
const { results, count, estimated } = await executionService.findRangeWithCount(query);
const data = results.map((execution) => ({
id: execution.id,
workflowId: execution.workflowId,
status: execution.status,
mode: execution.mode,
startedAt: execution.startedAt ?? null,
stoppedAt: execution.stoppedAt ?? null,
waitTill: execution.waitTill ?? null,
}));
const payload = { data, count, estimated };
telemetryPayload.results = {
success: true,
data: { count, estimated },
};
telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload);
return {
structuredContent: payload,
content: [{ type: 'text', text: JSON.stringify(payload) }],
};
}
catch (er) {
const error = er instanceof Error ? er : new Error(String(er));
const isAccessError = error instanceof mcp_errors_1.WorkflowAccessError;
telemetryPayload.results = {
success: false,
error: error.message,
error_reason: isAccessError ? error.reason : undefined,
};
telemetry.track(mcp_constants_1.USER_CALLED_MCP_TOOL_EVENT, telemetryPayload);
const output = { data: [], count: 0, estimated: false, error: error.message };
return {
content: [{ type: 'text', text: JSON.stringify(output) }],
structuredContent: output,
isError: true,
};
}
},
});
exports.createSearchExecutionsTool = createSearchExecutionsTool;
//# sourceMappingURL=search-executions.tool.js.map