@kairos-sdk/core 0.5.0 → 0.5.1

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/dist/index.d.cts CHANGED
@@ -4,7 +4,6 @@ export { A as ApiError, d as AttemptMetadata, e as CredentialRequirement, f as D
4
4
  declare class Kairos {
5
5
  private readonly provider;
6
6
  private readonly designer;
7
- private readonly validator;
8
7
  private readonly library;
9
8
  private readonly logger;
10
9
  private readonly telemetry;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,6 @@ export { A as ApiError, d as AttemptMetadata, e as CredentialRequirement, f as D
4
4
  declare class Kairos {
5
5
  private readonly provider;
6
6
  private readonly designer;
7
- private readonly validator;
8
7
  private readonly library;
9
8
  private readonly logger;
10
9
  private readonly telemetry;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Kairos
3
- } from "./chunk-HBGZTUUZ.js";
4
- import "./chunk-EVOAYH2K.js";
3
+ } from "./chunk-GVZKMS53.js";
4
+ import "./chunk-V2IZBZGB.js";
5
5
  import "./chunk-6FOFWVMG.js";
6
6
  import {
7
7
  GenerationError,
@@ -10,7 +10,7 @@ import {
10
10
  ResponseParseError,
11
11
  TemplateSyncer,
12
12
  ValidationError
13
- } from "./chunk-5GAY7CSJ.js";
13
+ } from "./chunk-MYAGTDQ2.js";
14
14
  import {
15
15
  ApiError,
16
16
  DEFAULT_REGISTRY,
@@ -30,7 +30,7 @@ import {
30
30
  nullLogger,
31
31
  rerank,
32
32
  tokenize
33
- } from "./chunk-KIFT5LA7.js";
33
+ } from "./chunk-VPPWTMRJ.js";
34
34
  export {
35
35
  ApiError,
36
36
  DEFAULT_REGISTRY,
@@ -4,6 +4,8 @@
4
4
  // src/mcp-server.ts
5
5
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
6
6
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
7
+ var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
8
+ var import_node_http = require("http");
7
9
  var import_zod = require("zod");
8
10
 
9
11
  // src/library/file-library.ts
@@ -2594,7 +2596,7 @@ var PatternAnalyzer = class _PatternAnalyzer {
2594
2596
  this._cachedEvents = events;
2595
2597
  const starts = events.filter((e) => e.eventType === "build_start");
2596
2598
  const attempts = events.filter((e) => e.eventType === "generation_attempt");
2597
- const passed = attempts.filter(
2599
+ const _passed = attempts.filter(
2598
2600
  (a) => a.data.validationPassed === true
2599
2601
  );
2600
2602
  const failed = attempts.filter(
@@ -3177,12 +3179,37 @@ function inferWorkflowType(description) {
3177
3179
 
3178
3180
  // src/mcp-server.ts
3179
3181
  var import_node_fs3 = require("fs");
3180
- var import_node_path7 = require("path");
3182
+ var import_node_path8 = require("path");
3181
3183
  var import_node_os6 = require("os");
3182
3184
  var import_node_url = require("url");
3185
+
3186
+ // src/utils/node-catalog-cache.ts
3187
+ var import_promises5 = require("fs/promises");
3188
+ var import_node_path7 = require("path");
3189
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
3190
+ async function readCatalogCache(cachePath) {
3191
+ try {
3192
+ const raw = await (0, import_promises5.readFile)(cachePath, "utf-8");
3193
+ const cached = JSON.parse(raw);
3194
+ if (Date.now() - cached.cachedAt > CACHE_TTL_MS) return null;
3195
+ return cached.syncResult;
3196
+ } catch {
3197
+ return null;
3198
+ }
3199
+ }
3200
+ async function writeCatalogCache(cachePath, syncResult) {
3201
+ try {
3202
+ await (0, import_promises5.mkdir)((0, import_node_path7.dirname)(cachePath), { recursive: true });
3203
+ const payload = { cachedAt: Date.now(), syncResult };
3204
+ await (0, import_promises5.writeFile)(cachePath, JSON.stringify(payload), "utf-8");
3205
+ } catch {
3206
+ }
3207
+ }
3208
+
3209
+ // src/mcp-server.ts
3183
3210
  var import_meta = {};
3184
- var __dirname = (0, import_node_path7.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
3185
- var pkg = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path7.join)(__dirname, "..", "package.json"), "utf-8"));
3211
+ var __dirname = (0, import_node_path8.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
3212
+ var pkg = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path8.join)(__dirname, "..", "package.json"), "utf-8"));
3186
3213
  var library = new FileLibrary();
3187
3214
  var _validator = new N8nValidator();
3188
3215
  function getValidator() {
@@ -3201,9 +3228,9 @@ function getMcpTelemetry() {
3201
3228
  function getMcpPatternsPath() {
3202
3229
  const val = process.env["KAIROS_TELEMETRY"];
3203
3230
  if (val && val !== "false" && val !== "true") {
3204
- return (0, import_node_path7.join)(val, "..", "patterns.json");
3231
+ return (0, import_node_path8.join)(val, "..", "patterns.json");
3205
3232
  }
3206
- return (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".kairos", "patterns.json");
3233
+ return (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".kairos", "patterns.json");
3207
3234
  }
3208
3235
  var mcpTelemetry = getMcpTelemetry();
3209
3236
  var mcpSessions = /* @__PURE__ */ new Map();
@@ -3221,7 +3248,14 @@ function getTelemetryReader() {
3221
3248
  return null;
3222
3249
  }
3223
3250
  }
3251
+ function getMcpMode() {
3252
+ const mode = process.env["KAIROS_MCP_MODE"]?.toLowerCase();
3253
+ if (mode === "readonly" || mode === "validate") return mode;
3254
+ return "deploy";
3255
+ }
3224
3256
  function isAllowed(action) {
3257
+ const mode = getMcpMode();
3258
+ if (mode === "readonly" || mode === "validate") return false;
3225
3259
  const key = `KAIROS_MCP_ALLOW_${action.toUpperCase()}`;
3226
3260
  return process.env[key] === "true";
3227
3261
  }
@@ -3245,8 +3279,20 @@ function getApiClient() {
3245
3279
  }
3246
3280
  return new N8nApiClient(baseUrl, apiKey, nullLogger);
3247
3281
  }
3282
+ function getCatalogCachePath() {
3283
+ const telemetry = process.env["KAIROS_TELEMETRY"];
3284
+ const base = telemetry ? (0, import_node_path8.join)(telemetry, "..") : (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".kairos");
3285
+ return (0, import_node_path8.join)(base, "node-catalog-cache.json");
3286
+ }
3248
3287
  async function autoSync() {
3249
3288
  if (lastSync) return lastSync;
3289
+ const cachePath = getCatalogCachePath();
3290
+ const cached = await readCatalogCache(cachePath);
3291
+ if (cached) {
3292
+ lastSync = cached;
3293
+ _validator = new N8nValidator(lastSync.registry);
3294
+ return lastSync;
3295
+ }
3250
3296
  const baseUrl = process.env["N8N_BASE_URL"];
3251
3297
  const apiKey = process.env["N8N_API_KEY"];
3252
3298
  if (!baseUrl || !apiKey) return null;
@@ -3256,6 +3302,8 @@ async function autoSync() {
3256
3302
  if (nodeTypes.length === 0) return null;
3257
3303
  lastSync = nodeSyncer.sync(nodeTypes);
3258
3304
  _validator = new N8nValidator(lastSync.registry);
3305
+ writeCatalogCache(cachePath, lastSync).catch(() => {
3306
+ });
3259
3307
  return lastSync;
3260
3308
  } catch {
3261
3309
  return null;
@@ -3274,13 +3322,9 @@ server.tool(
3274
3322
  },
3275
3323
  async ({ description, name }) => {
3276
3324
  evictStaleSessions();
3277
- const baseUrl = process.env["N8N_BASE_URL"];
3278
- const apiKey = process.env["N8N_API_KEY"];
3279
- if (!baseUrl || !apiKey) {
3280
- 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." }));
3281
- }
3282
3325
  const runId = generateUUID();
3283
3326
  const workflowType = inferWorkflowType(description);
3327
+ const hasN8nCreds = !!(process.env["N8N_BASE_URL"] && process.env["N8N_API_KEY"]);
3284
3328
  const syncPromise = autoSync();
3285
3329
  const syncTimeout = new Promise((resolve) => setTimeout(() => resolve(null), AUTO_SYNC_TIMEOUT_MS));
3286
3330
  await library.initialize();
@@ -3300,7 +3344,8 @@ server.tool(
3300
3344
  startTime: Date.now(),
3301
3345
  validateAttempts: 0,
3302
3346
  warnedRules: promptBuilder.getWarnedRules(),
3303
- workflowType
3347
+ workflowType,
3348
+ matchCount: matches.length
3304
3349
  });
3305
3350
  await mcpTelemetry.emit("build_start", { description, model: "mcp-decomposed", dryRun: false }, runId);
3306
3351
  }
@@ -3312,7 +3357,9 @@ server.tool(
3312
3357
  topMatchScore: matches[0]?.score ?? null,
3313
3358
  nodeCatalog: syncResult ? "synced" : "static",
3314
3359
  nodeCount: syncResult?.nodeCount ?? null,
3315
- ...syncResult ? {} : { syncWarning: "Could not sync node types from your n8n instance. Using static fallback catalog \u2014 generated workflows may not match your exact n8n setup." },
3360
+ ...syncResult ? {} : {
3361
+ 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."
3362
+ },
3316
3363
  systemPrompt: systemText,
3317
3364
  userMessage: built.userMessage,
3318
3365
  outputFormat: {
@@ -3385,10 +3432,11 @@ server.tool(
3385
3432
  {
3386
3433
  workflow: import_zod.z.string().describe("The validated workflow JSON string to deploy"),
3387
3434
  activate: import_zod.z.boolean().default(false).describe("Activate the workflow immediately after deployment"),
3435
+ description: import_zod.z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
3388
3436
  kairos_run_id: import_zod.z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
3389
3437
  kairos_secret: import_zod.z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
3390
3438
  },
3391
- async ({ workflow: workflowStr, activate, kairos_run_id, kairos_secret }) => {
3439
+ async ({ workflow: workflowStr, activate, description: userDescription, kairos_run_id, kairos_secret }) => {
3392
3440
  const authError = checkMcpAuth(kairos_secret);
3393
3441
  if (authError) return authError;
3394
3442
  if (!isAllowed("deploy")) {
@@ -3429,8 +3477,8 @@ server.tool(
3429
3477
  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.` : "";
3430
3478
  await library.initialize();
3431
3479
  await library.save(parsed, {
3432
- description: session?.description ?? parsed.name,
3433
- generationMode: "scratch",
3480
+ description: session?.description ?? userDescription ?? parsed.name,
3481
+ generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
3434
3482
  generationAttempts: session?.validateAttempts ?? 1,
3435
3483
  n8nWorkflowId: response.id
3436
3484
  });
@@ -3467,12 +3515,16 @@ server.tool(
3467
3515
  {
3468
3516
  workflow_id: import_zod.z.string().describe("The n8n workflow ID to replace"),
3469
3517
  workflow: import_zod.z.string().describe("The validated workflow JSON string"),
3518
+ description: import_zod.z.string().optional().describe("The original user intent / description for this workflow \u2014 used to improve library search quality over time"),
3470
3519
  kairos_run_id: import_zod.z.string().optional().describe("Run ID from kairos_prompt \u2014 enables telemetry correlation"),
3471
3520
  kairos_secret: import_zod.z.string().optional().describe("Required when KAIROS_MCP_SECRET env var is set")
3472
3521
  },
3473
- async ({ workflow_id, workflow: workflowStr, kairos_run_id, kairos_secret }) => {
3522
+ async ({ workflow_id, workflow: workflowStr, description: userDescription, kairos_run_id, kairos_secret }) => {
3474
3523
  const authError = checkMcpAuth(kairos_secret);
3475
3524
  if (authError) return authError;
3525
+ if (!isAllowed("deploy")) {
3526
+ return mcpError(JSON.stringify({ error: "Replace is disabled. Set KAIROS_MCP_ALLOW_DEPLOY=true or KAIROS_MCP_MODE=deploy to enable." }));
3527
+ }
3476
3528
  let parsed;
3477
3529
  try {
3478
3530
  parsed = JSON.parse(workflowStr);
@@ -3496,8 +3548,8 @@ server.tool(
3496
3548
  Note: kairos_run_id "${kairos_run_id}" was provided but no active session was found.` : "";
3497
3549
  await library.initialize();
3498
3550
  await library.save(parsed, {
3499
- description: session?.description ?? parsed.name,
3500
- generationMode: "scratch",
3551
+ description: session?.description ?? userDescription ?? parsed.name,
3552
+ generationMode: session && session.matchCount > 0 ? "reference" : "scratch",
3501
3553
  generationAttempts: session?.validateAttempts ?? 1,
3502
3554
  n8nWorkflowId: workflow_id
3503
3555
  });
@@ -3743,8 +3795,27 @@ async function main() {
3743
3795
  "[kairos-mcp] WARNING: ANTHROPIC_API_KEY is not set \u2014 kairos_prompt will fail. Set it before using workflow generation tools.\n"
3744
3796
  );
3745
3797
  }
3746
- const transport = new import_stdio.StdioServerTransport();
3747
- await server.connect(transport);
3798
+ const useHttp = process.argv.includes("--http");
3799
+ if (useHttp) {
3800
+ const port = parseInt(process.env["KAIROS_MCP_PORT"] ?? "3000", 10);
3801
+ const transport = new import_streamableHttp.StreamableHTTPServerTransport();
3802
+ await server.connect(transport);
3803
+ const httpServer = (0, import_node_http.createServer)(async (req, res) => {
3804
+ if (req.method === "GET" || req.method === "POST" || req.method === "DELETE") {
3805
+ await transport.handleRequest(req, res);
3806
+ } else {
3807
+ res.writeHead(405, { "Content-Type": "application/json" });
3808
+ res.end(JSON.stringify({ error: "Method not allowed" }));
3809
+ }
3810
+ });
3811
+ httpServer.listen(port, () => {
3812
+ process.stderr.write(`[kairos-mcp] HTTP transport listening on port ${port}
3813
+ `);
3814
+ });
3815
+ } else {
3816
+ const transport = new import_stdio.StdioServerTransport();
3817
+ await server.connect(transport);
3818
+ }
3748
3819
  }
3749
3820
  main().catch((err) => {
3750
3821
  console.error("Kairos MCP server failed to start:", err);