@path58/p58-n8n 0.2.13 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@path58/p58-n8n)](https://www.npmjs.com/package/@path58/p58-n8n)
6
6
  [![License: BSL 1.1](https://img.shields.io/badge/License-BSL_1.1-orange.svg)](LICENSE)
7
- [![Tests: 9,799 passed](https://img.shields.io/badge/Tests-9%2C799_passed-brightgreen)](https://github.com/tsvika58/p58-n8n/actions/workflows/ci.yml)
7
+ [![Tests: 9,840 passed](https://img.shields.io/badge/Tests-9%2C840_passed-brightgreen)](https://github.com/tsvika58/p58-n8n/actions/workflows/ci.yml)
8
8
  [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
9
9
 
10
10
  p58-n8n is an MCP server that gives your AI assistant deep knowledge of n8n — **1,545 nodes**, **12,619 operations**, **679 credential types** — so it can plan, build, validate, fix, and deploy workflows correctly.
@@ -40771,6 +40771,16 @@ function extractCredentialNames(nodes) {
40771
40771
  return [...new Set(names)];
40772
40772
  }
40773
40773
  async function testViaClone(workflow, triggerType, testPayload, timeoutMs, apiConfig) {
40774
+ const hasR2W = await hasRespondToWebhookNode(workflow);
40775
+ if (hasR2W) {
40776
+ return {
40777
+ execution_id: null,
40778
+ status: "error",
40779
+ trigger_type: triggerType,
40780
+ result_summary: "Cannot test via clone: workflow contains a Respond to Webhook node. Test this workflow using test_workflow with a direct webhook POST.",
40781
+ next_step: "This workflow has a Respond to Webhook node. Call test_workflow directly \u2014 it handles the response mode correctly."
40782
+ };
40783
+ }
40774
40784
  const nodes = workflow.nodes ?? [];
40775
40785
  const triggerNode = nodes.find((n) => {
40776
40786
  const type = String(n?.type ?? "").toLowerCase();
@@ -40989,7 +40999,14 @@ async function fetchWorkflowWithTrigger(apiConfig, workflowId, timeoutMs) {
40989
40999
  return { workflow, triggerType: await detectTriggerType(workflow) };
40990
41000
  }
40991
41001
  function buildTestSuccessResponse(result, correlationId, startTime) {
40992
- return toMCPResponse(createSuccessResponse({ success: true, ...result }, correlationId, { duration_ms: Math.round(performance.now() - startTime) }));
41002
+ const isTestError = result.status === "error";
41003
+ const resultData = isTestError ? {
41004
+ success: false,
41005
+ ...result,
41006
+ result_summary: result.result_summary.startsWith("\u274C") ? result.result_summary : `\u274C TEST FAILED: ${result.result_summary}`
41007
+ } : { success: true, ...result };
41008
+ const response = toMCPResponse(createSuccessResponse(resultData, correlationId, { duration_ms: Math.round(performance.now() - startTime) }));
41009
+ return isTestError ? { ...response, isError: true } : response;
40993
41010
  }
40994
41011
  async function executeTestWorkflow(args, correlationId, startTime) {
40995
41012
  const workflowId = args.workflow_id;
@@ -48360,7 +48377,12 @@ var SERVICE_MAPPINGS = [
48360
48377
  role: "action",
48361
48378
  defaultOperation: "sendMessage",
48362
48379
  defaultResource: "message",
48363
- credentialType: "telegramApi"
48380
+ credentialType: "telegramApi",
48381
+ defaultParameters: {
48382
+ chatId: '={{ $json.chatId || $json.chat_id || "" }}',
48383
+ text: "={{ $json.text || $json.message || $json.output || JSON.stringify($json).substring(0, 500) }}",
48384
+ additionalFields: {}
48385
+ }
48364
48386
  },
48365
48387
  {
48366
48388
  keywords: ["slack"],
@@ -48368,7 +48390,12 @@ var SERVICE_MAPPINGS = [
48368
48390
  displayName: "Slack",
48369
48391
  role: "action",
48370
48392
  defaultOperation: "postMessage",
48371
- credentialType: "slackApi"
48393
+ credentialType: "slackApi",
48394
+ defaultParameters: {
48395
+ channel: '={{ $json.channel || "" }}',
48396
+ text: "={{ $json.text || $json.message || $json.output || JSON.stringify($json).substring(0, 500) }}",
48397
+ select: "channel"
48398
+ }
48372
48399
  },
48373
48400
  {
48374
48401
  keywords: ["gmail", "send email"],
@@ -48376,21 +48403,38 @@ var SERVICE_MAPPINGS = [
48376
48403
  displayName: "Gmail",
48377
48404
  role: "action",
48378
48405
  defaultOperation: "send",
48379
- credentialType: "gmailOAuth2"
48406
+ defaultResource: "message",
48407
+ credentialType: "gmailOAuth2",
48408
+ defaultParameters: {
48409
+ sendTo: '={{ $json.to || $json.email || "" }}',
48410
+ subject: '={{ $json.subject || "Notification" }}',
48411
+ message: "={{ $json.text || $json.body || $json.message || JSON.stringify($json).substring(0, 500) }}",
48412
+ options: {}
48413
+ }
48380
48414
  },
48381
48415
  {
48382
- keywords: ["email", "smtp"],
48416
+ keywords: ["send email", "smtp", "email notification"],
48383
48417
  nodeType: "n8n-nodes-base.emailSend",
48384
48418
  displayName: "Send Email",
48385
48419
  role: "action",
48386
- credentialType: "smtp"
48420
+ credentialType: "smtp",
48421
+ defaultParameters: {
48422
+ fromEmail: '={{ $json.from || "" }}',
48423
+ toEmail: '={{ $json.to || $json.email || "" }}',
48424
+ subject: '={{ $json.subject || "Notification" }}',
48425
+ text: "={{ $json.text || $json.body || JSON.stringify($json).substring(0, 500) }}",
48426
+ options: {}
48427
+ }
48387
48428
  },
48388
48429
  {
48389
48430
  keywords: ["discord"],
48390
48431
  nodeType: "n8n-nodes-base.discord",
48391
48432
  displayName: "Discord",
48392
48433
  role: "action",
48393
- credentialType: "discordApi"
48434
+ credentialType: "discordApi",
48435
+ defaultParameters: {
48436
+ content: "={{ $json.text || $json.message || JSON.stringify($json).substring(0, 500) }}"
48437
+ }
48394
48438
  },
48395
48439
  // ── Data & Storage ──
48396
48440
  {
@@ -48398,14 +48442,15 @@ var SERVICE_MAPPINGS = [
48398
48442
  nodeType: "n8n-nodes-base.notion",
48399
48443
  displayName: "Notion",
48400
48444
  role: "action",
48401
- defaultOperation: "search",
48402
- defaultResource: "page",
48445
+ defaultOperation: "getAll",
48446
+ defaultResource: "databasePage",
48403
48447
  credentialType: "notionApi",
48404
48448
  defaultParameters: {
48449
+ databaseId: { __rl: true, mode: "id", value: '={{ $json.databaseId || $json.database_id || "" }}' },
48405
48450
  simple: true,
48406
48451
  returnAll: false,
48407
- limit: 5,
48408
- text: '={{ ($json.text || "").substring(0, 50) }}'
48452
+ limit: 3,
48453
+ options: {}
48409
48454
  }
48410
48455
  },
48411
48456
  {
@@ -48413,21 +48458,41 @@ var SERVICE_MAPPINGS = [
48413
48458
  nodeType: "n8n-nodes-base.googleSheets",
48414
48459
  displayName: "Google Sheets",
48415
48460
  role: "action",
48416
- credentialType: "googleSheetsOAuth2Api"
48461
+ defaultOperation: "append",
48462
+ defaultResource: "sheet",
48463
+ credentialType: "googleSheetsOAuth2Api",
48464
+ defaultParameters: {
48465
+ documentId: { __rl: true, mode: "url", value: '={{ $json.spreadsheetUrl || $json.spreadsheet_id || "" }}' },
48466
+ sheetName: { __rl: true, mode: "list", value: '={{ $json.sheetName || "Sheet1" }}' },
48467
+ columns: { mappingMode: "autoMapInputData", value: {} },
48468
+ options: {}
48469
+ }
48417
48470
  },
48418
48471
  {
48419
48472
  keywords: ["airtable"],
48420
48473
  nodeType: "n8n-nodes-base.airtable",
48421
48474
  displayName: "Airtable",
48422
48475
  role: "action",
48423
- credentialType: "airtableTokenApi"
48476
+ defaultOperation: "create",
48477
+ credentialType: "airtableTokenApi",
48478
+ defaultParameters: {
48479
+ base: { __rl: true, mode: "id", value: '={{ $json.baseId || "" }}' },
48480
+ table: { __rl: true, mode: "id", value: '={{ $json.tableId || "" }}' },
48481
+ columns: { mappingMode: "autoMapInputData", value: {} },
48482
+ options: {}
48483
+ }
48424
48484
  },
48425
48485
  {
48426
48486
  keywords: ["postgres", "postgresql", "database"],
48427
48487
  nodeType: "n8n-nodes-base.postgres",
48428
48488
  displayName: "Postgres",
48429
48489
  role: "action",
48430
- credentialType: "postgres"
48490
+ defaultOperation: "executeQuery",
48491
+ credentialType: "postgres",
48492
+ defaultParameters: {
48493
+ query: "SELECT 1 AS ok",
48494
+ options: {}
48495
+ }
48431
48496
  },
48432
48497
  // ── Dev Tools ──
48433
48498
  {
@@ -48435,7 +48500,16 @@ var SERVICE_MAPPINGS = [
48435
48500
  nodeType: "n8n-nodes-base.github",
48436
48501
  displayName: "GitHub",
48437
48502
  role: "action",
48438
- credentialType: "githubApi"
48503
+ defaultOperation: "create",
48504
+ defaultResource: "issue",
48505
+ credentialType: "githubApi",
48506
+ defaultParameters: {
48507
+ owner: '={{ $json.owner || "" }}',
48508
+ repository: '={{ $json.repo || $json.repository || "" }}',
48509
+ title: '={{ $json.title || $json.subject || "New issue" }}',
48510
+ body: '={{ $json.body || $json.text || $json.message || "" }}',
48511
+ additionalFields: {}
48512
+ }
48439
48513
  },
48440
48514
  // ── Web APIs → HTTP Request ──
48441
48515
  {
@@ -48504,22 +48578,34 @@ var SERVICE_MAPPINGS = [
48504
48578
  keywords: ["http request", "api call", "rest api", "fetch"],
48505
48579
  nodeType: "n8n-nodes-base.httpRequest",
48506
48580
  displayName: "HTTP Request",
48507
- role: "action"
48581
+ role: "action",
48582
+ defaultParameters: {
48583
+ method: "GET",
48584
+ url: '={{ $json.url || "https://httpbin.org/get" }}'
48585
+ }
48508
48586
  }
48509
48587
  ];
48510
48588
  function detectServicesInOrder(description) {
48511
48589
  const lower = description.toLowerCase();
48512
48590
  const detected = [];
48513
48591
  const usedNodeTypes = /* @__PURE__ */ new Set();
48592
+ const triggerServiceNames = /* @__PURE__ */ new Set();
48514
48593
  for (const mapping of SERVICE_MAPPINGS) {
48515
48594
  const dedupeKey = mapping.nodeType === "n8n-nodes-base.httpRequest" ? `${mapping.nodeType}:${mapping.keywords[0]}` : mapping.nodeType;
48516
48595
  if (usedNodeTypes.has(dedupeKey))
48517
48596
  continue;
48597
+ if (mapping.role === "action" && mapping.keywords.some((kw) => triggerServiceNames.has(kw))) {
48598
+ continue;
48599
+ }
48518
48600
  for (const keyword of mapping.keywords) {
48519
48601
  const idx = lower.indexOf(keyword);
48520
48602
  if (idx !== -1) {
48521
48603
  detected.push({ index: idx, mapping, matchedKeyword: keyword });
48522
48604
  usedNodeTypes.add(dedupeKey);
48605
+ if (mapping.role === "trigger") {
48606
+ for (const kw of mapping.keywords)
48607
+ triggerServiceNames.add(kw.split(/\s/)[0]);
48608
+ }
48523
48609
  break;
48524
48610
  }
48525
48611
  }
@@ -48610,9 +48696,7 @@ function applyExtractedParams(spec, params, description) {
48610
48696
  if (nodeTypeLower.includes("telegram") && params.telegramChatId) {
48611
48697
  spec.parameters = {
48612
48698
  ...spec.parameters,
48613
- chatId: params.telegramChatId,
48614
- text: "={{ $json.text || $json.output || JSON.stringify($json).substring(0, 500) }}",
48615
- additionalFields: {}
48699
+ chatId: params.telegramChatId
48616
48700
  };
48617
48701
  }
48618
48702
  if (nodeTypeLower.includes("chainllm")) {
@@ -48651,13 +48735,35 @@ function buildChainLlmPrompt(description) {
48651
48735
  {{ ($json.extract || $json.description || $json.text || JSON.stringify($json)).substring(0, 800) }}`;
48652
48736
  }
48653
48737
  function generateTestPayload(description, params) {
48738
+ const lower = description.toLowerCase();
48654
48739
  const payload = {};
48655
- if (description.toLowerCase().includes("company")) {
48740
+ if (lower.includes("company")) {
48656
48741
  payload.company = "Tesla";
48657
48742
  }
48658
- if (description.toLowerCase().includes("meeting") || description.toLowerCase().includes("topic")) {
48743
+ if (lower.includes("meeting") || lower.includes("topic")) {
48659
48744
  payload.meeting_topic = "Q1 Partnership Review";
48660
48745
  }
48746
+ if (lower.includes("telegram")) {
48747
+ payload.chatId = process.env.P58_TELEGRAM_CHAT_ID ?? "";
48748
+ payload.text = payload.text ?? "Test message from build_workflow";
48749
+ }
48750
+ if (lower.includes("slack")) {
48751
+ payload.text = payload.text ?? "Test message from build_workflow";
48752
+ }
48753
+ if (lower.includes("notion")) {
48754
+ payload.text = payload.text ?? "Test entry";
48755
+ payload.title = "Test entry from build_workflow";
48756
+ }
48757
+ if (lower.includes("postgres") || lower.includes("database")) {
48758
+ payload.query = "SELECT 1 AS ok";
48759
+ }
48760
+ if (lower.includes("github")) {
48761
+ payload.title = "Test issue from build_workflow";
48762
+ payload.body = "Automated test \u2014 can be deleted";
48763
+ }
48764
+ if (lower.includes("sheets") || lower.includes("spreadsheet")) {
48765
+ payload.data = { name: "test", value: 1 };
48766
+ }
48661
48767
  if (Object.keys(payload).length === 0) {
48662
48768
  payload.input = "test data";
48663
48769
  payload.test = true;
@@ -49040,8 +49146,8 @@ async function pollCloneExecution(config2, cloneId, budgetMs) {
49040
49146
  const latest = list.executions[0];
49041
49147
  if (latest?.id) {
49042
49148
  const full = await getExecution(config2, latest.id, true);
49043
- if (full.finished) {
49044
- const hasError = full.status === "error" || full.error != null;
49149
+ if (full.finished || full.status === "error" || full.status === "crashed") {
49150
+ const hasError = full.status === "error" || full.status === "crashed" || full.error != null;
49045
49151
  return { execId: latest.id, execStatus: hasError ? "error" : "success", nodeDebug: extractNodeDebugData(full) };
49046
49152
  }
49047
49153
  }
@@ -49074,6 +49180,15 @@ async function executeCloneTest(workflowId, testPayload, timeoutMs) {
49074
49180
  const config2 = buildCloneApiConfig(timeoutMs);
49075
49181
  const workflow = await getWorkflow(config2, workflowId);
49076
49182
  const nodes = workflow.nodes ?? [];
49183
+ const hasR2W = await hasRespondToWebhookNode(workflow);
49184
+ if (hasR2W) {
49185
+ return {
49186
+ success: false,
49187
+ error: "Cannot test via clone: workflow contains a Respond to Webhook node. Use direct webhook POST (test_workflow) instead.",
49188
+ duration_ms: Math.round(performance.now() - start),
49189
+ method: "clone_and_execute"
49190
+ };
49191
+ }
49077
49192
  const connections = workflow.connections ?? {};
49078
49193
  const { cloneNodes, cloneConnections, webhookPath } = buildCloneSpec(nodes, connections, workflow.settings);
49079
49194
  const { calculateAdaptiveTimeout: calculateAdaptiveTimeout2 } = await Promise.resolve().then(() => (init_timeout(), timeout_exports));
@@ -49105,6 +49220,28 @@ async function executeCloneTest(workflowId, testPayload, timeoutMs) {
49105
49220
  }
49106
49221
  }
49107
49222
  }
49223
+ async function retryWebhookTest(path3, testPayload, timeoutMs, start) {
49224
+ const delays = [1e3, 2e3, 4e3];
49225
+ let lastErr = "";
49226
+ for (const delayMs of delays) {
49227
+ await new Promise((r) => setTimeout(r, delayMs));
49228
+ try {
49229
+ const out = await withTimeout(postWebhook(buildN8nHostUrl(), path3, testPayload ?? {}, timeoutMs), timeoutMs, "build_workflow:retry");
49230
+ return { success: true, output: out, duration_ms: Math.round(performance.now() - start), method: "webhook_inline" };
49231
+ } catch (err) {
49232
+ lastErr = err instanceof Error ? err.message : String(err);
49233
+ if (!lastErr.includes("404")) {
49234
+ return { success: false, error: lastErr, duration_ms: Math.round(performance.now() - start), method: "webhook_inline" };
49235
+ }
49236
+ }
49237
+ }
49238
+ return {
49239
+ success: false,
49240
+ error: "Webhook endpoint not ready after 3 retries. The workflow was deployed and activated successfully \u2014 test it manually with test_workflow.",
49241
+ duration_ms: Math.round(performance.now() - start),
49242
+ method: "webhook_inline"
49243
+ };
49244
+ }
49108
49245
  async function runTestStep(workflow, workflowId, testPayload, timeoutMs) {
49109
49246
  const start = performance.now();
49110
49247
  const triggerType = await detectTriggerType(workflow);
@@ -49114,7 +49251,12 @@ async function runTestStep(workflow, workflowId, testPayload, timeoutMs) {
49114
49251
  return { success: false, error: "No webhook path configured", duration_ms: 0, method: "webhook_inline" };
49115
49252
  const testResult = await executeWebhookTest(path3, testPayload, timeoutMs, start);
49116
49253
  if (!testResult.success && testResult.error?.includes("404")) {
49117
- import_logging62.logger.info("build_workflow: webhook test endpoint returned 404, falling back to clone-and-execute", { workflowId });
49254
+ const hasR2W = await hasRespondToWebhookNode(workflow);
49255
+ if (hasR2W) {
49256
+ import_logging62.logger.info("build_workflow: Respond to Webhook detected, retrying direct POST (no clone)", { workflowId });
49257
+ return retryWebhookTest(path3, testPayload, timeoutMs, start);
49258
+ }
49259
+ import_logging62.logger.info("build_workflow: webhook 404, falling back to clone-and-execute", { workflowId });
49118
49260
  return executeCloneTest(workflowId, testPayload, timeoutMs);
49119
49261
  }
49120
49262
  return testResult;
@@ -49148,6 +49290,7 @@ async function runMainPipeline(assembled, args, timeoutMs, correlationId) {
49148
49290
  return runValidatedDeployPipeline(assembled, "auto", deployFn, timeoutMs, "build_workflow", correlationId);
49149
49291
  }
49150
49292
  function buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testResult, startTime) {
49293
+ const testFailed = testResult !== void 0 && !testResult.success;
49151
49294
  return {
49152
49295
  success: true,
49153
49296
  workflow_id: workflowId,
@@ -49157,7 +49300,9 @@ function buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testRe
49157
49300
  validation: buildValidationResult(result, result.fixesApplied.length),
49158
49301
  execution: testResult ? { tested: true, success: testResult.success, output: testResult.output, error: testResult.error, duration_ms: testResult.duration_ms, execution_id: testResult.execution_id, node_debug: testResult.node_debug, method: testResult.method } : { tested: false, reason: "test: false was explicitly set" },
49159
49302
  build_time_ms: Math.round(performance.now() - startTime),
49160
- tokens_saved_estimate: Math.max(args.nodes.length * 2e3, 4e3)
49303
+ tokens_saved_estimate: Math.max(args.nodes.length * 2e3, 4e3),
49304
+ ...testResult !== void 0 ? { test_passed: testResult.success } : {},
49305
+ ...testFailed ? { test_error: testResult.error ?? "Test failed" } : {}
49161
49306
  };
49162
49307
  }
49163
49308
  async function buildSuccessResponse2(args, result, builtSubWorkflows, timeoutMs, startTime) {
@@ -49545,8 +49690,9 @@ function extractWebhookPathFromSpec(cleanedArgs) {
49545
49690
  function buildRichResponseFields(result, cleanedArgs, exec) {
49546
49691
  const nodes = cleanedArgs.nodes ?? [];
49547
49692
  const nodeFlow = nodes.map((n) => n.name || String(n.type ?? "").split(".").pop()).join(" \u2192 ");
49693
+ const testFailed = exec?.tested && !exec.success;
49548
49694
  const parts = [
49549
- `\u2705 Workflow "${result.workflow_name}" deployed with ${nodes.length} nodes.`
49695
+ testFailed ? `\u26A0\uFE0F WORKFLOW DEPLOYED BUT TEST FAILED: "${result.workflow_name}" (${nodes.length} nodes) \u2014 ${exec?.error ?? "unknown error"}` : `\u2705 Workflow "${result.workflow_name}" deployed with ${nodes.length} nodes.`
49550
49696
  ];
49551
49697
  if (nodeFlow)
49552
49698
  parts.push(`Flow: ${nodeFlow}`);
@@ -51035,10 +51181,10 @@ async function getCatalogHealth() {
51035
51181
  return CATALOG_HEALTH_FALLBACK;
51036
51182
  }
51037
51183
  }
51038
- function collectIssues(apiKeySet, n8nConnected, probes, dbConnected, dbError, cbState) {
51184
+ function collectIssues(apiKeySet, workflowEngineConnected, probes, enhancedIntelligenceAvailable, invalidCredentials, cbState) {
51039
51185
  const issues = [];
51040
51186
  if (cbState && cbState !== "closed") {
51041
- issues.push("\u26D4 CIRCUIT BREAKER OPEN \u2014 catalog database temporarily unavailable. DO NOT call build_workflow or use Bash to work around it. Call setup_check again in 60 seconds to check if it has reset. If still open after 30 minutes, report to the user and stop.");
51187
+ issues.push("\u26D4 INTELLIGENCE TEMPORARILY UNAVAILABLE \u2014 enhanced catalog features are resetting. DO NOT call build_workflow or use Bash to work around it. Call setup_check again in 60 seconds. If still unavailable after 30 minutes, report to the user and stop.");
51042
51188
  }
51043
51189
  if (!apiKeySet) {
51044
51190
  issues.push("N8N_API_KEY not set \u2014 Tier 2-7 deploy tools unavailable");
@@ -51048,26 +51194,52 @@ function collectIssues(apiKeySet, n8nConnected, probes, dbConnected, dbError, cb
51048
51194
  const reachable = probes.filter((p) => p.reachable);
51049
51195
  if (reachable.length > 0) {
51050
51196
  const probeUrl = reachable[0].url.replace("/api/v1/workflows", "");
51051
- issues.push(`N8N_API_URL not set \u2014 n8n detected at ${probeUrl}. Set N8N_API_URL=${probeUrl}/api/v1 in your MCP config.`);
51197
+ issues.push(`N8N_API_URL not set \u2014 workflow engine detected at ${probeUrl}. Set N8N_API_URL=${probeUrl}/api/v1 in your MCP config.`);
51052
51198
  } else {
51053
- issues.push("N8N_API_URL not set \u2014 no n8n instance detected on localhost:5678 or localhost:5679");
51199
+ issues.push("N8N_API_URL not set \u2014 no workflow engine detected on localhost:5678 or localhost:5679");
51054
51200
  }
51055
51201
  }
51056
- if (apiKeySet && !n8nConnected) {
51057
- issues.push("N8N_API_KEY is set but n8n is unreachable \u2014 check N8N_API_URL and ensure n8n is running");
51202
+ if (apiKeySet && !workflowEngineConnected) {
51203
+ issues.push("N8N_API_KEY is set but workflow engine is unreachable \u2014 check N8N_API_URL and ensure n8n is running");
51204
+ }
51205
+ if (!enhancedIntelligenceAvailable) {
51206
+ issues.push("Enhanced intelligence unavailable \u2014 using built-in catalog. All tools fully functional.");
51058
51207
  }
51059
- if (!dbConnected) {
51060
- const detail = dbError ? `: ${dbError}` : "";
51061
- issues.push(`Database unavailable${detail} \u2014 catalog features degraded, pattern fallback active`);
51208
+ if (invalidCredentials > 0) {
51209
+ issues.push(`\u26A0\uFE0F ${invalidCredentials} credential(s) have connectivity issues \u2014 run list_credentials for details`);
51062
51210
  }
51063
51211
  return issues;
51064
51212
  }
51213
+ async function probeWorkflowEngine(n8nUrl) {
51214
+ const probe = await probeN8nUrl(`${n8nUrl}/settings`);
51215
+ return { connected: probe.reachable, error: probe.error };
51216
+ }
51217
+ function buildCredentialHealth(cacheStatus, credentials) {
51218
+ if (!cacheStatus.loaded)
51219
+ return null;
51220
+ const types = [...new Set(credentials.map((c) => c.type))];
51221
+ const h = cacheStatus.health_summary;
51222
+ return {
51223
+ total: cacheStatus.count,
51224
+ valid: h?.valid ?? 0,
51225
+ invalid: h?.invalid ?? 0,
51226
+ untested: h?.untested ?? cacheStatus.count,
51227
+ types
51228
+ };
51229
+ }
51065
51230
  async function buildReport() {
51066
51231
  const apiKeySet = Boolean(process.env.N8N_API_KEY);
51067
51232
  const n8nUrl = process.env.N8N_API_URL ?? process.env.N8N_API_BASE_URL ?? null;
51233
+ let workflowEngineConnected = false;
51234
+ if (n8nUrl) {
51235
+ const engineProbe = await probeWorkflowEngine(n8nUrl);
51236
+ workflowEngineConnected = engineProbe.connected;
51237
+ }
51068
51238
  const cacheStatus = getCredentialCacheStatus();
51069
- const n8nConnected = cacheStatus.loaded;
51070
51239
  const credentialCount = cacheStatus.loaded ? cacheStatus.count : null;
51240
+ const credentials = cacheStatus.loaded ? await getPreloadedCredentials() : [];
51241
+ const credentialHealth = buildCredentialHealth(cacheStatus, credentials);
51242
+ const invalidCredentials = credentialHealth?.invalid ?? 0;
51071
51243
  let probes;
51072
51244
  if (!n8nUrl) {
51073
51245
  probes = await discoverN8nInstances();
@@ -51076,7 +51248,9 @@ async function buildReport() {
51076
51248
  const catalogHealth = await getCatalogHealth();
51077
51249
  const cbStatus = import_validatorPostgresClient16.circuitBreaker.getStatus();
51078
51250
  const accessState = await getRuntimeAccessState();
51079
- const issues = collectIssues(apiKeySet, n8nConnected, probes, dbHealth.connected, dbHealth.error, cbStatus.state);
51251
+ const enhancedIntelligenceAvailable = dbHealth.connected;
51252
+ const intelligenceStatus = dbHealth.connected ? void 0 : "Running with built-in catalog (offline mode)";
51253
+ const issues = collectIssues(apiKeySet, workflowEngineConnected, probes, enhancedIntelligenceAvailable, invalidCredentials, cbStatus.state);
51080
51254
  const toolCounts = buildToolCounts();
51081
51255
  const report = {
51082
51256
  server_version: config.SERVER_VERSION,
@@ -51086,12 +51260,12 @@ async function buildReport() {
51086
51260
  allowlist_status: accessState.allowlist_status,
51087
51261
  n8n_configured: apiKeySet,
51088
51262
  n8n_url: n8nUrl,
51089
- n8n_connected: n8nConnected,
51263
+ workflow_engine_connected: workflowEngineConnected,
51090
51264
  n8n_api_key_set: apiKeySet,
51091
51265
  credential_count: credentialCount,
51092
- catalog_ready: dbHealth.connected,
51093
- db_connected: dbHealth.connected,
51094
- ...dbHealth.error ? { db_error: dbHealth.error } : {},
51266
+ credential_health: credentialHealth,
51267
+ enhanced_intelligence_available: enhancedIntelligenceAvailable,
51268
+ ...intelligenceStatus ? { intelligence_status: intelligenceStatus } : {},
51095
51269
  catalog: catalogHealth,
51096
51270
  db_circuit_breaker: {
51097
51271
  state: cbStatus.state,
@@ -51130,9 +51304,10 @@ var setupCheckToolDefinition = {
51130
51304
 
51131
51305
  Returns a diagnostic report including:
51132
51306
  - Server version
51133
- - n8n connection status (configured/connected/unreachable)
51307
+ - workflow_engine_connected: whether the n8n API is actually reachable (probe-based)
51134
51308
  - n8n URL (from env var or auto-detected)
51135
- - Credential count (if n8n is connected)
51309
+ - credential_count + credential_health: credentials loaded and their validity stats
51310
+ - enhanced_intelligence_available: whether Path58 premium catalog data is available
51136
51311
  - Tool availability by tier
51137
51312
  - List of issues with actionable guidance
51138
51313
  - Link to the setup guide
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@path58/p58-n8n",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "The smartest and fastest n8n MCP server — validate, fix, and discover workflows inside your LLM",
5
5
  "keywords": [
6
6
  "mcp",