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