Compare commits

..

4 Commits

Author SHA1 Message Date
Romuald Członkowski
70653b16bd Merge pull request #201 from czlonkowski/fix/update-partial-workflow-operation
Summary
Fixed critical bug in n8n_update_partial_workflow where operations were using wrong property name
Changed from changes to updates for consistency with operation naming
Resolves issues where AI agents had to fall back to expensive full workflow updates
Fixes
Resolves update_partial_workflow is invalid #159 - update_partial_workflow is invalid
Resolves Partial Workflow Update returns error #168 - Partial Workflow Update returns error
Changes Made
Updated UpdateNodeOperation interface to use updates instead of changes
Updated UpdateConnectionOperation for consistency
Fixed implementation in workflow-diff-engine.ts
Updated Zod schema validation in handlers-workflow-diff.ts
Fixed documentation and examples
Updated all tests to use new property name
Test Plan
 Build passes (npm run build)
 Tests pass for workflow-diff-engine
 Manually tested with real workflow - updates work correctly
 Verified connections are preserved after updates
Before & After
Before: {type: "updateNode", nodeId: "123", changes: {...}}  Failed
After: {type: "updateNode", nodeId: "123", updates: {...}}  Works
2025-09-17 23:52:56 +02:00
czlonkowski
e6f1d6bcf0 chore: bump version to 2.11.3 and update documentation
- Bump version from 2.11.2 to 2.11.3
- Update README.md version badge
- Add CHANGELOG.md entry documenting the fix for n8n_update_partial_workflow tool
- Fix resolves GitHub issues #159 and #168
2025-09-17 23:46:24 +02:00
czlonkowski
44f92063c3 test: update handlers-workflow-diff tests to use 'updates' property
Fixed remaining test cases that were still using 'changes' instead of 'updates'
for updateNode operations. All tests now pass.
2025-09-17 23:29:17 +02:00
czlonkowski
17530c0f72 fix: use 'updates' property consistently in updateNode operations
- Changed UpdateNodeOperation interface to use 'updates' instead of 'changes'
- Updated UpdateConnectionOperation for consistency
- Fixed implementation in workflow-diff-engine.ts
- Updated Zod schema validation
- Fixed documentation and examples
- Updated tests to match new property name

This resolves GitHub issues #159 and #168 where partial workflow updates
were failing, forcing AI agents to fall back to expensive full updates.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-17 23:22:51 +02:00
10 changed files with 40 additions and 31 deletions

View File

@@ -2,7 +2,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![GitHub stars](https://img.shields.io/github/stars/czlonkowski/n8n-mcp?style=social)](https://github.com/czlonkowski/n8n-mcp)
[![Version](https://img.shields.io/badge/version-2.11.2-blue.svg)](https://github.com/czlonkowski/n8n-mcp)
[![Version](https://img.shields.io/badge/version-2.11.3-blue.svg)](https://github.com/czlonkowski/n8n-mcp)
[![npm version](https://img.shields.io/npm/v/n8n-mcp.svg)](https://www.npmjs.com/package/n8n-mcp)
[![codecov](https://codecov.io/gh/czlonkowski/n8n-mcp/graph/badge.svg?token=YOUR_TOKEN)](https://codecov.io/gh/czlonkowski/n8n-mcp)
[![Tests](https://img.shields.io/badge/tests-1728%20passing-brightgreen.svg)](https://github.com/czlonkowski/n8n-mcp/actions)

View File

@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.11.3] - 2025-09-17
### Fixed
- **n8n_update_partial_workflow Tool**: Fixed critical bug where updateNode and updateConnection operations were using incorrect property name
- Changed from `changes` property to `updates` property to match documentation and expected behavior
- Resolves issue where AI agents would break workflow connections when updating nodes
- Fixes GitHub issues #159 (update_partial_workflow is invalid) and #168 (partial workflow update returns error)
- All related tests updated to use correct property name
## [2.11.2] - 2025-09-16
### Updated

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "n8n-mcp",
"version": "2.11.2",
"version": "2.11.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "n8n-mcp",
"version": "2.11.2",
"version": "2.11.3",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.13.2",

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-mcp",
"version": "2.11.2",
"version": "2.11.3",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js",
"bin": {

View File

@@ -21,7 +21,7 @@ const workflowDiffSchema = z.object({
node: z.any().optional(),
nodeId: z.string().optional(),
nodeName: z.string().optional(),
changes: z.any().optional(),
updates: z.any().optional(),
position: z.tuple([z.number(), z.number()]).optional(),
// Connection operations
source: z.string().optional(),

View File

@@ -48,7 +48,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
},
returns: 'Updated workflow object or validation results if validateOnly=true',
examples: [
'// Update node parameter\nn8n_update_partial_workflow({id: "abc", operations: [{type: "updateNode", nodeName: "HTTP Request", changes: {"parameters.url": "https://api.example.com"}}]})',
'// Update node parameter\nn8n_update_partial_workflow({id: "abc", operations: [{type: "updateNode", nodeName: "HTTP Request", updates: {"parameters.url": "https://api.example.com"}}]})',
'// Add connection between nodes\nn8n_update_partial_workflow({id: "xyz", operations: [{type: "addConnection", source: "Webhook", target: "Slack", sourceOutput: "main", targetInput: "main"}]})',
'// Multiple operations in one call\nn8n_update_partial_workflow({id: "123", operations: [\n {type: "addNode", node: {name: "Transform", type: "n8n-nodes-base.code", position: [400, 300]}},\n {type: "addConnection", source: "Webhook", target: "Transform"},\n {type: "updateSettings", settings: {timezone: "America/New_York"}}\n]})',
'// Validate before applying\nn8n_update_partial_workflow({id: "456", operations: [{type: "removeNode", nodeName: "Old Process"}], validateOnly: true})'
@@ -73,7 +73,7 @@ export const n8nUpdatePartialWorkflowDoc: ToolDocumentation = {
'Operations validated together - all must be valid',
'Order matters for dependent operations (e.g., must add node before connecting to it)',
'Node references accept ID or name, but name must be unique',
'Dot notation for nested updates: use "parameters.url" not nested objects'
'Use "updates" property for updateNode operations: {type: "updateNode", updates: {...}}'
],
relatedTools: ['n8n_update_full_workflow', 'n8n_get_workflow', 'validate_workflow', 'tools_documentation']
}

View File

@@ -453,8 +453,8 @@ export class WorkflowDiffEngine {
const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
if (!node) return;
// Apply changes using dot notation
Object.entries(operation.changes).forEach(([path, value]) => {
// Apply updates using dot notation
Object.entries(operation.updates).forEach(([path, value]) => {
this.setNestedProperty(node, path, value);
});
}
@@ -545,18 +545,18 @@ export class WorkflowDiffEngine {
type: 'removeConnection',
source: operation.source,
target: operation.target,
sourceOutput: operation.changes.sourceOutput,
targetInput: operation.changes.targetInput
sourceOutput: operation.updates.sourceOutput,
targetInput: operation.updates.targetInput
});
this.applyAddConnection(workflow, {
type: 'addConnection',
source: operation.source,
target: operation.target,
sourceOutput: operation.changes.sourceOutput,
targetInput: operation.changes.targetInput,
sourceIndex: operation.changes.sourceIndex,
targetIndex: operation.changes.targetIndex
sourceOutput: operation.updates.sourceOutput,
targetInput: operation.updates.targetInput,
sourceIndex: operation.updates.sourceIndex,
targetIndex: operation.updates.targetIndex
});
}

View File

@@ -31,7 +31,7 @@ export interface UpdateNodeOperation extends DiffOperation {
type: 'updateNode';
nodeId?: string; // Can use either ID or name
nodeName?: string;
changes: {
updates: {
[path: string]: any; // Dot notation paths like 'parameters.url'
};
}
@@ -78,7 +78,7 @@ export interface UpdateConnectionOperation extends DiffOperation {
type: 'updateConnection';
source: string;
target: string;
changes: {
updates: {
sourceOutput?: string;
targetInput?: string;
sourceIndex?: number;

View File

@@ -159,7 +159,7 @@ describe('handlers-workflow-diff', () => {
{
type: 'updateNode',
nodeId: 'node2',
changes: { name: 'Updated HTTP Request' },
updates: { name: 'Updated HTTP Request' },
},
],
validateOnly: true,
@@ -196,7 +196,7 @@ describe('handlers-workflow-diff', () => {
{
type: 'updateNode',
nodeId: 'node1',
changes: { name: 'Updated Start' },
updates: { name: 'Updated Start' },
},
{
type: 'addNode',
@@ -243,7 +243,7 @@ describe('handlers-workflow-diff', () => {
{
type: 'updateNode',
nodeId: 'non-existent-node',
changes: { name: 'Updated' },
updates: { name: 'Updated' },
},
],
};
@@ -320,7 +320,7 @@ describe('handlers-workflow-diff', () => {
const result = await handleUpdatePartialWorkflow({
id: 'test-id',
operations: [{ type: 'updateNode', nodeId: 'node1', changes: {} }],
operations: [{ type: 'updateNode', nodeId: 'node1', updates: {} }],
});
expect(result).toEqual({
@@ -341,7 +341,7 @@ describe('handlers-workflow-diff', () => {
{
// Missing required 'type' field
nodeId: 'node1',
changes: {},
updates: {},
},
],
};
@@ -417,7 +417,7 @@ describe('handlers-workflow-diff', () => {
await handleUpdatePartialWorkflow({
id: 'test-id',
operations: [{ type: 'updateNode', nodeId: 'node1', changes: {} }],
operations: [{ type: 'updateNode', nodeId: 'node1', updates: {} }],
});
expect(logger.debug).toHaveBeenCalledWith(
@@ -502,7 +502,7 @@ describe('handlers-workflow-diff', () => {
type: 'updateNode',
nodeId: 'node1',
nodeName: 'Start', // Both nodeId and nodeName provided
changes: { name: 'New Start' },
updates: { name: 'New Start' },
description: 'Update start node name',
},
{
@@ -561,8 +561,8 @@ describe('handlers-workflow-diff', () => {
const diffRequest = {
id: 'test-workflow-id',
operations: [
{ type: 'updateNode', nodeId: 'node1', changes: { name: 'Updated' } },
{ type: 'updateNode', nodeId: 'invalid-node', changes: { name: 'Fail' } },
{ type: 'updateNode', nodeId: 'node1', updates: { name: 'Updated' } },
{ type: 'updateNode', nodeId: 'invalid-node', updates: { name: 'Fail' } },
{ type: 'addTag', tag: 'test' },
],
};

View File

@@ -281,7 +281,7 @@ describe('WorkflowDiffEngine', () => {
const operation: UpdateNodeOperation = {
type: 'updateNode',
nodeId: 'http-1',
changes: {
updates: {
'parameters.method': 'POST',
'parameters.url': 'https://new-api.example.com'
}
@@ -304,7 +304,7 @@ describe('WorkflowDiffEngine', () => {
const operation: UpdateNodeOperation = {
type: 'updateNode',
nodeName: 'Slack',
changes: {
updates: {
'parameters.resource': 'channel',
'parameters.operation': 'create',
'credentials.slackApi.name': 'New Slack Account'
@@ -329,7 +329,7 @@ describe('WorkflowDiffEngine', () => {
const operation: UpdateNodeOperation = {
type: 'updateNode',
nodeId: 'non-existent',
changes: {
updates: {
'parameters.test': 'value'
}
};
@@ -617,7 +617,7 @@ describe('WorkflowDiffEngine', () => {
type: 'updateConnection',
source: 'IF',
target: 'slack-1',
changes: {
updates: {
sourceOutput: 'false',
sourceIndex: 0,
targetIndex: 0
@@ -1039,7 +1039,7 @@ describe('WorkflowDiffEngine', () => {
const operation: UpdateNodeOperation = {
type: 'updateNode',
nodeId: 'Webhook', // Using name as ID
changes: {
updates: {
'parameters.path': 'new-webhook-path'
}
};