Skip to content

Commit 5ef43ad

Browse files
czlonkowskiclaude
andauthored
fix: forward workflow settings outside the typed create schema (v2.74.1) (czlonkowski#1027)
* fix: forward workflow settings outside the typed create schema (v2.74.1) n8n_create_workflow declared settings as a closed z.object, so keys such as availableInMCP, callerPolicy and timeSavedPerExecution were stripped before the payload reached the API client, while updateSettings forwarded them. The typed keys stay validated; everything else now passes through, as on the update path. Tool schema and docs mention availableInMCP. Fixes czlonkowski#1026 Conceived by Romuald Członkowski - www.aiadvisors.pl/en Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5E2LQVUpMQDBDueBzieGp * test: give the second memory node its own id in the multiple-memory fixture n8n 2.36 rejects a workflow whose nodes share an id, so the live integration test "should validate memory connections" has failed on main since the 2.36 update. The helper defaults every memory node to 'memory-1'; the second one now gets 'memory-2'. Conceived by Romuald Członkowski - www.aiadvisors.pl/en Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5E2LQVUpMQDBDueBzieGp --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 71f338b commit 5ef43ad

9 files changed

Lines changed: 59 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [2.74.1] - 2026-08-27
11+
12+
### Fixed
13+
14+
- **`n8n_create_workflow` silently dropped workflow settings outside its own list** (#1026). The create input schema was a closed object with eight settings keys, so `availableInMCP`, `callerPolicy`, `callerIds`, `timeSavedPerExecution`, `customTelemetryTags`, `redactionPolicy` and `timeSavedMode` were stripped before the payload reached the API client. A workflow created with `settings.availableInMCP: true` therefore never appeared in n8n's instance-level MCP server, while the same key set through `updateSettings` worked. The typed keys are still validated; every other key is now forwarded, as on the update path; the create cleaner still drops the derived properties n8n ignores on write. The tool schema and documentation now mention `availableInMCP`.
15+
1016
## [2.74.0] - 2026-08-27
1117

1218
### Changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "n8n-mcp",
3-
"version": "2.74.0",
3+
"version": "2.74.1",
44
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

package.runtime.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "n8n-mcp-runtime",
3-
"version": "2.74.0",
3+
"version": "2.74.1",
44
"description": "n8n MCP Server Runtime Dependencies Only",
55
"private": true,
66
"dependencies": {

src/mcp/handlers-n8n-manager.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ const createWorkflowSchema = z.object({
443443
// Two-arg z.record(keySchema, valueSchema) — see services/n8n-validation.ts for the
444444
// Zod 3/4 compatibility rationale (#744).
445445
connections: z.preprocess(normalizeMcpWorkflowConnections, z.record(z.string(), z.any())),
446+
// The typed keys are validated; every other key is forwarded, as on the update path.
447+
// A closed object here silently dropped `availableInMCP`, `callerPolicy` and the other
448+
// settings added since n8n 1.119 before they reached the cleaner (issue #1026).
446449
settings: z.preprocess(normalizeMcpJsonValue, z.object({
447450
executionOrder: z.enum(['v0', 'v1']).optional(),
448451
timezone: z.string().optional(),
@@ -452,7 +455,7 @@ const createWorkflowSchema = z.object({
452455
saveExecutionProgress: z.boolean().optional(),
453456
executionTimeout: z.number().optional(),
454457
errorWorkflow: z.string().optional(),
455-
})).optional(),
458+
}).passthrough()).optional(),
456459
// Validated by parseNodeGroupsInput() — see services/node-groups.ts
457460
nodeGroups: z.any().optional(),
458461
projectId: z.string().optional(),

src/mcp/tool-docs/workflow_management/n8n-create-workflow.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const n8nCreateWorkflowDoc: ToolDocumentation = {
2121
name: { type: 'string', required: true, description: 'Workflow name' },
2222
nodes: { type: 'array', required: true, description: 'Array of nodes with id, name, type, typeVersion, position, parameters' },
2323
connections: { type: 'object', required: true, description: 'Node connections. Keys are source node names (not IDs)' },
24-
settings: { type: 'object', description: 'Optional workflow settings (timezone, error handling, etc.)' },
24+
settings: { type: 'object', description: 'Optional workflow settings (timezone, error handling, etc.). Every key the n8n Public API accepts is forwarded, including availableInMCP, which exposes the workflow to n8n\'s instance-level MCP server.' },
2525
nodeGroups: { type: 'array', description: 'Optional canvas groups (n8n 2.28+): [{name, nodeIds, description?}]. Members are node IDs from nodes[] and must form a connected run with no trigger among them. Dropped with a warning on n8n older than 2.28.' },
2626
projectId: { type: 'string', description: 'Optional project to create the workflow in (enterprise feature). Defaults to the personal project.' },
2727
parentFolderId: { type: 'string', description: 'Optional folder to place the workflow in (n8n 2.32+; rejected with a 400 on older instances). Omit for the project root. Manage folders with n8n_manage_folders.' }
@@ -72,7 +72,8 @@ n8n_create_workflow({
7272
timezone: "America/New_York",
7373
errorWorkflow: "error_handler_workflow_id",
7474
saveDataSuccessExecution: "all",
75-
saveDataErrorExecution: "all"
75+
saveDataErrorExecution: "all",
76+
availableInMCP: true // expose to n8n's instance-level MCP server
7677
}
7778
})`
7879
],

src/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
5050
- **replaceConnections**: Replace entire connections object
5151
5252
### Metadata Operations (5 types):
53-
- **updateSettings**: Modify workflow settings
53+
- **updateSettings**: Modify workflow settings (merged over the current ones). Any key the n8n Public API accepts is forwarded, e.g. \`availableInMCP: true\` exposes the workflow to n8n's instance-level MCP server.
5454
- **updateName**: Rename the workflow
5555
- **setNodeGroups**: Replace the workflow's canvas groups (n8n 2.28+). Full replacement — pass every group to keep, or \`[]\` to ungroup everything. Each group takes \`name\` plus either \`nodeNames\` or \`nodeIds\`, and an optional \`description\` (max 155 chars, n8n 2.32+; dropped automatically on older instances). Group members must form a connected run with no trigger among them; n8n validates that on save and its message is returned unchanged if a group you asked for is rejected.
5656
- **addTag**: Add a workflow tag

src/mcp/tools-n8n-manager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const n8nManagementTools: ToolDefinition[] = [
5252
},
5353
settings: {
5454
type: 'object',
55-
description: 'Optional workflow settings (execution order, timezone, error handling)',
55+
description: 'Optional workflow settings (execution order, timezone, error handling). Any other key the n8n Public API accepts is forwarded as well, e.g. availableInMCP (expose the workflow to n8n\'s instance-level MCP server), callerPolicy, callerIds.',
5656
properties: {
5757
executionOrder: { type: 'string', enum: ['v0', 'v1'] },
5858
timezone: { type: 'string' },
@@ -61,7 +61,8 @@ export const n8nManagementTools: ToolDefinition[] = [
6161
saveManualExecutions: { type: 'boolean' },
6262
saveExecutionProgress: { type: 'boolean' },
6363
executionTimeout: { type: 'number' },
64-
errorWorkflow: { type: 'string' }
64+
errorWorkflow: { type: 'string' },
65+
availableInMCP: { type: 'boolean', description: 'Expose the workflow to n8n\'s instance-level MCP server (n8n 1.119+)' }
6566
}
6667
},
6768
nodeGroups: {

tests/integration/ai-validation/ai-agent-validation.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ describe('Integration: AI Agent Validation', () => {
317317
});
318318

319319
const memory2 = createMemoryNode({
320+
id: 'memory-2', // the helper defaults to 'memory-1'; n8n 2.36 rejects duplicate node ids
320321
name: 'Memory 2'
321322
});
322323

tests/unit/mcp/handlers-n8n-manager.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,45 @@ describe('handlers-n8n-manager', () => {
308308
expect(n8nValidation.validateWorkflowStructure).toHaveBeenCalledWith(input);
309309
});
310310

311+
it('should forward settings keys outside the typed schema, such as availableInMCP (issue #1026)', async () => {
312+
// Regression: the create schema was a closed z.object, so Zod stripped every settings key
313+
// it did not list before the payload reached the API client. The update path forwards them.
314+
const testWorkflow = createTestWorkflow();
315+
const input = {
316+
name: 'Test Workflow',
317+
nodes: testWorkflow.nodes,
318+
connections: testWorkflow.connections,
319+
settings: {
320+
executionOrder: 'v1',
321+
availableInMCP: true,
322+
callerPolicy: 'workflowsFromSameOwner',
323+
timeSavedPerExecution: 5,
324+
},
325+
};
326+
327+
mockApiClient.createWorkflow.mockResolvedValue(testWorkflow);
328+
329+
const result = await handlers.handleCreateWorkflow(input);
330+
331+
expect(result.success).toBe(true);
332+
const sentWorkflow = mockApiClient.createWorkflow.mock.calls[0][0];
333+
expect(sentWorkflow.settings).toEqual(input.settings);
334+
});
335+
336+
it('should still reject invalid values for the typed settings keys', async () => {
337+
const testWorkflow = createTestWorkflow();
338+
const result = await handlers.handleCreateWorkflow({
339+
name: 'Test Workflow',
340+
nodes: testWorkflow.nodes,
341+
connections: testWorkflow.connections,
342+
settings: { executionOrder: 'v2', availableInMCP: true },
343+
});
344+
345+
expect(result.success).toBe(false);
346+
expect(result.error).toBe('Invalid input');
347+
expect(mockApiClient.createWorkflow).not.toHaveBeenCalled();
348+
});
349+
311350
it('forwards parentFolderId to the create payload (folder placement, n8n 2.32+)', async () => {
312351
const testWorkflow = createTestWorkflow();
313352
const input = {

0 commit comments

Comments
 (0)