@path58/p58-n8n 0.2.5 → 0.2.7

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/AGENT_INSTALL.md CHANGED
@@ -232,4 +232,4 @@ Once installed, the user can ask their AI assistant to:
232
232
 
233
233
  ---
234
234
 
235
- **Package:** `@path58/p58-n8n` | **npm:** https://www.npmjs.com/package/@path58/p58-n8n | **Version:** 0.2.4+
235
+ **Package:** `@path58/p58-n8n` | **npm:** https://www.npmjs.com/package/@path58/p58-n8n | **Version:** 0.2.6+
package/CHANGELOG.md CHANGED
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.7] - 2026-03-11
9
+
10
+ ### Fixed
11
+
12
+ - **LangChain connection type pattern fallback** — `ConnectionTypeResolver` now includes a static prefix-based resolver that correctly identifies LangChain AI sub-node types (`ai_languageModel`, `ai_tool`, `ai_memory`, etc.) even when the database is unreachable (e.g., Supabase circuit breaker open). Previously, any DB connectivity failure caused all connections to silently default to `"main"`, breaking LangChain workflows.
13
+ - Error message for DB load failures now surfaces the actual error string for easier diagnostics
14
+
15
+ ## [0.2.6] - 2026-03-11
16
+
17
+ ### Added
18
+
19
+ - **LangChain AI connection type support** — `build_workflow` now automatically detects and wires all 10 LangChain connection types (`ai_languageModel`, `ai_tool`, `ai_memory`, `ai_embedding`, `ai_outputParser`, `ai_document`, `ai_vectorStore`, `ai_textSplitter`, `ai_retriever`, `ai_reranker`) instead of hardcoding all connections as `main`
20
+ - **Sub-node positioning** — LLM models, memory modules, and tools now position below their parent chain/agent node in the n8n UI (same X, Y+200), matching native n8n layout conventions
21
+ - **3-tier connection type resolver** — explicit user override → `connection_rules` catalog lookup (496 AI rules) → `ai_nodes.ai_type` fallback → `main` default
22
+ - **`is_sub_node` metadata coverage** — backfilled from ~45% to 98.4% across all AI nodes in catalog
23
+
24
+ ### Fixed
25
+
26
+ - LangChain workflows now deploy with correct connection wiring — models appear on diamond ports, not circle ports
27
+ - v0.2.5 was published with stale bundle (did not include LangChain fix); this release rebuilds from fixed source with correct `SERVER_VERSION`
28
+
29
+ ## [0.2.5] - 2026-03-11
30
+
31
+ ### Fixed
32
+
33
+ - Internal release — npm publish from stale source (superseded by v0.2.6)
34
+
8
35
  ## [0.2.4] - 2026-03-10
9
36
 
10
37
  ### Added
@@ -130,6 +157,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
130
157
  - npm package published as `@path58/p58-n8n`
131
158
  - ESM module support with shebang for direct `npx` execution
132
159
 
160
+ [0.2.7]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.7
161
+ [0.2.6]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.6
162
+ [0.2.5]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.5
133
163
  [0.2.4]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.4
134
164
  [0.2.3]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.3
135
165
  [0.2.2]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.2
@@ -3014,22 +3014,28 @@ function buildPoolOptions() {
3014
3014
  }
3015
3015
  function createPool(cfg) {
3016
3016
  const timeoutMs = getEnvNumber("DB_STATEMENT_TIMEOUT_MS", 3e4);
3017
+ const isCatalogOnly = !!(process.env.P58_DATABASE_URL || process.env.MCP_DATABASE_URL) && !process.env.N8N_API_KEY;
3017
3018
  const pool = new import_pg.Pool({
3018
3019
  host: cfg.host,
3019
3020
  port: resolvePoolPort(cfg),
3020
3021
  user: cfg.user,
3021
3022
  password: cfg.password,
3022
3023
  database: cfg.database,
3023
- max: getEnvNumber("VALIDATOR_DB_POOL_MAX", 10),
3024
- min: getEnvNumber("VALIDATOR_DB_POOL_MIN", 1),
3024
+ // Friends: 3 max (1 active + 2 burst). Full mode: 10.
3025
+ max: getEnvNumber("VALIDATOR_DB_POOL_MAX", isCatalogOnly ? 3 : 10),
3026
+ // Friends: 0 min — don't hold connections when idle. Full mode: 1.
3027
+ min: getEnvNumber("VALIDATOR_DB_POOL_MIN", isCatalogOnly ? 0 : 1),
3025
3028
  // allowExitOnIdle: true — prevents zombie processes when MCP session ends (RAG-4.35.5)
3026
- idleTimeoutMillis: 6e4,
3027
- connectionTimeoutMillis: 15e3,
3029
+ // Friends: 30s idle (release fast). Full mode: 60s.
3030
+ idleTimeoutMillis: isCatalogOnly ? 3e4 : 6e4,
3031
+ // Friends: 10s connect timeout (fail fast). Full mode: 15s.
3032
+ connectionTimeoutMillis: isCatalogOnly ? 1e4 : 15e3,
3028
3033
  statement_timeout: timeoutMs,
3029
3034
  options: buildPoolOptions(),
3030
3035
  allowExitOnIdle: true,
3031
- keepAlive: true,
3032
- keepAliveInitialDelayMillis: 1e4
3036
+ // Friends: no keepAlive (let connections close naturally). Full mode: keepAlive.
3037
+ keepAlive: !isCatalogOnly,
3038
+ ...isCatalogOnly ? {} : { keepAliveInitialDelayMillis: 1e4 }
3033
3039
  });
3034
3040
  pool.on("error", (err) => {
3035
3041
  console.warn(`[pg-pool] Idle connection error (non-fatal): ${err.message}`);
@@ -3041,6 +3047,24 @@ function getValidatorPool() {
3041
3047
  validatorPool = createPool(getValidatorDbConfig());
3042
3048
  return validatorPool;
3043
3049
  }
3050
+ async function warmSingleConnection(pool) {
3051
+ const client = await pool.connect();
3052
+ await client.query("SELECT 1");
3053
+ client.release();
3054
+ }
3055
+ async function warmValidatorPool() {
3056
+ if (poolWarmed)
3057
+ return;
3058
+ const pool = getValidatorPool();
3059
+ const minConns = getEnvNumber("VALIDATOR_DB_POOL_MIN", 1);
3060
+ try {
3061
+ for (let i = 0; i < minConns; i++)
3062
+ await warmSingleConnection(pool);
3063
+ poolWarmed = true;
3064
+ } catch (error) {
3065
+ console.warn("Validator pool warm-up failed, continuing with lazy connection creation:", error);
3066
+ }
3067
+ }
3044
3068
  async function validatorQuery(sql, params = []) {
3045
3069
  const client = await getValidatorPool().connect();
3046
3070
  const onClientError = (err) => {
@@ -3062,7 +3086,7 @@ async function shutdownValidatorPool() {
3062
3086
  validatorPool = null;
3063
3087
  }
3064
3088
  }
3065
- var import_pg, dotenv, path, import_url, import_connection, __filename, __dirname, PROJECT_ROOT, validatorPool;
3089
+ var import_pg, dotenv, path, import_url, import_connection, __filename, __dirname, PROJECT_ROOT, validatorPool, poolWarmed;
3066
3090
  var init_validatorPostgresClient = __esm({
3067
3091
  "dist/db/validatorPostgresClient.js"() {
3068
3092
  "use strict";
@@ -3077,6 +3101,7 @@ var init_validatorPostgresClient = __esm({
3077
3101
  dotenv.config({ path: path.resolve(PROJECT_ROOT, ".env.supabase") });
3078
3102
  dotenv.config({ path: path.resolve(PROJECT_ROOT, ".env") });
3079
3103
  validatorPool = null;
3104
+ poolWarmed = false;
3080
3105
  }
3081
3106
  });
3082
3107
 
@@ -3086,7 +3111,7 @@ var require_retryUtils = __commonJS({
3086
3111
  "use strict";
3087
3112
  Object.defineProperty(exports2, "__esModule", { value: true });
3088
3113
  exports2.retry = retry;
3089
- exports2.retryDbQuery = retryDbQuery9;
3114
+ exports2.retryDbQuery = retryDbQuery10;
3090
3115
  var logger_1 = require_logger();
3091
3116
  var DEFAULT_OPTIONS = {
3092
3117
  maxRetries: 3,
@@ -3145,7 +3170,7 @@ var require_retryUtils = __commonJS({
3145
3170
  }
3146
3171
  throw lastError || new Error(`${operationName} failed after ${config3.maxRetries} retries`);
3147
3172
  }
3148
- async function retryDbQuery9(queryFn, maxRetries = DEFAULT_OPTIONS.maxRetries, operationName = "Database query") {
3173
+ async function retryDbQuery10(queryFn, maxRetries = DEFAULT_OPTIONS.maxRetries, operationName = "Database query") {
3149
3174
  return retry(queryFn, { maxRetries }, operationName);
3150
3175
  }
3151
3176
  }
@@ -18436,6 +18461,7 @@ var init_cached_catalog_adapter = __esm({
18436
18461
  // dist/mcp/server.js
18437
18462
  var server_exports = {};
18438
18463
  __export(server_exports, {
18464
+ isServerInitialized: () => isServerInitialized,
18439
18465
  logStartupSummary: () => logStartupSummary
18440
18466
  });
18441
18467
  module.exports = __toCommonJS(server_exports);
@@ -18447,7 +18473,7 @@ var import_types22 = require("@modelcontextprotocol/sdk/types.js");
18447
18473
  var config = {
18448
18474
  // Server identity
18449
18475
  SERVER_NAME: "p58-n8n",
18450
- SERVER_VERSION: "0.2.3",
18476
+ SERVER_VERSION: "0.2.7",
18451
18477
  // Database configuration (from environment)
18452
18478
  SUPABASE_URL: process.env.SUPABASE_URL,
18453
18479
  SUPABASE_KEY: process.env.SUPABASE_KEY,
@@ -19179,6 +19205,30 @@ async function injectResponseMetadata(sessionId, toolName, response, executionSt
19179
19205
  return instrumented;
19180
19206
  }
19181
19207
 
19208
+ // dist/mcp/in-flight-tracker.js
19209
+ var inflightCount = 0;
19210
+ function incrementInflight() {
19211
+ inflightCount++;
19212
+ }
19213
+ function decrementInflight() {
19214
+ if (inflightCount > 0)
19215
+ inflightCount--;
19216
+ }
19217
+ function waitForInflightRequests(timeoutMs) {
19218
+ if (inflightCount === 0)
19219
+ return Promise.resolve();
19220
+ return new Promise((resolve2) => {
19221
+ const deadline = setTimeout(resolve2, timeoutMs);
19222
+ const poll = setInterval(() => {
19223
+ if (inflightCount <= 0) {
19224
+ clearInterval(poll);
19225
+ clearTimeout(deadline);
19226
+ resolve2();
19227
+ }
19228
+ }, 50);
19229
+ });
19230
+ }
19231
+
19182
19232
  // dist/validation/l1-structure.js
19183
19233
  async function validateL1Structure(workflowJson) {
19184
19234
  const startTime = performance.now();
@@ -30388,7 +30438,13 @@ var AutoFixerRegistry = class _AutoFixerRegistry {
30388
30438
  }
30389
30439
  }
30390
30440
  };
30391
- var fixerRegistry = new AutoFixerRegistry();
30441
+ var _fixerRegistry = null;
30442
+ function getFixerRegistry() {
30443
+ if (!_fixerRegistry) {
30444
+ _fixerRegistry = new AutoFixerRegistry();
30445
+ }
30446
+ return _fixerRegistry;
30447
+ }
30392
30448
 
30393
30449
  // node_modules/axios/lib/helpers/bind.js
30394
30450
  function bind(fn, thisArg) {
@@ -34886,7 +34942,7 @@ async function runAutofixOnWorkflow(workflowJson, issues) {
34886
34942
  const { applied: credApplied, changelog: credChangelog } = preAssignCredentials(workflow, issues);
34887
34943
  const errorIssues = issues.filter((i) => i.severity === "error");
34888
34944
  const problems = errorIssues.map(issueToValidationProblem);
34889
- const fixResult = await fixerRegistry.applyFixes(workflow, problems);
34945
+ const fixResult = await getFixerRegistry().applyFixes(workflow, problems);
34890
34946
  const fixChangelog = buildChangelog(fixResult.results);
34891
34947
  return {
34892
34948
  fixedWorkflow: fixResult.workflow,
@@ -35281,10 +35337,10 @@ How it works:
35281
35337
 
35282
35338
  Validation levels:
35283
35339
  - L1: JSON structure, required fields, duplicate node names
35284
- - L2: Node types exist in catalog (1,982 nodes)
35285
- - L3: Credential types valid (654 credentials) \u2014 includes available_credentials lookup
35286
- - L4: Connection patterns exist (6,900 rules)
35287
- - L5: Required parameters configured (35,143 params)
35340
+ - L2: Node types exist in catalog (1,545 nodes)
35341
+ - L3: Credential types valid (679 credentials) \u2014 includes available_credentials lookup
35342
+ - L4: Connection patterns exist (7,642 rules)
35343
+ - L5: Required parameters configured (38,005 params)
35288
35344
  - L6: Pattern validation (flow integrity, security, expressions)
35289
35345
 
35290
35346
  Returns detailed issues with severity, location, and suggested fixes.
@@ -35487,7 +35543,7 @@ var getOperationSchemaToolDefinition = {
35487
35543
  name: "get_operation_schema",
35488
35544
  description: `Get parameter requirements for a specific n8n node operation.
35489
35545
 
35490
- Returns the exact required and optional parameters from our 35,143 parameter catalog.
35546
+ Returns the exact required and optional parameters from our 38,005 parameter catalog.
35491
35547
  Use this to prevent parameter hallucination when generating n8n workflows.
35492
35548
 
35493
35549
  Examples:
@@ -36573,6 +36629,77 @@ Examples:
36573
36629
  }
36574
36630
  };
36575
36631
 
36632
+ // dist/mcp/tools/handlers/shared/n8n-guard.js
36633
+ var SETUP_GUIDE_URL = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
36634
+ var OFFLINE_TOOLS = /* @__PURE__ */ new Set([
36635
+ "validate_workflow",
36636
+ "get_operation_schema",
36637
+ "check_parameter",
36638
+ "suggest_fix",
36639
+ "list_operations",
36640
+ "list_nodes",
36641
+ "get_node_info",
36642
+ "find_similar_pattern",
36643
+ "get_session_metrics",
36644
+ "get_credential_schema",
36645
+ "setup_check"
36646
+ ]);
36647
+ function requiresN8nApiKey(toolName) {
36648
+ return !OFFLINE_TOOLS.has(toolName);
36649
+ }
36650
+ function makeN8nNotConfiguredError() {
36651
+ return {
36652
+ content: [
36653
+ {
36654
+ type: "text",
36655
+ text: JSON.stringify({
36656
+ error: `This tool requires n8n connection. Set N8N_API_URL and N8N_API_KEY in your MCP config. See: ${SETUP_GUIDE_URL}`
36657
+ })
36658
+ }
36659
+ ],
36660
+ isError: true
36661
+ };
36662
+ }
36663
+ function isDbConfigError(error) {
36664
+ const msg = error instanceof Error ? error.message : String(error);
36665
+ return msg.includes("Missing required validator DB environment variable");
36666
+ }
36667
+ function isDbConnectionError(error) {
36668
+ const msg = error instanceof Error ? error.message : String(error);
36669
+ return msg.includes("ECONNREFUSED") || msg.includes("ETIMEDOUT") || msg.includes("connect ETIMEDOUT") || msg.includes("after calling end on the pool") || msg.includes("Connection terminated") || msg.includes("getaddrinfo ENOTFOUND") || msg.includes("too many connections") || msg.includes("remaining connection slots");
36670
+ }
36671
+ function makeDbNotConfiguredResponse() {
36672
+ return {
36673
+ content: [
36674
+ {
36675
+ type: "text",
36676
+ text: JSON.stringify({
36677
+ error: "DB_NOT_CONFIGURED",
36678
+ message: "Catalog features require P58_DATABASE_URL. Set P58_DATABASE_URL in your MCP config to enable this tool.",
36679
+ setup_guide: SETUP_GUIDE_URL
36680
+ })
36681
+ }
36682
+ ],
36683
+ isError: true
36684
+ };
36685
+ }
36686
+ function makeDbConnectionErrorResponse() {
36687
+ return {
36688
+ content: [
36689
+ {
36690
+ type: "text",
36691
+ text: JSON.stringify({
36692
+ error: "CATALOG_UNREACHABLE",
36693
+ message: "Catalog database is unreachable. Verify your P58_DATABASE_URL is correct and the database server is accessible. If the problem persists, contact Tsvika.",
36694
+ hint: "Check: (1) connection string starts with postgresql://mcp_friend... (2) port is 6543 (transaction pooler) (3) network allows outbound to AWS eu-north-1",
36695
+ setup_guide: SETUP_GUIDE_URL
36696
+ })
36697
+ }
36698
+ ],
36699
+ isError: true
36700
+ };
36701
+ }
36702
+
36576
36703
  // dist/mcp/middleware/response-compression.js
36577
36704
  function selectFields(item, fields) {
36578
36705
  const result = {};
@@ -36692,8 +36819,11 @@ async function handleListOperations(args) {
36692
36819
  return toMCPResponse(response);
36693
36820
  } catch (error) {
36694
36821
  const originalError = error instanceof Error ? error : new Error(String(error));
36695
- const errorResponse = createInternalError(correlationId, originalError);
36696
- return toMCPResponse(errorResponse);
36822
+ if (isDbConfigError(originalError))
36823
+ return makeDbNotConfiguredResponse();
36824
+ if (isDbConnectionError(originalError))
36825
+ return makeDbConnectionErrorResponse();
36826
+ return toMCPResponse(createInternalError(correlationId, originalError));
36697
36827
  }
36698
36828
  }
36699
36829
  var listOperationsToolDefinition = {
@@ -36790,13 +36920,16 @@ async function handleListNodes(args) {
36790
36920
  return toMCPResponse(response);
36791
36921
  } catch (error) {
36792
36922
  const originalError = error instanceof Error ? error : new Error(String(error));
36793
- const errorResponse = createInternalError(correlationId, originalError);
36794
- return toMCPResponse(errorResponse);
36923
+ if (isDbConfigError(originalError))
36924
+ return makeDbNotConfiguredResponse();
36925
+ if (isDbConnectionError(originalError))
36926
+ return makeDbConnectionErrorResponse();
36927
+ return toMCPResponse(createInternalError(correlationId, originalError));
36795
36928
  }
36796
36929
  }
36797
36930
  var listNodesToolDefinition = {
36798
36931
  name: "list_nodes",
36799
- description: `List all available n8n nodes (1,982 total) with filtering and pagination.
36932
+ description: `List all available n8n nodes (1,545 total) with filtering and pagination.
36800
36933
 
36801
36934
  Use this to discover available nodes before generating workflows. The tool supports:
36802
36935
  - Pagination: Use limit (1-100) and offset for large result sets
@@ -36953,8 +37086,11 @@ async function handleGetNodeInfo(args) {
36953
37086
  return toMCPResponse(response);
36954
37087
  } catch (error) {
36955
37088
  const originalError = error instanceof Error ? error : new Error(String(error));
36956
- const errorResponse = createInternalError(correlationId, originalError);
36957
- return toMCPResponse(errorResponse);
37089
+ if (isDbConfigError(originalError))
37090
+ return makeDbNotConfiguredResponse();
37091
+ if (isDbConnectionError(originalError))
37092
+ return makeDbConnectionErrorResponse();
37093
+ return toMCPResponse(createInternalError(correlationId, originalError));
36958
37094
  }
36959
37095
  }
36960
37096
  var getNodeInfoToolDefinition = {
@@ -37042,8 +37178,11 @@ async function handleFindSimilarPattern(args) {
37042
37178
  return toMCPResponse(response);
37043
37179
  } catch (error) {
37044
37180
  const originalError = error instanceof Error ? error : new Error(String(error));
37045
- const errorResponse = createInternalError(correlationId, originalError);
37046
- return toMCPResponse(errorResponse);
37181
+ if (isDbConfigError(originalError))
37182
+ return makeDbNotConfiguredResponse();
37183
+ if (isDbConnectionError(originalError))
37184
+ return makeDbConnectionErrorResponse();
37185
+ return toMCPResponse(createInternalError(correlationId, originalError));
37047
37186
  }
37048
37187
  }
37049
37188
  var findSimilarPatternToolDefinition = {
@@ -37240,8 +37379,8 @@ schemas, build_workflow handles validation, autofix, and deployment automaticall
37240
37379
 
37241
37380
  Runs L1-L4 validation before creating:
37242
37381
  - L1: JSON structure, required fields
37243
- - L2: Node types exist in catalog (1,982 nodes)
37244
- - L3: Credential types are valid (654 credentials)
37382
+ - L2: Node types exist in catalog (1,545 nodes)
37383
+ - L3: Credential types are valid (679 credentials)
37245
37384
  - L4: Connection patterns exist
37246
37385
 
37247
37386
  Refuses creation if any L1-L4 errors are found.
@@ -38612,7 +38751,7 @@ async function validateWorkflowObject(workflow, timeoutMs, label) {
38612
38751
  }
38613
38752
  async function runAutoFix(workflow, validationResult, timeoutMs, toolName) {
38614
38753
  const problems = validationResult.issues.filter((i) => i.severity === "error").map(issueToValidationProblem2);
38615
- const fixResult = await withTimeout(fixerRegistry.applyFixes(workflow, problems), timeoutMs, `${toolName} autofix`);
38754
+ const fixResult = await withTimeout(getFixerRegistry().applyFixes(workflow, problems), timeoutMs, `${toolName} autofix`);
38616
38755
  return { fixedWorkflow: fixResult.workflow, fixResult };
38617
38756
  }
38618
38757
  async function applyAutoFixIfNeeded(workflow, validationResult, hasErrors, shouldFix, timeoutMs, correlationId, toolName) {
@@ -39259,7 +39398,7 @@ function issueToValidationProblem3(issue) {
39259
39398
  }
39260
39399
  async function runFixStep(workflow, validation, timeoutMs) {
39261
39400
  const problems = validation.issues.filter((i) => i.severity === "error").map(issueToValidationProblem3);
39262
- const fixResult = await withTimeout(fixerRegistry.applyFixes(workflow, problems), timeoutMs, "server_autofix:fix");
39401
+ const fixResult = await withTimeout(getFixerRegistry().applyFixes(workflow, problems), timeoutMs, "server_autofix:fix");
39263
39402
  return {
39264
39403
  fixedWorkflow: fixResult.workflow,
39265
39404
  fixes: extractAppliedFixes(fixResult)
@@ -41289,6 +41428,10 @@ and what service it connects to \u2014 don't attempt to build with a broken cred
41289
41428
  Part of the generation lifecycle: plan_workflow \u2192 test_credential(s) \u2192 build_workflow.
41290
41429
  NOT needed for editing or updating existing workflows (use update_workflow or partial_update_workflow).
41291
41430
 
41431
+ Note: plan_workflow works without n8n (catalog-only mode). To actually deploy
41432
+ workflows with build_workflow, N8N_API_KEY and N8N_API_URL must be configured.
41433
+ Without n8n, use plan_workflow for research and architecture planning only.
41434
+
41292
41435
  Response modes:
41293
41436
  - "full": All research data within token_budget (default 8,000)
41294
41437
  - "summary" (default): Key fields within token_budget (default 4,000)
@@ -41340,14 +41483,15 @@ Examples:
41340
41483
  // dist/generation/build/WorkflowBuilder.js
41341
41484
  var import_crypto4 = require("crypto");
41342
41485
  init_validatorPostgresClient();
41343
- var import_retry7 = __toESM(require_retryUtils(), 1);
41344
- var import_logging53 = __toESM(require_logging(), 1);
41486
+ var import_retry8 = __toESM(require_retryUtils(), 1);
41487
+ var import_logging54 = __toESM(require_logging(), 1);
41345
41488
 
41346
41489
  // dist/generation/build/PositionCalculator.js
41347
41490
  var HORIZONTAL_SPACING = 250;
41348
41491
  var VERTICAL_SPACING = 150;
41349
41492
  var START_X = 250;
41350
41493
  var START_Y = 300;
41494
+ var SUB_NODE_Y_OFFSET = 200;
41351
41495
  function findRootNodes(nodeNames, connections) {
41352
41496
  const hasIncoming = new Set(connections.map((c) => c.to));
41353
41497
  const roots = nodeNames.filter((name) => !hasIncoming.has(name));
@@ -41399,14 +41543,45 @@ function assignFallbacks(nodeNames, positions) {
41399
41543
  }
41400
41544
  }
41401
41545
  }
41402
- function calculatePositions(nodeNames, connections) {
41546
+ function detectSubNodes(connections, resolvedTypes) {
41547
+ const subNodes = /* @__PURE__ */ new Set();
41548
+ const parentOf = /* @__PURE__ */ new Map();
41549
+ for (const conn of connections) {
41550
+ const connType = resolvedTypes.get(`${conn.from}\u2192${conn.to}`);
41551
+ if (connType && connType !== "main") {
41552
+ subNodes.add(conn.from);
41553
+ parentOf.set(conn.from, conn.to);
41554
+ }
41555
+ }
41556
+ return { subNodes, parentOf };
41557
+ }
41558
+ function assignSubNodePositions(subNodes, parentOf, positions) {
41559
+ const countPerParent = /* @__PURE__ */ new Map();
41560
+ for (const subNode of subNodes) {
41561
+ const parent = parentOf.get(subNode);
41562
+ if (!parent)
41563
+ continue;
41564
+ const parentPos = positions.get(parent);
41565
+ if (!parentPos)
41566
+ continue;
41567
+ const count = countPerParent.get(parent) ?? 0;
41568
+ positions.set(subNode, [parentPos[0], parentPos[1] + SUB_NODE_Y_OFFSET * (count + 1)]);
41569
+ countPerParent.set(parent, count + 1);
41570
+ }
41571
+ }
41572
+ function calculatePositions(nodeNames, connections, resolvedTypes) {
41403
41573
  const positions = /* @__PURE__ */ new Map();
41404
41574
  if (nodeNames.length === 0)
41405
41575
  return positions;
41406
- const roots = findRootNodes(nodeNames, connections);
41407
- const adjacency = buildAdjacency(connections);
41576
+ const { subNodes, parentOf } = resolvedTypes ? detectSubNodes(connections, resolvedTypes) : { subNodes: /* @__PURE__ */ new Set(), parentOf: /* @__PURE__ */ new Map() };
41577
+ const mainNames = nodeNames.filter((n) => !subNodes.has(n));
41578
+ const mainConns = connections.filter((c) => !subNodes.has(c.from));
41579
+ const roots = findRootNodes(mainNames, mainConns);
41580
+ const adjacency = buildAdjacency(mainConns);
41408
41581
  assignRootPositions(roots, positions);
41409
41582
  bfsAssign(roots, adjacency, positions);
41583
+ assignFallbacks(mainNames, positions);
41584
+ assignSubNodePositions(subNodes, parentOf, positions);
41410
41585
  assignFallbacks(nodeNames, positions);
41411
41586
  return positions;
41412
41587
  }
@@ -41519,6 +41694,142 @@ function getResourceLocatorParams(nodeType) {
41519
41694
  return RESOURCE_LOCATOR_PARAMS[nodeType.toLowerCase()];
41520
41695
  }
41521
41696
 
41697
+ // dist/generation/build/connectionTypeResolver.js
41698
+ init_validatorPostgresClient();
41699
+ var import_retry7 = __toESM(require_retryUtils(), 1);
41700
+ var import_logging53 = __toESM(require_logging(), 1);
41701
+ var AI_TYPE_TO_CONNECTION_TYPE = {
41702
+ llm: "ai_languageModel",
41703
+ memory: "ai_memory",
41704
+ tool: "ai_tool",
41705
+ embedding: "ai_embedding",
41706
+ output_parser: "ai_outputParser",
41707
+ retriever: "ai_retriever",
41708
+ document_loader: "ai_document",
41709
+ text_splitter: "ai_textSplitter",
41710
+ vector_store: "ai_vectorStore",
41711
+ reranker: "ai_reranker"
41712
+ };
41713
+ var DEFAULT_CONNECTION_TYPE = "main";
41714
+ var LANGCHAIN_PREFIX_TO_CONNECTION = [
41715
+ ["@n8n/n8n-nodes-langchain.lmchat", "ai_languageModel"],
41716
+ ["@n8n/n8n-nodes-langchain.lm", "ai_languageModel"],
41717
+ ["@n8n/n8n-nodes-langchain.memory", "ai_memory"],
41718
+ ["@n8n/n8n-nodes-langchain.tool", "ai_tool"],
41719
+ ["@n8n/n8n-nodes-langchain.embeddings", "ai_embedding"],
41720
+ ["@n8n/n8n-nodes-langchain.outputparser", "ai_outputParser"],
41721
+ ["@n8n/n8n-nodes-langchain.retriever", "ai_retriever"],
41722
+ ["@n8n/n8n-nodes-langchain.document", "ai_document"],
41723
+ ["@n8n/n8n-nodes-langchain.textsplitter", "ai_textSplitter"],
41724
+ ["@n8n/n8n-nodes-langchain.vectorstore", "ai_vectorStore"],
41725
+ ["@n8n/n8n-nodes-langchain.reranker", "ai_reranker"]
41726
+ ];
41727
+ function resolveByPattern(nodeType) {
41728
+ const lower = nodeType.toLowerCase();
41729
+ for (const [prefix, connType] of LANGCHAIN_PREFIX_TO_CONNECTION) {
41730
+ if (lower.startsWith(prefix))
41731
+ return connType;
41732
+ }
41733
+ return void 0;
41734
+ }
41735
+ async function loadConnectionRules2() {
41736
+ const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT LOWER(source_node_type) AS source_node_type,
41737
+ LOWER(target_node_type) AS target_node_type,
41738
+ source_output
41739
+ FROM bluelime.connection_rules
41740
+ WHERE source_output != 'main'
41741
+ AND is_valid = true
41742
+ ORDER BY occurrence_count DESC`), 2, "connectionTypeResolver:loadConnectionRules");
41743
+ return rows;
41744
+ }
41745
+ async function loadAINodeTypes() {
41746
+ const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT LOWER(n.node_type) AS node_type, ai.ai_type
41747
+ FROM bluelime.ai_nodes ai
41748
+ JOIN bluelime.nodes n ON ai.node_id = n.id
41749
+ WHERE ai.is_sub_node = true
41750
+ AND ai.ai_type IS NOT NULL`), 2, "connectionTypeResolver:loadAINodeTypes");
41751
+ return rows;
41752
+ }
41753
+ function buildRuleMap(rows) {
41754
+ const map = /* @__PURE__ */ new Map();
41755
+ for (const row of rows) {
41756
+ const key = `${row.source_node_type}|${row.target_node_type}`;
41757
+ if (!map.has(key))
41758
+ map.set(key, row.source_output);
41759
+ }
41760
+ return map;
41761
+ }
41762
+ function buildAITypeMap(rows) {
41763
+ const map = /* @__PURE__ */ new Map();
41764
+ for (const row of rows) {
41765
+ const connType = AI_TYPE_TO_CONNECTION_TYPE[row.ai_type];
41766
+ if (connType && !map.has(row.node_type))
41767
+ map.set(row.node_type, connType);
41768
+ }
41769
+ return map;
41770
+ }
41771
+ var ConnectionTypeResolver = class _ConnectionTypeResolver {
41772
+ ruleMap;
41773
+ aiTypeMap;
41774
+ constructor(ruleMap, aiTypeMap) {
41775
+ this.ruleMap = ruleMap;
41776
+ this.aiTypeMap = aiTypeMap;
41777
+ }
41778
+ /**
41779
+ * Factory: load catalog data and return a ready resolver.
41780
+ * Falls back to empty maps (all "main") if DB query fails.
41781
+ */
41782
+ static async create() {
41783
+ try {
41784
+ const [ruleRows, aiNodeRows] = await Promise.all([
41785
+ loadConnectionRules2(),
41786
+ loadAINodeTypes()
41787
+ ]);
41788
+ import_logging53.logger.debug("connectionTypeResolver: loaded catalog", {
41789
+ ruleCount: ruleRows.length,
41790
+ aiNodeCount: aiNodeRows.length
41791
+ });
41792
+ return new _ConnectionTypeResolver(buildRuleMap(ruleRows), buildAITypeMap(aiNodeRows));
41793
+ } catch (err) {
41794
+ const msg = err instanceof Error ? err.message : String(err);
41795
+ import_logging53.logger.warn("connectionTypeResolver: DB load failed \u2014 pattern fallback active for LangChain nodes", { error: msg });
41796
+ return new _ConnectionTypeResolver(/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map());
41797
+ }
41798
+ }
41799
+ /**
41800
+ * Resolve the n8n connection type for a single connection.
41801
+ *
41802
+ * @param explicit - Caller-provided override (e.g., from BuildConnectionSpec.type)
41803
+ * @param sourceNodeType - Lowercased full n8n type of the source node
41804
+ * @param targetNodeType - Lowercased full n8n type of the target node
41805
+ * @returns Resolved connection type string
41806
+ */
41807
+ resolve(explicit, sourceNodeType, targetNodeType) {
41808
+ if (explicit)
41809
+ return explicit;
41810
+ if (sourceNodeType && targetNodeType) {
41811
+ const catalogType = this.ruleMap.get(`${sourceNodeType}|${targetNodeType}`);
41812
+ if (catalogType)
41813
+ return catalogType;
41814
+ }
41815
+ if (sourceNodeType) {
41816
+ const aiType = this.aiTypeMap.get(sourceNodeType);
41817
+ if (aiType)
41818
+ return aiType;
41819
+ const patternType = resolveByPattern(sourceNodeType);
41820
+ if (patternType)
41821
+ return patternType;
41822
+ }
41823
+ return DEFAULT_CONNECTION_TYPE;
41824
+ }
41825
+ };
41826
+ function buildNodeTypeMap4(nodes) {
41827
+ const map = /* @__PURE__ */ new Map();
41828
+ for (const node of nodes)
41829
+ map.set(node.name, node.type.toLowerCase());
41830
+ return map;
41831
+ }
41832
+
41522
41833
  // dist/generation/build/version-credential-resolver.js
41523
41834
  var FALLBACK_VERSION = 1;
41524
41835
  var UNKNOWN_AUTH_MODE = "unknown";
@@ -42099,7 +42410,7 @@ function buildPlaceholders(count) {
42099
42410
  async function queryTypeVersions(nodeTypes) {
42100
42411
  const normalized = nodeTypes.map((t) => t.toLowerCase());
42101
42412
  const placeholders = buildPlaceholders(normalized.length);
42102
- const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT node_type, default_version, supported_versions FROM bluelime.nodes WHERE LOWER(node_type) IN (${placeholders})`, normalized), 3, "build_workflow_typeversion_batch");
42413
+ const { rows } = await (0, import_retry8.retryDbQuery)(() => validatorQuery(`SELECT node_type, default_version, supported_versions FROM bluelime.nodes WHERE LOWER(node_type) IN (${placeholders})`, normalized), 3, "build_workflow_typeversion_batch");
42103
42414
  const groups = /* @__PURE__ */ new Map();
42104
42415
  for (const row of rows) {
42105
42416
  const key = row.node_type.toLowerCase();
@@ -42290,7 +42601,7 @@ function findTerminalNodes(nodes, connections) {
42290
42601
  function buildNodeBase(spec, position, typeVersion, casingMap) {
42291
42602
  const correctedType = resolveNodeTypeCasing(spec.type, casingMap);
42292
42603
  if (correctedType !== spec.type) {
42293
- import_logging53.logger.debug("build_workflow: corrected node type casing", { original: spec.type, corrected: correctedType, nodeName: spec.name });
42604
+ import_logging54.logger.debug("build_workflow: corrected node type casing", { original: spec.type, corrected: correctedType, nodeName: spec.name });
42294
42605
  }
42295
42606
  return {
42296
42607
  id: (0, import_crypto4.randomUUID)(),
@@ -42312,20 +42623,20 @@ function assembleNode(spec, position, typeVersion, credential, isTerminal, casin
42312
42623
  node.continueOnFail = true;
42313
42624
  return node;
42314
42625
  }
42315
- function assembleConnections(connections) {
42626
+ function assembleConnections(connections, nodeTypeMap, resolver) {
42316
42627
  const result = {};
42317
42628
  for (const conn of connections) {
42629
+ const sourceType = nodeTypeMap.get(conn.from);
42630
+ const targetType = nodeTypeMap.get(conn.to);
42631
+ const connType = resolver.resolve(conn.type, sourceType, targetType);
42318
42632
  if (!result[conn.from])
42319
- result[conn.from] = { main: [] };
42633
+ result[conn.from] = {};
42634
+ if (!result[conn.from][connType])
42635
+ result[conn.from][connType] = [];
42320
42636
  const outputIdx = conn.from_output ?? 0;
42321
- while (result[conn.from].main.length <= outputIdx) {
42322
- result[conn.from].main.push([]);
42323
- }
42324
- result[conn.from].main[outputIdx].push({
42325
- node: conn.to,
42326
- type: "main",
42327
- index: conn.to_input ?? 0
42328
- });
42637
+ while (result[conn.from][connType].length <= outputIdx)
42638
+ result[conn.from][connType].push([]);
42639
+ result[conn.from][connType][outputIdx].push({ node: conn.to, type: connType, index: conn.to_input ?? 0 });
42329
42640
  }
42330
42641
  return result;
42331
42642
  }
@@ -42336,36 +42647,49 @@ async function resolveCatalogData(input) {
42336
42647
  const uniqueTypes = [...new Set(input.nodes.map((n) => n.type))];
42337
42648
  const [typeResult, credentialMap] = await Promise.all([
42338
42649
  queryTypeVersions(uniqueTypes).catch((err) => {
42339
- import_logging53.logger.warn("WorkflowBuilder: typeVersion lookup failed, using defaults", { error: err });
42650
+ import_logging54.logger.warn("WorkflowBuilder: typeVersion lookup failed, using defaults", { error: err });
42340
42651
  return { versions: /* @__PURE__ */ new Map(), casingMap: /* @__PURE__ */ new Map() };
42341
42652
  }),
42342
42653
  resolveAllCredentials(input.nodes)
42343
42654
  ]);
42344
42655
  return { versionMap: typeResult.versions, casingMap: typeResult.casingMap, credentialMap };
42345
42656
  }
42346
- function buildNodeList(nodes, connections, versionMap, casingMap, credentialMap) {
42347
- const autoPositions = calculatePositions(nodes.map((n) => n.name), connections);
42657
+ function computeResolvedTypes(connections, nodeTypeMap, resolver) {
42658
+ const map = /* @__PURE__ */ new Map();
42659
+ for (const conn of connections) {
42660
+ const connType = resolver.resolve(conn.type, nodeTypeMap.get(conn.from), nodeTypeMap.get(conn.to));
42661
+ map.set(`${conn.from}\u2192${conn.to}`, connType);
42662
+ }
42663
+ return map;
42664
+ }
42665
+ function buildNodeList(nodes, connections, versionMap, casingMap, credentialMap, nodeTypeMap, resolver) {
42666
+ const resolvedTypes = computeResolvedTypes(connections, nodeTypeMap, resolver);
42667
+ const autoPositions = calculatePositions(nodes.map((n) => n.name), connections, resolvedTypes);
42348
42668
  const terminalNodes = findTerminalNodes(nodes, connections);
42349
42669
  return nodes.map((spec) => {
42350
42670
  const resolved = resolveNodeVersion(spec, versionMap, credentialMap);
42351
- import_logging53.logger.debug("build_workflow: typeVersion resolved", { nodeType: spec.type, nodeName: spec.name, ...resolved });
42671
+ import_logging54.logger.debug("build_workflow: typeVersion resolved", { nodeType: spec.type, nodeName: spec.name, ...resolved });
42352
42672
  return assembleNode(spec, getNodePosition(spec, autoPositions), resolved.typeVersion, credentialMap.get(spec.name), terminalNodes.has(spec.name), casingMap);
42353
42673
  });
42354
42674
  }
42355
42675
  async function assembleWorkflow(input) {
42356
- const { versionMap, casingMap, credentialMap } = await resolveCatalogData(input);
42357
- const nodes = buildNodeList(input.nodes, input.connections, versionMap, casingMap, credentialMap);
42676
+ const [{ versionMap, casingMap, credentialMap }, resolver] = await Promise.all([
42677
+ resolveCatalogData(input),
42678
+ ConnectionTypeResolver.create()
42679
+ ]);
42680
+ const nodeTypeMap = buildNodeTypeMap4(input.nodes);
42681
+ const nodes = buildNodeList(input.nodes, input.connections, versionMap, casingMap, credentialMap, nodeTypeMap, resolver);
42358
42682
  return {
42359
42683
  name: input.name,
42360
42684
  nodes,
42361
- connections: assembleConnections(input.connections),
42685
+ connections: assembleConnections(input.connections, nodeTypeMap, resolver),
42362
42686
  settings: {},
42363
42687
  staticData: null
42364
42688
  };
42365
42689
  }
42366
42690
 
42367
42691
  // dist/generation/build/SubWorkflowBuilder.js
42368
- var import_logging54 = __toESM(require_logging(), 1);
42692
+ var import_logging55 = __toESM(require_logging(), 1);
42369
42693
  var EXECUTE_WORKFLOW_TYPE = "n8n-nodes-base.executeWorkflow";
42370
42694
  var PLACEHOLDER = "{{SUB_WORKFLOW_ID}}";
42371
42695
  function findExecuteWorkflowNodes(nodes) {
@@ -42398,17 +42722,17 @@ async function buildSubWorkflows(parentSpec, buildSingle) {
42398
42722
  }
42399
42723
  const builtSubWorkflows = [];
42400
42724
  for (const subSpec of parentSpec.sub_workflows) {
42401
- import_logging54.logger.info("SubWorkflowBuilder: building sub-workflow", { name: subSpec.name });
42725
+ import_logging55.logger.info("SubWorkflowBuilder: building sub-workflow", { name: subSpec.name });
42402
42726
  const { workflow_id, workflow_url } = await buildSingle(subSpec);
42403
42727
  builtSubWorkflows.push({ name: subSpec.name, workflow_id, workflow_url });
42404
- import_logging54.logger.info("SubWorkflowBuilder: sub-workflow built", { name: subSpec.name, workflow_id });
42728
+ import_logging55.logger.info("SubWorkflowBuilder: sub-workflow built", { name: subSpec.name, workflow_id });
42405
42729
  }
42406
42730
  const wiredSpec = wireSubWorkflowIds(parentSpec, builtSubWorkflows);
42407
42731
  return { wiredSpec, builtSubWorkflows };
42408
42732
  }
42409
42733
 
42410
42734
  // dist/mcp/tools/handlers/build-workflow.js
42411
- var import_logging55 = __toESM(require_logging(), 1);
42735
+ var import_logging56 = __toESM(require_logging(), 1);
42412
42736
  function buildWorkflowUrl(workflowId) {
42413
42737
  const apiBase = process.env.N8N_API_BASE_URL ?? "http://localhost:5678/api/v1";
42414
42738
  return `${apiBase.replace("/api/v1", "")}/workflow/${workflowId}`;
@@ -42501,7 +42825,7 @@ function buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testRe
42501
42825
  async function buildSuccessResponse2(args, result, builtSubWorkflows, timeoutMs, startTime) {
42502
42826
  const workflowId = result.serverResponse.id;
42503
42827
  const testResult = args.test !== false ? await runTestStep(result.finalWorkflow, workflowId, args.test_payload, timeoutMs) : void 0;
42504
- import_logging55.logger.info("build_workflow: complete", { workflowId, fixes: result.fixesApplied.length });
42828
+ import_logging56.logger.info("build_workflow: complete", { workflowId, fixes: result.fixesApplied.length });
42505
42829
  return buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testResult, startTime);
42506
42830
  }
42507
42831
  async function executeBuildPipeline(args, timeoutMs, correlationId, startTime) {
@@ -42635,7 +42959,7 @@ function stripExtraWebhookTriggers(args) {
42635
42959
  const webhookNames = new Set(webhookNodes.map((n) => n.name));
42636
42960
  const cleanedNodes = args.nodes.filter((n) => !webhookNames.has(n.name));
42637
42961
  const cleanedConnections = args.connections?.filter((c) => !webhookNames.has(c.from) && !webhookNames.has(c.to));
42638
- import_logging55.logger.info("build_workflow: stripped webhook triggers from schedule workflow", {
42962
+ import_logging56.logger.info("build_workflow: stripped webhook triggers from schedule workflow", {
42639
42963
  removed: [...webhookNames]
42640
42964
  });
42641
42965
  return {
@@ -42650,7 +42974,7 @@ async function handleBuildWorkflow(args) {
42650
42974
  const correlationId = generateCorrelationId();
42651
42975
  const startTime = performance.now();
42652
42976
  const timeoutMs = normalizeTimeout();
42653
- import_logging55.logger.debug("build_workflow: starting", { correlationId, workflowName: args.name });
42977
+ import_logging56.logger.debug("build_workflow: starting", { correlationId, workflowName: args.name });
42654
42978
  try {
42655
42979
  const { cleaned: triggerCleaned, warnings: triggerWarnings } = await substituteUnavailableTriggers(args);
42656
42980
  const { cleaned, warning: webhookWarning } = stripExtraWebhookTriggers(triggerCleaned);
@@ -42679,7 +43003,7 @@ async function handleBuildWorkflow(args) {
42679
43003
  } catch (error) {
42680
43004
  if (isTimeoutError(error))
42681
43005
  return toMCPResponse(createTimeoutError(correlationId, "build_workflow"));
42682
- import_logging55.logger.error("build_workflow: unexpected error", { correlationId, error });
43006
+ import_logging56.logger.error("build_workflow: unexpected error", { correlationId, error });
42683
43007
  const e = error instanceof Error ? error : new Error(String(error));
42684
43008
  return toMCPResponse(createInternalError(correlationId, e));
42685
43009
  }
@@ -42733,14 +43057,15 @@ Examples:
42733
43057
  },
42734
43058
  connections: {
42735
43059
  type: "array",
42736
- description: "Node connections",
43060
+ description: "Node connections. For LangChain/AI nodes the connection type is auto-detected from the catalog \u2014 no need to set `type` manually. Use `type` only to override auto-detection.",
42737
43061
  items: {
42738
43062
  type: "object",
42739
43063
  properties: {
42740
43064
  from: { type: "string", description: "Source node name" },
42741
43065
  to: { type: "string", description: "Target node name" },
42742
43066
  from_output: { type: "number", description: "Output port index (default 0)" },
42743
- to_input: { type: "number", description: "Input port index (default 0)" }
43067
+ to_input: { type: "number", description: "Input port index (default 0)" },
43068
+ type: { type: "string", description: 'Connection type override (e.g., "ai_languageModel", "ai_tool", "ai_memory"). Auto-resolved from catalog if omitted.' }
42744
43069
  },
42745
43070
  required: ["from", "to"]
42746
43071
  }
@@ -42759,7 +43084,7 @@ Examples:
42759
43084
  };
42760
43085
 
42761
43086
  // dist/mcp/tools/handlers/list-credentials.js
42762
- var import_logging56 = __toESM(require_logging(), 1);
43087
+ var import_logging57 = __toESM(require_logging(), 1);
42763
43088
  init_validatorPostgresClient();
42764
43089
  function buildApiConfig14(timeoutMs) {
42765
43090
  return {
@@ -42865,10 +43190,10 @@ function handleListError2(error, correlationId) {
42865
43190
  }
42866
43191
  const statusCode = error?.statusCode;
42867
43192
  if (statusCode === 401 || statusCode === 403) {
42868
- import_logging56.logger.warn("list_credentials: auth error", { correlationId, statusCode });
43193
+ import_logging57.logger.warn("list_credentials: auth error", { correlationId, statusCode });
42869
43194
  return toMCPResponse(createSuccessResponse({ success: false, error: "Authentication failed" }, correlationId, { duration_ms: 0 }));
42870
43195
  }
42871
- import_logging56.logger.error("list_credentials: unexpected error", { correlationId, error });
43196
+ import_logging57.logger.error("list_credentials: unexpected error", { correlationId, error });
42872
43197
  const originalError = error instanceof Error ? error : new Error(String(error));
42873
43198
  return toMCPResponse(createInternalError(correlationId, originalError));
42874
43199
  }
@@ -42879,13 +43204,13 @@ async function handleListCredentials(args) {
42879
43204
  const correlationId = generateCorrelationId();
42880
43205
  const startTime = performance.now();
42881
43206
  const timeoutMs = normalizeTimeout();
42882
- import_logging56.logger.debug("list_credentials: starting", { correlationId, options: args });
43207
+ import_logging57.logger.debug("list_credentials: starting", { correlationId, options: args });
42883
43208
  try {
42884
43209
  const result = await fetchRawCredentials(args, timeoutMs);
42885
43210
  const { credentials: filtered, neededCredentialTypes } = await applyForNodesFilter(result.credentials, args.forNodes);
42886
43211
  const enriched = await enrichWithNodeTypes(filtered);
42887
43212
  const final = args.includeSchema ? await enrichWithSchema(enriched) : enriched;
42888
- import_logging56.logger.info("list_credentials: retrieved", { correlationId, count: final.length });
43213
+ import_logging57.logger.info("list_credentials: retrieved", { correlationId, count: final.length });
42889
43214
  const data = buildResponseData(final, neededCredentialTypes);
42890
43215
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: Math.round(performance.now() - startTime) }));
42891
43216
  } catch (error) {
@@ -42941,7 +43266,7 @@ listing which credential types need to be created.`,
42941
43266
  };
42942
43267
 
42943
43268
  // dist/mcp/tools/handlers/get-credential-schema.js
42944
- var import_logging57 = __toESM(require_logging(), 1);
43269
+ var import_logging58 = __toESM(require_logging(), 1);
42945
43270
  init_validatorPostgresClient();
42946
43271
  var SENSITIVE_PATTERNS = /* @__PURE__ */ new Set([
42947
43272
  "apikey",
@@ -43080,22 +43405,27 @@ function buildResult(credentialType, rows, compatibleNodes) {
43080
43405
  function buildSchemaResponse(credentialType, rows, nodes, correlationId, startTime) {
43081
43406
  const data = buildResult(credentialType, rows, nodes);
43082
43407
  const duration_ms = Math.round(performance.now() - startTime);
43083
- import_logging57.logger.info("get_credential_schema: found", { correlationId, credential_type: credentialType, fieldCount: rows.length });
43408
+ import_logging58.logger.info("get_credential_schema: found", { correlationId, credential_type: credentialType, fieldCount: rows.length });
43084
43409
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms }));
43085
43410
  }
43086
43411
  async function handleGetCredentialSchema(args) {
43087
43412
  const correlationId = generateCorrelationId();
43088
43413
  const startTime = performance.now();
43089
43414
  const { credential_type } = args;
43090
- import_logging57.logger.debug("get_credential_schema: starting", { correlationId, credential_type });
43415
+ import_logging58.logger.debug("get_credential_schema: starting", { correlationId, credential_type });
43091
43416
  try {
43092
43417
  const [rows, nodes] = await Promise.all([fetchSchemaRows(credential_type), fetchCompatibleNodes(credential_type)]);
43093
43418
  if (rows.length === 0)
43094
43419
  return handleUnknownType(credential_type, correlationId);
43095
43420
  return buildSchemaResponse(credential_type, rows, nodes, correlationId, startTime);
43096
43421
  } catch (error) {
43097
- import_logging57.logger.error("get_credential_schema: unexpected error", { correlationId, error });
43098
- return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
43422
+ import_logging58.logger.error("get_credential_schema: unexpected error", { correlationId, error });
43423
+ const originalError = error instanceof Error ? error : new Error(String(error));
43424
+ if (isDbConfigError(originalError))
43425
+ return makeDbNotConfiguredResponse();
43426
+ if (isDbConnectionError(originalError))
43427
+ return makeDbConnectionErrorResponse();
43428
+ return toMCPResponse(createInternalError(correlationId, originalError));
43099
43429
  }
43100
43430
  }
43101
43431
  async function handleUnknownType(credentialType, correlationId) {
@@ -43138,7 +43468,7 @@ Example usage:
43138
43468
  };
43139
43469
 
43140
43470
  // dist/mcp/tools/handlers/create-credential.js
43141
- var import_logging58 = __toESM(require_logging(), 1);
43471
+ var import_logging59 = __toESM(require_logging(), 1);
43142
43472
  init_validatorPostgresClient();
43143
43473
  var DEFAULT_TIMEOUT_MS2 = 1e4;
43144
43474
  var FIELDS_SQL = `
@@ -43204,11 +43534,11 @@ function buildCreateError(error, correlationId) {
43204
43534
  const statusCode = error?.statusCode;
43205
43535
  const message = error instanceof Error ? error.message : String(error);
43206
43536
  if (statusCode === 401 || statusCode === 403) {
43207
- import_logging58.logger.warn("create_credential: auth error", { correlationId, statusCode });
43537
+ import_logging59.logger.warn("create_credential: auth error", { correlationId, statusCode });
43208
43538
  const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
43209
43539
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43210
43540
  }
43211
- import_logging58.logger.error("create_credential: unexpected error", { correlationId, error });
43541
+ import_logging59.logger.error("create_credential: unexpected error", { correlationId, error });
43212
43542
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
43213
43543
  }
43214
43544
  function buildExistsResponse(existing, correlationId) {
@@ -43228,7 +43558,7 @@ async function handleCreateCredential(args) {
43228
43558
  const correlationId = generateCorrelationId();
43229
43559
  const startTime = performance.now();
43230
43560
  const { credential_type, name, data } = args;
43231
- import_logging58.logger.debug("create_credential: starting", { correlationId, credential_type, name });
43561
+ import_logging59.logger.debug("create_credential: starting", { correlationId, credential_type, name });
43232
43562
  try {
43233
43563
  const fields = await fetchCredentialFields(credential_type);
43234
43564
  if (fields.length === 0)
@@ -43239,13 +43569,13 @@ async function handleCreateCredential(args) {
43239
43569
  const config3 = buildApiConfig15();
43240
43570
  const existing = await findExistingCredential(config3, credential_type, name);
43241
43571
  if (existing) {
43242
- import_logging58.logger.warn("create_credential: duplicate blocked", { correlationId, existing_id: existing.id, credential_type, name });
43572
+ import_logging59.logger.warn("create_credential: duplicate blocked", { correlationId, existing_id: existing.id, credential_type, name });
43243
43573
  return buildExistsResponse(existing, correlationId);
43244
43574
  }
43245
43575
  const result = await createCredential(config3, { name, type: credential_type, data });
43246
43576
  clearCredentialCache();
43247
43577
  const duration_ms = Math.round(performance.now() - startTime);
43248
- import_logging58.logger.info("create_credential: created", { correlationId, id: result.id, credential_type, name });
43578
+ import_logging59.logger.info("create_credential: created", { correlationId, id: result.id, credential_type, name });
43249
43579
  return buildCreateSuccessResponse(result, correlationId, duration_ms);
43250
43580
  } catch (error) {
43251
43581
  return buildCreateError(error, correlationId);
@@ -43297,7 +43627,7 @@ Example usage:
43297
43627
 
43298
43628
  // dist/mcp/tools/handlers/test-credential.js
43299
43629
  var net = __toESM(require("net"), 1);
43300
- var import_logging59 = __toESM(require_logging(), 1);
43630
+ var import_logging60 = __toESM(require_logging(), 1);
43301
43631
  var PROBE_TIMEOUT_MS = 5e3;
43302
43632
  var RATE_LIMIT_MS = 3e4;
43303
43633
  var lastTestTimestamps = /* @__PURE__ */ new Map();
@@ -43477,7 +43807,7 @@ function buildProbeError(error, correlationId) {
43477
43807
  const data = { supported: true, valid: false, message: "Credential not found. Check the credential ID." };
43478
43808
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43479
43809
  }
43480
- import_logging59.logger.error("test_credential: unexpected error", { correlationId, error });
43810
+ import_logging60.logger.error("test_credential: unexpected error", { correlationId, error });
43481
43811
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
43482
43812
  }
43483
43813
  async function handleTestCredential(args) {
@@ -43493,7 +43823,7 @@ async function handleTestCredential(args) {
43493
43823
  recordTestTimestamp(credential_id);
43494
43824
  try {
43495
43825
  const n8nResult = await testCredentialViaN8n(config3, credential_id, credential.name, credential.type, credential.data, restCreds);
43496
- import_logging59.logger.info("test_credential: n8n built-in test", {
43826
+ import_logging60.logger.info("test_credential: n8n built-in test", {
43497
43827
  correlationId,
43498
43828
  type: credential.type,
43499
43829
  valid: n8nResult.valid
@@ -43501,9 +43831,9 @@ async function handleTestCredential(args) {
43501
43831
  return buildProbeResponse(n8nResult, correlationId);
43502
43832
  } catch (n8nTestError) {
43503
43833
  if (n8nTestError?.code === "NEEDS_N8N_LOGIN") {
43504
- import_logging59.logger.debug("test_credential: n8n test unavailable, falling back to HTTP probes");
43834
+ import_logging60.logger.debug("test_credential: n8n test unavailable, falling back to HTTP probes");
43505
43835
  } else {
43506
- import_logging59.logger.warn("test_credential: n8n test failed, falling back to HTTP probes", {
43836
+ import_logging60.logger.warn("test_credential: n8n test failed, falling back to HTTP probes", {
43507
43837
  error: n8nTestError instanceof Error ? n8nTestError.message : String(n8nTestError)
43508
43838
  });
43509
43839
  }
@@ -43513,10 +43843,10 @@ async function handleTestCredential(args) {
43513
43843
  return buildUnsupportedResponse(credential.type, correlationId);
43514
43844
  const hasBlankValues = Object.values(credential.data).some((v) => typeof v === "string" && v.includes("__n8n_BLANK_VALUE_"));
43515
43845
  if (hasBlankValues) {
43516
- import_logging59.logger.warn("test_credential: credential data contains encrypted placeholders, probe may be unreliable");
43846
+ import_logging60.logger.warn("test_credential: credential data contains encrypted placeholders, probe may be unreliable");
43517
43847
  }
43518
43848
  const result = await probe(credential.data);
43519
- import_logging59.logger.info("test_credential: HTTP probe fallback", {
43849
+ import_logging60.logger.info("test_credential: HTTP probe fallback", {
43520
43850
  correlationId,
43521
43851
  type: credential.type,
43522
43852
  valid: result.valid,
@@ -43570,7 +43900,7 @@ Use list_credentials to find credential IDs.`,
43570
43900
  };
43571
43901
 
43572
43902
  // dist/mcp/tools/handlers/update-credential.js
43573
- var import_logging60 = __toESM(require_logging(), 1);
43903
+ var import_logging61 = __toESM(require_logging(), 1);
43574
43904
  var DEFAULT_TIMEOUT_MS3 = 1e4;
43575
43905
  function buildApiConfig17() {
43576
43906
  return {
@@ -43594,16 +43924,16 @@ function buildUpdateError(error, correlationId) {
43594
43924
  const statusCode = error?.statusCode;
43595
43925
  const message = error instanceof Error ? error.message : String(error);
43596
43926
  if (statusCode === 404) {
43597
- import_logging60.logger.warn("update_credential: not found", { correlationId, statusCode });
43927
+ import_logging61.logger.warn("update_credential: not found", { correlationId, statusCode });
43598
43928
  const data = { success: false, error: "NOT_FOUND", message: "Credential not found. Use list_credentials to find valid IDs." };
43599
43929
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43600
43930
  }
43601
43931
  if (statusCode === 401 || statusCode === 403) {
43602
- import_logging60.logger.warn("update_credential: auth error", { correlationId, statusCode });
43932
+ import_logging61.logger.warn("update_credential: auth error", { correlationId, statusCode });
43603
43933
  const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
43604
43934
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43605
43935
  }
43606
- import_logging60.logger.error("update_credential: unexpected error", { correlationId, error });
43936
+ import_logging61.logger.error("update_credential: unexpected error", { correlationId, error });
43607
43937
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
43608
43938
  }
43609
43939
  async function buildUpdatePayload(config3, args) {
@@ -43618,9 +43948,9 @@ function buildUpdateSuccessResponse2(result, correlationId, duration_ms) {
43618
43948
  async function handleUpdateCredential(args) {
43619
43949
  const correlationId = generateCorrelationId();
43620
43950
  const startTime = performance.now();
43621
- import_logging60.logger.debug("update_credential: starting", { correlationId, credential_id: args.credential_id });
43951
+ import_logging61.logger.debug("update_credential: starting", { correlationId, credential_id: args.credential_id });
43622
43952
  if (args.data && Object.keys(args.data).length > 0) {
43623
- import_logging60.logger.warn("update_credential: rejected data write attempt", { correlationId, credential_id: args.credential_id });
43953
+ import_logging61.logger.warn("update_credential: rejected data write attempt", { correlationId, credential_id: args.credential_id });
43624
43954
  const data = {
43625
43955
  success: false,
43626
43956
  error: "DATA_WRITE_DENIED",
@@ -43641,7 +43971,7 @@ async function handleUpdateCredential(args) {
43641
43971
  const payload = await buildUpdatePayload(config3, { ...args, data: void 0 });
43642
43972
  const result = await updateCredential(config3, args.credential_id, payload);
43643
43973
  invalidate();
43644
- import_logging60.logger.info("update_credential: updated (name only)", { correlationId, id: result.id });
43974
+ import_logging61.logger.info("update_credential: updated (name only)", { correlationId, id: result.id });
43645
43975
  return buildUpdateSuccessResponse2(result, correlationId, Math.round(performance.now() - startTime));
43646
43976
  } catch (error) {
43647
43977
  return buildUpdateError(error, correlationId);
@@ -43676,7 +44006,7 @@ On not found: returns NOT_FOUND error with guidance.`,
43676
44006
  };
43677
44007
 
43678
44008
  // dist/mcp/tools/handlers/delete-credential.js
43679
- var import_logging61 = __toESM(require_logging(), 1);
44009
+ var import_logging62 = __toESM(require_logging(), 1);
43680
44010
  var DEFAULT_TIMEOUT_MS4 = 1e4;
43681
44011
  var MAX_WORKFLOWS_TO_SCAN = 100;
43682
44012
  function buildApiConfig18() {
@@ -43735,18 +44065,18 @@ function buildDeleteError(error, correlationId) {
43735
44065
  const statusCode = error?.statusCode;
43736
44066
  const message = error instanceof Error ? error.message : String(error);
43737
44067
  if (statusCode === 401 || statusCode === 403) {
43738
- import_logging61.logger.warn("delete_credential: auth error", { correlationId, statusCode });
44068
+ import_logging62.logger.warn("delete_credential: auth error", { correlationId, statusCode });
43739
44069
  const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
43740
44070
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43741
44071
  }
43742
- import_logging61.logger.error("delete_credential: unexpected error", { correlationId, error });
44072
+ import_logging62.logger.error("delete_credential: unexpected error", { correlationId, error });
43743
44073
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
43744
44074
  }
43745
44075
  async function checkAndWarnDependents(config3, credential_id, correlationId) {
43746
44076
  const dependentWorkflows = await findDependentWorkflows(config3, credential_id);
43747
44077
  if (dependentWorkflows.length === 0)
43748
44078
  return null;
43749
- import_logging61.logger.warn("delete_credential: dependent workflows found", { correlationId, credential_id, count: dependentWorkflows.length });
44079
+ import_logging62.logger.warn("delete_credential: dependent workflows found", { correlationId, credential_id, count: dependentWorkflows.length });
43750
44080
  return buildDependentWarningResponse(credential_id, dependentWorkflows, correlationId);
43751
44081
  }
43752
44082
  async function executeDelete(config3, credential_id, force, correlationId, startTime) {
@@ -43757,14 +44087,14 @@ async function executeDelete(config3, credential_id, force, correlationId, start
43757
44087
  }
43758
44088
  await deleteCredential(config3, credential_id);
43759
44089
  invalidate();
43760
- import_logging61.logger.info("delete_credential: deleted", { correlationId, credential_id, force });
44090
+ import_logging62.logger.info("delete_credential: deleted", { correlationId, credential_id, force });
43761
44091
  return buildDeleteSuccessResponse(credential_id, correlationId, Math.round(performance.now() - startTime));
43762
44092
  }
43763
44093
  async function handleDeleteCredential(args) {
43764
44094
  const correlationId = generateCorrelationId();
43765
44095
  const startTime = performance.now();
43766
44096
  const { credential_id, force = false } = args;
43767
- import_logging61.logger.debug("delete_credential: starting", { correlationId, credential_id, force });
44097
+ import_logging62.logger.debug("delete_credential: starting", { correlationId, credential_id, force });
43768
44098
  try {
43769
44099
  return await executeDelete(buildApiConfig18(), credential_id, force, correlationId, startTime);
43770
44100
  } catch (error) {
@@ -43809,12 +44139,12 @@ On auth error: returns AUTH_ERROR with guidance.`,
43809
44139
  };
43810
44140
 
43811
44141
  // dist/mcp/tools/handlers/collect-config.js
43812
- var import_logging63 = __toESM(require_logging(), 1);
44142
+ var import_logging64 = __toESM(require_logging(), 1);
43813
44143
  init_validatorPostgresClient();
43814
- var import_retry8 = __toESM(require_retryUtils(), 1);
44144
+ var import_retry9 = __toESM(require_retryUtils(), 1);
43815
44145
 
43816
44146
  // dist/mcp/session/config-cache.js
43817
- var import_logging62 = __toESM(require_logging(), 1);
44147
+ var import_logging63 = __toESM(require_logging(), 1);
43818
44148
  var CONFIG_CACHE_TTL_MS = 30 * 60 * 1e3;
43819
44149
  var CONFIG_CACHE_MAX_ENTRIES = 500;
43820
44150
  var cacheState2 = null;
@@ -43857,12 +44187,12 @@ function set(key, value) {
43857
44187
  stored_at: Date.now(),
43858
44188
  expires_at: Date.now() + CONFIG_CACHE_TTL_MS
43859
44189
  });
43860
- import_logging62.logger.debug("config-cache: stored value", { key });
44190
+ import_logging63.logger.debug("config-cache: stored value", { key });
43861
44191
  }
43862
44192
  function clearConfigCache() {
43863
44193
  cacheState2 = null;
43864
44194
  warming = null;
43865
- import_logging62.logger.debug("config-cache: cleared");
44195
+ import_logging63.logger.debug("config-cache: cleared");
43866
44196
  }
43867
44197
  function getSuggestion(configType, nodeType, parameterName) {
43868
44198
  const serviceId = extractServiceId(nodeType);
@@ -43994,7 +44324,7 @@ function buildConfigValues(answered) {
43994
44324
  return result;
43995
44325
  }
43996
44326
  async function markGapsResolved(gapType, nodeType, notes) {
43997
- await (0, import_retry8.retryDbQuery)(() => validatorQuery(RESOLVE_GAPS_SQL, [gapType, nodeType, notes]), 3, "collect_config_resolve_gaps");
44327
+ await (0, import_retry9.retryDbQuery)(() => validatorQuery(RESOLVE_GAPS_SQL, [gapType, nodeType, notes]), 3, "collect_config_resolve_gaps");
43998
44328
  }
43999
44329
  async function resolveOneGap(req) {
44000
44330
  const gapType = CATEGORY_TO_GAP_TYPE[req.configType];
@@ -44003,7 +44333,7 @@ async function resolveOneGap(req) {
44003
44333
  try {
44004
44334
  await markGapsResolved(gapType, req.nodeType, `Resolved via collect_config: ${req.parameterName}`);
44005
44335
  } catch (error) {
44006
- import_logging63.logger.warn("collect_config: gap resolution failed (non-fatal)", {
44336
+ import_logging64.logger.warn("collect_config: gap resolution failed (non-fatal)", {
44007
44337
  nodeType: req.nodeType,
44008
44338
  gapType,
44009
44339
  error: error instanceof Error ? error.message : String(error)
@@ -44045,14 +44375,14 @@ async function executeCollect(args) {
44045
44375
  async function handleCollectConfig(args) {
44046
44376
  const correlationId = generateCorrelationId();
44047
44377
  const startTime = performance.now();
44048
- import_logging63.logger.debug("collect_config: starting", { correlationId });
44378
+ import_logging64.logger.debug("collect_config: starting", { correlationId });
44049
44379
  try {
44050
44380
  const { output, logMeta } = await executeCollect(args);
44051
44381
  const duration_ms = Math.round(performance.now() - startTime);
44052
- import_logging63.logger.info("collect_config: completed", { correlationId, ...logMeta });
44382
+ import_logging64.logger.info("collect_config: completed", { correlationId, ...logMeta });
44053
44383
  return toMCPResponse(createSuccessResponse(output, correlationId, { duration_ms }));
44054
44384
  } catch (error) {
44055
- import_logging63.logger.error("collect_config: failed", { correlationId, error });
44385
+ import_logging64.logger.error("collect_config: failed", { correlationId, error });
44056
44386
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
44057
44387
  }
44058
44388
  }
@@ -44109,40 +44439,8 @@ When values are collected, marks matching factory.gaps entries as resolved for a
44109
44439
  }
44110
44440
  };
44111
44441
 
44112
- // dist/mcp/tools/handlers/shared/n8n-guard.js
44113
- var SETUP_GUIDE_URL = "https://github.com/tsvika58/n8n-workflow-validator/blob/main/docs/AGENT_INSTALL.md";
44114
- var OFFLINE_TOOLS = /* @__PURE__ */ new Set([
44115
- "validate_workflow",
44116
- "get_operation_schema",
44117
- "check_parameter",
44118
- "suggest_fix",
44119
- "list_operations",
44120
- "list_nodes",
44121
- "get_node_info",
44122
- "find_similar_pattern",
44123
- "get_session_metrics",
44124
- "get_credential_schema",
44125
- "setup_check"
44126
- ]);
44127
- function requiresN8nApiKey(toolName) {
44128
- return !OFFLINE_TOOLS.has(toolName);
44129
- }
44130
- function makeN8nNotConfiguredError() {
44131
- return {
44132
- content: [
44133
- {
44134
- type: "text",
44135
- text: JSON.stringify({
44136
- error: `This tool requires n8n connection. Set N8N_API_URL and N8N_API_KEY in your MCP config. See: ${SETUP_GUIDE_URL}`
44137
- })
44138
- }
44139
- ],
44140
- isError: true
44141
- };
44142
- }
44143
-
44144
44442
  // dist/mcp/tools/handlers/setup-check.js
44145
- var SETUP_GUIDE_URL2 = "https://github.com/tsvika58/n8n-workflow-validator/blob/main/docs/AGENT_INSTALL.md";
44443
+ var SETUP_GUIDE_URL2 = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
44146
44444
  var N8N_PROBE_TIMEOUT_MS = 2e3;
44147
44445
  var N8N_PROBE_PORTS = [5678, 5679];
44148
44446
  async function probeN8nUrl(url2) {
@@ -44332,13 +44630,18 @@ async function dispatchToolCall(name, args) {
44332
44630
  if (requiresN8nApiKey(name) && !process.env.N8N_API_KEY) {
44333
44631
  return makeN8nNotConfiguredError();
44334
44632
  }
44335
- if (tool.inputSchema && hasSchema(name)) {
44336
- const validation = validateToolInput(name, args);
44337
- if (!validation.success)
44338
- return createValidationErrorResponse(validation.error);
44339
- return tool.handler(validation.data);
44633
+ incrementInflight();
44634
+ try {
44635
+ if (tool.inputSchema && hasSchema(name)) {
44636
+ const validation = validateToolInput(name, args);
44637
+ if (!validation.success)
44638
+ return createValidationErrorResponse(validation.error);
44639
+ return await tool.handler(validation.data);
44640
+ }
44641
+ return await tool.handler(args);
44642
+ } finally {
44643
+ decrementInflight();
44340
44644
  }
44341
- return tool.handler(args);
44342
44645
  }
44343
44646
  async function handleToolCall(name, args, sessionId = "default") {
44344
44647
  const startTime = Date.now();
@@ -44356,7 +44659,9 @@ function registerAllTools() {
44356
44659
  registerTool({ ...entry, inputSchema: toolSchemas[entry.name] });
44357
44660
  }
44358
44661
  }
44359
- registerAllTools();
44662
+
44663
+ // dist/mcp/server.js
44664
+ init_validatorPostgresClient();
44360
44665
 
44361
44666
  // dist/mcp/middleware/credential-redaction.js
44362
44667
  var MAX_DEPTH = 20;
@@ -44521,7 +44826,7 @@ function redactMCPResponse(response) {
44521
44826
 
44522
44827
  // dist/mcp/server-lifecycle.js
44523
44828
  init_validatorPostgresClient();
44524
- var import_logging64 = __toESM(require_logging(), 1);
44829
+ var import_logging65 = __toESM(require_logging(), 1);
44525
44830
  var defaultDeps = {
44526
44831
  shutdownPool: shutdownValidatorPool,
44527
44832
  clearCache: clearCredentialCache,
@@ -44529,11 +44834,12 @@ var defaultDeps = {
44529
44834
  };
44530
44835
  async function runCleanup(serverName, deps) {
44531
44836
  try {
44837
+ await waitForInflightRequests(5e3);
44532
44838
  await deps.shutdownPool();
44533
44839
  deps.clearCache();
44534
44840
  deps.clearConfigCache?.();
44535
44841
  } catch (err) {
44536
- import_logging64.logger.error(`[${serverName}] Error during shutdown:`, err);
44842
+ import_logging65.logger.error(`[${serverName}] Error during shutdown:`, err);
44537
44843
  }
44538
44844
  }
44539
44845
  function createShutdownHandler(serverName, deps = defaultDeps) {
@@ -44542,7 +44848,7 @@ function createShutdownHandler(serverName, deps = defaultDeps) {
44542
44848
  if (isShuttingDown)
44543
44849
  return;
44544
44850
  isShuttingDown = true;
44545
- import_logging64.logger.info(`[${serverName}] Received ${signal}, shutting down...`);
44851
+ import_logging65.logger.info(`[${serverName}] Received ${signal}, shutting down...`);
44546
44852
  await runCleanup(serverName, deps);
44547
44853
  process.exit(0);
44548
44854
  };
@@ -44555,12 +44861,29 @@ function registerShutdownHandlers(serverName, deps) {
44555
44861
  process.stdin.on("end", () => void shutdown("stdin-close"));
44556
44862
  process.stdin.on("close", () => void shutdown("stdin-close"));
44557
44863
  process.on("exit", (code) => {
44558
- import_logging64.logger.debug(`[${serverName}] Process exiting (code ${code})`);
44864
+ import_logging65.logger.debug(`[${serverName}] Process exiting (code ${code})`);
44559
44865
  });
44560
44866
  }
44561
44867
 
44562
44868
  // dist/mcp/server.js
44563
- var SETUP_GUIDE_URL3 = "https://github.com/tsvika58/n8n-workflow-validator/blob/main/docs/AGENT_INSTALL.md";
44869
+ var SETUP_GUIDE_URL3 = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
44870
+ var _serverInitialized = false;
44871
+ function isServerInitialized() {
44872
+ return _serverInitialized;
44873
+ }
44874
+ function buildInitializingResponse() {
44875
+ return {
44876
+ content: [{
44877
+ type: "text",
44878
+ text: JSON.stringify({
44879
+ error: "SERVER_INITIALIZING",
44880
+ message: "Server is still loading. Please retry in a moment.",
44881
+ retryAfterMs: 2e3
44882
+ })
44883
+ }],
44884
+ isError: true
44885
+ };
44886
+ }
44564
44887
  function logStartupSummary(n8nConnected) {
44565
44888
  const apiKey = process.env.N8N_API_KEY;
44566
44889
  const n8nUrl = process.env.N8N_API_URL ?? process.env.N8N_API_BASE_URL ?? "http://localhost:5678/api/v1";
@@ -44605,6 +44928,9 @@ function toCallToolResult(response) {
44605
44928
  server.setRequestHandler(import_types22.CallToolRequestSchema, async (request) => {
44606
44929
  const { name, arguments: args } = request.params;
44607
44930
  try {
44931
+ if (!_serverInitialized) {
44932
+ return toCallToolResult(buildInitializingResponse());
44933
+ }
44608
44934
  const response = await handleToolCall(name, args ?? {});
44609
44935
  return toCallToolResult(redactMCPResponse(response));
44610
44936
  } catch (error) {
@@ -44614,13 +44940,25 @@ server.setRequestHandler(import_types22.CallToolRequestSchema, async (request) =
44614
44940
  });
44615
44941
  async function main() {
44616
44942
  const transport = new import_stdio.StdioServerTransport();
44943
+ const startMs = Date.now();
44617
44944
  await server.connect(transport);
44945
+ console.error(`[INIT] MCP handshake complete in ${Date.now() - startMs}ms`);
44946
+ registerAllTools();
44947
+ _serverInitialized = true;
44618
44948
  const toolCount = getRegisteredTools().length;
44619
- console.error(`${config.SERVER_NAME} v${config.SERVER_VERSION} running on stdio (${toolCount} tools registered)`);
44949
+ console.error(`[INIT] ${toolCount} tools registered in ${Date.now() - startMs}ms`);
44620
44950
  logStartupSummary(Boolean(process.env.N8N_API_KEY));
44621
44951
  registerShutdownHandlers(config.SERVER_NAME);
44622
44952
  void warmCredentialCache().catch(() => {
44623
44953
  });
44954
+ if (process.env.P58_DATABASE_URL || process.env.MCP_DATABASE_URL) {
44955
+ void warmValidatorPool().then(() => {
44956
+ console.error("catalog: connected \u2713");
44957
+ }).catch((err) => {
44958
+ console.error(`catalog: WARNING \u2014 connection failed: ${err.message}`);
44959
+ console.error("catalog: tools will retry on first use, but check P58_DATABASE_URL if errors persist");
44960
+ });
44961
+ }
44624
44962
  }
44625
44963
  main().catch((error) => {
44626
44964
  console.error("Server failed to start:", error);
@@ -44628,6 +44966,7 @@ main().catch((error) => {
44628
44966
  });
44629
44967
  // Annotate the CommonJS export names for ESM import in node:
44630
44968
  0 && (module.exports = {
44969
+ isServerInitialized,
44631
44970
  logStartupSummary
44632
44971
  });
44633
44972
  /*! Bundled license information:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@path58/p58-n8n",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "The smartest and fastest n8n MCP server — validate, fix, and discover workflows inside your LLM",
5
5
  "keywords": [
6
6
  "mcp",