@path58/p58-n8n 0.2.5 → 0.2.6

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,26 @@ 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.6] - 2026-03-11
9
+
10
+ ### Added
11
+
12
+ - **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`
13
+ - **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
14
+ - **3-tier connection type resolver** — explicit user override → `connection_rules` catalog lookup (496 AI rules) → `ai_nodes.ai_type` fallback → `main` default
15
+ - **`is_sub_node` metadata coverage** — backfilled from ~45% to 98.4% across all AI nodes in catalog
16
+
17
+ ### Fixed
18
+
19
+ - LangChain workflows now deploy with correct connection wiring — models appear on diamond ports, not circle ports
20
+ - v0.2.5 was published with stale bundle (did not include LangChain fix); this release rebuilds from fixed source with correct `SERVER_VERSION`
21
+
22
+ ## [0.2.5] - 2026-03-11
23
+
24
+ ### Fixed
25
+
26
+ - Internal release — npm publish from stale source (superseded by v0.2.6)
27
+
8
28
  ## [0.2.4] - 2026-03-10
9
29
 
10
30
  ### Added
@@ -130,6 +150,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
130
150
  - npm package published as `@path58/p58-n8n`
131
151
  - ESM module support with shebang for direct `npx` execution
132
152
 
153
+ [0.2.6]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.6
154
+ [0.2.5]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.5
133
155
  [0.2.4]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.4
134
156
  [0.2.3]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.3
135
157
  [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.6",
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,117 @@ 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
+ async function loadConnectionRules2() {
41715
+ const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT LOWER(source_node_type) AS source_node_type,
41716
+ LOWER(target_node_type) AS target_node_type,
41717
+ source_output
41718
+ FROM bluelime.connection_rules
41719
+ WHERE source_output != 'main'
41720
+ AND is_valid = true
41721
+ ORDER BY occurrence_count DESC`), 2, "connectionTypeResolver:loadConnectionRules");
41722
+ return rows;
41723
+ }
41724
+ async function loadAINodeTypes() {
41725
+ const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT LOWER(n.node_type) AS node_type, ai.ai_type
41726
+ FROM bluelime.ai_nodes ai
41727
+ JOIN bluelime.nodes n ON ai.node_id = n.id
41728
+ WHERE ai.is_sub_node = true
41729
+ AND ai.ai_type IS NOT NULL`), 2, "connectionTypeResolver:loadAINodeTypes");
41730
+ return rows;
41731
+ }
41732
+ function buildRuleMap(rows) {
41733
+ const map = /* @__PURE__ */ new Map();
41734
+ for (const row of rows) {
41735
+ const key = `${row.source_node_type}|${row.target_node_type}`;
41736
+ if (!map.has(key))
41737
+ map.set(key, row.source_output);
41738
+ }
41739
+ return map;
41740
+ }
41741
+ function buildAITypeMap(rows) {
41742
+ const map = /* @__PURE__ */ new Map();
41743
+ for (const row of rows) {
41744
+ const connType = AI_TYPE_TO_CONNECTION_TYPE[row.ai_type];
41745
+ if (connType && !map.has(row.node_type))
41746
+ map.set(row.node_type, connType);
41747
+ }
41748
+ return map;
41749
+ }
41750
+ var ConnectionTypeResolver = class _ConnectionTypeResolver {
41751
+ ruleMap;
41752
+ aiTypeMap;
41753
+ constructor(ruleMap, aiTypeMap) {
41754
+ this.ruleMap = ruleMap;
41755
+ this.aiTypeMap = aiTypeMap;
41756
+ }
41757
+ /**
41758
+ * Factory: load catalog data and return a ready resolver.
41759
+ * Falls back to empty maps (all "main") if DB query fails.
41760
+ */
41761
+ static async create() {
41762
+ try {
41763
+ const [ruleRows, aiNodeRows] = await Promise.all([
41764
+ loadConnectionRules2(),
41765
+ loadAINodeTypes()
41766
+ ]);
41767
+ import_logging53.logger.debug("connectionTypeResolver: loaded catalog", {
41768
+ ruleCount: ruleRows.length,
41769
+ aiNodeCount: aiNodeRows.length
41770
+ });
41771
+ return new _ConnectionTypeResolver(buildRuleMap(ruleRows), buildAITypeMap(aiNodeRows));
41772
+ } catch (err) {
41773
+ import_logging53.logger.warn('connectionTypeResolver: DB load failed, defaulting all connections to "main"', { error: err });
41774
+ return new _ConnectionTypeResolver(/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map());
41775
+ }
41776
+ }
41777
+ /**
41778
+ * Resolve the n8n connection type for a single connection.
41779
+ *
41780
+ * @param explicit - Caller-provided override (e.g., from BuildConnectionSpec.type)
41781
+ * @param sourceNodeType - Lowercased full n8n type of the source node
41782
+ * @param targetNodeType - Lowercased full n8n type of the target node
41783
+ * @returns Resolved connection type string
41784
+ */
41785
+ resolve(explicit, sourceNodeType, targetNodeType) {
41786
+ if (explicit)
41787
+ return explicit;
41788
+ if (sourceNodeType && targetNodeType) {
41789
+ const catalogType = this.ruleMap.get(`${sourceNodeType}|${targetNodeType}`);
41790
+ if (catalogType)
41791
+ return catalogType;
41792
+ }
41793
+ if (sourceNodeType) {
41794
+ const aiType = this.aiTypeMap.get(sourceNodeType);
41795
+ if (aiType)
41796
+ return aiType;
41797
+ }
41798
+ return DEFAULT_CONNECTION_TYPE;
41799
+ }
41800
+ };
41801
+ function buildNodeTypeMap4(nodes) {
41802
+ const map = /* @__PURE__ */ new Map();
41803
+ for (const node of nodes)
41804
+ map.set(node.name, node.type.toLowerCase());
41805
+ return map;
41806
+ }
41807
+
41522
41808
  // dist/generation/build/version-credential-resolver.js
41523
41809
  var FALLBACK_VERSION = 1;
41524
41810
  var UNKNOWN_AUTH_MODE = "unknown";
@@ -42099,7 +42385,7 @@ function buildPlaceholders(count) {
42099
42385
  async function queryTypeVersions(nodeTypes) {
42100
42386
  const normalized = nodeTypes.map((t) => t.toLowerCase());
42101
42387
  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");
42388
+ 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
42389
  const groups = /* @__PURE__ */ new Map();
42104
42390
  for (const row of rows) {
42105
42391
  const key = row.node_type.toLowerCase();
@@ -42290,7 +42576,7 @@ function findTerminalNodes(nodes, connections) {
42290
42576
  function buildNodeBase(spec, position, typeVersion, casingMap) {
42291
42577
  const correctedType = resolveNodeTypeCasing(spec.type, casingMap);
42292
42578
  if (correctedType !== spec.type) {
42293
- import_logging53.logger.debug("build_workflow: corrected node type casing", { original: spec.type, corrected: correctedType, nodeName: spec.name });
42579
+ import_logging54.logger.debug("build_workflow: corrected node type casing", { original: spec.type, corrected: correctedType, nodeName: spec.name });
42294
42580
  }
42295
42581
  return {
42296
42582
  id: (0, import_crypto4.randomUUID)(),
@@ -42312,20 +42598,20 @@ function assembleNode(spec, position, typeVersion, credential, isTerminal, casin
42312
42598
  node.continueOnFail = true;
42313
42599
  return node;
42314
42600
  }
42315
- function assembleConnections(connections) {
42601
+ function assembleConnections(connections, nodeTypeMap, resolver) {
42316
42602
  const result = {};
42317
42603
  for (const conn of connections) {
42604
+ const sourceType = nodeTypeMap.get(conn.from);
42605
+ const targetType = nodeTypeMap.get(conn.to);
42606
+ const connType = resolver.resolve(conn.type, sourceType, targetType);
42318
42607
  if (!result[conn.from])
42319
- result[conn.from] = { main: [] };
42608
+ result[conn.from] = {};
42609
+ if (!result[conn.from][connType])
42610
+ result[conn.from][connType] = [];
42320
42611
  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
- });
42612
+ while (result[conn.from][connType].length <= outputIdx)
42613
+ result[conn.from][connType].push([]);
42614
+ result[conn.from][connType][outputIdx].push({ node: conn.to, type: connType, index: conn.to_input ?? 0 });
42329
42615
  }
42330
42616
  return result;
42331
42617
  }
@@ -42336,36 +42622,49 @@ async function resolveCatalogData(input) {
42336
42622
  const uniqueTypes = [...new Set(input.nodes.map((n) => n.type))];
42337
42623
  const [typeResult, credentialMap] = await Promise.all([
42338
42624
  queryTypeVersions(uniqueTypes).catch((err) => {
42339
- import_logging53.logger.warn("WorkflowBuilder: typeVersion lookup failed, using defaults", { error: err });
42625
+ import_logging54.logger.warn("WorkflowBuilder: typeVersion lookup failed, using defaults", { error: err });
42340
42626
  return { versions: /* @__PURE__ */ new Map(), casingMap: /* @__PURE__ */ new Map() };
42341
42627
  }),
42342
42628
  resolveAllCredentials(input.nodes)
42343
42629
  ]);
42344
42630
  return { versionMap: typeResult.versions, casingMap: typeResult.casingMap, credentialMap };
42345
42631
  }
42346
- function buildNodeList(nodes, connections, versionMap, casingMap, credentialMap) {
42347
- const autoPositions = calculatePositions(nodes.map((n) => n.name), connections);
42632
+ function computeResolvedTypes(connections, nodeTypeMap, resolver) {
42633
+ const map = /* @__PURE__ */ new Map();
42634
+ for (const conn of connections) {
42635
+ const connType = resolver.resolve(conn.type, nodeTypeMap.get(conn.from), nodeTypeMap.get(conn.to));
42636
+ map.set(`${conn.from}\u2192${conn.to}`, connType);
42637
+ }
42638
+ return map;
42639
+ }
42640
+ function buildNodeList(nodes, connections, versionMap, casingMap, credentialMap, nodeTypeMap, resolver) {
42641
+ const resolvedTypes = computeResolvedTypes(connections, nodeTypeMap, resolver);
42642
+ const autoPositions = calculatePositions(nodes.map((n) => n.name), connections, resolvedTypes);
42348
42643
  const terminalNodes = findTerminalNodes(nodes, connections);
42349
42644
  return nodes.map((spec) => {
42350
42645
  const resolved = resolveNodeVersion(spec, versionMap, credentialMap);
42351
- import_logging53.logger.debug("build_workflow: typeVersion resolved", { nodeType: spec.type, nodeName: spec.name, ...resolved });
42646
+ import_logging54.logger.debug("build_workflow: typeVersion resolved", { nodeType: spec.type, nodeName: spec.name, ...resolved });
42352
42647
  return assembleNode(spec, getNodePosition(spec, autoPositions), resolved.typeVersion, credentialMap.get(spec.name), terminalNodes.has(spec.name), casingMap);
42353
42648
  });
42354
42649
  }
42355
42650
  async function assembleWorkflow(input) {
42356
- const { versionMap, casingMap, credentialMap } = await resolveCatalogData(input);
42357
- const nodes = buildNodeList(input.nodes, input.connections, versionMap, casingMap, credentialMap);
42651
+ const [{ versionMap, casingMap, credentialMap }, resolver] = await Promise.all([
42652
+ resolveCatalogData(input),
42653
+ ConnectionTypeResolver.create()
42654
+ ]);
42655
+ const nodeTypeMap = buildNodeTypeMap4(input.nodes);
42656
+ const nodes = buildNodeList(input.nodes, input.connections, versionMap, casingMap, credentialMap, nodeTypeMap, resolver);
42358
42657
  return {
42359
42658
  name: input.name,
42360
42659
  nodes,
42361
- connections: assembleConnections(input.connections),
42660
+ connections: assembleConnections(input.connections, nodeTypeMap, resolver),
42362
42661
  settings: {},
42363
42662
  staticData: null
42364
42663
  };
42365
42664
  }
42366
42665
 
42367
42666
  // dist/generation/build/SubWorkflowBuilder.js
42368
- var import_logging54 = __toESM(require_logging(), 1);
42667
+ var import_logging55 = __toESM(require_logging(), 1);
42369
42668
  var EXECUTE_WORKFLOW_TYPE = "n8n-nodes-base.executeWorkflow";
42370
42669
  var PLACEHOLDER = "{{SUB_WORKFLOW_ID}}";
42371
42670
  function findExecuteWorkflowNodes(nodes) {
@@ -42398,17 +42697,17 @@ async function buildSubWorkflows(parentSpec, buildSingle) {
42398
42697
  }
42399
42698
  const builtSubWorkflows = [];
42400
42699
  for (const subSpec of parentSpec.sub_workflows) {
42401
- import_logging54.logger.info("SubWorkflowBuilder: building sub-workflow", { name: subSpec.name });
42700
+ import_logging55.logger.info("SubWorkflowBuilder: building sub-workflow", { name: subSpec.name });
42402
42701
  const { workflow_id, workflow_url } = await buildSingle(subSpec);
42403
42702
  builtSubWorkflows.push({ name: subSpec.name, workflow_id, workflow_url });
42404
- import_logging54.logger.info("SubWorkflowBuilder: sub-workflow built", { name: subSpec.name, workflow_id });
42703
+ import_logging55.logger.info("SubWorkflowBuilder: sub-workflow built", { name: subSpec.name, workflow_id });
42405
42704
  }
42406
42705
  const wiredSpec = wireSubWorkflowIds(parentSpec, builtSubWorkflows);
42407
42706
  return { wiredSpec, builtSubWorkflows };
42408
42707
  }
42409
42708
 
42410
42709
  // dist/mcp/tools/handlers/build-workflow.js
42411
- var import_logging55 = __toESM(require_logging(), 1);
42710
+ var import_logging56 = __toESM(require_logging(), 1);
42412
42711
  function buildWorkflowUrl(workflowId) {
42413
42712
  const apiBase = process.env.N8N_API_BASE_URL ?? "http://localhost:5678/api/v1";
42414
42713
  return `${apiBase.replace("/api/v1", "")}/workflow/${workflowId}`;
@@ -42501,7 +42800,7 @@ function buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testRe
42501
42800
  async function buildSuccessResponse2(args, result, builtSubWorkflows, timeoutMs, startTime) {
42502
42801
  const workflowId = result.serverResponse.id;
42503
42802
  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 });
42803
+ import_logging56.logger.info("build_workflow: complete", { workflowId, fixes: result.fixesApplied.length });
42505
42804
  return buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testResult, startTime);
42506
42805
  }
42507
42806
  async function executeBuildPipeline(args, timeoutMs, correlationId, startTime) {
@@ -42635,7 +42934,7 @@ function stripExtraWebhookTriggers(args) {
42635
42934
  const webhookNames = new Set(webhookNodes.map((n) => n.name));
42636
42935
  const cleanedNodes = args.nodes.filter((n) => !webhookNames.has(n.name));
42637
42936
  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", {
42937
+ import_logging56.logger.info("build_workflow: stripped webhook triggers from schedule workflow", {
42639
42938
  removed: [...webhookNames]
42640
42939
  });
42641
42940
  return {
@@ -42650,7 +42949,7 @@ async function handleBuildWorkflow(args) {
42650
42949
  const correlationId = generateCorrelationId();
42651
42950
  const startTime = performance.now();
42652
42951
  const timeoutMs = normalizeTimeout();
42653
- import_logging55.logger.debug("build_workflow: starting", { correlationId, workflowName: args.name });
42952
+ import_logging56.logger.debug("build_workflow: starting", { correlationId, workflowName: args.name });
42654
42953
  try {
42655
42954
  const { cleaned: triggerCleaned, warnings: triggerWarnings } = await substituteUnavailableTriggers(args);
42656
42955
  const { cleaned, warning: webhookWarning } = stripExtraWebhookTriggers(triggerCleaned);
@@ -42679,7 +42978,7 @@ async function handleBuildWorkflow(args) {
42679
42978
  } catch (error) {
42680
42979
  if (isTimeoutError(error))
42681
42980
  return toMCPResponse(createTimeoutError(correlationId, "build_workflow"));
42682
- import_logging55.logger.error("build_workflow: unexpected error", { correlationId, error });
42981
+ import_logging56.logger.error("build_workflow: unexpected error", { correlationId, error });
42683
42982
  const e = error instanceof Error ? error : new Error(String(error));
42684
42983
  return toMCPResponse(createInternalError(correlationId, e));
42685
42984
  }
@@ -42733,14 +43032,15 @@ Examples:
42733
43032
  },
42734
43033
  connections: {
42735
43034
  type: "array",
42736
- description: "Node connections",
43035
+ 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
43036
  items: {
42738
43037
  type: "object",
42739
43038
  properties: {
42740
43039
  from: { type: "string", description: "Source node name" },
42741
43040
  to: { type: "string", description: "Target node name" },
42742
43041
  from_output: { type: "number", description: "Output port index (default 0)" },
42743
- to_input: { type: "number", description: "Input port index (default 0)" }
43042
+ to_input: { type: "number", description: "Input port index (default 0)" },
43043
+ type: { type: "string", description: 'Connection type override (e.g., "ai_languageModel", "ai_tool", "ai_memory"). Auto-resolved from catalog if omitted.' }
42744
43044
  },
42745
43045
  required: ["from", "to"]
42746
43046
  }
@@ -42759,7 +43059,7 @@ Examples:
42759
43059
  };
42760
43060
 
42761
43061
  // dist/mcp/tools/handlers/list-credentials.js
42762
- var import_logging56 = __toESM(require_logging(), 1);
43062
+ var import_logging57 = __toESM(require_logging(), 1);
42763
43063
  init_validatorPostgresClient();
42764
43064
  function buildApiConfig14(timeoutMs) {
42765
43065
  return {
@@ -42865,10 +43165,10 @@ function handleListError2(error, correlationId) {
42865
43165
  }
42866
43166
  const statusCode = error?.statusCode;
42867
43167
  if (statusCode === 401 || statusCode === 403) {
42868
- import_logging56.logger.warn("list_credentials: auth error", { correlationId, statusCode });
43168
+ import_logging57.logger.warn("list_credentials: auth error", { correlationId, statusCode });
42869
43169
  return toMCPResponse(createSuccessResponse({ success: false, error: "Authentication failed" }, correlationId, { duration_ms: 0 }));
42870
43170
  }
42871
- import_logging56.logger.error("list_credentials: unexpected error", { correlationId, error });
43171
+ import_logging57.logger.error("list_credentials: unexpected error", { correlationId, error });
42872
43172
  const originalError = error instanceof Error ? error : new Error(String(error));
42873
43173
  return toMCPResponse(createInternalError(correlationId, originalError));
42874
43174
  }
@@ -42879,13 +43179,13 @@ async function handleListCredentials(args) {
42879
43179
  const correlationId = generateCorrelationId();
42880
43180
  const startTime = performance.now();
42881
43181
  const timeoutMs = normalizeTimeout();
42882
- import_logging56.logger.debug("list_credentials: starting", { correlationId, options: args });
43182
+ import_logging57.logger.debug("list_credentials: starting", { correlationId, options: args });
42883
43183
  try {
42884
43184
  const result = await fetchRawCredentials(args, timeoutMs);
42885
43185
  const { credentials: filtered, neededCredentialTypes } = await applyForNodesFilter(result.credentials, args.forNodes);
42886
43186
  const enriched = await enrichWithNodeTypes(filtered);
42887
43187
  const final = args.includeSchema ? await enrichWithSchema(enriched) : enriched;
42888
- import_logging56.logger.info("list_credentials: retrieved", { correlationId, count: final.length });
43188
+ import_logging57.logger.info("list_credentials: retrieved", { correlationId, count: final.length });
42889
43189
  const data = buildResponseData(final, neededCredentialTypes);
42890
43190
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: Math.round(performance.now() - startTime) }));
42891
43191
  } catch (error) {
@@ -42941,7 +43241,7 @@ listing which credential types need to be created.`,
42941
43241
  };
42942
43242
 
42943
43243
  // dist/mcp/tools/handlers/get-credential-schema.js
42944
- var import_logging57 = __toESM(require_logging(), 1);
43244
+ var import_logging58 = __toESM(require_logging(), 1);
42945
43245
  init_validatorPostgresClient();
42946
43246
  var SENSITIVE_PATTERNS = /* @__PURE__ */ new Set([
42947
43247
  "apikey",
@@ -43080,22 +43380,27 @@ function buildResult(credentialType, rows, compatibleNodes) {
43080
43380
  function buildSchemaResponse(credentialType, rows, nodes, correlationId, startTime) {
43081
43381
  const data = buildResult(credentialType, rows, nodes);
43082
43382
  const duration_ms = Math.round(performance.now() - startTime);
43083
- import_logging57.logger.info("get_credential_schema: found", { correlationId, credential_type: credentialType, fieldCount: rows.length });
43383
+ import_logging58.logger.info("get_credential_schema: found", { correlationId, credential_type: credentialType, fieldCount: rows.length });
43084
43384
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms }));
43085
43385
  }
43086
43386
  async function handleGetCredentialSchema(args) {
43087
43387
  const correlationId = generateCorrelationId();
43088
43388
  const startTime = performance.now();
43089
43389
  const { credential_type } = args;
43090
- import_logging57.logger.debug("get_credential_schema: starting", { correlationId, credential_type });
43390
+ import_logging58.logger.debug("get_credential_schema: starting", { correlationId, credential_type });
43091
43391
  try {
43092
43392
  const [rows, nodes] = await Promise.all([fetchSchemaRows(credential_type), fetchCompatibleNodes(credential_type)]);
43093
43393
  if (rows.length === 0)
43094
43394
  return handleUnknownType(credential_type, correlationId);
43095
43395
  return buildSchemaResponse(credential_type, rows, nodes, correlationId, startTime);
43096
43396
  } 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))));
43397
+ import_logging58.logger.error("get_credential_schema: unexpected error", { correlationId, error });
43398
+ const originalError = error instanceof Error ? error : new Error(String(error));
43399
+ if (isDbConfigError(originalError))
43400
+ return makeDbNotConfiguredResponse();
43401
+ if (isDbConnectionError(originalError))
43402
+ return makeDbConnectionErrorResponse();
43403
+ return toMCPResponse(createInternalError(correlationId, originalError));
43099
43404
  }
43100
43405
  }
43101
43406
  async function handleUnknownType(credentialType, correlationId) {
@@ -43138,7 +43443,7 @@ Example usage:
43138
43443
  };
43139
43444
 
43140
43445
  // dist/mcp/tools/handlers/create-credential.js
43141
- var import_logging58 = __toESM(require_logging(), 1);
43446
+ var import_logging59 = __toESM(require_logging(), 1);
43142
43447
  init_validatorPostgresClient();
43143
43448
  var DEFAULT_TIMEOUT_MS2 = 1e4;
43144
43449
  var FIELDS_SQL = `
@@ -43204,11 +43509,11 @@ function buildCreateError(error, correlationId) {
43204
43509
  const statusCode = error?.statusCode;
43205
43510
  const message = error instanceof Error ? error.message : String(error);
43206
43511
  if (statusCode === 401 || statusCode === 403) {
43207
- import_logging58.logger.warn("create_credential: auth error", { correlationId, statusCode });
43512
+ import_logging59.logger.warn("create_credential: auth error", { correlationId, statusCode });
43208
43513
  const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
43209
43514
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43210
43515
  }
43211
- import_logging58.logger.error("create_credential: unexpected error", { correlationId, error });
43516
+ import_logging59.logger.error("create_credential: unexpected error", { correlationId, error });
43212
43517
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
43213
43518
  }
43214
43519
  function buildExistsResponse(existing, correlationId) {
@@ -43228,7 +43533,7 @@ async function handleCreateCredential(args) {
43228
43533
  const correlationId = generateCorrelationId();
43229
43534
  const startTime = performance.now();
43230
43535
  const { credential_type, name, data } = args;
43231
- import_logging58.logger.debug("create_credential: starting", { correlationId, credential_type, name });
43536
+ import_logging59.logger.debug("create_credential: starting", { correlationId, credential_type, name });
43232
43537
  try {
43233
43538
  const fields = await fetchCredentialFields(credential_type);
43234
43539
  if (fields.length === 0)
@@ -43239,13 +43544,13 @@ async function handleCreateCredential(args) {
43239
43544
  const config3 = buildApiConfig15();
43240
43545
  const existing = await findExistingCredential(config3, credential_type, name);
43241
43546
  if (existing) {
43242
- import_logging58.logger.warn("create_credential: duplicate blocked", { correlationId, existing_id: existing.id, credential_type, name });
43547
+ import_logging59.logger.warn("create_credential: duplicate blocked", { correlationId, existing_id: existing.id, credential_type, name });
43243
43548
  return buildExistsResponse(existing, correlationId);
43244
43549
  }
43245
43550
  const result = await createCredential(config3, { name, type: credential_type, data });
43246
43551
  clearCredentialCache();
43247
43552
  const duration_ms = Math.round(performance.now() - startTime);
43248
- import_logging58.logger.info("create_credential: created", { correlationId, id: result.id, credential_type, name });
43553
+ import_logging59.logger.info("create_credential: created", { correlationId, id: result.id, credential_type, name });
43249
43554
  return buildCreateSuccessResponse(result, correlationId, duration_ms);
43250
43555
  } catch (error) {
43251
43556
  return buildCreateError(error, correlationId);
@@ -43297,7 +43602,7 @@ Example usage:
43297
43602
 
43298
43603
  // dist/mcp/tools/handlers/test-credential.js
43299
43604
  var net = __toESM(require("net"), 1);
43300
- var import_logging59 = __toESM(require_logging(), 1);
43605
+ var import_logging60 = __toESM(require_logging(), 1);
43301
43606
  var PROBE_TIMEOUT_MS = 5e3;
43302
43607
  var RATE_LIMIT_MS = 3e4;
43303
43608
  var lastTestTimestamps = /* @__PURE__ */ new Map();
@@ -43477,7 +43782,7 @@ function buildProbeError(error, correlationId) {
43477
43782
  const data = { supported: true, valid: false, message: "Credential not found. Check the credential ID." };
43478
43783
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43479
43784
  }
43480
- import_logging59.logger.error("test_credential: unexpected error", { correlationId, error });
43785
+ import_logging60.logger.error("test_credential: unexpected error", { correlationId, error });
43481
43786
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
43482
43787
  }
43483
43788
  async function handleTestCredential(args) {
@@ -43493,7 +43798,7 @@ async function handleTestCredential(args) {
43493
43798
  recordTestTimestamp(credential_id);
43494
43799
  try {
43495
43800
  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", {
43801
+ import_logging60.logger.info("test_credential: n8n built-in test", {
43497
43802
  correlationId,
43498
43803
  type: credential.type,
43499
43804
  valid: n8nResult.valid
@@ -43501,9 +43806,9 @@ async function handleTestCredential(args) {
43501
43806
  return buildProbeResponse(n8nResult, correlationId);
43502
43807
  } catch (n8nTestError) {
43503
43808
  if (n8nTestError?.code === "NEEDS_N8N_LOGIN") {
43504
- import_logging59.logger.debug("test_credential: n8n test unavailable, falling back to HTTP probes");
43809
+ import_logging60.logger.debug("test_credential: n8n test unavailable, falling back to HTTP probes");
43505
43810
  } else {
43506
- import_logging59.logger.warn("test_credential: n8n test failed, falling back to HTTP probes", {
43811
+ import_logging60.logger.warn("test_credential: n8n test failed, falling back to HTTP probes", {
43507
43812
  error: n8nTestError instanceof Error ? n8nTestError.message : String(n8nTestError)
43508
43813
  });
43509
43814
  }
@@ -43513,10 +43818,10 @@ async function handleTestCredential(args) {
43513
43818
  return buildUnsupportedResponse(credential.type, correlationId);
43514
43819
  const hasBlankValues = Object.values(credential.data).some((v) => typeof v === "string" && v.includes("__n8n_BLANK_VALUE_"));
43515
43820
  if (hasBlankValues) {
43516
- import_logging59.logger.warn("test_credential: credential data contains encrypted placeholders, probe may be unreliable");
43821
+ import_logging60.logger.warn("test_credential: credential data contains encrypted placeholders, probe may be unreliable");
43517
43822
  }
43518
43823
  const result = await probe(credential.data);
43519
- import_logging59.logger.info("test_credential: HTTP probe fallback", {
43824
+ import_logging60.logger.info("test_credential: HTTP probe fallback", {
43520
43825
  correlationId,
43521
43826
  type: credential.type,
43522
43827
  valid: result.valid,
@@ -43570,7 +43875,7 @@ Use list_credentials to find credential IDs.`,
43570
43875
  };
43571
43876
 
43572
43877
  // dist/mcp/tools/handlers/update-credential.js
43573
- var import_logging60 = __toESM(require_logging(), 1);
43878
+ var import_logging61 = __toESM(require_logging(), 1);
43574
43879
  var DEFAULT_TIMEOUT_MS3 = 1e4;
43575
43880
  function buildApiConfig17() {
43576
43881
  return {
@@ -43594,16 +43899,16 @@ function buildUpdateError(error, correlationId) {
43594
43899
  const statusCode = error?.statusCode;
43595
43900
  const message = error instanceof Error ? error.message : String(error);
43596
43901
  if (statusCode === 404) {
43597
- import_logging60.logger.warn("update_credential: not found", { correlationId, statusCode });
43902
+ import_logging61.logger.warn("update_credential: not found", { correlationId, statusCode });
43598
43903
  const data = { success: false, error: "NOT_FOUND", message: "Credential not found. Use list_credentials to find valid IDs." };
43599
43904
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43600
43905
  }
43601
43906
  if (statusCode === 401 || statusCode === 403) {
43602
- import_logging60.logger.warn("update_credential: auth error", { correlationId, statusCode });
43907
+ import_logging61.logger.warn("update_credential: auth error", { correlationId, statusCode });
43603
43908
  const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
43604
43909
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43605
43910
  }
43606
- import_logging60.logger.error("update_credential: unexpected error", { correlationId, error });
43911
+ import_logging61.logger.error("update_credential: unexpected error", { correlationId, error });
43607
43912
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
43608
43913
  }
43609
43914
  async function buildUpdatePayload(config3, args) {
@@ -43618,9 +43923,9 @@ function buildUpdateSuccessResponse2(result, correlationId, duration_ms) {
43618
43923
  async function handleUpdateCredential(args) {
43619
43924
  const correlationId = generateCorrelationId();
43620
43925
  const startTime = performance.now();
43621
- import_logging60.logger.debug("update_credential: starting", { correlationId, credential_id: args.credential_id });
43926
+ import_logging61.logger.debug("update_credential: starting", { correlationId, credential_id: args.credential_id });
43622
43927
  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 });
43928
+ import_logging61.logger.warn("update_credential: rejected data write attempt", { correlationId, credential_id: args.credential_id });
43624
43929
  const data = {
43625
43930
  success: false,
43626
43931
  error: "DATA_WRITE_DENIED",
@@ -43641,7 +43946,7 @@ async function handleUpdateCredential(args) {
43641
43946
  const payload = await buildUpdatePayload(config3, { ...args, data: void 0 });
43642
43947
  const result = await updateCredential(config3, args.credential_id, payload);
43643
43948
  invalidate();
43644
- import_logging60.logger.info("update_credential: updated (name only)", { correlationId, id: result.id });
43949
+ import_logging61.logger.info("update_credential: updated (name only)", { correlationId, id: result.id });
43645
43950
  return buildUpdateSuccessResponse2(result, correlationId, Math.round(performance.now() - startTime));
43646
43951
  } catch (error) {
43647
43952
  return buildUpdateError(error, correlationId);
@@ -43676,7 +43981,7 @@ On not found: returns NOT_FOUND error with guidance.`,
43676
43981
  };
43677
43982
 
43678
43983
  // dist/mcp/tools/handlers/delete-credential.js
43679
- var import_logging61 = __toESM(require_logging(), 1);
43984
+ var import_logging62 = __toESM(require_logging(), 1);
43680
43985
  var DEFAULT_TIMEOUT_MS4 = 1e4;
43681
43986
  var MAX_WORKFLOWS_TO_SCAN = 100;
43682
43987
  function buildApiConfig18() {
@@ -43735,18 +44040,18 @@ function buildDeleteError(error, correlationId) {
43735
44040
  const statusCode = error?.statusCode;
43736
44041
  const message = error instanceof Error ? error.message : String(error);
43737
44042
  if (statusCode === 401 || statusCode === 403) {
43738
- import_logging61.logger.warn("delete_credential: auth error", { correlationId, statusCode });
44043
+ import_logging62.logger.warn("delete_credential: auth error", { correlationId, statusCode });
43739
44044
  const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
43740
44045
  return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
43741
44046
  }
43742
- import_logging61.logger.error("delete_credential: unexpected error", { correlationId, error });
44047
+ import_logging62.logger.error("delete_credential: unexpected error", { correlationId, error });
43743
44048
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
43744
44049
  }
43745
44050
  async function checkAndWarnDependents(config3, credential_id, correlationId) {
43746
44051
  const dependentWorkflows = await findDependentWorkflows(config3, credential_id);
43747
44052
  if (dependentWorkflows.length === 0)
43748
44053
  return null;
43749
- import_logging61.logger.warn("delete_credential: dependent workflows found", { correlationId, credential_id, count: dependentWorkflows.length });
44054
+ import_logging62.logger.warn("delete_credential: dependent workflows found", { correlationId, credential_id, count: dependentWorkflows.length });
43750
44055
  return buildDependentWarningResponse(credential_id, dependentWorkflows, correlationId);
43751
44056
  }
43752
44057
  async function executeDelete(config3, credential_id, force, correlationId, startTime) {
@@ -43757,14 +44062,14 @@ async function executeDelete(config3, credential_id, force, correlationId, start
43757
44062
  }
43758
44063
  await deleteCredential(config3, credential_id);
43759
44064
  invalidate();
43760
- import_logging61.logger.info("delete_credential: deleted", { correlationId, credential_id, force });
44065
+ import_logging62.logger.info("delete_credential: deleted", { correlationId, credential_id, force });
43761
44066
  return buildDeleteSuccessResponse(credential_id, correlationId, Math.round(performance.now() - startTime));
43762
44067
  }
43763
44068
  async function handleDeleteCredential(args) {
43764
44069
  const correlationId = generateCorrelationId();
43765
44070
  const startTime = performance.now();
43766
44071
  const { credential_id, force = false } = args;
43767
- import_logging61.logger.debug("delete_credential: starting", { correlationId, credential_id, force });
44072
+ import_logging62.logger.debug("delete_credential: starting", { correlationId, credential_id, force });
43768
44073
  try {
43769
44074
  return await executeDelete(buildApiConfig18(), credential_id, force, correlationId, startTime);
43770
44075
  } catch (error) {
@@ -43809,12 +44114,12 @@ On auth error: returns AUTH_ERROR with guidance.`,
43809
44114
  };
43810
44115
 
43811
44116
  // dist/mcp/tools/handlers/collect-config.js
43812
- var import_logging63 = __toESM(require_logging(), 1);
44117
+ var import_logging64 = __toESM(require_logging(), 1);
43813
44118
  init_validatorPostgresClient();
43814
- var import_retry8 = __toESM(require_retryUtils(), 1);
44119
+ var import_retry9 = __toESM(require_retryUtils(), 1);
43815
44120
 
43816
44121
  // dist/mcp/session/config-cache.js
43817
- var import_logging62 = __toESM(require_logging(), 1);
44122
+ var import_logging63 = __toESM(require_logging(), 1);
43818
44123
  var CONFIG_CACHE_TTL_MS = 30 * 60 * 1e3;
43819
44124
  var CONFIG_CACHE_MAX_ENTRIES = 500;
43820
44125
  var cacheState2 = null;
@@ -43857,12 +44162,12 @@ function set(key, value) {
43857
44162
  stored_at: Date.now(),
43858
44163
  expires_at: Date.now() + CONFIG_CACHE_TTL_MS
43859
44164
  });
43860
- import_logging62.logger.debug("config-cache: stored value", { key });
44165
+ import_logging63.logger.debug("config-cache: stored value", { key });
43861
44166
  }
43862
44167
  function clearConfigCache() {
43863
44168
  cacheState2 = null;
43864
44169
  warming = null;
43865
- import_logging62.logger.debug("config-cache: cleared");
44170
+ import_logging63.logger.debug("config-cache: cleared");
43866
44171
  }
43867
44172
  function getSuggestion(configType, nodeType, parameterName) {
43868
44173
  const serviceId = extractServiceId(nodeType);
@@ -43994,7 +44299,7 @@ function buildConfigValues(answered) {
43994
44299
  return result;
43995
44300
  }
43996
44301
  async function markGapsResolved(gapType, nodeType, notes) {
43997
- await (0, import_retry8.retryDbQuery)(() => validatorQuery(RESOLVE_GAPS_SQL, [gapType, nodeType, notes]), 3, "collect_config_resolve_gaps");
44302
+ await (0, import_retry9.retryDbQuery)(() => validatorQuery(RESOLVE_GAPS_SQL, [gapType, nodeType, notes]), 3, "collect_config_resolve_gaps");
43998
44303
  }
43999
44304
  async function resolveOneGap(req) {
44000
44305
  const gapType = CATEGORY_TO_GAP_TYPE[req.configType];
@@ -44003,7 +44308,7 @@ async function resolveOneGap(req) {
44003
44308
  try {
44004
44309
  await markGapsResolved(gapType, req.nodeType, `Resolved via collect_config: ${req.parameterName}`);
44005
44310
  } catch (error) {
44006
- import_logging63.logger.warn("collect_config: gap resolution failed (non-fatal)", {
44311
+ import_logging64.logger.warn("collect_config: gap resolution failed (non-fatal)", {
44007
44312
  nodeType: req.nodeType,
44008
44313
  gapType,
44009
44314
  error: error instanceof Error ? error.message : String(error)
@@ -44045,14 +44350,14 @@ async function executeCollect(args) {
44045
44350
  async function handleCollectConfig(args) {
44046
44351
  const correlationId = generateCorrelationId();
44047
44352
  const startTime = performance.now();
44048
- import_logging63.logger.debug("collect_config: starting", { correlationId });
44353
+ import_logging64.logger.debug("collect_config: starting", { correlationId });
44049
44354
  try {
44050
44355
  const { output, logMeta } = await executeCollect(args);
44051
44356
  const duration_ms = Math.round(performance.now() - startTime);
44052
- import_logging63.logger.info("collect_config: completed", { correlationId, ...logMeta });
44357
+ import_logging64.logger.info("collect_config: completed", { correlationId, ...logMeta });
44053
44358
  return toMCPResponse(createSuccessResponse(output, correlationId, { duration_ms }));
44054
44359
  } catch (error) {
44055
- import_logging63.logger.error("collect_config: failed", { correlationId, error });
44360
+ import_logging64.logger.error("collect_config: failed", { correlationId, error });
44056
44361
  return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
44057
44362
  }
44058
44363
  }
@@ -44109,40 +44414,8 @@ When values are collected, marks matching factory.gaps entries as resolved for a
44109
44414
  }
44110
44415
  };
44111
44416
 
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
44417
  // 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";
44418
+ var SETUP_GUIDE_URL2 = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
44146
44419
  var N8N_PROBE_TIMEOUT_MS = 2e3;
44147
44420
  var N8N_PROBE_PORTS = [5678, 5679];
44148
44421
  async function probeN8nUrl(url2) {
@@ -44332,13 +44605,18 @@ async function dispatchToolCall(name, args) {
44332
44605
  if (requiresN8nApiKey(name) && !process.env.N8N_API_KEY) {
44333
44606
  return makeN8nNotConfiguredError();
44334
44607
  }
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);
44608
+ incrementInflight();
44609
+ try {
44610
+ if (tool.inputSchema && hasSchema(name)) {
44611
+ const validation = validateToolInput(name, args);
44612
+ if (!validation.success)
44613
+ return createValidationErrorResponse(validation.error);
44614
+ return await tool.handler(validation.data);
44615
+ }
44616
+ return await tool.handler(args);
44617
+ } finally {
44618
+ decrementInflight();
44340
44619
  }
44341
- return tool.handler(args);
44342
44620
  }
44343
44621
  async function handleToolCall(name, args, sessionId = "default") {
44344
44622
  const startTime = Date.now();
@@ -44356,7 +44634,9 @@ function registerAllTools() {
44356
44634
  registerTool({ ...entry, inputSchema: toolSchemas[entry.name] });
44357
44635
  }
44358
44636
  }
44359
- registerAllTools();
44637
+
44638
+ // dist/mcp/server.js
44639
+ init_validatorPostgresClient();
44360
44640
 
44361
44641
  // dist/mcp/middleware/credential-redaction.js
44362
44642
  var MAX_DEPTH = 20;
@@ -44521,7 +44801,7 @@ function redactMCPResponse(response) {
44521
44801
 
44522
44802
  // dist/mcp/server-lifecycle.js
44523
44803
  init_validatorPostgresClient();
44524
- var import_logging64 = __toESM(require_logging(), 1);
44804
+ var import_logging65 = __toESM(require_logging(), 1);
44525
44805
  var defaultDeps = {
44526
44806
  shutdownPool: shutdownValidatorPool,
44527
44807
  clearCache: clearCredentialCache,
@@ -44529,11 +44809,12 @@ var defaultDeps = {
44529
44809
  };
44530
44810
  async function runCleanup(serverName, deps) {
44531
44811
  try {
44812
+ await waitForInflightRequests(5e3);
44532
44813
  await deps.shutdownPool();
44533
44814
  deps.clearCache();
44534
44815
  deps.clearConfigCache?.();
44535
44816
  } catch (err) {
44536
- import_logging64.logger.error(`[${serverName}] Error during shutdown:`, err);
44817
+ import_logging65.logger.error(`[${serverName}] Error during shutdown:`, err);
44537
44818
  }
44538
44819
  }
44539
44820
  function createShutdownHandler(serverName, deps = defaultDeps) {
@@ -44542,7 +44823,7 @@ function createShutdownHandler(serverName, deps = defaultDeps) {
44542
44823
  if (isShuttingDown)
44543
44824
  return;
44544
44825
  isShuttingDown = true;
44545
- import_logging64.logger.info(`[${serverName}] Received ${signal}, shutting down...`);
44826
+ import_logging65.logger.info(`[${serverName}] Received ${signal}, shutting down...`);
44546
44827
  await runCleanup(serverName, deps);
44547
44828
  process.exit(0);
44548
44829
  };
@@ -44555,12 +44836,29 @@ function registerShutdownHandlers(serverName, deps) {
44555
44836
  process.stdin.on("end", () => void shutdown("stdin-close"));
44556
44837
  process.stdin.on("close", () => void shutdown("stdin-close"));
44557
44838
  process.on("exit", (code) => {
44558
- import_logging64.logger.debug(`[${serverName}] Process exiting (code ${code})`);
44839
+ import_logging65.logger.debug(`[${serverName}] Process exiting (code ${code})`);
44559
44840
  });
44560
44841
  }
44561
44842
 
44562
44843
  // dist/mcp/server.js
44563
- var SETUP_GUIDE_URL3 = "https://github.com/tsvika58/n8n-workflow-validator/blob/main/docs/AGENT_INSTALL.md";
44844
+ var SETUP_GUIDE_URL3 = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
44845
+ var _serverInitialized = false;
44846
+ function isServerInitialized() {
44847
+ return _serverInitialized;
44848
+ }
44849
+ function buildInitializingResponse() {
44850
+ return {
44851
+ content: [{
44852
+ type: "text",
44853
+ text: JSON.stringify({
44854
+ error: "SERVER_INITIALIZING",
44855
+ message: "Server is still loading. Please retry in a moment.",
44856
+ retryAfterMs: 2e3
44857
+ })
44858
+ }],
44859
+ isError: true
44860
+ };
44861
+ }
44564
44862
  function logStartupSummary(n8nConnected) {
44565
44863
  const apiKey = process.env.N8N_API_KEY;
44566
44864
  const n8nUrl = process.env.N8N_API_URL ?? process.env.N8N_API_BASE_URL ?? "http://localhost:5678/api/v1";
@@ -44605,6 +44903,9 @@ function toCallToolResult(response) {
44605
44903
  server.setRequestHandler(import_types22.CallToolRequestSchema, async (request) => {
44606
44904
  const { name, arguments: args } = request.params;
44607
44905
  try {
44906
+ if (!_serverInitialized) {
44907
+ return toCallToolResult(buildInitializingResponse());
44908
+ }
44608
44909
  const response = await handleToolCall(name, args ?? {});
44609
44910
  return toCallToolResult(redactMCPResponse(response));
44610
44911
  } catch (error) {
@@ -44614,13 +44915,25 @@ server.setRequestHandler(import_types22.CallToolRequestSchema, async (request) =
44614
44915
  });
44615
44916
  async function main() {
44616
44917
  const transport = new import_stdio.StdioServerTransport();
44918
+ const startMs = Date.now();
44617
44919
  await server.connect(transport);
44920
+ console.error(`[INIT] MCP handshake complete in ${Date.now() - startMs}ms`);
44921
+ registerAllTools();
44922
+ _serverInitialized = true;
44618
44923
  const toolCount = getRegisteredTools().length;
44619
- console.error(`${config.SERVER_NAME} v${config.SERVER_VERSION} running on stdio (${toolCount} tools registered)`);
44924
+ console.error(`[INIT] ${toolCount} tools registered in ${Date.now() - startMs}ms`);
44620
44925
  logStartupSummary(Boolean(process.env.N8N_API_KEY));
44621
44926
  registerShutdownHandlers(config.SERVER_NAME);
44622
44927
  void warmCredentialCache().catch(() => {
44623
44928
  });
44929
+ if (process.env.P58_DATABASE_URL || process.env.MCP_DATABASE_URL) {
44930
+ void warmValidatorPool().then(() => {
44931
+ console.error("catalog: connected \u2713");
44932
+ }).catch((err) => {
44933
+ console.error(`catalog: WARNING \u2014 connection failed: ${err.message}`);
44934
+ console.error("catalog: tools will retry on first use, but check P58_DATABASE_URL if errors persist");
44935
+ });
44936
+ }
44624
44937
  }
44625
44938
  main().catch((error) => {
44626
44939
  console.error("Server failed to start:", error);
@@ -44628,6 +44941,7 @@ main().catch((error) => {
44628
44941
  });
44629
44942
  // Annotate the CommonJS export names for ESM import in node:
44630
44943
  0 && (module.exports = {
44944
+ isServerInitialized,
44631
44945
  logStartupSummary
44632
44946
  });
44633
44947
  /*! 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.6",
4
4
  "description": "The smartest and fastest n8n MCP server — validate, fix, and discover workflows inside your LLM",
5
5
  "keywords": [
6
6
  "mcp",