@kairos-sdk/core 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -16
- package/dist/{chunk-KIFT5LA7.js → chunk-2ZHNO37N.js} +49 -5
- package/dist/chunk-2ZHNO37N.js.map +1 -0
- package/dist/chunk-GG4B4TYG.js +153 -0
- package/dist/chunk-GG4B4TYG.js.map +1 -0
- package/dist/{chunk-5GAY7CSJ.js → chunk-PCNW5ZUD.js} +2 -2
- package/dist/chunk-SC6CLQZB.js +144 -0
- package/dist/chunk-SC6CLQZB.js.map +1 -0
- package/dist/chunk-SQS4QHDH.js +44 -0
- package/dist/chunk-SQS4QHDH.js.map +1 -0
- package/dist/{chunk-EVOAYH2K.js → chunk-STG7Z2SS.js} +2 -2
- package/dist/{chunk-HBGZTUUZ.js → chunk-YOQTEVDB.js} +5 -7
- package/dist/chunk-YOQTEVDB.js.map +1 -0
- package/dist/cli.cjs +702 -40
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +262 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +417 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -4
- package/dist/index.d.ts +86 -4
- package/dist/index.js +19 -5
- package/dist/mcp-server.cjs +139 -24
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/mcp-server.js +90 -19
- package/dist/mcp-server.js.map +1 -1
- package/dist/pack-builder-RTQWXGIS.js +9 -0
- package/dist/pack-builder-RTQWXGIS.js.map +1 -0
- package/dist/pack-exporter-KFNLSP5V.js +7 -0
- package/dist/pack-exporter-KFNLSP5V.js.map +1 -0
- package/dist/pack-validator-HZPB2XJ3.js +7 -0
- package/dist/pack-validator-HZPB2XJ3.js.map +1 -0
- package/dist/{reader-B5mV20H6.d.ts → reader-CfWGpL4V.d.cts} +2 -1
- package/dist/{reader-B5mV20H6.d.cts → reader-CfWGpL4V.d.ts} +2 -1
- package/dist/standalone.cjs +44 -4
- package/dist/standalone.cjs.map +1 -1
- package/dist/standalone.d.cts +1 -1
- package/dist/standalone.d.ts +1 -1
- package/dist/standalone.js +2 -2
- package/package.json +12 -5
- package/dist/chunk-HBGZTUUZ.js.map +0 -1
- package/dist/chunk-KIFT5LA7.js.map +0 -1
- /package/dist/{chunk-5GAY7CSJ.js.map → chunk-PCNW5ZUD.js.map} +0 -0
- /package/dist/{chunk-EVOAYH2K.js.map → chunk-STG7Z2SS.js.map} +0 -0
package/dist/mcp-server.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PromptBuilder,
|
|
4
4
|
inferWorkflowType
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-STG7Z2SS.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_REGISTRY,
|
|
8
8
|
FileLibrary,
|
|
@@ -16,11 +16,13 @@ import {
|
|
|
16
16
|
TelemetryReader,
|
|
17
17
|
generateUUID,
|
|
18
18
|
nullLogger
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2ZHNO37N.js";
|
|
20
20
|
|
|
21
21
|
// src/mcp-server.ts
|
|
22
22
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23
23
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
24
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
25
|
+
import { createServer } from "http";
|
|
24
26
|
import { z } from "zod";
|
|
25
27
|
|
|
26
28
|
// src/validation/node-syncer.ts
|
|
@@ -82,10 +84,35 @@ ${regularLines}`;
|
|
|
82
84
|
|
|
83
85
|
// src/mcp-server.ts
|
|
84
86
|
import { readFileSync } from "fs";
|
|
85
|
-
import { dirname, join } from "path";
|
|
87
|
+
import { dirname as dirname2, join } from "path";
|
|
86
88
|
import { homedir } from "os";
|
|
87
89
|
import { fileURLToPath } from "url";
|
|
88
|
-
|
|
90
|
+
|
|
91
|
+
// src/utils/node-catalog-cache.ts
|
|
92
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
93
|
+
import { dirname } from "path";
|
|
94
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
95
|
+
async function readCatalogCache(cachePath) {
|
|
96
|
+
try {
|
|
97
|
+
const raw = await readFile(cachePath, "utf-8");
|
|
98
|
+
const cached = JSON.parse(raw);
|
|
99
|
+
if (Date.now() - cached.cachedAt > CACHE_TTL_MS) return null;
|
|
100
|
+
return cached.syncResult;
|
|
101
|
+
} catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function writeCatalogCache(cachePath, syncResult) {
|
|
106
|
+
try {
|
|
107
|
+
await mkdir(dirname(cachePath), { recursive: true });
|
|
108
|
+
const payload = { cachedAt: Date.now(), syncResult };
|
|
109
|
+
await writeFile(cachePath, JSON.stringify(payload), "utf-8");
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/mcp-server.ts
|
|
115
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
89
116
|
var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
90
117
|
var library = new FileLibrary();
|
|
91
118
|
var _validator = new N8nValidator();
|
|
@@ -125,7 +152,14 @@ function getTelemetryReader() {
|
|
|
125
152
|
return null;
|
|
126
153
|
}
|
|
127
154
|
}
|
|
155
|
+
function getMcpMode() {
|
|
156
|
+
const mode = process.env["KAIROS_MCP_MODE"]?.toLowerCase();
|
|
157
|
+
if (mode === "readonly" || mode === "validate") return mode;
|
|
158
|
+
return "deploy";
|
|
159
|
+
}
|
|
128
160
|
function isAllowed(action) {
|
|
161
|
+
const mode = getMcpMode();
|
|
162
|
+
if (mode === "readonly" || mode === "validate") return false;
|
|
129
163
|
const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`;
|
|
130
164
|
return process.env[key] === "true";
|
|
131
165
|
}
|
|
@@ -149,8 +183,20 @@ function getApiClient() {
|
|
|
149
183
|
}
|
|
150
184
|
return new N8nApiClient(baseUrl, apiKey, nullLogger);
|
|
151
185
|
}
|
|
186
|
+
function getCatalogCachePath() {
|
|
187
|
+
const telemetry = process.env["KAIROS_TELEMETRY"];
|
|
188
|
+
const base = telemetry ? join(telemetry, "..") : join(homedir(), ".kairos");
|
|
189
|
+
return join(base, "node-catalog-cache.json");
|
|
190
|
+
}
|
|
152
191
|
async function autoSync() {
|
|
153
192
|
if (lastSync) return lastSync;
|
|
193
|
+
const cachePath = getCatalogCachePath();
|
|
194
|
+
const cached = await readCatalogCache(cachePath);
|
|
195
|
+
if (cached) {
|
|
196
|
+
lastSync = cached;
|
|
197
|
+
_validator = new N8nValidator(lastSync.registry);
|
|
198
|
+
return lastSync;
|
|
199
|
+
}
|
|
154
200
|
const baseUrl = process.env["N8N_BASE_URL"];
|
|
155
201
|
const apiKey = process.env["N8N_API_KEY"];
|
|
156
202
|
if (!baseUrl || !apiKey) return null;
|
|
@@ -160,6 +206,8 @@ async function autoSync() {
|
|
|
160
206
|
if (nodeTypes.length === 0) return null;
|
|
161
207
|
lastSync = nodeSyncer.sync(nodeTypes);
|
|
162
208
|
_validator = new N8nValidator(lastSync.registry);
|
|
209
|
+
writeCatalogCache(cachePath, lastSync).catch(() => {
|
|
210
|
+
});
|
|
163
211
|
return lastSync;
|
|
164
212
|
} catch {
|
|
165
213
|
return null;
|
|
@@ -178,13 +226,9 @@ server.tool(
|
|
|
178
226
|
},
|
|
179
227
|
async ({ description, name }) => {
|
|
180
228
|
evictStaleSessions();
|
|
181
|
-
const baseUrl = process.env["N8N_BASE_URL"];
|
|
182
|
-
const apiKey = process.env["N8N_API_KEY"];
|
|
183
|
-
if (!baseUrl || !apiKey) {
|
|
184
|
-
return mcpError(JSON.stringify({ error: "N8N_BASE_URL and N8N_API_KEY are required. Kairos needs to sync your n8n instance's node types to generate accurate workflows." }));
|
|
185
|
-
}
|
|
186
229
|
const runId = generateUUID();
|
|
187
230
|
const workflowType = inferWorkflowType(description);
|
|
231
|
+
const hasN8nCreds = !!(process.env["N8N_BASE_URL"] && process.env["N8N_API_KEY"]);
|
|
188
232
|
const syncPromise = autoSync();
|
|
189
233
|
const syncTimeout = new Promise((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS));
|
|
190
234
|
await library.initialize();
|
|
@@ -204,7 +248,8 @@ server.tool(
|
|
|
204
248
|
startTime: Date.now(),
|
|
205
249
|
validateAttempts: 0,
|
|
206
250
|
warnedRules: promptBuilder.getWarnedRules(),
|
|
207
|
-
workflowType
|
|
251
|
+
workflowType,
|
|
252
|
+
matchCount: matches.length
|
|
208
253
|
});
|
|
209
254
|
await mcpTelemetry.emit("build_start", { description, model: "mcp-decomposed", dryRun: false }, runId);
|
|
210
255
|
}
|
|
@@ -216,7 +261,9 @@ server.tool(
|
|
|
216
261
|
topMatchScore: matches[0]?.score ?? null,
|
|
217
262
|
nodeCatalog: syncResult ? "synced" : "static",
|
|
218
263
|
nodeCount: syncResult?.nodeCount ?? null,
|
|
219
|
-
...syncResult ? {} : {
|
|
264
|
+
...syncResult ? {} : {
|
|
265
|
+
syncWarning: hasN8nCreds ? "Could not sync node types from your n8n instance. Using static fallback catalog \u2014 generated workflows may not match your exact n8n setup." : "N8N_BASE_URL and N8N_API_KEY are not set. Using static fallback catalog \u2014 node types may not match your n8n instance. Set these env vars to enable accurate generation and deployment."
|
|
266
|
+
},
|
|
220
267
|
systemPrompt: systemText,
|
|
221
268
|
userMessage: built.userMessage,
|
|
222
269
|
outputFormat: {
|
|
@@ -289,10 +336,11 @@ server.tool(
|
|
|
289
336
|
{
|
|
290
337
|
workflow: z.string().describe("The validated workflow JSON string to deploy"),
|
|
291
338
|
activate: z.boolean().default(false).describe("Activate the workflow immediately after deployment"),
|
|
339
|
+
description: z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
|
|
292
340
|
kairos_run_id: z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
|
|
293
341
|
kairos_secret: z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
|
|
294
342
|
},
|
|
295
|
-
async ({ workflow: workflowStr, activate, kairos_run_id, kairos_secret }) => {
|
|
343
|
+
async ({ workflow: workflowStr, activate, description: userDescription, kairos_run_id, kairos_secret }) => {
|
|
296
344
|
const authError = checkMcpAuth(kairos_secret);
|
|
297
345
|
if (authError) return authError;
|
|
298
346
|
if (!isAllowed("deploy")) {
|
|
@@ -333,8 +381,8 @@ server.tool(
|
|
|
333
381
|
Note: kairos_run_id "${kairos_run_id}" was provided but no active session was found. This usually means kairos_deploy was called without a prior kairos_prompt call, or the session expired. Telemetry and pattern learning for this build were skipped.` : "";
|
|
334
382
|
await library.initialize();
|
|
335
383
|
await library.save(parsed, {
|
|
336
|
-
description: session?.description ?? parsed.name,
|
|
337
|
-
generationMode: "scratch",
|
|
384
|
+
description: session?.description ?? userDescription ?? parsed.name,
|
|
385
|
+
generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
|
|
338
386
|
generationAttempts: session?.validateAttempts ?? 1,
|
|
339
387
|
n8nWorkflowId: response.id
|
|
340
388
|
});
|
|
@@ -371,12 +419,16 @@ server.tool(
|
|
|
371
419
|
{
|
|
372
420
|
workflow_id: z.string().describe("The n8n workflow ID to replace"),
|
|
373
421
|
workflow: z.string().describe("The validated workflow JSON string"),
|
|
422
|
+
description: z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
|
|
374
423
|
kairos_run_id: z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
|
|
375
424
|
kairos_secret: z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
|
|
376
425
|
},
|
|
377
|
-
async ({ workflow_id, workflow: workflowStr, kairos_run_id, kairos_secret }) => {
|
|
426
|
+
async ({ workflow_id, workflow: workflowStr, description: userDescription, kairos_run_id, kairos_secret }) => {
|
|
378
427
|
const authError = checkMcpAuth(kairos_secret);
|
|
379
428
|
if (authError) return authError;
|
|
429
|
+
if (!isAllowed("deploy")) {
|
|
430
|
+
return mcpError(JSON.stringify({ error: "Replace is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true or KAIROS_MCP_MODE=deploy to enable." }));
|
|
431
|
+
}
|
|
380
432
|
let parsed;
|
|
381
433
|
try {
|
|
382
434
|
parsed = JSON.parse(workflowStr);
|
|
@@ -400,8 +452,8 @@ server.tool(
|
|
|
400
452
|
Note: kairos_run_id "${kairos_run_id}" was provided but no active session was found.` : "";
|
|
401
453
|
await library.initialize();
|
|
402
454
|
await library.save(parsed, {
|
|
403
|
-
description: session?.description ?? parsed.name,
|
|
404
|
-
generationMode: "scratch",
|
|
455
|
+
description: session?.description ?? userDescription ?? parsed.name,
|
|
456
|
+
generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
|
|
405
457
|
generationAttempts: session?.validateAttempts ?? 1,
|
|
406
458
|
n8nWorkflowId: workflow_id
|
|
407
459
|
});
|
|
@@ -647,8 +699,27 @@ async function main() {
|
|
|
647
699
|
"[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set \u2014 kairos_prompt will fail. Set it before using workflow generation tools.\n"
|
|
648
700
|
);
|
|
649
701
|
}
|
|
650
|
-
const
|
|
651
|
-
|
|
702
|
+
const useHttp = process.argv.includes("--http");
|
|
703
|
+
if (useHttp) {
|
|
704
|
+
const port = parseInt(process.env["KAIROS_MCP_PORT"] ?? "3000", 10);
|
|
705
|
+
const transport = new StreamableHTTPServerTransport();
|
|
706
|
+
await server.connect(transport);
|
|
707
|
+
const httpServer = createServer(async (req, res) => {
|
|
708
|
+
if (req.method === "GET" || req.method === "POST" || req.method === "DELETE") {
|
|
709
|
+
await transport.handleRequest(req, res);
|
|
710
|
+
} else {
|
|
711
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
712
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
httpServer.listen(port, () => {
|
|
716
|
+
process.stderr.write(`[kairos-mcp] HTTP transport listening on port ${port}
|
|
717
|
+
`);
|
|
718
|
+
});
|
|
719
|
+
} else {
|
|
720
|
+
const transport = new StdioServerTransport();
|
|
721
|
+
await server.connect(transport);
|
|
722
|
+
}
|
|
652
723
|
}
|
|
653
724
|
main().catch((err) => {
|
|
654
725
|
console.error("Kairos MCP server failed to start:", err);
|
package/dist/mcp-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp-server.ts","../src/validation/node-syncer.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Kairos MCP Server — decomposed architecture.\n *\n * The host LLM (Claude, GPT, Gemini, whatever) generates the workflow.\n * Kairos provides the knowledge (system prompt, library, failure patterns)\n * and guardrails (validator, deployer). Zero Anthropic API key needed.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { FileLibrary } from './library/file-library.js'\nimport { N8nValidator } from './validation/validator.js'\nimport { N8nFieldStripper } from './providers/n8n/stripper.js'\nimport { N8nApiClient } from './providers/n8n/api-client.js'\nimport { PromptBuilder } from './generation/prompt-builder.js'\nimport { TelemetryReader } from './telemetry/reader.js'\nimport { PatternAnalyzer } from './telemetry/pattern-analyzer.js'\nimport { NodeSyncer, type SyncResult } from './validation/node-syncer.js'\nimport { TelemetryCollector } from './telemetry/collector.js'\nimport { nullLogger } from './utils/logger.js'\nimport { GuardError } from './errors/guard-error.js'\nimport { generateUUID } from './utils/uuid.js'\nimport { inferWorkflowType } from './utils/workflow-type.js'\nimport type { N8nWorkflow } from './types/workflow.js'\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version: string }\n\nconst library = new FileLibrary()\nlet _validator = new N8nValidator()\n// Accessor so kairos_sync can swap the registry without callers closing over the old instance\nfunction getValidator(): N8nValidator { return _validator }\nconst nodeSyncer = new NodeSyncer()\nlet lastSync: SyncResult | null = null\nconst AUTO_SYNC_TIMEOUT_MS = 5_000 // cap how long kairos_prompt waits for n8n node sync\nconst stripper = new N8nFieldStripper()\nconst promptBuilder = new PromptBuilder(getMcpPatternsPath())\n\nfunction getMcpTelemetry(): TelemetryCollector | null {\n const val = process.env['KAIROS_TELEMETRY']\n if (!val || val === 'false') return null\n return val === 'true' ? new TelemetryCollector() : new TelemetryCollector(val)\n}\n\n/**\n * Derive the patterns.json path from KAIROS_TELEMETRY so the MCP server's\n * PromptBuilder reads from the same location that PatternAnalyzer.fromEnv() writes to.\n */\nfunction getMcpPatternsPath(): string {\n const val = process.env['KAIROS_TELEMETRY']\n if (val && val !== 'false' && val !== 'true') {\n return join(val, '..', 'patterns.json')\n }\n return join(homedir(), '.kairos', 'patterns.json')\n}\n\nconst mcpTelemetry = getMcpTelemetry()\n\ninterface McpBuildSession {\n description: string\n startTime: number\n validateAttempts: number\n warnedRules: number[]\n workflowType: string | null\n}\nconst mcpSessions = new Map<string, McpBuildSession>()\nconst SESSION_TTL_MS = 60 * 60 * 1000 // 1 hour: abandon sessions not completed by deploy\n\nfunction evictStaleSessions(): void {\n const cutoff = Date.now() - SESSION_TTL_MS\n for (const [id, session] of mcpSessions) {\n if (session.startTime < cutoff) mcpSessions.delete(id)\n }\n}\n\nfunction getTelemetryReader(): TelemetryReader | null {\n try {\n return new TelemetryReader()\n } catch {\n return null\n }\n}\n\nfunction isAllowed(action: 'deploy' | 'activate' | 'delete'): boolean {\n const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`\n return process.env[key] === 'true'\n}\n\ntype McpTextContent = { type: 'text'; text: string }\ntype McpToolResult = { content: McpTextContent[]; isError?: true }\n\nfunction mcpText(text: string): McpToolResult { return { content: [{ type: 'text', text }] } }\nfunction mcpError(text: string): McpToolResult { return { content: [{ type: 'text', text }], isError: true } }\n\n/**\n * Returns an error result if KAIROS_MCP_SECRET is set and the provided secret\n * doesn't match. Returns null if auth passes (no secret configured, or correct secret).\n */\nfunction checkMcpAuth(provided: string | undefined): McpToolResult | null {\n const expected = process.env['KAIROS_MCP_SECRET']\n if (!expected) return null\n if (provided === expected) return null\n return mcpError(JSON.stringify({ error: 'Unauthorized: missing or incorrect kairos_secret' }))\n}\n\nfunction getApiClient(): N8nApiClient {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n throw new GuardError('N8N_BASE_URL and N8N_API_KEY environment variables are required for n8n operations')\n }\n return new N8nApiClient(baseUrl, apiKey, nullLogger)\n}\n\nasync function autoSync(): Promise<SyncResult | null> {\n if (lastSync) return lastSync\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) return null\n try {\n const client = new N8nApiClient(baseUrl, apiKey, nullLogger)\n const nodeTypes = await client.getNodeTypes()\n if (nodeTypes.length === 0) return null\n lastSync = nodeSyncer.sync(nodeTypes)\n _validator = new N8nValidator(lastSync.registry)\n return lastSync\n } catch {\n return null\n }\n}\n\nconst server = new McpServer({\n name: 'kairos',\n version: pkg.version,\n})\n\n// ── Core generation tools (no API key needed) ──────────────────────────────\n\nserver.tool(\n 'kairos_prompt',\n 'Get the specialized n8n workflow generation context. Returns a system prompt with node catalog, connection rules, validation rules, plus library matches and failure patterns for the given description. Feed this to yourself as context, then generate the workflow JSON.',\n {\n description: z.string().describe('Plain-English description of the workflow to build'),\n name: z.string().optional().describe('Optional workflow name override'),\n },\n async ({ description, name }) => {\n evictStaleSessions()\n\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n return mcpError(JSON.stringify({ error: 'N8N_BASE_URL and N8N_API_KEY are required. Kairos needs to sync your n8n instance\\'s node types to generate accurate workflows.' }))\n }\n\n const runId = generateUUID()\n const workflowType = inferWorkflowType(description)\n\n // Start sync in background — race against timeout so slow n8n instances don't block the prompt\n const syncPromise = autoSync()\n const syncTimeout = new Promise<null>((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS))\n\n await library.initialize()\n const [syncResult, matches, failureRates] = await Promise.all([\n Promise.race([syncPromise, syncTimeout]),\n library.search(description),\n (async () => {\n const reader = getTelemetryReader()\n return reader ? reader.getFailureRates() : []\n })(),\n ])\n\n const request = { description, ...(name ? { name } : {}) }\n const built = promptBuilder.build(request, matches, failureRates, syncResult?.catalogText)\n\n if (mcpTelemetry) {\n mcpSessions.set(runId, {\n description,\n startTime: Date.now(),\n validateAttempts: 0,\n warnedRules: promptBuilder.getWarnedRules(),\n workflowType,\n })\n await mcpTelemetry.emit('build_start', { description, model: 'mcp-decomposed', dryRun: false }, runId)\n }\n\n const systemText = built.system.map(block => block.text).join('\\n\\n---\\n\\n')\n\n return mcpText(JSON.stringify({\n kairos_run_id: runId,\n mode: built.mode,\n matchCount: matches.length,\n topMatchScore: matches[0]?.score ?? null,\n nodeCatalog: syncResult ? 'synced' : 'static',\n nodeCount: syncResult?.nodeCount ?? null,\n ...(syncResult ? {} : { syncWarning: 'Could not sync node types from your n8n instance. Using static fallback catalog — generated workflows may not match your exact n8n setup.' }),\n systemPrompt: systemText,\n userMessage: built.userMessage,\n outputFormat: {\n description: 'Generate a JSON object with this exact structure. The workflow field contains the n8n workflow. credentialsNeeded lists services requiring credentials.',\n schema: {\n workflow: {\n name: 'string — descriptive workflow name',\n nodes: 'array — n8n node objects with id (UUID v4), type, typeVersion, name, position, parameters',\n connections: 'object — keyed by source node NAME, maps to target nodes',\n settings: 'object — include executionOrder: \"v1\"',\n },\n credentialsNeeded: [{\n service: 'string — e.g. \"Slack\"',\n credentialType: 'string — e.g. \"slackOAuth2Api\"',\n description: 'string — what the user needs to set up',\n }],\n },\n },\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_validate',\n 'Validate n8n workflow JSON against 34 structural rules. Returns pass/fail with specific issues. If validation fails, fix the issues and call this again. Errors block deployment; warnings are advisory.',\n {\n workflow: z.string().describe('The workflow JSON string to validate'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n },\n async ({ workflow: workflowStr, kairos_run_id }) => {\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpText(JSON.stringify({ valid: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }, null, 2))\n }\n\n const result = getValidator().validate(parsed)\n const errors = result.issues.filter(i => i.severity === 'error')\n const warnings = result.issues.filter(i => i.severity === 'warn')\n\n if (mcpTelemetry && kairos_run_id) {\n const session = mcpSessions.get(kairos_run_id)\n if (session) {\n session.validateAttempts++\n await mcpTelemetry.emit('generation_attempt', {\n description: session.description,\n attempt: session.validateAttempts,\n temperature: 0,\n durationMs: 0,\n tokensInput: 0,\n tokensOutput: 0,\n validationPassed: result.valid,\n issueCount: result.issues.length,\n issues: result.issues.map(i => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null })),\n workflowType: session.workflowType,\n }, kairos_run_id)\n }\n }\n\n return mcpText(JSON.stringify({\n valid: result.valid,\n errorCount: errors.length,\n warningCount: warnings.length,\n errors: errors.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n warnings: warnings.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n deployable: errors.length === 0,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_deploy',\n 'Deploy a validated workflow to n8n. Pass the workflow JSON that passed kairos_validate. Strips server-assigned fields automatically. Requires N8N_BASE_URL and N8N_API_KEY.',\n {\n workflow: z.string().describe('The validated workflow JSON string to deploy'),\n activate: z.boolean().default(false).describe('Activate the workflow immediately after deployment'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow: workflowStr, activate, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('deploy')) {\n return mcpError(JSON.stringify({ error: 'Deploy is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true to enable.' }))\n }\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before deploying',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForCreate(parsed)\n const response = await client.createWorkflow(stripped)\n\n if (activate) {\n if (!isAllowed('activate')) {\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: false,\n warning: 'Workflow deployed but activation is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.',\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2))\n }\n await client.activateWorkflow(response.id)\n }\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n\n // Warn when kairos_run_id is provided but no matching session exists — telemetry will be skipped\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found. This usually means kairos_deploy was called without a prior kairos_prompt call, or the session expired. Telemetry and pattern learning for this build were skipped.`\n : ''\n\n // Save to library (n8nWorkflowId enables dedup on future redeployment)\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? parsed.name,\n generationMode: 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: response.id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: activate,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_replace',\n 'Replace an existing n8n workflow with a new version. Validates before updating. Use kairos_prompt → kairos_validate → kairos_replace for iteration on existing workflows.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to replace'),\n workflow: z.string().describe('The validated workflow JSON string'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, workflow: workflowStr, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before replacing',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForUpdate(parsed)\n const response = await client.updateWorkflow(workflow_id, stripped)\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found.`\n : ''\n\n // Save to library — D4 dedup updates the existing entry rather than creating a duplicate\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? parsed.name,\n generationMode: 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: workflow_id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_search',\n 'Search the local workflow library for similar past builds. Returns matching workflows with scores, useful for finding examples and reusing patterns.',\n {\n query: z.string().describe('Search query — a workflow description or keywords'),\n limit: z.number().default(5).describe('Maximum number of results'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n const matches = await library.search(query)\n\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n description: m.workflow.description,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n failurePatterns: m.workflow.failurePatterns ?? [],\n })),\n null,\n 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_sync',\n 'Sync the node catalog from your live n8n instance. Fetches all installed node types and versions so Kairos knows exactly what your n8n supports. Automatically called by kairos_prompt when n8n credentials are set, but you can call this manually to force a refresh.',\n {},\n async () => {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n return mcpError(JSON.stringify({ error: 'N8N_BASE_URL and N8N_API_KEY are required for sync.' }))\n }\n\n lastSync = null\n const result = await autoSync()\n if (!result) {\n return mcpError(JSON.stringify({ error: 'Failed to fetch node types from n8n. Check your credentials and that your instance is running.' }))\n }\n\n return mcpText(JSON.stringify({\n synced: true,\n nodeCount: result.nodeCount,\n newNodes: result.newNodes,\n message: `Synced ${result.nodeCount} node types from your n8n instance (${result.newNodes} not in default catalog).`,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_patterns',\n 'Analyze telemetry data and return failure patterns, build stats, and credential breakdowns. Useful for understanding what goes wrong most often and how to prevent it.',\n {\n days: z.number().default(30).describe('Number of days of telemetry to analyze'),\n limit: z.number().optional().describe('Maximum number of failure patterns to return'),\n },\n async ({ days, limit }) => {\n const analyzer = PatternAnalyzer.fromEnv()\n const analysis = await analyzer.analyzeAndSave(days)\n\n if (limit !== undefined && limit > 0) {\n analysis.topFailureRules = analysis.topFailureRules.slice(0, limit)\n }\n\n return mcpText(JSON.stringify(analysis, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_library',\n 'Browse the local Kairos workflow library. Returns saved workflow metadata. Use the optional query to search, or omit it to list all entries.',\n {\n query: z.string().optional().describe('Optional search query — omit to list all entries'),\n limit: z.number().default(20).describe('Maximum entries to return'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n\n if (query) {\n const matches = await library.search(query)\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n description: m.workflow.description,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n deployCount: m.workflow.deployCount,\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n createdAt: m.workflow.createdAt,\n })),\n null, 2,\n ))\n }\n\n const all = await library.list()\n return mcpText(JSON.stringify(\n all.slice(0, limit).map(w => ({\n id: w.id,\n description: w.description,\n nodeCount: w.workflow.nodes.length,\n nodes: w.workflow.nodes.map(n => n.name),\n deployCount: w.deployCount,\n n8nWorkflowId: w.n8nWorkflowId ?? null,\n timesRetrieved: w.timesRetrieved ?? 0,\n createdAt: w.createdAt,\n })),\n null, 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_outcome',\n 'Record the outcome of a workflow build against a library entry. Trains the pattern learning system to know what works and what fails over time.',\n {\n library_id: z.string().describe('The Kairos library entry ID (returned by kairos_deploy, kairos_replace, or kairos_library)'),\n attempts: z.number().describe('Number of generation+validation attempts before success'),\n first_try_pass: z.boolean().describe('Whether the first attempt passed validation'),\n failed_rules: z.array(z.number()).describe('Validation rule IDs that failed during generation'),\n mode: z.enum(['direct', 'reference']).describe('How the library entry was used during generation'),\n },\n async ({ library_id, attempts, first_try_pass, failed_rules, mode }) => {\n await library.initialize()\n await library.recordOutcome(library_id, {\n attempts,\n firstTryPass: first_try_pass,\n failedRules: failed_rules,\n mode,\n })\n return mcpText(JSON.stringify({ recorded: true, libraryId: library_id }))\n },\n)\n\n// ── n8n management tools (need N8N_BASE_URL + N8N_API_KEY) ─────────────────\n\nserver.tool(\n 'kairos_list',\n 'List all workflows deployed on the connected n8n instance.',\n {},\n async () => {\n const client = getApiClient()\n const workflows = await client.listWorkflows()\n\n return mcpText(JSON.stringify(workflows, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_get',\n 'Get the full JSON definition of a specific workflow by ID.',\n {\n workflow_id: z.string().describe('The n8n workflow ID'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n const workflow = await client.getWorkflow(workflow_id)\n\n return mcpText(JSON.stringify(workflow, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_activate',\n 'Activate a deployed workflow so it starts running on triggers.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to activate'),\n },\n async ({ workflow_id }) => {\n if (!isAllowed('activate')) {\n return mcpError(JSON.stringify({ error: 'Activate is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.activateWorkflow(workflow_id)\n\n return mcpText(`Activated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_deactivate',\n 'Deactivate a running workflow.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to deactivate'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n await client.deactivateWorkflow(workflow_id)\n\n return mcpText(`Deactivated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_delete',\n 'Delete a workflow from n8n. This is irreversible.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to delete'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('delete')) {\n return mcpError(JSON.stringify({ error: 'Delete is disabled. Set KAIROS_MCP_ALLOW_DELETE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.deleteWorkflow(workflow_id)\n\n return mcpText(`Deleted workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_executions',\n 'List recent executions for a workflow, showing status and timing.',\n {\n workflow_id: z.string().optional().describe('Filter to a specific workflow ID (omit for all)'),\n limit: z.number().default(20).describe('Maximum number of executions to return'),\n },\n async ({ workflow_id, limit }) => {\n const client = getApiClient()\n const executions = await client.getExecutions(workflow_id, { limit })\n\n return mcpText(JSON.stringify(executions, null, 2))\n },\n)\n\nasync function main() {\n if (!process.env['ANTHROPIC_API_KEY']) {\n process.stderr.write(\n '[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set — kairos_prompt will fail. Set it before using workflow generation tools.\\n',\n )\n }\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err: unknown) => {\n console.error('Kairos MCP server failed to start:', err)\n process.exit(1)\n})\n","import type { N8nNodeTypeInfo } from '../providers/n8n/types.js'\nimport type { NodeDefinition } from './registry.js'\nimport { NodeRegistry, DEFAULT_REGISTRY } from './registry.js'\n\nconst TRIGGER_PATTERNS = [/trigger/i, /Trigger$/]\n\nexport interface SyncResult {\n registry: NodeRegistry\n catalogText: string\n nodeCount: number\n newNodes: number\n}\n\nexport class NodeSyncer {\n private readonly baseRegistry: Map<string, NodeDefinition>\n\n constructor() {\n this.baseRegistry = new Map(DEFAULT_REGISTRY.map(d => [d.type, d]))\n }\n\n sync(liveNodes: N8nNodeTypeInfo[]): SyncResult {\n const merged = new Map(this.baseRegistry)\n let newNodes = 0\n\n for (const node of liveNodes) {\n const versions = Array.isArray(node.version) ? node.version : [node.version]\n const isTrigger = TRIGGER_PATTERNS.some(p => p.test(node.name))\n const credentialType = node.credentials?.[0]?.name\n\n const existing = merged.get(node.name)\n if (existing) {\n const allVersions = new Set([...existing.safeTypeVersions, ...versions])\n merged.set(node.name, {\n ...existing,\n safeTypeVersions: [...allVersions].sort((a, b) => a - b),\n })\n } else {\n newNodes++\n merged.set(node.name, {\n type: node.name,\n safeTypeVersions: versions.sort((a, b) => a - b),\n requiredParams: [],\n ...(credentialType ? { credentialType } : {}),\n ...(isTrigger ? { isTrigger: true } : {}),\n })\n }\n }\n\n const definitions = [...merged.values()]\n const registry = new NodeRegistry(definitions)\n const catalogText = this.buildCatalog(definitions)\n\n return { registry, catalogText, nodeCount: definitions.length, newNodes }\n }\n\n private buildCatalog(definitions: NodeDefinition[]): string {\n const triggers = definitions.filter(d => d.isTrigger)\n const regular = definitions.filter(d => !d.isTrigger)\n\n const formatEntry = (d: NodeDefinition): string => {\n const versions = d.safeTypeVersions.join(', ')\n const cred = d.credentialType ? ` — cred: ${d.credentialType}` : ''\n return `${d.type} typeVersion: ${versions}${cred}`\n }\n\n const triggerLines = triggers.map(formatEntry).join('\\n')\n const regularLines = regular.map(formatEntry).join('\\n')\n\n return `## NODE CATALOG — synced from your n8n instance (${definitions.length} node types)\n\n### Triggers:\n${triggerLines}\n\n### Regular nodes:\n${regularLines}`\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp-server.ts","../src/validation/node-syncer.ts","../src/utils/node-catalog-cache.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Kairos MCP Server — decomposed architecture.\n *\n * The host LLM (Claude, GPT, Gemini, whatever) generates the workflow.\n * Kairos provides the knowledge (system prompt, library, failure patterns)\n * and guardrails (validator, deployer). Zero Anthropic API key needed.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'\nimport { createServer } from 'node:http'\nimport { z } from 'zod'\nimport { FileLibrary } from './library/file-library.js'\nimport { N8nValidator } from './validation/validator.js'\nimport { N8nFieldStripper } from './providers/n8n/stripper.js'\nimport { N8nApiClient } from './providers/n8n/api-client.js'\nimport { PromptBuilder } from './generation/prompt-builder.js'\nimport { TelemetryReader } from './telemetry/reader.js'\nimport { PatternAnalyzer } from './telemetry/pattern-analyzer.js'\nimport { NodeSyncer, type SyncResult } from './validation/node-syncer.js'\nimport { TelemetryCollector } from './telemetry/collector.js'\nimport { nullLogger } from './utils/logger.js'\nimport { GuardError } from './errors/guard-error.js'\nimport { generateUUID } from './utils/uuid.js'\nimport { inferWorkflowType } from './utils/workflow-type.js'\nimport type { N8nWorkflow } from './types/workflow.js'\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { fileURLToPath } from 'node:url'\nimport { readCatalogCache, writeCatalogCache } from './utils/node-catalog-cache.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')) as { version: string }\n\nconst library = new FileLibrary()\nlet _validator = new N8nValidator()\n// Accessor so kairos_sync can swap the registry without callers closing over the old instance\nfunction getValidator(): N8nValidator { return _validator }\nconst nodeSyncer = new NodeSyncer()\nlet lastSync: SyncResult | null = null\nconst AUTO_SYNC_TIMEOUT_MS = 5_000 // cap how long kairos_prompt waits for n8n node sync\nconst stripper = new N8nFieldStripper()\nconst promptBuilder = new PromptBuilder(getMcpPatternsPath())\n\nfunction getMcpTelemetry(): TelemetryCollector | null {\n const val = process.env['KAIROS_TELEMETRY']\n if (!val || val === 'false') return null\n return val === 'true' ? new TelemetryCollector() : new TelemetryCollector(val)\n}\n\n/**\n * Derive the patterns.json path from KAIROS_TELEMETRY so the MCP server's\n * PromptBuilder reads from the same location that PatternAnalyzer.fromEnv() writes to.\n */\nfunction getMcpPatternsPath(): string {\n const val = process.env['KAIROS_TELEMETRY']\n if (val && val !== 'false' && val !== 'true') {\n return join(val, '..', 'patterns.json')\n }\n return join(homedir(), '.kairos', 'patterns.json')\n}\n\nconst mcpTelemetry = getMcpTelemetry()\n\ninterface McpBuildSession {\n description: string\n startTime: number\n validateAttempts: number\n warnedRules: number[]\n workflowType: string | null\n matchCount: number\n}\nconst mcpSessions = new Map<string, McpBuildSession>()\nconst SESSION_TTL_MS = 60 * 60 * 1000 // 1 hour: abandon sessions not completed by deploy\n\nfunction evictStaleSessions(): void {\n const cutoff = Date.now() - SESSION_TTL_MS\n for (const [id, session] of mcpSessions) {\n if (session.startTime < cutoff) mcpSessions.delete(id)\n }\n}\n\nfunction getTelemetryReader(): TelemetryReader | null {\n try {\n return new TelemetryReader()\n } catch {\n return null\n }\n}\n\ntype McpMode = 'readonly' | 'validate' | 'deploy'\n\nfunction getMcpMode(): McpMode {\n const mode = process.env['KAIROS_MCP_MODE']?.toLowerCase()\n if (mode === 'readonly' || mode === 'validate') return mode\n return 'deploy'\n}\n\nfunction isAllowed(action: 'deploy' | 'activate' | 'delete'): boolean {\n // readonly and validate modes block all write ops — mode restriction overrides ALLOW_* flags\n const mode = getMcpMode()\n if (mode === 'readonly' || mode === 'validate') return false\n // deploy mode (default): require explicit opt-in via ALLOW_* flags (preserves existing behavior)\n const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`\n return process.env[key] === 'true'\n}\n\ntype McpTextContent = { type: 'text'; text: string }\ntype McpToolResult = { content: McpTextContent[]; isError?: true }\n\nfunction mcpText(text: string): McpToolResult { return { content: [{ type: 'text', text }] } }\nfunction mcpError(text: string): McpToolResult { return { content: [{ type: 'text', text }], isError: true } }\n\n/**\n * Returns an error result if KAIROS_MCP_SECRET is set and the provided secret\n * doesn't match. Returns null if auth passes (no secret configured, or correct secret).\n */\nfunction checkMcpAuth(provided: string | undefined): McpToolResult | null {\n const expected = process.env['KAIROS_MCP_SECRET']\n if (!expected) return null\n if (provided === expected) return null\n return mcpError(JSON.stringify({ error: 'Unauthorized: missing or incorrect kairos_secret' }))\n}\n\nfunction getApiClient(): N8nApiClient {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n throw new GuardError('N8N_BASE_URL and N8N_API_KEY environment variables are required for n8n operations')\n }\n return new N8nApiClient(baseUrl, apiKey, nullLogger)\n}\n\nfunction getCatalogCachePath(): string {\n const telemetry = process.env['KAIROS_TELEMETRY']\n const base = telemetry ? join(telemetry, '..') : join(homedir(), '.kairos')\n return join(base, 'node-catalog-cache.json')\n}\n\nasync function autoSync(): Promise<SyncResult | null> {\n if (lastSync) return lastSync\n\n // Try disk cache before hitting the network\n const cachePath = getCatalogCachePath()\n const cached = await readCatalogCache(cachePath)\n if (cached) {\n lastSync = cached\n _validator = new N8nValidator(lastSync.registry)\n return lastSync\n }\n\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) return null\n try {\n const client = new N8nApiClient(baseUrl, apiKey, nullLogger)\n const nodeTypes = await client.getNodeTypes()\n if (nodeTypes.length === 0) return null\n lastSync = nodeSyncer.sync(nodeTypes)\n _validator = new N8nValidator(lastSync.registry)\n writeCatalogCache(cachePath, lastSync).catch(() => {})\n return lastSync\n } catch {\n return null\n }\n}\n\nconst server = new McpServer({\n name: 'kairos',\n version: pkg.version,\n})\n\n// ── Core generation tools (no API key needed) ──────────────────────────────\n\nserver.tool(\n 'kairos_prompt',\n 'Get the specialized n8n workflow generation context. Returns a system prompt with node catalog, connection rules, validation rules, plus library matches and failure patterns for the given description. Feed this to yourself as context, then generate the workflow JSON.',\n {\n description: z.string().describe('Plain-English description of the workflow to build'),\n name: z.string().optional().describe('Optional workflow name override'),\n },\n async ({ description, name }) => {\n evictStaleSessions()\n\n const runId = generateUUID()\n const workflowType = inferWorkflowType(description)\n const hasN8nCreds = !!(process.env['N8N_BASE_URL'] && process.env['N8N_API_KEY'])\n\n // Start sync in background — race against timeout so slow n8n instances don't block the prompt\n const syncPromise = autoSync()\n const syncTimeout = new Promise<null>((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS))\n\n await library.initialize()\n const [syncResult, matches, failureRates] = await Promise.all([\n Promise.race([syncPromise, syncTimeout]),\n library.search(description),\n (async () => {\n const reader = getTelemetryReader()\n return reader ? reader.getFailureRates() : []\n })(),\n ])\n\n const request = { description, ...(name ? { name } : {}) }\n const built = promptBuilder.build(request, matches, failureRates, syncResult?.catalogText)\n\n if (mcpTelemetry) {\n mcpSessions.set(runId, {\n description,\n startTime: Date.now(),\n validateAttempts: 0,\n warnedRules: promptBuilder.getWarnedRules(),\n workflowType,\n matchCount: matches.length,\n })\n await mcpTelemetry.emit('build_start', { description, model: 'mcp-decomposed', dryRun: false }, runId)\n }\n\n const systemText = built.system.map(block => block.text).join('\\n\\n---\\n\\n')\n\n return mcpText(JSON.stringify({\n kairos_run_id: runId,\n mode: built.mode,\n matchCount: matches.length,\n topMatchScore: matches[0]?.score ?? null,\n nodeCatalog: syncResult ? 'synced' : 'static',\n nodeCount: syncResult?.nodeCount ?? null,\n ...(syncResult ? {} : {\n syncWarning: hasN8nCreds\n ? 'Could not sync node types from your n8n instance. Using static fallback catalog — generated workflows may not match your exact n8n setup.'\n : 'N8N_BASE_URL and N8N_API_KEY are not set. Using static fallback catalog — node types may not match your n8n instance. Set these env vars to enable accurate generation and deployment.',\n }),\n systemPrompt: systemText,\n userMessage: built.userMessage,\n outputFormat: {\n description: 'Generate a JSON object with this exact structure. The workflow field contains the n8n workflow. credentialsNeeded lists services requiring credentials.',\n schema: {\n workflow: {\n name: 'string — descriptive workflow name',\n nodes: 'array — n8n node objects with id (UUID v4), type, typeVersion, name, position, parameters',\n connections: 'object — keyed by source node NAME, maps to target nodes',\n settings: 'object — include executionOrder: \"v1\"',\n },\n credentialsNeeded: [{\n service: 'string — e.g. \"Slack\"',\n credentialType: 'string — e.g. \"slackOAuth2Api\"',\n description: 'string — what the user needs to set up',\n }],\n },\n },\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_validate',\n 'Validate n8n workflow JSON against 34 structural rules. Returns pass/fail with specific issues. If validation fails, fix the issues and call this again. Errors block deployment; warnings are advisory.',\n {\n workflow: z.string().describe('The workflow JSON string to validate'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n },\n async ({ workflow: workflowStr, kairos_run_id }) => {\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpText(JSON.stringify({ valid: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }, null, 2))\n }\n\n const result = getValidator().validate(parsed)\n const errors = result.issues.filter(i => i.severity === 'error')\n const warnings = result.issues.filter(i => i.severity === 'warn')\n\n if (mcpTelemetry && kairos_run_id) {\n const session = mcpSessions.get(kairos_run_id)\n if (session) {\n session.validateAttempts++\n await mcpTelemetry.emit('generation_attempt', {\n description: session.description,\n attempt: session.validateAttempts,\n temperature: 0,\n durationMs: 0,\n tokensInput: 0,\n tokensOutput: 0,\n validationPassed: result.valid,\n issueCount: result.issues.length,\n issues: result.issues.map(i => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null })),\n workflowType: session.workflowType,\n }, kairos_run_id)\n }\n }\n\n return mcpText(JSON.stringify({\n valid: result.valid,\n errorCount: errors.length,\n warningCount: warnings.length,\n errors: errors.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n warnings: warnings.map(i => ({ rule: i.rule, message: i.message, nodeId: i.nodeId ?? null })),\n deployable: errors.length === 0,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_deploy',\n 'Deploy a validated workflow to n8n. Pass the workflow JSON that passed kairos_validate. Strips server-assigned fields automatically. Requires N8N_BASE_URL and N8N_API_KEY.',\n {\n workflow: z.string().describe('The validated workflow JSON string to deploy'),\n activate: z.boolean().default(false).describe('Activate the workflow immediately after deployment'),\n description: z.string().optional().describe('The original user intent / description for this workflow — used to improve library search quality over time'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow: workflowStr, activate, description: userDescription, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('deploy')) {\n return mcpError(JSON.stringify({ error: 'Deploy is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true to enable.' }))\n }\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before deploying',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForCreate(parsed)\n const response = await client.createWorkflow(stripped)\n\n if (activate) {\n if (!isAllowed('activate')) {\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: false,\n warning: 'Workflow deployed but activation is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.',\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2))\n }\n await client.activateWorkflow(response.id)\n }\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n\n // Warn when kairos_run_id is provided but no matching session exists — telemetry will be skipped\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found. This usually means kairos_deploy was called without a prior kairos_prompt call, or the session expired. Telemetry and pattern learning for this build were skipped.`\n : ''\n\n // Save to library (n8nWorkflowId enables dedup on future redeployment)\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? userDescription ?? parsed.name,\n generationMode: session && session.matchCount > 0 ? 'reference' : 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: response.id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n activated: activate,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_replace',\n 'Replace an existing n8n workflow with a new version. Validates before updating. Use kairos_prompt → kairos_validate → kairos_replace for iteration on existing workflows.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to replace'),\n workflow: z.string().describe('The validated workflow JSON string'),\n description: z.string().optional().describe('The original user intent / description for this workflow — used to improve library search quality over time'),\n kairos_run_id: z.string().optional().describe('Run ID from kairos_prompt — enables telemetry correlation'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, workflow: workflowStr, description: userDescription, kairos_run_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('deploy')) {\n return mcpError(JSON.stringify({ error: 'Replace is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true or KAIROS_MCP_MODE=deploy to enable.' }))\n }\n\n let parsed: N8nWorkflow\n try {\n parsed = JSON.parse(workflowStr) as N8nWorkflow\n } catch (e) {\n return mcpError(JSON.stringify({ error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` }))\n }\n\n const validation = getValidator().validate(parsed)\n const errors = validation.issues.filter(i => i.severity === 'error')\n if (errors.length > 0) {\n return mcpError(JSON.stringify({\n error: 'Workflow has validation errors — fix them before replacing',\n errors: errors.map(i => ({ rule: i.rule, message: i.message })),\n }, null, 2))\n }\n\n const client = getApiClient()\n const stripped = stripper.stripForUpdate(parsed)\n const response = await client.updateWorkflow(workflow_id, stripped)\n\n const session = kairos_run_id ? mcpSessions.get(kairos_run_id) : undefined\n const missingSessionWarning = (kairos_run_id && !session)\n ? `\\n\\nNote: kairos_run_id \"${kairos_run_id}\" was provided but no active session was found.`\n : ''\n\n // Save to library — D4 dedup updates the existing entry rather than creating a duplicate\n await library.initialize()\n await library.save(parsed, {\n description: session?.description ?? userDescription ?? parsed.name,\n generationMode: session && session.matchCount > 0 ? 'reference' : 'scratch',\n generationAttempts: session?.validateAttempts ?? 1,\n n8nWorkflowId: workflow_id,\n })\n\n if (mcpTelemetry && kairos_run_id && session) {\n await mcpTelemetry.emit('build_complete', {\n description: session.description,\n success: true,\n totalAttempts: session.validateAttempts,\n totalDurationMs: Date.now() - session.startTime,\n totalTokensInput: 0,\n totalTokensOutput: 0,\n workflowName: response.name,\n workflowId: response.id,\n dryRun: false,\n credentialsNeeded: 0,\n warnedRules: session.warnedRules,\n workflowType: session.workflowType,\n }, kairos_run_id)\n mcpSessions.delete(kairos_run_id)\n PatternAnalyzer.fromEnv().analyzeAndSave().catch(() => {})\n }\n\n return mcpText(JSON.stringify({\n workflowId: response.id,\n name: response.name,\n url: `${process.env['N8N_BASE_URL']}/workflow/${response.id}`,\n }, null, 2) + missingSessionWarning)\n },\n)\n\nserver.tool(\n 'kairos_search',\n 'Search the local workflow library for similar past builds. Returns matching workflows with scores, useful for finding examples and reusing patterns.',\n {\n query: z.string().describe('Search query — a workflow description or keywords'),\n limit: z.number().default(5).describe('Maximum number of results'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n const matches = await library.search(query)\n\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n description: m.workflow.description,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n failurePatterns: m.workflow.failurePatterns ?? [],\n })),\n null,\n 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_sync',\n 'Sync the node catalog from your live n8n instance. Fetches all installed node types and versions so Kairos knows exactly what your n8n supports. Automatically called by kairos_prompt when n8n credentials are set, but you can call this manually to force a refresh.',\n {},\n async () => {\n const baseUrl = process.env['N8N_BASE_URL']\n const apiKey = process.env['N8N_API_KEY']\n if (!baseUrl || !apiKey) {\n return mcpError(JSON.stringify({ error: 'N8N_BASE_URL and N8N_API_KEY are required for sync.' }))\n }\n\n lastSync = null\n const result = await autoSync()\n if (!result) {\n return mcpError(JSON.stringify({ error: 'Failed to fetch node types from n8n. Check your credentials and that your instance is running.' }))\n }\n\n return mcpText(JSON.stringify({\n synced: true,\n nodeCount: result.nodeCount,\n newNodes: result.newNodes,\n message: `Synced ${result.nodeCount} node types from your n8n instance (${result.newNodes} not in default catalog).`,\n }, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_patterns',\n 'Analyze telemetry data and return failure patterns, build stats, and credential breakdowns. Useful for understanding what goes wrong most often and how to prevent it.',\n {\n days: z.number().default(30).describe('Number of days of telemetry to analyze'),\n limit: z.number().optional().describe('Maximum number of failure patterns to return'),\n },\n async ({ days, limit }) => {\n const analyzer = PatternAnalyzer.fromEnv()\n const analysis = await analyzer.analyzeAndSave(days)\n\n if (limit !== undefined && limit > 0) {\n analysis.topFailureRules = analysis.topFailureRules.slice(0, limit)\n }\n\n return mcpText(JSON.stringify(analysis, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_library',\n 'Browse the local Kairos workflow library. Returns saved workflow metadata. Use the optional query to search, or omit it to list all entries.',\n {\n query: z.string().optional().describe('Optional search query — omit to list all entries'),\n limit: z.number().default(20).describe('Maximum entries to return'),\n },\n async ({ query, limit }) => {\n await library.initialize()\n\n if (query) {\n const matches = await library.search(query)\n return mcpText(JSON.stringify(\n matches.slice(0, limit).map(m => ({\n id: m.workflow.id,\n description: m.workflow.description,\n score: Number(m.score.toFixed(3)),\n mode: m.mode,\n nodeCount: m.workflow.workflow.nodes.length,\n nodes: m.workflow.workflow.nodes.map(n => n.name),\n deployCount: m.workflow.deployCount,\n n8nWorkflowId: m.workflow.n8nWorkflowId ?? null,\n createdAt: m.workflow.createdAt,\n })),\n null, 2,\n ))\n }\n\n const all = await library.list()\n return mcpText(JSON.stringify(\n all.slice(0, limit).map(w => ({\n id: w.id,\n description: w.description,\n nodeCount: w.workflow.nodes.length,\n nodes: w.workflow.nodes.map(n => n.name),\n deployCount: w.deployCount,\n n8nWorkflowId: w.n8nWorkflowId ?? null,\n timesRetrieved: w.timesRetrieved ?? 0,\n createdAt: w.createdAt,\n })),\n null, 2,\n ))\n },\n)\n\nserver.tool(\n 'kairos_outcome',\n 'Record the outcome of a workflow build against a library entry. Trains the pattern learning system to know what works and what fails over time.',\n {\n library_id: z.string().describe('The Kairos library entry ID (returned by kairos_deploy, kairos_replace, or kairos_library)'),\n attempts: z.number().describe('Number of generation+validation attempts before success'),\n first_try_pass: z.boolean().describe('Whether the first attempt passed validation'),\n failed_rules: z.array(z.number()).describe('Validation rule IDs that failed during generation'),\n mode: z.enum(['direct', 'reference']).describe('How the library entry was used during generation'),\n },\n async ({ library_id, attempts, first_try_pass, failed_rules, mode }) => {\n await library.initialize()\n await library.recordOutcome(library_id, {\n attempts,\n firstTryPass: first_try_pass,\n failedRules: failed_rules,\n mode,\n })\n return mcpText(JSON.stringify({ recorded: true, libraryId: library_id }))\n },\n)\n\n// ── n8n management tools (need N8N_BASE_URL + N8N_API_KEY) ─────────────────\n\nserver.tool(\n 'kairos_list',\n 'List all workflows deployed on the connected n8n instance.',\n {},\n async () => {\n const client = getApiClient()\n const workflows = await client.listWorkflows()\n\n return mcpText(JSON.stringify(workflows, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_get',\n 'Get the full JSON definition of a specific workflow by ID.',\n {\n workflow_id: z.string().describe('The n8n workflow ID'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n const workflow = await client.getWorkflow(workflow_id)\n\n return mcpText(JSON.stringify(workflow, null, 2))\n },\n)\n\nserver.tool(\n 'kairos_activate',\n 'Activate a deployed workflow so it starts running on triggers.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to activate'),\n },\n async ({ workflow_id }) => {\n if (!isAllowed('activate')) {\n return mcpError(JSON.stringify({ error: 'Activate is disabled. Set KAIROS_MCP_ALLOW_ACTIVATE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.activateWorkflow(workflow_id)\n\n return mcpText(`Activated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_deactivate',\n 'Deactivate a running workflow.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to deactivate'),\n },\n async ({ workflow_id }) => {\n const client = getApiClient()\n await client.deactivateWorkflow(workflow_id)\n\n return mcpText(`Deactivated workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_delete',\n 'Delete a workflow from n8n. This is irreversible.',\n {\n workflow_id: z.string().describe('The n8n workflow ID to delete'),\n kairos_secret: z.string().optional().describe('Required when KAIROS_MCP_SECRET env var is set'),\n },\n async ({ workflow_id, kairos_secret }) => {\n const authError = checkMcpAuth(kairos_secret)\n if (authError) return authError\n\n if (!isAllowed('delete')) {\n return mcpError(JSON.stringify({ error: 'Delete is disabled. Set KAIROS_MCP_ALLOW_DELETE=true to enable.' }))\n }\n\n const client = getApiClient()\n await client.deleteWorkflow(workflow_id)\n\n return mcpText(`Deleted workflow ${workflow_id}`)\n },\n)\n\nserver.tool(\n 'kairos_executions',\n 'List recent executions for a workflow, showing status and timing.',\n {\n workflow_id: z.string().optional().describe('Filter to a specific workflow ID (omit for all)'),\n limit: z.number().default(20).describe('Maximum number of executions to return'),\n },\n async ({ workflow_id, limit }) => {\n const client = getApiClient()\n const executions = await client.getExecutions(workflow_id, { limit })\n\n return mcpText(JSON.stringify(executions, null, 2))\n },\n)\n\nasync function main() {\n if (!process.env['ANTHROPIC_API_KEY']) {\n process.stderr.write(\n '[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set — kairos_prompt will fail. Set it before using workflow generation tools.\\n',\n )\n }\n\n const useHttp = process.argv.includes('--http')\n\n if (useHttp) {\n const port = parseInt(process.env['KAIROS_MCP_PORT'] ?? '3000', 10)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const transport = new StreamableHTTPServerTransport() as any\n await server.connect(transport)\n\n const httpServer = createServer(async (req, res) => {\n if (req.method === 'GET' || req.method === 'POST' || req.method === 'DELETE') {\n await transport.handleRequest(req, res)\n } else {\n res.writeHead(405, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Method not allowed' }))\n }\n })\n\n httpServer.listen(port, () => {\n process.stderr.write(`[kairos-mcp] HTTP transport listening on port ${port}\\n`)\n })\n } else {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n }\n}\n\nmain().catch((err: unknown) => {\n console.error('Kairos MCP server failed to start:', err)\n process.exit(1)\n})\n","import type { N8nNodeTypeInfo } from '../providers/n8n/types.js'\nimport type { NodeDefinition } from './registry.js'\nimport { NodeRegistry, DEFAULT_REGISTRY } from './registry.js'\n\nconst TRIGGER_PATTERNS = [/trigger/i, /Trigger$/]\n\nexport interface SyncResult {\n registry: NodeRegistry\n catalogText: string\n nodeCount: number\n newNodes: number\n}\n\nexport class NodeSyncer {\n private readonly baseRegistry: Map<string, NodeDefinition>\n\n constructor() {\n this.baseRegistry = new Map(DEFAULT_REGISTRY.map(d => [d.type, d]))\n }\n\n sync(liveNodes: N8nNodeTypeInfo[]): SyncResult {\n const merged = new Map(this.baseRegistry)\n let newNodes = 0\n\n for (const node of liveNodes) {\n const versions = Array.isArray(node.version) ? node.version : [node.version]\n const isTrigger = TRIGGER_PATTERNS.some(p => p.test(node.name))\n const credentialType = node.credentials?.[0]?.name\n\n const existing = merged.get(node.name)\n if (existing) {\n const allVersions = new Set([...existing.safeTypeVersions, ...versions])\n merged.set(node.name, {\n ...existing,\n safeTypeVersions: [...allVersions].sort((a, b) => a - b),\n })\n } else {\n newNodes++\n merged.set(node.name, {\n type: node.name,\n safeTypeVersions: versions.sort((a, b) => a - b),\n requiredParams: [],\n ...(credentialType ? { credentialType } : {}),\n ...(isTrigger ? { isTrigger: true } : {}),\n })\n }\n }\n\n const definitions = [...merged.values()]\n const registry = new NodeRegistry(definitions)\n const catalogText = this.buildCatalog(definitions)\n\n return { registry, catalogText, nodeCount: definitions.length, newNodes }\n }\n\n private buildCatalog(definitions: NodeDefinition[]): string {\n const triggers = definitions.filter(d => d.isTrigger)\n const regular = definitions.filter(d => !d.isTrigger)\n\n const formatEntry = (d: NodeDefinition): string => {\n const versions = d.safeTypeVersions.join(', ')\n const cred = d.credentialType ? ` — cred: ${d.credentialType}` : ''\n return `${d.type} typeVersion: ${versions}${cred}`\n }\n\n const triggerLines = triggers.map(formatEntry).join('\\n')\n const regularLines = regular.map(formatEntry).join('\\n')\n\n return `## NODE CATALOG — synced from your n8n instance (${definitions.length} node types)\n\n### Triggers:\n${triggerLines}\n\n### Regular nodes:\n${regularLines}`\n }\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport type { SyncResult } from '../validation/node-syncer.js'\n\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours\n\ninterface CachedCatalog {\n cachedAt: number\n syncResult: SyncResult\n}\n\nexport async function readCatalogCache(cachePath: string): Promise<SyncResult | null> {\n try {\n const raw = await readFile(cachePath, 'utf-8')\n const cached = JSON.parse(raw) as CachedCatalog\n if (Date.now() - cached.cachedAt > CACHE_TTL_MS) return null\n return cached.syncResult\n } catch {\n return null\n }\n}\n\nexport async function writeCatalogCache(cachePath: string, syncResult: SyncResult): Promise<void> {\n try {\n await mkdir(dirname(cachePath), { recursive: true })\n const payload: CachedCatalog = { cachedAt: Date.now(), syncResult }\n await writeFile(cachePath, JSON.stringify(payload), 'utf-8')\n } catch {\n // Cache write failure is non-fatal — next startup will just re-fetch\n }\n}\n\nexport function isCacheExpired(cachedAt: number, ttlMs = CACHE_TTL_MS): boolean {\n return Date.now() - cachedAt > ttlMs\n}\n"],"mappings":"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","names":["dirname","dirname"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -489,6 +489,7 @@ declare class N8nValidator {
|
|
|
489
489
|
private checkRule31;
|
|
490
490
|
private checkRule32;
|
|
491
491
|
private checkRule33;
|
|
492
|
+
private checkRule35;
|
|
492
493
|
private checkRule34;
|
|
493
494
|
}
|
|
494
495
|
|
|
@@ -593,4 +594,4 @@ declare class TelemetryReader {
|
|
|
593
594
|
private readRecentEvents;
|
|
594
595
|
}
|
|
595
596
|
|
|
596
|
-
export {
|
|
597
|
+
export { type WorkflowMetadataInput as $, ApiError as A, type BuildOptions as B, type ClientOptions as C, DEFAULT_REGISTRY as D, type ExecutionDetail as E, type FailurePattern as F, GenerationError as G, type SyncProgress as H, type ILogger as I, TelemetryCollector as J, KairosError as K, type TelemetryEvent as L, TelemetryReader as M, N8nApiClient as N, type OutcomeData as O, ProviderError as P, TemplateSyncer as Q, ResponseParseError as R, type ScoredEntry as S, type Tag as T, type TrustLevel as U, ValidationError as V, type ValidationIssue as W, type ValidationResult as X, type WorkflowCluster as Y, type WorkflowListItem as Z, type WorkflowMatch as _, type AttemptMetadata as a, buildSearchCorpus as a0, clusterWorkflows as a1, hybridScore as a2, nullLogger as a3, rerank as a4, tokenize as a5, type BuildResult as b, type CredentialRequirement as c, type DeleteOptions as d, type DeployResult as e, type ExecutionFilter as f, type ExecutionSummary as g, FileLibrary as h, GuardError as i, type IProvider as j, type IWorkflowLibrary as k, type N8nConnections as l, N8nFieldStripper as m, type N8nNode as n, N8nProvider as o, type N8nSettings as p, N8nValidator as q, type N8nWorkflow as r, NodeRegistry as s, NullLibrary as t, type OutcomeStats as u, type RuleFailureRate as v, type SmokeTestResult as w, type SmokeTestStatus as x, type SourceKind as y, type StoredWorkflow as z };
|
|
@@ -489,6 +489,7 @@ declare class N8nValidator {
|
|
|
489
489
|
private checkRule31;
|
|
490
490
|
private checkRule32;
|
|
491
491
|
private checkRule33;
|
|
492
|
+
private checkRule35;
|
|
492
493
|
private checkRule34;
|
|
493
494
|
}
|
|
494
495
|
|
|
@@ -593,4 +594,4 @@ declare class TelemetryReader {
|
|
|
593
594
|
private readRecentEvents;
|
|
594
595
|
}
|
|
595
596
|
|
|
596
|
-
export {
|
|
597
|
+
export { type WorkflowMetadataInput as $, ApiError as A, type BuildOptions as B, type ClientOptions as C, DEFAULT_REGISTRY as D, type ExecutionDetail as E, type FailurePattern as F, GenerationError as G, type SyncProgress as H, type ILogger as I, TelemetryCollector as J, KairosError as K, type TelemetryEvent as L, TelemetryReader as M, N8nApiClient as N, type OutcomeData as O, ProviderError as P, TemplateSyncer as Q, ResponseParseError as R, type ScoredEntry as S, type Tag as T, type TrustLevel as U, ValidationError as V, type ValidationIssue as W, type ValidationResult as X, type WorkflowCluster as Y, type WorkflowListItem as Z, type WorkflowMatch as _, type AttemptMetadata as a, buildSearchCorpus as a0, clusterWorkflows as a1, hybridScore as a2, nullLogger as a3, rerank as a4, tokenize as a5, type BuildResult as b, type CredentialRequirement as c, type DeleteOptions as d, type DeployResult as e, type ExecutionFilter as f, type ExecutionSummary as g, FileLibrary as h, GuardError as i, type IProvider as j, type IWorkflowLibrary as k, type N8nConnections as l, N8nFieldStripper as m, type N8nNode as n, N8nProvider as o, type N8nSettings as p, N8nValidator as q, type N8nWorkflow as r, NodeRegistry as s, NullLibrary as t, type OutcomeStats as u, type RuleFailureRate as v, type SmokeTestResult as w, type SmokeTestStatus as x, type SourceKind as y, type StoredWorkflow as z };
|
package/dist/standalone.cjs
CHANGED
|
@@ -1388,6 +1388,7 @@ var N8nValidator = class {
|
|
|
1388
1388
|
this.checkRule32(workflow, issues);
|
|
1389
1389
|
this.checkRule33(workflow, issues);
|
|
1390
1390
|
this.checkRule34(workflow, issues);
|
|
1391
|
+
this.checkRule35(workflow, issues);
|
|
1391
1392
|
if (Array.isArray(workflow.nodes)) {
|
|
1392
1393
|
const nodeById = new Map(workflow.nodes.map((n) => [n.id, n.type]));
|
|
1393
1394
|
for (const issue of issues) {
|
|
@@ -1960,6 +1961,43 @@ var N8nValidator = class {
|
|
|
1960
1961
|
}
|
|
1961
1962
|
}
|
|
1962
1963
|
}
|
|
1964
|
+
// Rule 35 (WARN): email-sending node with no duplicate-prevention signal
|
|
1965
|
+
checkRule35(w, issues) {
|
|
1966
|
+
if (!Array.isArray(w.nodes)) return;
|
|
1967
|
+
const sendNodes = w.nodes.filter((node) => {
|
|
1968
|
+
if (node.type === "n8n-nodes-base.gmail") {
|
|
1969
|
+
const op = node.parameters?.["operation"];
|
|
1970
|
+
return !op || op === "send" || op === "sendEmail" || op === "reply";
|
|
1971
|
+
}
|
|
1972
|
+
return node.type === "n8n-nodes-base.emailSend" || node.type === "n8n-nodes-base.sendEmail";
|
|
1973
|
+
});
|
|
1974
|
+
if (sendNodes.length === 0) return;
|
|
1975
|
+
const workflowText = JSON.stringify(w).toLowerCase();
|
|
1976
|
+
const IDEMPOTENCY_SIGNALS = [
|
|
1977
|
+
"sent_at",
|
|
1978
|
+
"last_sent",
|
|
1979
|
+
"last_reminder",
|
|
1980
|
+
"processed_at",
|
|
1981
|
+
"already_sent",
|
|
1982
|
+
"email_sent",
|
|
1983
|
+
"notified_at",
|
|
1984
|
+
"reminder_sent",
|
|
1985
|
+
"contacted_at",
|
|
1986
|
+
"dedupe",
|
|
1987
|
+
"idempotent"
|
|
1988
|
+
];
|
|
1989
|
+
const hasIdempotencySignal = IDEMPOTENCY_SIGNALS.some((s) => workflowText.includes(s));
|
|
1990
|
+
if (!hasIdempotencySignal) {
|
|
1991
|
+
for (const node of sendNodes) {
|
|
1992
|
+
this.warn(
|
|
1993
|
+
issues,
|
|
1994
|
+
35,
|
|
1995
|
+
`Node "${node.name}" sends email but no duplicate-prevention signal detected \u2014 add a sent_at timestamp field, a prior-send IF check, or a deduplication key to avoid repeat sends`,
|
|
1996
|
+
node.id
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
1963
2001
|
// Rule 34 (WARN): webhook path contains spaces, starts with slash, or looks like a full URL
|
|
1964
2002
|
checkRule34(w, issues) {
|
|
1965
2003
|
if (!Array.isArray(w.nodes)) return;
|
|
@@ -2394,7 +2432,7 @@ var import_node_path5 = require("path");
|
|
|
2394
2432
|
var import_node_os4 = require("os");
|
|
2395
2433
|
|
|
2396
2434
|
// src/validation/rule-metadata.ts
|
|
2397
|
-
var VALIDATOR_RULE_IDS = Array.from({ length:
|
|
2435
|
+
var VALIDATOR_RULE_IDS = Array.from({ length: 35 }, (_, i) => i + 1);
|
|
2398
2436
|
var RULE_PIPELINE_STAGES = {
|
|
2399
2437
|
1: "node_generation",
|
|
2400
2438
|
2: "node_generation",
|
|
@@ -2429,7 +2467,8 @@ var RULE_PIPELINE_STAGES = {
|
|
|
2429
2467
|
31: "node_generation",
|
|
2430
2468
|
32: "node_generation",
|
|
2431
2469
|
33: "node_generation",
|
|
2432
|
-
34: "node_generation"
|
|
2470
|
+
34: "node_generation",
|
|
2471
|
+
35: "node_generation"
|
|
2433
2472
|
};
|
|
2434
2473
|
var RULE_MITIGATIONS = {
|
|
2435
2474
|
1: "Provide a non-empty workflow name string",
|
|
@@ -2465,7 +2504,8 @@ var RULE_MITIGATIONS = {
|
|
|
2465
2504
|
31: "Add at least one condition to the if node \u2014 conditions.conditions array must be non-empty",
|
|
2466
2505
|
32: "Add field assignments to the set node \u2014 assignments.assignments array must be non-empty for typeVersion 3.x",
|
|
2467
2506
|
33: "Add at least one schedule rule to scheduleTrigger \u2014 rule.interval array must have at least one entry",
|
|
2468
|
-
34: 'Webhook path must be a relative path without spaces, leading slashes, or protocol prefixes (e.g. "my-hook")'
|
|
2507
|
+
34: 'Webhook path must be a relative path without spaces, leading slashes, or protocol prefixes (e.g. "my-hook")',
|
|
2508
|
+
35: "Add duplicate-prevention to email-sending workflows: a sent_at timestamp field updated after each send, or an IF node that checks prior-send status before sending"
|
|
2469
2509
|
};
|
|
2470
2510
|
|
|
2471
2511
|
// src/telemetry/pattern-analyzer.ts
|
|
@@ -2523,7 +2563,7 @@ var PatternAnalyzer = class _PatternAnalyzer {
|
|
|
2523
2563
|
this._cachedEvents = events;
|
|
2524
2564
|
const starts = events.filter((e) => e.eventType === "build_start");
|
|
2525
2565
|
const attempts = events.filter((e) => e.eventType === "generation_attempt");
|
|
2526
|
-
const
|
|
2566
|
+
const _passed = attempts.filter(
|
|
2527
2567
|
(a) => a.data.validationPassed === true
|
|
2528
2568
|
);
|
|
2529
2569
|
const failed = attempts.filter(
|