dist / toolsProvider.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.toolsProvider = toolsProvider;
const http = __importStar(require("http"));
const https = __importStar(require("https"));
const sdk_1 = require("@lmstudio/sdk");
const zod_1 = require("zod");
const n8nConfig_1 = require("./n8nConfig");
function normalizeBaseUrl(apiUrl, versionPath) {
const trimmedApiUrl = apiUrl.replace(/\/+$/, "");
const normalizedVersionPath = versionPath.startsWith("/") ? versionPath : `/${versionPath}`;
if (trimmedApiUrl.endsWith(normalizedVersionPath)) {
return trimmedApiUrl;
}
return `${trimmedApiUrl}${normalizedVersionPath}`;
}
function buildUrl(baseUrl, path, query) {
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
const url = new URL(`${baseUrl}${normalizedPath}`);
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== undefined) {
url.searchParams.set(key, String(value));
}
}
}
return url;
}
function apiRequest(method, path, apiKey, baseUrl, query, body, timeoutMs = 30000) {
return new Promise((resolve) => {
const url = buildUrl(baseUrl, path, query);
const isHttps = url.protocol === "https:";
const client = isHttps ? https : http;
const serializedBody = body === undefined ? undefined : JSON.stringify(body);
const request = client.request({
protocol: url.protocol,
hostname: url.hostname,
port: url.port,
path: `${url.pathname}${url.search}`,
method,
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"X-N8N-API-KEY": apiKey,
...(serializedBody ? { "Content-Length": Buffer.byteLength(serializedBody) } : {}),
},
timeout: timeoutMs,
}, (response) => {
let raw = "";
response.on("data", (chunk) => {
raw += chunk.toString();
});
response.on("end", () => {
const status = response.statusCode ?? 0;
let parsed = raw;
if (raw.length > 0) {
try {
parsed = JSON.parse(raw);
}
catch {
parsed = raw;
}
}
if (status >= 200 && status < 300) {
resolve({ success: true, status, data: parsed });
}
else {
const msg = typeof parsed === "string" ? parsed : JSON.stringify(parsed);
resolve({ success: false, status, error: msg || "n8n API request failed" });
}
});
});
request.on("error", (err) => {
resolve({ success: false, status: 0, error: err.message });
});
request.on("timeout", () => {
request.destroy();
resolve({ success: false, status: 0, error: `Request timed out after ${timeoutMs}ms` });
});
if (serializedBody) {
request.write(serializedBody);
}
request.end();
});
}
function getConnectionSettings() {
const cfg = (0, n8nConfig_1.loadN8nConfig)();
if (!cfg.api_url) {
return { error: "Missing api_url in n8n-config.json" };
}
if (!cfg.api_key) {
return { error: "Missing api_key in n8n-config.json" };
}
return {
apiKey: cfg.api_key,
baseUrl: normalizeBaseUrl(cfg.api_url, cfg.api_version_path),
};
}
async function toolsProvider(_ctl) {
const tools = [];
tools.push((0, sdk_1.tool)({
name: "n8n_list_workflows",
description: (0, sdk_1.text) `
List workflows from n8n.
Optional filters: active, tags, projectId, and pagination with limit/cursor.
`,
parameters: {
active: zod_1.z.boolean().optional(),
tags: zod_1.z.string().optional().describe("Comma separated tags"),
projectId: zod_1.z.string().optional(),
limit: zod_1.z.number().int().min(1).max(250).default(100),
cursor: zod_1.z.string().optional(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ active, tags, projectId, limit, cursor, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("GET", "/workflows", conn.apiKey, conn.baseUrl, {
active,
tags,
projectId,
limit,
cursor,
}, undefined, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_get_workflow",
description: (0, sdk_1.text) `Get a single workflow by workflow ID.`,
parameters: {
workflow_id: zod_1.z.string(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ workflow_id, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("GET", `/workflows/${encodeURIComponent(workflow_id)}`, conn.apiKey, conn.baseUrl, undefined, undefined, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_create_workflow",
description: (0, sdk_1.text) `
Create a new workflow in n8n.
Provide the full workflow object as JSON in workflow.
`,
parameters: {
workflow: zod_1.z.record(zod_1.z.any()).describe("Full n8n workflow JSON object"),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ workflow, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("POST", "/workflows", conn.apiKey, conn.baseUrl, undefined, workflow, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_update_workflow",
description: (0, sdk_1.text) `
Update an existing workflow by ID.
Provide the workflow object payload according to n8n API.
`,
parameters: {
workflow_id: zod_1.z.string(),
workflow: zod_1.z.record(zod_1.z.any()),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ workflow_id, workflow, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("PUT", `/workflows/${encodeURIComponent(workflow_id)}`, conn.apiKey, conn.baseUrl, undefined, workflow, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_delete_workflow",
description: (0, sdk_1.text) `Delete a workflow by workflow ID.`,
parameters: {
workflow_id: zod_1.z.string(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ workflow_id, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("DELETE", `/workflows/${encodeURIComponent(workflow_id)}`, conn.apiKey, conn.baseUrl, undefined, undefined, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_set_workflow_active",
description: (0, sdk_1.text) `Set a workflow active or inactive.`,
parameters: {
workflow_id: zod_1.z.string(),
active: zod_1.z.boolean(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ workflow_id, active, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("PATCH", `/workflows/${encodeURIComponent(workflow_id)}`, conn.apiKey, conn.baseUrl, undefined, { active }, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_list_executions",
description: (0, sdk_1.text) `
List executions from n8n.
Supports pagination and basic filtering.
`,
parameters: {
workflowId: zod_1.z.string().optional(),
status: zod_1.z.enum(["new", "running", "success", "error", "canceled", "crashed", "waiting"]).optional(),
projectId: zod_1.z.string().optional(),
includeData: zod_1.z.boolean().optional(),
limit: zod_1.z.number().int().min(1).max(250).default(100),
cursor: zod_1.z.string().optional(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ workflowId, status, projectId, includeData, limit, cursor, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("GET", "/executions", conn.apiKey, conn.baseUrl, {
workflowId,
status,
projectId,
includeData,
limit,
cursor,
}, undefined, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_get_execution",
description: (0, sdk_1.text) `Get execution details by execution ID.`,
parameters: {
execution_id: zod_1.z.string(),
includeData: zod_1.z.boolean().optional(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ execution_id, includeData, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("GET", `/executions/${encodeURIComponent(execution_id)}`, conn.apiKey, conn.baseUrl, { includeData }, undefined, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_delete_execution",
description: (0, sdk_1.text) `Delete execution data by execution ID.`,
parameters: {
execution_id: zod_1.z.string(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ execution_id, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest("DELETE", `/executions/${encodeURIComponent(execution_id)}`, conn.apiKey, conn.baseUrl, undefined, undefined, timeout_ms);
},
}));
tools.push((0, sdk_1.tool)({
name: "n8n_api_request",
description: (0, sdk_1.text) `
Generic n8n API request tool.
Use this when you need an endpoint not covered by the specialized tools.
path must start with '/' and is appended to api_version_path (default /api/v1).
`,
parameters: {
method: zod_1.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
path: zod_1.z.string().describe("Example: /users or /projects/<id>"),
query: zod_1.z.record(zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()])).optional(),
body: zod_1.z.record(zod_1.z.any()).optional(),
timeout_ms: zod_1.z.number().int().positive().default(30000),
},
implementation: async ({ method, path, query, body, timeout_ms }) => {
const conn = getConnectionSettings();
if ("error" in conn)
return { success: false, error: conn.error };
return apiRequest(method, path, conn.apiKey, conn.baseUrl, query, body, timeout_ms);
},
}));
return tools;
}