@path58/p58-n8n 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_INSTALL.md +1 -1
- package/CHANGELOG.md +30 -0
- package/dist/mcp/server.bundle.cjs +495 -156
- package/package.json +1 -1
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.
|
|
235
|
+
**Package:** `@path58/p58-n8n` | **npm:** https://www.npmjs.com/package/@path58/p58-n8n | **Version:** 0.2.6+
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.7] - 2026-03-11
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **LangChain connection type pattern fallback** — `ConnectionTypeResolver` now includes a static prefix-based resolver that correctly identifies LangChain AI sub-node types (`ai_languageModel`, `ai_tool`, `ai_memory`, etc.) even when the database is unreachable (e.g., Supabase circuit breaker open). Previously, any DB connectivity failure caused all connections to silently default to `"main"`, breaking LangChain workflows.
|
|
13
|
+
- Error message for DB load failures now surfaces the actual error string for easier diagnostics
|
|
14
|
+
|
|
15
|
+
## [0.2.6] - 2026-03-11
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **LangChain AI connection type support** — `build_workflow` now automatically detects and wires all 10 LangChain connection types (`ai_languageModel`, `ai_tool`, `ai_memory`, `ai_embedding`, `ai_outputParser`, `ai_document`, `ai_vectorStore`, `ai_textSplitter`, `ai_retriever`, `ai_reranker`) instead of hardcoding all connections as `main`
|
|
20
|
+
- **Sub-node positioning** — LLM models, memory modules, and tools now position below their parent chain/agent node in the n8n UI (same X, Y+200), matching native n8n layout conventions
|
|
21
|
+
- **3-tier connection type resolver** — explicit user override → `connection_rules` catalog lookup (496 AI rules) → `ai_nodes.ai_type` fallback → `main` default
|
|
22
|
+
- **`is_sub_node` metadata coverage** — backfilled from ~45% to 98.4% across all AI nodes in catalog
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- LangChain workflows now deploy with correct connection wiring — models appear on diamond ports, not circle ports
|
|
27
|
+
- v0.2.5 was published with stale bundle (did not include LangChain fix); this release rebuilds from fixed source with correct `SERVER_VERSION`
|
|
28
|
+
|
|
29
|
+
## [0.2.5] - 2026-03-11
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Internal release — npm publish from stale source (superseded by v0.2.6)
|
|
34
|
+
|
|
8
35
|
## [0.2.4] - 2026-03-10
|
|
9
36
|
|
|
10
37
|
### Added
|
|
@@ -130,6 +157,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
130
157
|
- npm package published as `@path58/p58-n8n`
|
|
131
158
|
- ESM module support with shebang for direct `npx` execution
|
|
132
159
|
|
|
160
|
+
[0.2.7]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.7
|
|
161
|
+
[0.2.6]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.6
|
|
162
|
+
[0.2.5]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.5
|
|
133
163
|
[0.2.4]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.4
|
|
134
164
|
[0.2.3]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.3
|
|
135
165
|
[0.2.2]: https://github.com/tsvika58/p58-n8n/releases/tag/v0.2.2
|
|
@@ -3014,22 +3014,28 @@ function buildPoolOptions() {
|
|
|
3014
3014
|
}
|
|
3015
3015
|
function createPool(cfg) {
|
|
3016
3016
|
const timeoutMs = getEnvNumber("DB_STATEMENT_TIMEOUT_MS", 3e4);
|
|
3017
|
+
const isCatalogOnly = !!(process.env.P58_DATABASE_URL || process.env.MCP_DATABASE_URL) && !process.env.N8N_API_KEY;
|
|
3017
3018
|
const pool = new import_pg.Pool({
|
|
3018
3019
|
host: cfg.host,
|
|
3019
3020
|
port: resolvePoolPort(cfg),
|
|
3020
3021
|
user: cfg.user,
|
|
3021
3022
|
password: cfg.password,
|
|
3022
3023
|
database: cfg.database,
|
|
3023
|
-
|
|
3024
|
-
|
|
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
|
-
|
|
3027
|
-
|
|
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:
|
|
3032
|
-
|
|
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 =
|
|
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
|
|
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.
|
|
18476
|
+
SERVER_VERSION: "0.2.7",
|
|
18451
18477
|
// Database configuration (from environment)
|
|
18452
18478
|
SUPABASE_URL: process.env.SUPABASE_URL,
|
|
18453
18479
|
SUPABASE_KEY: process.env.SUPABASE_KEY,
|
|
@@ -19179,6 +19205,30 @@ async function injectResponseMetadata(sessionId, toolName, response, executionSt
|
|
|
19179
19205
|
return instrumented;
|
|
19180
19206
|
}
|
|
19181
19207
|
|
|
19208
|
+
// dist/mcp/in-flight-tracker.js
|
|
19209
|
+
var inflightCount = 0;
|
|
19210
|
+
function incrementInflight() {
|
|
19211
|
+
inflightCount++;
|
|
19212
|
+
}
|
|
19213
|
+
function decrementInflight() {
|
|
19214
|
+
if (inflightCount > 0)
|
|
19215
|
+
inflightCount--;
|
|
19216
|
+
}
|
|
19217
|
+
function waitForInflightRequests(timeoutMs) {
|
|
19218
|
+
if (inflightCount === 0)
|
|
19219
|
+
return Promise.resolve();
|
|
19220
|
+
return new Promise((resolve2) => {
|
|
19221
|
+
const deadline = setTimeout(resolve2, timeoutMs);
|
|
19222
|
+
const poll = setInterval(() => {
|
|
19223
|
+
if (inflightCount <= 0) {
|
|
19224
|
+
clearInterval(poll);
|
|
19225
|
+
clearTimeout(deadline);
|
|
19226
|
+
resolve2();
|
|
19227
|
+
}
|
|
19228
|
+
}, 50);
|
|
19229
|
+
});
|
|
19230
|
+
}
|
|
19231
|
+
|
|
19182
19232
|
// dist/validation/l1-structure.js
|
|
19183
19233
|
async function validateL1Structure(workflowJson) {
|
|
19184
19234
|
const startTime = performance.now();
|
|
@@ -30388,7 +30438,13 @@ var AutoFixerRegistry = class _AutoFixerRegistry {
|
|
|
30388
30438
|
}
|
|
30389
30439
|
}
|
|
30390
30440
|
};
|
|
30391
|
-
var
|
|
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
|
|
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,
|
|
35285
|
-
- L3: Credential types valid (
|
|
35286
|
-
- L4: Connection patterns exist (
|
|
35287
|
-
- L5: Required parameters configured (
|
|
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
|
|
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
|
-
|
|
36696
|
-
|
|
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
|
-
|
|
36794
|
-
|
|
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,
|
|
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
|
-
|
|
36957
|
-
|
|
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
|
-
|
|
37046
|
-
|
|
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,
|
|
37244
|
-
- L3: Credential types are valid (
|
|
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(
|
|
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(
|
|
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
|
|
41344
|
-
var
|
|
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
|
|
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
|
|
41407
|
-
const
|
|
41576
|
+
const { subNodes, parentOf } = resolvedTypes ? detectSubNodes(connections, resolvedTypes) : { subNodes: /* @__PURE__ */ new Set(), parentOf: /* @__PURE__ */ new Map() };
|
|
41577
|
+
const mainNames = nodeNames.filter((n) => !subNodes.has(n));
|
|
41578
|
+
const mainConns = connections.filter((c) => !subNodes.has(c.from));
|
|
41579
|
+
const roots = findRootNodes(mainNames, mainConns);
|
|
41580
|
+
const adjacency = buildAdjacency(mainConns);
|
|
41408
41581
|
assignRootPositions(roots, positions);
|
|
41409
41582
|
bfsAssign(roots, adjacency, positions);
|
|
41583
|
+
assignFallbacks(mainNames, positions);
|
|
41584
|
+
assignSubNodePositions(subNodes, parentOf, positions);
|
|
41410
41585
|
assignFallbacks(nodeNames, positions);
|
|
41411
41586
|
return positions;
|
|
41412
41587
|
}
|
|
@@ -41519,6 +41694,142 @@ function getResourceLocatorParams(nodeType) {
|
|
|
41519
41694
|
return RESOURCE_LOCATOR_PARAMS[nodeType.toLowerCase()];
|
|
41520
41695
|
}
|
|
41521
41696
|
|
|
41697
|
+
// dist/generation/build/connectionTypeResolver.js
|
|
41698
|
+
init_validatorPostgresClient();
|
|
41699
|
+
var import_retry7 = __toESM(require_retryUtils(), 1);
|
|
41700
|
+
var import_logging53 = __toESM(require_logging(), 1);
|
|
41701
|
+
var AI_TYPE_TO_CONNECTION_TYPE = {
|
|
41702
|
+
llm: "ai_languageModel",
|
|
41703
|
+
memory: "ai_memory",
|
|
41704
|
+
tool: "ai_tool",
|
|
41705
|
+
embedding: "ai_embedding",
|
|
41706
|
+
output_parser: "ai_outputParser",
|
|
41707
|
+
retriever: "ai_retriever",
|
|
41708
|
+
document_loader: "ai_document",
|
|
41709
|
+
text_splitter: "ai_textSplitter",
|
|
41710
|
+
vector_store: "ai_vectorStore",
|
|
41711
|
+
reranker: "ai_reranker"
|
|
41712
|
+
};
|
|
41713
|
+
var DEFAULT_CONNECTION_TYPE = "main";
|
|
41714
|
+
var LANGCHAIN_PREFIX_TO_CONNECTION = [
|
|
41715
|
+
["@n8n/n8n-nodes-langchain.lmchat", "ai_languageModel"],
|
|
41716
|
+
["@n8n/n8n-nodes-langchain.lm", "ai_languageModel"],
|
|
41717
|
+
["@n8n/n8n-nodes-langchain.memory", "ai_memory"],
|
|
41718
|
+
["@n8n/n8n-nodes-langchain.tool", "ai_tool"],
|
|
41719
|
+
["@n8n/n8n-nodes-langchain.embeddings", "ai_embedding"],
|
|
41720
|
+
["@n8n/n8n-nodes-langchain.outputparser", "ai_outputParser"],
|
|
41721
|
+
["@n8n/n8n-nodes-langchain.retriever", "ai_retriever"],
|
|
41722
|
+
["@n8n/n8n-nodes-langchain.document", "ai_document"],
|
|
41723
|
+
["@n8n/n8n-nodes-langchain.textsplitter", "ai_textSplitter"],
|
|
41724
|
+
["@n8n/n8n-nodes-langchain.vectorstore", "ai_vectorStore"],
|
|
41725
|
+
["@n8n/n8n-nodes-langchain.reranker", "ai_reranker"]
|
|
41726
|
+
];
|
|
41727
|
+
function resolveByPattern(nodeType) {
|
|
41728
|
+
const lower = nodeType.toLowerCase();
|
|
41729
|
+
for (const [prefix, connType] of LANGCHAIN_PREFIX_TO_CONNECTION) {
|
|
41730
|
+
if (lower.startsWith(prefix))
|
|
41731
|
+
return connType;
|
|
41732
|
+
}
|
|
41733
|
+
return void 0;
|
|
41734
|
+
}
|
|
41735
|
+
async function loadConnectionRules2() {
|
|
41736
|
+
const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT LOWER(source_node_type) AS source_node_type,
|
|
41737
|
+
LOWER(target_node_type) AS target_node_type,
|
|
41738
|
+
source_output
|
|
41739
|
+
FROM bluelime.connection_rules
|
|
41740
|
+
WHERE source_output != 'main'
|
|
41741
|
+
AND is_valid = true
|
|
41742
|
+
ORDER BY occurrence_count DESC`), 2, "connectionTypeResolver:loadConnectionRules");
|
|
41743
|
+
return rows;
|
|
41744
|
+
}
|
|
41745
|
+
async function loadAINodeTypes() {
|
|
41746
|
+
const { rows } = await (0, import_retry7.retryDbQuery)(() => validatorQuery(`SELECT LOWER(n.node_type) AS node_type, ai.ai_type
|
|
41747
|
+
FROM bluelime.ai_nodes ai
|
|
41748
|
+
JOIN bluelime.nodes n ON ai.node_id = n.id
|
|
41749
|
+
WHERE ai.is_sub_node = true
|
|
41750
|
+
AND ai.ai_type IS NOT NULL`), 2, "connectionTypeResolver:loadAINodeTypes");
|
|
41751
|
+
return rows;
|
|
41752
|
+
}
|
|
41753
|
+
function buildRuleMap(rows) {
|
|
41754
|
+
const map = /* @__PURE__ */ new Map();
|
|
41755
|
+
for (const row of rows) {
|
|
41756
|
+
const key = `${row.source_node_type}|${row.target_node_type}`;
|
|
41757
|
+
if (!map.has(key))
|
|
41758
|
+
map.set(key, row.source_output);
|
|
41759
|
+
}
|
|
41760
|
+
return map;
|
|
41761
|
+
}
|
|
41762
|
+
function buildAITypeMap(rows) {
|
|
41763
|
+
const map = /* @__PURE__ */ new Map();
|
|
41764
|
+
for (const row of rows) {
|
|
41765
|
+
const connType = AI_TYPE_TO_CONNECTION_TYPE[row.ai_type];
|
|
41766
|
+
if (connType && !map.has(row.node_type))
|
|
41767
|
+
map.set(row.node_type, connType);
|
|
41768
|
+
}
|
|
41769
|
+
return map;
|
|
41770
|
+
}
|
|
41771
|
+
var ConnectionTypeResolver = class _ConnectionTypeResolver {
|
|
41772
|
+
ruleMap;
|
|
41773
|
+
aiTypeMap;
|
|
41774
|
+
constructor(ruleMap, aiTypeMap) {
|
|
41775
|
+
this.ruleMap = ruleMap;
|
|
41776
|
+
this.aiTypeMap = aiTypeMap;
|
|
41777
|
+
}
|
|
41778
|
+
/**
|
|
41779
|
+
* Factory: load catalog data and return a ready resolver.
|
|
41780
|
+
* Falls back to empty maps (all "main") if DB query fails.
|
|
41781
|
+
*/
|
|
41782
|
+
static async create() {
|
|
41783
|
+
try {
|
|
41784
|
+
const [ruleRows, aiNodeRows] = await Promise.all([
|
|
41785
|
+
loadConnectionRules2(),
|
|
41786
|
+
loadAINodeTypes()
|
|
41787
|
+
]);
|
|
41788
|
+
import_logging53.logger.debug("connectionTypeResolver: loaded catalog", {
|
|
41789
|
+
ruleCount: ruleRows.length,
|
|
41790
|
+
aiNodeCount: aiNodeRows.length
|
|
41791
|
+
});
|
|
41792
|
+
return new _ConnectionTypeResolver(buildRuleMap(ruleRows), buildAITypeMap(aiNodeRows));
|
|
41793
|
+
} catch (err) {
|
|
41794
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41795
|
+
import_logging53.logger.warn("connectionTypeResolver: DB load failed \u2014 pattern fallback active for LangChain nodes", { error: msg });
|
|
41796
|
+
return new _ConnectionTypeResolver(/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map());
|
|
41797
|
+
}
|
|
41798
|
+
}
|
|
41799
|
+
/**
|
|
41800
|
+
* Resolve the n8n connection type for a single connection.
|
|
41801
|
+
*
|
|
41802
|
+
* @param explicit - Caller-provided override (e.g., from BuildConnectionSpec.type)
|
|
41803
|
+
* @param sourceNodeType - Lowercased full n8n type of the source node
|
|
41804
|
+
* @param targetNodeType - Lowercased full n8n type of the target node
|
|
41805
|
+
* @returns Resolved connection type string
|
|
41806
|
+
*/
|
|
41807
|
+
resolve(explicit, sourceNodeType, targetNodeType) {
|
|
41808
|
+
if (explicit)
|
|
41809
|
+
return explicit;
|
|
41810
|
+
if (sourceNodeType && targetNodeType) {
|
|
41811
|
+
const catalogType = this.ruleMap.get(`${sourceNodeType}|${targetNodeType}`);
|
|
41812
|
+
if (catalogType)
|
|
41813
|
+
return catalogType;
|
|
41814
|
+
}
|
|
41815
|
+
if (sourceNodeType) {
|
|
41816
|
+
const aiType = this.aiTypeMap.get(sourceNodeType);
|
|
41817
|
+
if (aiType)
|
|
41818
|
+
return aiType;
|
|
41819
|
+
const patternType = resolveByPattern(sourceNodeType);
|
|
41820
|
+
if (patternType)
|
|
41821
|
+
return patternType;
|
|
41822
|
+
}
|
|
41823
|
+
return DEFAULT_CONNECTION_TYPE;
|
|
41824
|
+
}
|
|
41825
|
+
};
|
|
41826
|
+
function buildNodeTypeMap4(nodes) {
|
|
41827
|
+
const map = /* @__PURE__ */ new Map();
|
|
41828
|
+
for (const node of nodes)
|
|
41829
|
+
map.set(node.name, node.type.toLowerCase());
|
|
41830
|
+
return map;
|
|
41831
|
+
}
|
|
41832
|
+
|
|
41522
41833
|
// dist/generation/build/version-credential-resolver.js
|
|
41523
41834
|
var FALLBACK_VERSION = 1;
|
|
41524
41835
|
var UNKNOWN_AUTH_MODE = "unknown";
|
|
@@ -42099,7 +42410,7 @@ function buildPlaceholders(count) {
|
|
|
42099
42410
|
async function queryTypeVersions(nodeTypes) {
|
|
42100
42411
|
const normalized = nodeTypes.map((t) => t.toLowerCase());
|
|
42101
42412
|
const placeholders = buildPlaceholders(normalized.length);
|
|
42102
|
-
const { rows } = await (0,
|
|
42413
|
+
const { rows } = await (0, import_retry8.retryDbQuery)(() => validatorQuery(`SELECT node_type, default_version, supported_versions FROM bluelime.nodes WHERE LOWER(node_type) IN (${placeholders})`, normalized), 3, "build_workflow_typeversion_batch");
|
|
42103
42414
|
const groups = /* @__PURE__ */ new Map();
|
|
42104
42415
|
for (const row of rows) {
|
|
42105
42416
|
const key = row.node_type.toLowerCase();
|
|
@@ -42290,7 +42601,7 @@ function findTerminalNodes(nodes, connections) {
|
|
|
42290
42601
|
function buildNodeBase(spec, position, typeVersion, casingMap) {
|
|
42291
42602
|
const correctedType = resolveNodeTypeCasing(spec.type, casingMap);
|
|
42292
42603
|
if (correctedType !== spec.type) {
|
|
42293
|
-
|
|
42604
|
+
import_logging54.logger.debug("build_workflow: corrected node type casing", { original: spec.type, corrected: correctedType, nodeName: spec.name });
|
|
42294
42605
|
}
|
|
42295
42606
|
return {
|
|
42296
42607
|
id: (0, import_crypto4.randomUUID)(),
|
|
@@ -42312,20 +42623,20 @@ function assembleNode(spec, position, typeVersion, credential, isTerminal, casin
|
|
|
42312
42623
|
node.continueOnFail = true;
|
|
42313
42624
|
return node;
|
|
42314
42625
|
}
|
|
42315
|
-
function assembleConnections(connections) {
|
|
42626
|
+
function assembleConnections(connections, nodeTypeMap, resolver) {
|
|
42316
42627
|
const result = {};
|
|
42317
42628
|
for (const conn of connections) {
|
|
42629
|
+
const sourceType = nodeTypeMap.get(conn.from);
|
|
42630
|
+
const targetType = nodeTypeMap.get(conn.to);
|
|
42631
|
+
const connType = resolver.resolve(conn.type, sourceType, targetType);
|
|
42318
42632
|
if (!result[conn.from])
|
|
42319
|
-
result[conn.from] = {
|
|
42633
|
+
result[conn.from] = {};
|
|
42634
|
+
if (!result[conn.from][connType])
|
|
42635
|
+
result[conn.from][connType] = [];
|
|
42320
42636
|
const outputIdx = conn.from_output ?? 0;
|
|
42321
|
-
while (result[conn.from].
|
|
42322
|
-
result[conn.from].
|
|
42323
|
-
}
|
|
42324
|
-
result[conn.from].main[outputIdx].push({
|
|
42325
|
-
node: conn.to,
|
|
42326
|
-
type: "main",
|
|
42327
|
-
index: conn.to_input ?? 0
|
|
42328
|
-
});
|
|
42637
|
+
while (result[conn.from][connType].length <= outputIdx)
|
|
42638
|
+
result[conn.from][connType].push([]);
|
|
42639
|
+
result[conn.from][connType][outputIdx].push({ node: conn.to, type: connType, index: conn.to_input ?? 0 });
|
|
42329
42640
|
}
|
|
42330
42641
|
return result;
|
|
42331
42642
|
}
|
|
@@ -42336,36 +42647,49 @@ async function resolveCatalogData(input) {
|
|
|
42336
42647
|
const uniqueTypes = [...new Set(input.nodes.map((n) => n.type))];
|
|
42337
42648
|
const [typeResult, credentialMap] = await Promise.all([
|
|
42338
42649
|
queryTypeVersions(uniqueTypes).catch((err) => {
|
|
42339
|
-
|
|
42650
|
+
import_logging54.logger.warn("WorkflowBuilder: typeVersion lookup failed, using defaults", { error: err });
|
|
42340
42651
|
return { versions: /* @__PURE__ */ new Map(), casingMap: /* @__PURE__ */ new Map() };
|
|
42341
42652
|
}),
|
|
42342
42653
|
resolveAllCredentials(input.nodes)
|
|
42343
42654
|
]);
|
|
42344
42655
|
return { versionMap: typeResult.versions, casingMap: typeResult.casingMap, credentialMap };
|
|
42345
42656
|
}
|
|
42346
|
-
function
|
|
42347
|
-
const
|
|
42657
|
+
function computeResolvedTypes(connections, nodeTypeMap, resolver) {
|
|
42658
|
+
const map = /* @__PURE__ */ new Map();
|
|
42659
|
+
for (const conn of connections) {
|
|
42660
|
+
const connType = resolver.resolve(conn.type, nodeTypeMap.get(conn.from), nodeTypeMap.get(conn.to));
|
|
42661
|
+
map.set(`${conn.from}\u2192${conn.to}`, connType);
|
|
42662
|
+
}
|
|
42663
|
+
return map;
|
|
42664
|
+
}
|
|
42665
|
+
function buildNodeList(nodes, connections, versionMap, casingMap, credentialMap, nodeTypeMap, resolver) {
|
|
42666
|
+
const resolvedTypes = computeResolvedTypes(connections, nodeTypeMap, resolver);
|
|
42667
|
+
const autoPositions = calculatePositions(nodes.map((n) => n.name), connections, resolvedTypes);
|
|
42348
42668
|
const terminalNodes = findTerminalNodes(nodes, connections);
|
|
42349
42669
|
return nodes.map((spec) => {
|
|
42350
42670
|
const resolved = resolveNodeVersion(spec, versionMap, credentialMap);
|
|
42351
|
-
|
|
42671
|
+
import_logging54.logger.debug("build_workflow: typeVersion resolved", { nodeType: spec.type, nodeName: spec.name, ...resolved });
|
|
42352
42672
|
return assembleNode(spec, getNodePosition(spec, autoPositions), resolved.typeVersion, credentialMap.get(spec.name), terminalNodes.has(spec.name), casingMap);
|
|
42353
42673
|
});
|
|
42354
42674
|
}
|
|
42355
42675
|
async function assembleWorkflow(input) {
|
|
42356
|
-
const { versionMap, casingMap, credentialMap } = await
|
|
42357
|
-
|
|
42676
|
+
const [{ versionMap, casingMap, credentialMap }, resolver] = await Promise.all([
|
|
42677
|
+
resolveCatalogData(input),
|
|
42678
|
+
ConnectionTypeResolver.create()
|
|
42679
|
+
]);
|
|
42680
|
+
const nodeTypeMap = buildNodeTypeMap4(input.nodes);
|
|
42681
|
+
const nodes = buildNodeList(input.nodes, input.connections, versionMap, casingMap, credentialMap, nodeTypeMap, resolver);
|
|
42358
42682
|
return {
|
|
42359
42683
|
name: input.name,
|
|
42360
42684
|
nodes,
|
|
42361
|
-
connections: assembleConnections(input.connections),
|
|
42685
|
+
connections: assembleConnections(input.connections, nodeTypeMap, resolver),
|
|
42362
42686
|
settings: {},
|
|
42363
42687
|
staticData: null
|
|
42364
42688
|
};
|
|
42365
42689
|
}
|
|
42366
42690
|
|
|
42367
42691
|
// dist/generation/build/SubWorkflowBuilder.js
|
|
42368
|
-
var
|
|
42692
|
+
var import_logging55 = __toESM(require_logging(), 1);
|
|
42369
42693
|
var EXECUTE_WORKFLOW_TYPE = "n8n-nodes-base.executeWorkflow";
|
|
42370
42694
|
var PLACEHOLDER = "{{SUB_WORKFLOW_ID}}";
|
|
42371
42695
|
function findExecuteWorkflowNodes(nodes) {
|
|
@@ -42398,17 +42722,17 @@ async function buildSubWorkflows(parentSpec, buildSingle) {
|
|
|
42398
42722
|
}
|
|
42399
42723
|
const builtSubWorkflows = [];
|
|
42400
42724
|
for (const subSpec of parentSpec.sub_workflows) {
|
|
42401
|
-
|
|
42725
|
+
import_logging55.logger.info("SubWorkflowBuilder: building sub-workflow", { name: subSpec.name });
|
|
42402
42726
|
const { workflow_id, workflow_url } = await buildSingle(subSpec);
|
|
42403
42727
|
builtSubWorkflows.push({ name: subSpec.name, workflow_id, workflow_url });
|
|
42404
|
-
|
|
42728
|
+
import_logging55.logger.info("SubWorkflowBuilder: sub-workflow built", { name: subSpec.name, workflow_id });
|
|
42405
42729
|
}
|
|
42406
42730
|
const wiredSpec = wireSubWorkflowIds(parentSpec, builtSubWorkflows);
|
|
42407
42731
|
return { wiredSpec, builtSubWorkflows };
|
|
42408
42732
|
}
|
|
42409
42733
|
|
|
42410
42734
|
// dist/mcp/tools/handlers/build-workflow.js
|
|
42411
|
-
var
|
|
42735
|
+
var import_logging56 = __toESM(require_logging(), 1);
|
|
42412
42736
|
function buildWorkflowUrl(workflowId) {
|
|
42413
42737
|
const apiBase = process.env.N8N_API_BASE_URL ?? "http://localhost:5678/api/v1";
|
|
42414
42738
|
return `${apiBase.replace("/api/v1", "")}/workflow/${workflowId}`;
|
|
@@ -42501,7 +42825,7 @@ function buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testRe
|
|
|
42501
42825
|
async function buildSuccessResponse2(args, result, builtSubWorkflows, timeoutMs, startTime) {
|
|
42502
42826
|
const workflowId = result.serverResponse.id;
|
|
42503
42827
|
const testResult = args.test !== false ? await runTestStep(result.finalWorkflow, workflowId, args.test_payload, timeoutMs) : void 0;
|
|
42504
|
-
|
|
42828
|
+
import_logging56.logger.info("build_workflow: complete", { workflowId, fixes: result.fixesApplied.length });
|
|
42505
42829
|
return buildSuccessPayload(args, result, builtSubWorkflows, workflowId, testResult, startTime);
|
|
42506
42830
|
}
|
|
42507
42831
|
async function executeBuildPipeline(args, timeoutMs, correlationId, startTime) {
|
|
@@ -42635,7 +42959,7 @@ function stripExtraWebhookTriggers(args) {
|
|
|
42635
42959
|
const webhookNames = new Set(webhookNodes.map((n) => n.name));
|
|
42636
42960
|
const cleanedNodes = args.nodes.filter((n) => !webhookNames.has(n.name));
|
|
42637
42961
|
const cleanedConnections = args.connections?.filter((c) => !webhookNames.has(c.from) && !webhookNames.has(c.to));
|
|
42638
|
-
|
|
42962
|
+
import_logging56.logger.info("build_workflow: stripped webhook triggers from schedule workflow", {
|
|
42639
42963
|
removed: [...webhookNames]
|
|
42640
42964
|
});
|
|
42641
42965
|
return {
|
|
@@ -42650,7 +42974,7 @@ async function handleBuildWorkflow(args) {
|
|
|
42650
42974
|
const correlationId = generateCorrelationId();
|
|
42651
42975
|
const startTime = performance.now();
|
|
42652
42976
|
const timeoutMs = normalizeTimeout();
|
|
42653
|
-
|
|
42977
|
+
import_logging56.logger.debug("build_workflow: starting", { correlationId, workflowName: args.name });
|
|
42654
42978
|
try {
|
|
42655
42979
|
const { cleaned: triggerCleaned, warnings: triggerWarnings } = await substituteUnavailableTriggers(args);
|
|
42656
42980
|
const { cleaned, warning: webhookWarning } = stripExtraWebhookTriggers(triggerCleaned);
|
|
@@ -42679,7 +43003,7 @@ async function handleBuildWorkflow(args) {
|
|
|
42679
43003
|
} catch (error) {
|
|
42680
43004
|
if (isTimeoutError(error))
|
|
42681
43005
|
return toMCPResponse(createTimeoutError(correlationId, "build_workflow"));
|
|
42682
|
-
|
|
43006
|
+
import_logging56.logger.error("build_workflow: unexpected error", { correlationId, error });
|
|
42683
43007
|
const e = error instanceof Error ? error : new Error(String(error));
|
|
42684
43008
|
return toMCPResponse(createInternalError(correlationId, e));
|
|
42685
43009
|
}
|
|
@@ -42733,14 +43057,15 @@ Examples:
|
|
|
42733
43057
|
},
|
|
42734
43058
|
connections: {
|
|
42735
43059
|
type: "array",
|
|
42736
|
-
description: "Node connections",
|
|
43060
|
+
description: "Node connections. For LangChain/AI nodes the connection type is auto-detected from the catalog \u2014 no need to set `type` manually. Use `type` only to override auto-detection.",
|
|
42737
43061
|
items: {
|
|
42738
43062
|
type: "object",
|
|
42739
43063
|
properties: {
|
|
42740
43064
|
from: { type: "string", description: "Source node name" },
|
|
42741
43065
|
to: { type: "string", description: "Target node name" },
|
|
42742
43066
|
from_output: { type: "number", description: "Output port index (default 0)" },
|
|
42743
|
-
to_input: { type: "number", description: "Input port index (default 0)" }
|
|
43067
|
+
to_input: { type: "number", description: "Input port index (default 0)" },
|
|
43068
|
+
type: { type: "string", description: 'Connection type override (e.g., "ai_languageModel", "ai_tool", "ai_memory"). Auto-resolved from catalog if omitted.' }
|
|
42744
43069
|
},
|
|
42745
43070
|
required: ["from", "to"]
|
|
42746
43071
|
}
|
|
@@ -42759,7 +43084,7 @@ Examples:
|
|
|
42759
43084
|
};
|
|
42760
43085
|
|
|
42761
43086
|
// dist/mcp/tools/handlers/list-credentials.js
|
|
42762
|
-
var
|
|
43087
|
+
var import_logging57 = __toESM(require_logging(), 1);
|
|
42763
43088
|
init_validatorPostgresClient();
|
|
42764
43089
|
function buildApiConfig14(timeoutMs) {
|
|
42765
43090
|
return {
|
|
@@ -42865,10 +43190,10 @@ function handleListError2(error, correlationId) {
|
|
|
42865
43190
|
}
|
|
42866
43191
|
const statusCode = error?.statusCode;
|
|
42867
43192
|
if (statusCode === 401 || statusCode === 403) {
|
|
42868
|
-
|
|
43193
|
+
import_logging57.logger.warn("list_credentials: auth error", { correlationId, statusCode });
|
|
42869
43194
|
return toMCPResponse(createSuccessResponse({ success: false, error: "Authentication failed" }, correlationId, { duration_ms: 0 }));
|
|
42870
43195
|
}
|
|
42871
|
-
|
|
43196
|
+
import_logging57.logger.error("list_credentials: unexpected error", { correlationId, error });
|
|
42872
43197
|
const originalError = error instanceof Error ? error : new Error(String(error));
|
|
42873
43198
|
return toMCPResponse(createInternalError(correlationId, originalError));
|
|
42874
43199
|
}
|
|
@@ -42879,13 +43204,13 @@ async function handleListCredentials(args) {
|
|
|
42879
43204
|
const correlationId = generateCorrelationId();
|
|
42880
43205
|
const startTime = performance.now();
|
|
42881
43206
|
const timeoutMs = normalizeTimeout();
|
|
42882
|
-
|
|
43207
|
+
import_logging57.logger.debug("list_credentials: starting", { correlationId, options: args });
|
|
42883
43208
|
try {
|
|
42884
43209
|
const result = await fetchRawCredentials(args, timeoutMs);
|
|
42885
43210
|
const { credentials: filtered, neededCredentialTypes } = await applyForNodesFilter(result.credentials, args.forNodes);
|
|
42886
43211
|
const enriched = await enrichWithNodeTypes(filtered);
|
|
42887
43212
|
const final = args.includeSchema ? await enrichWithSchema(enriched) : enriched;
|
|
42888
|
-
|
|
43213
|
+
import_logging57.logger.info("list_credentials: retrieved", { correlationId, count: final.length });
|
|
42889
43214
|
const data = buildResponseData(final, neededCredentialTypes);
|
|
42890
43215
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: Math.round(performance.now() - startTime) }));
|
|
42891
43216
|
} catch (error) {
|
|
@@ -42941,7 +43266,7 @@ listing which credential types need to be created.`,
|
|
|
42941
43266
|
};
|
|
42942
43267
|
|
|
42943
43268
|
// dist/mcp/tools/handlers/get-credential-schema.js
|
|
42944
|
-
var
|
|
43269
|
+
var import_logging58 = __toESM(require_logging(), 1);
|
|
42945
43270
|
init_validatorPostgresClient();
|
|
42946
43271
|
var SENSITIVE_PATTERNS = /* @__PURE__ */ new Set([
|
|
42947
43272
|
"apikey",
|
|
@@ -43080,22 +43405,27 @@ function buildResult(credentialType, rows, compatibleNodes) {
|
|
|
43080
43405
|
function buildSchemaResponse(credentialType, rows, nodes, correlationId, startTime) {
|
|
43081
43406
|
const data = buildResult(credentialType, rows, nodes);
|
|
43082
43407
|
const duration_ms = Math.round(performance.now() - startTime);
|
|
43083
|
-
|
|
43408
|
+
import_logging58.logger.info("get_credential_schema: found", { correlationId, credential_type: credentialType, fieldCount: rows.length });
|
|
43084
43409
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms }));
|
|
43085
43410
|
}
|
|
43086
43411
|
async function handleGetCredentialSchema(args) {
|
|
43087
43412
|
const correlationId = generateCorrelationId();
|
|
43088
43413
|
const startTime = performance.now();
|
|
43089
43414
|
const { credential_type } = args;
|
|
43090
|
-
|
|
43415
|
+
import_logging58.logger.debug("get_credential_schema: starting", { correlationId, credential_type });
|
|
43091
43416
|
try {
|
|
43092
43417
|
const [rows, nodes] = await Promise.all([fetchSchemaRows(credential_type), fetchCompatibleNodes(credential_type)]);
|
|
43093
43418
|
if (rows.length === 0)
|
|
43094
43419
|
return handleUnknownType(credential_type, correlationId);
|
|
43095
43420
|
return buildSchemaResponse(credential_type, rows, nodes, correlationId, startTime);
|
|
43096
43421
|
} catch (error) {
|
|
43097
|
-
|
|
43098
|
-
|
|
43422
|
+
import_logging58.logger.error("get_credential_schema: unexpected error", { correlationId, error });
|
|
43423
|
+
const originalError = error instanceof Error ? error : new Error(String(error));
|
|
43424
|
+
if (isDbConfigError(originalError))
|
|
43425
|
+
return makeDbNotConfiguredResponse();
|
|
43426
|
+
if (isDbConnectionError(originalError))
|
|
43427
|
+
return makeDbConnectionErrorResponse();
|
|
43428
|
+
return toMCPResponse(createInternalError(correlationId, originalError));
|
|
43099
43429
|
}
|
|
43100
43430
|
}
|
|
43101
43431
|
async function handleUnknownType(credentialType, correlationId) {
|
|
@@ -43138,7 +43468,7 @@ Example usage:
|
|
|
43138
43468
|
};
|
|
43139
43469
|
|
|
43140
43470
|
// dist/mcp/tools/handlers/create-credential.js
|
|
43141
|
-
var
|
|
43471
|
+
var import_logging59 = __toESM(require_logging(), 1);
|
|
43142
43472
|
init_validatorPostgresClient();
|
|
43143
43473
|
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
43144
43474
|
var FIELDS_SQL = `
|
|
@@ -43204,11 +43534,11 @@ function buildCreateError(error, correlationId) {
|
|
|
43204
43534
|
const statusCode = error?.statusCode;
|
|
43205
43535
|
const message = error instanceof Error ? error.message : String(error);
|
|
43206
43536
|
if (statusCode === 401 || statusCode === 403) {
|
|
43207
|
-
|
|
43537
|
+
import_logging59.logger.warn("create_credential: auth error", { correlationId, statusCode });
|
|
43208
43538
|
const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
|
|
43209
43539
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
|
|
43210
43540
|
}
|
|
43211
|
-
|
|
43541
|
+
import_logging59.logger.error("create_credential: unexpected error", { correlationId, error });
|
|
43212
43542
|
return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
|
|
43213
43543
|
}
|
|
43214
43544
|
function buildExistsResponse(existing, correlationId) {
|
|
@@ -43228,7 +43558,7 @@ async function handleCreateCredential(args) {
|
|
|
43228
43558
|
const correlationId = generateCorrelationId();
|
|
43229
43559
|
const startTime = performance.now();
|
|
43230
43560
|
const { credential_type, name, data } = args;
|
|
43231
|
-
|
|
43561
|
+
import_logging59.logger.debug("create_credential: starting", { correlationId, credential_type, name });
|
|
43232
43562
|
try {
|
|
43233
43563
|
const fields = await fetchCredentialFields(credential_type);
|
|
43234
43564
|
if (fields.length === 0)
|
|
@@ -43239,13 +43569,13 @@ async function handleCreateCredential(args) {
|
|
|
43239
43569
|
const config3 = buildApiConfig15();
|
|
43240
43570
|
const existing = await findExistingCredential(config3, credential_type, name);
|
|
43241
43571
|
if (existing) {
|
|
43242
|
-
|
|
43572
|
+
import_logging59.logger.warn("create_credential: duplicate blocked", { correlationId, existing_id: existing.id, credential_type, name });
|
|
43243
43573
|
return buildExistsResponse(existing, correlationId);
|
|
43244
43574
|
}
|
|
43245
43575
|
const result = await createCredential(config3, { name, type: credential_type, data });
|
|
43246
43576
|
clearCredentialCache();
|
|
43247
43577
|
const duration_ms = Math.round(performance.now() - startTime);
|
|
43248
|
-
|
|
43578
|
+
import_logging59.logger.info("create_credential: created", { correlationId, id: result.id, credential_type, name });
|
|
43249
43579
|
return buildCreateSuccessResponse(result, correlationId, duration_ms);
|
|
43250
43580
|
} catch (error) {
|
|
43251
43581
|
return buildCreateError(error, correlationId);
|
|
@@ -43297,7 +43627,7 @@ Example usage:
|
|
|
43297
43627
|
|
|
43298
43628
|
// dist/mcp/tools/handlers/test-credential.js
|
|
43299
43629
|
var net = __toESM(require("net"), 1);
|
|
43300
|
-
var
|
|
43630
|
+
var import_logging60 = __toESM(require_logging(), 1);
|
|
43301
43631
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
43302
43632
|
var RATE_LIMIT_MS = 3e4;
|
|
43303
43633
|
var lastTestTimestamps = /* @__PURE__ */ new Map();
|
|
@@ -43477,7 +43807,7 @@ function buildProbeError(error, correlationId) {
|
|
|
43477
43807
|
const data = { supported: true, valid: false, message: "Credential not found. Check the credential ID." };
|
|
43478
43808
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
|
|
43479
43809
|
}
|
|
43480
|
-
|
|
43810
|
+
import_logging60.logger.error("test_credential: unexpected error", { correlationId, error });
|
|
43481
43811
|
return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
|
|
43482
43812
|
}
|
|
43483
43813
|
async function handleTestCredential(args) {
|
|
@@ -43493,7 +43823,7 @@ async function handleTestCredential(args) {
|
|
|
43493
43823
|
recordTestTimestamp(credential_id);
|
|
43494
43824
|
try {
|
|
43495
43825
|
const n8nResult = await testCredentialViaN8n(config3, credential_id, credential.name, credential.type, credential.data, restCreds);
|
|
43496
|
-
|
|
43826
|
+
import_logging60.logger.info("test_credential: n8n built-in test", {
|
|
43497
43827
|
correlationId,
|
|
43498
43828
|
type: credential.type,
|
|
43499
43829
|
valid: n8nResult.valid
|
|
@@ -43501,9 +43831,9 @@ async function handleTestCredential(args) {
|
|
|
43501
43831
|
return buildProbeResponse(n8nResult, correlationId);
|
|
43502
43832
|
} catch (n8nTestError) {
|
|
43503
43833
|
if (n8nTestError?.code === "NEEDS_N8N_LOGIN") {
|
|
43504
|
-
|
|
43834
|
+
import_logging60.logger.debug("test_credential: n8n test unavailable, falling back to HTTP probes");
|
|
43505
43835
|
} else {
|
|
43506
|
-
|
|
43836
|
+
import_logging60.logger.warn("test_credential: n8n test failed, falling back to HTTP probes", {
|
|
43507
43837
|
error: n8nTestError instanceof Error ? n8nTestError.message : String(n8nTestError)
|
|
43508
43838
|
});
|
|
43509
43839
|
}
|
|
@@ -43513,10 +43843,10 @@ async function handleTestCredential(args) {
|
|
|
43513
43843
|
return buildUnsupportedResponse(credential.type, correlationId);
|
|
43514
43844
|
const hasBlankValues = Object.values(credential.data).some((v) => typeof v === "string" && v.includes("__n8n_BLANK_VALUE_"));
|
|
43515
43845
|
if (hasBlankValues) {
|
|
43516
|
-
|
|
43846
|
+
import_logging60.logger.warn("test_credential: credential data contains encrypted placeholders, probe may be unreliable");
|
|
43517
43847
|
}
|
|
43518
43848
|
const result = await probe(credential.data);
|
|
43519
|
-
|
|
43849
|
+
import_logging60.logger.info("test_credential: HTTP probe fallback", {
|
|
43520
43850
|
correlationId,
|
|
43521
43851
|
type: credential.type,
|
|
43522
43852
|
valid: result.valid,
|
|
@@ -43570,7 +43900,7 @@ Use list_credentials to find credential IDs.`,
|
|
|
43570
43900
|
};
|
|
43571
43901
|
|
|
43572
43902
|
// dist/mcp/tools/handlers/update-credential.js
|
|
43573
|
-
var
|
|
43903
|
+
var import_logging61 = __toESM(require_logging(), 1);
|
|
43574
43904
|
var DEFAULT_TIMEOUT_MS3 = 1e4;
|
|
43575
43905
|
function buildApiConfig17() {
|
|
43576
43906
|
return {
|
|
@@ -43594,16 +43924,16 @@ function buildUpdateError(error, correlationId) {
|
|
|
43594
43924
|
const statusCode = error?.statusCode;
|
|
43595
43925
|
const message = error instanceof Error ? error.message : String(error);
|
|
43596
43926
|
if (statusCode === 404) {
|
|
43597
|
-
|
|
43927
|
+
import_logging61.logger.warn("update_credential: not found", { correlationId, statusCode });
|
|
43598
43928
|
const data = { success: false, error: "NOT_FOUND", message: "Credential not found. Use list_credentials to find valid IDs." };
|
|
43599
43929
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
|
|
43600
43930
|
}
|
|
43601
43931
|
if (statusCode === 401 || statusCode === 403) {
|
|
43602
|
-
|
|
43932
|
+
import_logging61.logger.warn("update_credential: auth error", { correlationId, statusCode });
|
|
43603
43933
|
const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
|
|
43604
43934
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
|
|
43605
43935
|
}
|
|
43606
|
-
|
|
43936
|
+
import_logging61.logger.error("update_credential: unexpected error", { correlationId, error });
|
|
43607
43937
|
return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
|
|
43608
43938
|
}
|
|
43609
43939
|
async function buildUpdatePayload(config3, args) {
|
|
@@ -43618,9 +43948,9 @@ function buildUpdateSuccessResponse2(result, correlationId, duration_ms) {
|
|
|
43618
43948
|
async function handleUpdateCredential(args) {
|
|
43619
43949
|
const correlationId = generateCorrelationId();
|
|
43620
43950
|
const startTime = performance.now();
|
|
43621
|
-
|
|
43951
|
+
import_logging61.logger.debug("update_credential: starting", { correlationId, credential_id: args.credential_id });
|
|
43622
43952
|
if (args.data && Object.keys(args.data).length > 0) {
|
|
43623
|
-
|
|
43953
|
+
import_logging61.logger.warn("update_credential: rejected data write attempt", { correlationId, credential_id: args.credential_id });
|
|
43624
43954
|
const data = {
|
|
43625
43955
|
success: false,
|
|
43626
43956
|
error: "DATA_WRITE_DENIED",
|
|
@@ -43641,7 +43971,7 @@ async function handleUpdateCredential(args) {
|
|
|
43641
43971
|
const payload = await buildUpdatePayload(config3, { ...args, data: void 0 });
|
|
43642
43972
|
const result = await updateCredential(config3, args.credential_id, payload);
|
|
43643
43973
|
invalidate();
|
|
43644
|
-
|
|
43974
|
+
import_logging61.logger.info("update_credential: updated (name only)", { correlationId, id: result.id });
|
|
43645
43975
|
return buildUpdateSuccessResponse2(result, correlationId, Math.round(performance.now() - startTime));
|
|
43646
43976
|
} catch (error) {
|
|
43647
43977
|
return buildUpdateError(error, correlationId);
|
|
@@ -43676,7 +44006,7 @@ On not found: returns NOT_FOUND error with guidance.`,
|
|
|
43676
44006
|
};
|
|
43677
44007
|
|
|
43678
44008
|
// dist/mcp/tools/handlers/delete-credential.js
|
|
43679
|
-
var
|
|
44009
|
+
var import_logging62 = __toESM(require_logging(), 1);
|
|
43680
44010
|
var DEFAULT_TIMEOUT_MS4 = 1e4;
|
|
43681
44011
|
var MAX_WORKFLOWS_TO_SCAN = 100;
|
|
43682
44012
|
function buildApiConfig18() {
|
|
@@ -43735,18 +44065,18 @@ function buildDeleteError(error, correlationId) {
|
|
|
43735
44065
|
const statusCode = error?.statusCode;
|
|
43736
44066
|
const message = error instanceof Error ? error.message : String(error);
|
|
43737
44067
|
if (statusCode === 401 || statusCode === 403) {
|
|
43738
|
-
|
|
44068
|
+
import_logging62.logger.warn("delete_credential: auth error", { correlationId, statusCode });
|
|
43739
44069
|
const data = { success: false, error: "AUTH_ERROR", message: "Authentication failed. Check N8N_API_KEY." };
|
|
43740
44070
|
return toMCPResponse(createSuccessResponse(data, correlationId, { duration_ms: 0 }));
|
|
43741
44071
|
}
|
|
43742
|
-
|
|
44072
|
+
import_logging62.logger.error("delete_credential: unexpected error", { correlationId, error });
|
|
43743
44073
|
return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(message)));
|
|
43744
44074
|
}
|
|
43745
44075
|
async function checkAndWarnDependents(config3, credential_id, correlationId) {
|
|
43746
44076
|
const dependentWorkflows = await findDependentWorkflows(config3, credential_id);
|
|
43747
44077
|
if (dependentWorkflows.length === 0)
|
|
43748
44078
|
return null;
|
|
43749
|
-
|
|
44079
|
+
import_logging62.logger.warn("delete_credential: dependent workflows found", { correlationId, credential_id, count: dependentWorkflows.length });
|
|
43750
44080
|
return buildDependentWarningResponse(credential_id, dependentWorkflows, correlationId);
|
|
43751
44081
|
}
|
|
43752
44082
|
async function executeDelete(config3, credential_id, force, correlationId, startTime) {
|
|
@@ -43757,14 +44087,14 @@ async function executeDelete(config3, credential_id, force, correlationId, start
|
|
|
43757
44087
|
}
|
|
43758
44088
|
await deleteCredential(config3, credential_id);
|
|
43759
44089
|
invalidate();
|
|
43760
|
-
|
|
44090
|
+
import_logging62.logger.info("delete_credential: deleted", { correlationId, credential_id, force });
|
|
43761
44091
|
return buildDeleteSuccessResponse(credential_id, correlationId, Math.round(performance.now() - startTime));
|
|
43762
44092
|
}
|
|
43763
44093
|
async function handleDeleteCredential(args) {
|
|
43764
44094
|
const correlationId = generateCorrelationId();
|
|
43765
44095
|
const startTime = performance.now();
|
|
43766
44096
|
const { credential_id, force = false } = args;
|
|
43767
|
-
|
|
44097
|
+
import_logging62.logger.debug("delete_credential: starting", { correlationId, credential_id, force });
|
|
43768
44098
|
try {
|
|
43769
44099
|
return await executeDelete(buildApiConfig18(), credential_id, force, correlationId, startTime);
|
|
43770
44100
|
} catch (error) {
|
|
@@ -43809,12 +44139,12 @@ On auth error: returns AUTH_ERROR with guidance.`,
|
|
|
43809
44139
|
};
|
|
43810
44140
|
|
|
43811
44141
|
// dist/mcp/tools/handlers/collect-config.js
|
|
43812
|
-
var
|
|
44142
|
+
var import_logging64 = __toESM(require_logging(), 1);
|
|
43813
44143
|
init_validatorPostgresClient();
|
|
43814
|
-
var
|
|
44144
|
+
var import_retry9 = __toESM(require_retryUtils(), 1);
|
|
43815
44145
|
|
|
43816
44146
|
// dist/mcp/session/config-cache.js
|
|
43817
|
-
var
|
|
44147
|
+
var import_logging63 = __toESM(require_logging(), 1);
|
|
43818
44148
|
var CONFIG_CACHE_TTL_MS = 30 * 60 * 1e3;
|
|
43819
44149
|
var CONFIG_CACHE_MAX_ENTRIES = 500;
|
|
43820
44150
|
var cacheState2 = null;
|
|
@@ -43857,12 +44187,12 @@ function set(key, value) {
|
|
|
43857
44187
|
stored_at: Date.now(),
|
|
43858
44188
|
expires_at: Date.now() + CONFIG_CACHE_TTL_MS
|
|
43859
44189
|
});
|
|
43860
|
-
|
|
44190
|
+
import_logging63.logger.debug("config-cache: stored value", { key });
|
|
43861
44191
|
}
|
|
43862
44192
|
function clearConfigCache() {
|
|
43863
44193
|
cacheState2 = null;
|
|
43864
44194
|
warming = null;
|
|
43865
|
-
|
|
44195
|
+
import_logging63.logger.debug("config-cache: cleared");
|
|
43866
44196
|
}
|
|
43867
44197
|
function getSuggestion(configType, nodeType, parameterName) {
|
|
43868
44198
|
const serviceId = extractServiceId(nodeType);
|
|
@@ -43994,7 +44324,7 @@ function buildConfigValues(answered) {
|
|
|
43994
44324
|
return result;
|
|
43995
44325
|
}
|
|
43996
44326
|
async function markGapsResolved(gapType, nodeType, notes) {
|
|
43997
|
-
await (0,
|
|
44327
|
+
await (0, import_retry9.retryDbQuery)(() => validatorQuery(RESOLVE_GAPS_SQL, [gapType, nodeType, notes]), 3, "collect_config_resolve_gaps");
|
|
43998
44328
|
}
|
|
43999
44329
|
async function resolveOneGap(req) {
|
|
44000
44330
|
const gapType = CATEGORY_TO_GAP_TYPE[req.configType];
|
|
@@ -44003,7 +44333,7 @@ async function resolveOneGap(req) {
|
|
|
44003
44333
|
try {
|
|
44004
44334
|
await markGapsResolved(gapType, req.nodeType, `Resolved via collect_config: ${req.parameterName}`);
|
|
44005
44335
|
} catch (error) {
|
|
44006
|
-
|
|
44336
|
+
import_logging64.logger.warn("collect_config: gap resolution failed (non-fatal)", {
|
|
44007
44337
|
nodeType: req.nodeType,
|
|
44008
44338
|
gapType,
|
|
44009
44339
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -44045,14 +44375,14 @@ async function executeCollect(args) {
|
|
|
44045
44375
|
async function handleCollectConfig(args) {
|
|
44046
44376
|
const correlationId = generateCorrelationId();
|
|
44047
44377
|
const startTime = performance.now();
|
|
44048
|
-
|
|
44378
|
+
import_logging64.logger.debug("collect_config: starting", { correlationId });
|
|
44049
44379
|
try {
|
|
44050
44380
|
const { output, logMeta } = await executeCollect(args);
|
|
44051
44381
|
const duration_ms = Math.round(performance.now() - startTime);
|
|
44052
|
-
|
|
44382
|
+
import_logging64.logger.info("collect_config: completed", { correlationId, ...logMeta });
|
|
44053
44383
|
return toMCPResponse(createSuccessResponse(output, correlationId, { duration_ms }));
|
|
44054
44384
|
} catch (error) {
|
|
44055
|
-
|
|
44385
|
+
import_logging64.logger.error("collect_config: failed", { correlationId, error });
|
|
44056
44386
|
return toMCPResponse(createInternalError(correlationId, error instanceof Error ? error : new Error(String(error))));
|
|
44057
44387
|
}
|
|
44058
44388
|
}
|
|
@@ -44109,40 +44439,8 @@ When values are collected, marks matching factory.gaps entries as resolved for a
|
|
|
44109
44439
|
}
|
|
44110
44440
|
};
|
|
44111
44441
|
|
|
44112
|
-
// dist/mcp/tools/handlers/shared/n8n-guard.js
|
|
44113
|
-
var SETUP_GUIDE_URL = "https://github.com/tsvika58/n8n-workflow-validator/blob/main/docs/AGENT_INSTALL.md";
|
|
44114
|
-
var OFFLINE_TOOLS = /* @__PURE__ */ new Set([
|
|
44115
|
-
"validate_workflow",
|
|
44116
|
-
"get_operation_schema",
|
|
44117
|
-
"check_parameter",
|
|
44118
|
-
"suggest_fix",
|
|
44119
|
-
"list_operations",
|
|
44120
|
-
"list_nodes",
|
|
44121
|
-
"get_node_info",
|
|
44122
|
-
"find_similar_pattern",
|
|
44123
|
-
"get_session_metrics",
|
|
44124
|
-
"get_credential_schema",
|
|
44125
|
-
"setup_check"
|
|
44126
|
-
]);
|
|
44127
|
-
function requiresN8nApiKey(toolName) {
|
|
44128
|
-
return !OFFLINE_TOOLS.has(toolName);
|
|
44129
|
-
}
|
|
44130
|
-
function makeN8nNotConfiguredError() {
|
|
44131
|
-
return {
|
|
44132
|
-
content: [
|
|
44133
|
-
{
|
|
44134
|
-
type: "text",
|
|
44135
|
-
text: JSON.stringify({
|
|
44136
|
-
error: `This tool requires n8n connection. Set N8N_API_URL and N8N_API_KEY in your MCP config. See: ${SETUP_GUIDE_URL}`
|
|
44137
|
-
})
|
|
44138
|
-
}
|
|
44139
|
-
],
|
|
44140
|
-
isError: true
|
|
44141
|
-
};
|
|
44142
|
-
}
|
|
44143
|
-
|
|
44144
44442
|
// dist/mcp/tools/handlers/setup-check.js
|
|
44145
|
-
var SETUP_GUIDE_URL2 = "https://github.com/tsvika58/n8n
|
|
44443
|
+
var SETUP_GUIDE_URL2 = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
|
|
44146
44444
|
var N8N_PROBE_TIMEOUT_MS = 2e3;
|
|
44147
44445
|
var N8N_PROBE_PORTS = [5678, 5679];
|
|
44148
44446
|
async function probeN8nUrl(url2) {
|
|
@@ -44332,13 +44630,18 @@ async function dispatchToolCall(name, args) {
|
|
|
44332
44630
|
if (requiresN8nApiKey(name) && !process.env.N8N_API_KEY) {
|
|
44333
44631
|
return makeN8nNotConfiguredError();
|
|
44334
44632
|
}
|
|
44335
|
-
|
|
44336
|
-
|
|
44337
|
-
if (
|
|
44338
|
-
|
|
44339
|
-
|
|
44633
|
+
incrementInflight();
|
|
44634
|
+
try {
|
|
44635
|
+
if (tool.inputSchema && hasSchema(name)) {
|
|
44636
|
+
const validation = validateToolInput(name, args);
|
|
44637
|
+
if (!validation.success)
|
|
44638
|
+
return createValidationErrorResponse(validation.error);
|
|
44639
|
+
return await tool.handler(validation.data);
|
|
44640
|
+
}
|
|
44641
|
+
return await tool.handler(args);
|
|
44642
|
+
} finally {
|
|
44643
|
+
decrementInflight();
|
|
44340
44644
|
}
|
|
44341
|
-
return tool.handler(args);
|
|
44342
44645
|
}
|
|
44343
44646
|
async function handleToolCall(name, args, sessionId = "default") {
|
|
44344
44647
|
const startTime = Date.now();
|
|
@@ -44356,7 +44659,9 @@ function registerAllTools() {
|
|
|
44356
44659
|
registerTool({ ...entry, inputSchema: toolSchemas[entry.name] });
|
|
44357
44660
|
}
|
|
44358
44661
|
}
|
|
44359
|
-
|
|
44662
|
+
|
|
44663
|
+
// dist/mcp/server.js
|
|
44664
|
+
init_validatorPostgresClient();
|
|
44360
44665
|
|
|
44361
44666
|
// dist/mcp/middleware/credential-redaction.js
|
|
44362
44667
|
var MAX_DEPTH = 20;
|
|
@@ -44521,7 +44826,7 @@ function redactMCPResponse(response) {
|
|
|
44521
44826
|
|
|
44522
44827
|
// dist/mcp/server-lifecycle.js
|
|
44523
44828
|
init_validatorPostgresClient();
|
|
44524
|
-
var
|
|
44829
|
+
var import_logging65 = __toESM(require_logging(), 1);
|
|
44525
44830
|
var defaultDeps = {
|
|
44526
44831
|
shutdownPool: shutdownValidatorPool,
|
|
44527
44832
|
clearCache: clearCredentialCache,
|
|
@@ -44529,11 +44834,12 @@ var defaultDeps = {
|
|
|
44529
44834
|
};
|
|
44530
44835
|
async function runCleanup(serverName, deps) {
|
|
44531
44836
|
try {
|
|
44837
|
+
await waitForInflightRequests(5e3);
|
|
44532
44838
|
await deps.shutdownPool();
|
|
44533
44839
|
deps.clearCache();
|
|
44534
44840
|
deps.clearConfigCache?.();
|
|
44535
44841
|
} catch (err) {
|
|
44536
|
-
|
|
44842
|
+
import_logging65.logger.error(`[${serverName}] Error during shutdown:`, err);
|
|
44537
44843
|
}
|
|
44538
44844
|
}
|
|
44539
44845
|
function createShutdownHandler(serverName, deps = defaultDeps) {
|
|
@@ -44542,7 +44848,7 @@ function createShutdownHandler(serverName, deps = defaultDeps) {
|
|
|
44542
44848
|
if (isShuttingDown)
|
|
44543
44849
|
return;
|
|
44544
44850
|
isShuttingDown = true;
|
|
44545
|
-
|
|
44851
|
+
import_logging65.logger.info(`[${serverName}] Received ${signal}, shutting down...`);
|
|
44546
44852
|
await runCleanup(serverName, deps);
|
|
44547
44853
|
process.exit(0);
|
|
44548
44854
|
};
|
|
@@ -44555,12 +44861,29 @@ function registerShutdownHandlers(serverName, deps) {
|
|
|
44555
44861
|
process.stdin.on("end", () => void shutdown("stdin-close"));
|
|
44556
44862
|
process.stdin.on("close", () => void shutdown("stdin-close"));
|
|
44557
44863
|
process.on("exit", (code) => {
|
|
44558
|
-
|
|
44864
|
+
import_logging65.logger.debug(`[${serverName}] Process exiting (code ${code})`);
|
|
44559
44865
|
});
|
|
44560
44866
|
}
|
|
44561
44867
|
|
|
44562
44868
|
// dist/mcp/server.js
|
|
44563
|
-
var SETUP_GUIDE_URL3 = "https://github.com/tsvika58/n8n
|
|
44869
|
+
var SETUP_GUIDE_URL3 = "https://github.com/tsvika58/p58-n8n/blob/main/docs/AGENT_INSTALL.md";
|
|
44870
|
+
var _serverInitialized = false;
|
|
44871
|
+
function isServerInitialized() {
|
|
44872
|
+
return _serverInitialized;
|
|
44873
|
+
}
|
|
44874
|
+
function buildInitializingResponse() {
|
|
44875
|
+
return {
|
|
44876
|
+
content: [{
|
|
44877
|
+
type: "text",
|
|
44878
|
+
text: JSON.stringify({
|
|
44879
|
+
error: "SERVER_INITIALIZING",
|
|
44880
|
+
message: "Server is still loading. Please retry in a moment.",
|
|
44881
|
+
retryAfterMs: 2e3
|
|
44882
|
+
})
|
|
44883
|
+
}],
|
|
44884
|
+
isError: true
|
|
44885
|
+
};
|
|
44886
|
+
}
|
|
44564
44887
|
function logStartupSummary(n8nConnected) {
|
|
44565
44888
|
const apiKey = process.env.N8N_API_KEY;
|
|
44566
44889
|
const n8nUrl = process.env.N8N_API_URL ?? process.env.N8N_API_BASE_URL ?? "http://localhost:5678/api/v1";
|
|
@@ -44605,6 +44928,9 @@ function toCallToolResult(response) {
|
|
|
44605
44928
|
server.setRequestHandler(import_types22.CallToolRequestSchema, async (request) => {
|
|
44606
44929
|
const { name, arguments: args } = request.params;
|
|
44607
44930
|
try {
|
|
44931
|
+
if (!_serverInitialized) {
|
|
44932
|
+
return toCallToolResult(buildInitializingResponse());
|
|
44933
|
+
}
|
|
44608
44934
|
const response = await handleToolCall(name, args ?? {});
|
|
44609
44935
|
return toCallToolResult(redactMCPResponse(response));
|
|
44610
44936
|
} catch (error) {
|
|
@@ -44614,13 +44940,25 @@ server.setRequestHandler(import_types22.CallToolRequestSchema, async (request) =
|
|
|
44614
44940
|
});
|
|
44615
44941
|
async function main() {
|
|
44616
44942
|
const transport = new import_stdio.StdioServerTransport();
|
|
44943
|
+
const startMs = Date.now();
|
|
44617
44944
|
await server.connect(transport);
|
|
44945
|
+
console.error(`[INIT] MCP handshake complete in ${Date.now() - startMs}ms`);
|
|
44946
|
+
registerAllTools();
|
|
44947
|
+
_serverInitialized = true;
|
|
44618
44948
|
const toolCount = getRegisteredTools().length;
|
|
44619
|
-
console.error(
|
|
44949
|
+
console.error(`[INIT] ${toolCount} tools registered in ${Date.now() - startMs}ms`);
|
|
44620
44950
|
logStartupSummary(Boolean(process.env.N8N_API_KEY));
|
|
44621
44951
|
registerShutdownHandlers(config.SERVER_NAME);
|
|
44622
44952
|
void warmCredentialCache().catch(() => {
|
|
44623
44953
|
});
|
|
44954
|
+
if (process.env.P58_DATABASE_URL || process.env.MCP_DATABASE_URL) {
|
|
44955
|
+
void warmValidatorPool().then(() => {
|
|
44956
|
+
console.error("catalog: connected \u2713");
|
|
44957
|
+
}).catch((err) => {
|
|
44958
|
+
console.error(`catalog: WARNING \u2014 connection failed: ${err.message}`);
|
|
44959
|
+
console.error("catalog: tools will retry on first use, but check P58_DATABASE_URL if errors persist");
|
|
44960
|
+
});
|
|
44961
|
+
}
|
|
44624
44962
|
}
|
|
44625
44963
|
main().catch((error) => {
|
|
44626
44964
|
console.error("Server failed to start:", error);
|
|
@@ -44628,6 +44966,7 @@ main().catch((error) => {
|
|
|
44628
44966
|
});
|
|
44629
44967
|
// Annotate the CommonJS export names for ESM import in node:
|
|
44630
44968
|
0 && (module.exports = {
|
|
44969
|
+
isServerInitialized,
|
|
44631
44970
|
logStartupSummary
|
|
44632
44971
|
});
|
|
44633
44972
|
/*! Bundled license information:
|