Compare commits

...

3 Commits

Author SHA1 Message Date
Romuald Członkowski
ca20586eda fix: use stdio-wrapper as bin entry and preserve credentials on workflow update (v2.45.1) (#695)
Switch the npm bin entry from index.js to stdio-wrapper.js to prevent
INFO-level logs from corrupting the JSON-RPC stdio transport. Also update
both publish scripts so the fix persists across releases. Fixes #693.

Preserve node credentials during full workflow updates. AI-generated node
updates typically omit credential references, causing the n8n API to reject
the PUT. The update handler now merges credentials from the current server-side
workflow when user-provided nodes lack them. Fixes #689.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:40:17 +02:00
Romuald Członkowski
8888c63e7a chore: update n8n to 2.14.2 and bump version to 2.45.0 (#692)
Update n8n dependencies:
- n8n: 2.13.3 → 2.14.2
- n8n-core: 2.13.1 → 2.14.1
- n8n-workflow: 2.13.1 → 2.14.1
- @n8n/n8n-nodes-langchain: 2.13.1 → 2.14.1

Preserved 584 community nodes and rebuilt FTS5 search index.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 07:38:58 +02:00
Romuald Członkowski
4b109b0870 fix: upgrade GitHub Actions and patch dependency vulnerabilities (v2.44.1) (#690) 2026-04-01 21:17:59 +02:00
17 changed files with 1483 additions and 1096 deletions

View File

@@ -1,10 +1,9 @@
--- ---
name: n8n-mcp-tester name: n8n-mcp-tester
description: Use this agent when you need to test n8n-mcp functionality after implementing fixes or new features. This agent should be invoked proactively after code changes to validate that the MCP tools work correctly with real n8n workflows. The agent tests the actual MCP server tools, not just unit tests. Examples:\n\n<example>\nContext: Developer has just fixed a validation bug in the n8n-mcp server\nuser: "I've fixed the validation issue for HTTP Request nodes"\nassistant: "I've completed the fix. Now let me build the project and then test it."\n<builds project>\nassistant: "Project built successfully. Please reload the MCP server in Claude Desktop to apply the changes."\nuser: "Reloaded"\nassistant: "Great! Now let me invoke the n8n-mcp-tester agent to verify the fix works correctly."\n<commentary>\nSince a fix has been implemented and the MCP server reloaded, use the n8n-mcp-tester agent to validate the fix works in real-world usage.\n</commentary>\n</example>\n\n<example>\nContext: Developer has added a new MCP tool for workflow validation\nuser: "I've implemented the new workflow validation tool"\nassistant: "Implementation complete. Let me build and prepare for testing."\n<builds project>\nassistant: "Build successful. Please reload the MCP server to load the new tool."\nuser: "Done"\nassistant: "Perfect! I'll now use the n8n-mcp-tester agent to test the new workflow validation tool."\n<commentary>\nAfter implementing new MCP functionality and reloading the server, invoke n8n-mcp-tester to verify it works correctly.\n</commentary>\n</example> description: "Use this agent when you need to test n8n-mcp functionality after implementing fixes or new features. This agent should be invoked proactively after code changes to validate that the MCP tools work correctly with real n8n workflows. The agent tests the actual MCP server tools, not just unit tests. Examples:\\n\\n<example>\\nContext: Developer has just fixed a validation bug in the n8n-mcp server\\nuser: \"I've fixed the validation issue for HTTP Request nodes\"\\nassistant: \"I've completed the fix. Now let me build the project and then test it.\"\\n<builds project>\\nassistant: \"Project built successfully. Please reload the MCP server in Claude Desktop to apply the changes.\"\\nuser: \"Reloaded\"\\nassistant: \"Great! Now let me invoke the n8n-mcp-tester agent to verify the fix works correctly.\"\\n<commentary>\\nSince a fix has been implemented and the MCP server reloaded, use the n8n-mcp-tester agent to validate the fix works in real-world usage.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: Developer has added a new MCP tool for workflow validation\\nuser: \"I've implemented the new workflow validation tool\"\\nassistant: \"Implementation complete. Let me build and prepare for testing.\"\\n<builds project>\\nassistant: \"Build successful. Please reload the MCP server to load the new tool.\"\\nuser: \"Done\"\\nassistant: \"Perfect! I'll now use the n8n-mcp-tester agent to test the new workflow validation tool.\"\\n<commentary>\\nAfter implementing new MCP functionality and reloading the server, invoke n8n-mcp-tester to verify it works correctly.\\n</commentary>\\n</example>"
tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, mcp__supabase__create_branch, mcp__supabase__list_branches, mcp__supabase__delete_branch, mcp__supabase__merge_branch, mcp__supabase__reset_branch, mcp__supabase__rebase_branch, mcp__supabase__list_tables, mcp__supabase__list_extensions, mcp__supabase__list_migrations, mcp__supabase__apply_migration, mcp__supabase__execute_sql, mcp__supabase__get_logs, mcp__supabase__get_advisors, mcp__supabase__get_project_url, mcp__supabase__generate_typescript_types, mcp__supabase__search_docs, mcp__supabase__list_edge_functions, mcp__supabase__deploy_edge_function, mcp__n8n-mcp__tools_documentation, mcp__n8n-mcp__search_nodes, mcp__n8n-mcp__get_template, mcp__n8n-mcp__search_templates, mcp__n8n-mcp__validate_workflow, mcp__n8n-mcp__n8n_create_workflow, mcp__n8n-mcp__n8n_get_workflow, mcp__n8n-mcp__n8n_update_full_workflow, mcp__n8n-mcp__n8n_update_partial_workflow, mcp__n8n-mcp__n8n_delete_workflow, mcp__n8n-mcp__n8n_list_workflows, mcp__n8n-mcp__n8n_validate_workflow, mcp__n8n-mcp__n8n_trigger_webhook_workflow, mcp__n8n-mcp__n8n_health_check, mcp__brightdata-mcp__search_engine, mcp__brightdata-mcp__scrape_as_markdown, mcp__brightdata-mcp__search_engine_batch, mcp__brightdata-mcp__scrape_batch, mcp__supabase__get_publishable_keys, mcp__supabase__get_edge_function, mcp__n8n-mcp__get_node, mcp__n8n-mcp__validate_node, mcp__n8n-mcp__n8n_autofix_workflow, mcp__n8n-mcp__n8n_executions, mcp__n8n-mcp__n8n_workflow_versions, mcp__n8n-mcp__n8n_deploy_template, mcp__ide__getDiagnostics, mcp__ide__executeCode tools: "Glob, Grep, Read, WebFetch, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, Bash, mcp__context7__query-docs, mcp__context7__resolve-library-id, mcp__n8n-mcp-testing__get_node, mcp__n8n-mcp-testing__get_template, mcp__n8n-mcp-testing__n8n_autofix_workflow, mcp__n8n-mcp-testing__n8n_create_workflow, mcp__n8n-mcp-testing__n8n_delete_workflow, mcp__n8n-mcp-testing__n8n_deploy_template, mcp__n8n-mcp-testing__n8n_executions, mcp__n8n-mcp-testing__n8n_generate_workflow, mcp__n8n-mcp-testing__n8n_get_workflow, mcp__n8n-mcp-testing__n8n_health_check, mcp__n8n-mcp-testing__n8n_list_workflows, mcp__n8n-mcp-testing__n8n_manage_datatable, mcp__n8n-mcp-testing__n8n_test_workflow, mcp__n8n-mcp-testing__n8n_update_full_workflow, mcp__n8n-mcp-testing__n8n_update_partial_workflow, mcp__n8n-mcp-testing__n8n_validate_workflow, mcp__n8n-mcp-testing__n8n_workflow_versions, mcp__n8n-mcp-testing__search_nodes, mcp__n8n-mcp-testing__search_templates, mcp__n8n-mcp-testing__tools_documentation, mcp__n8n-mcp-testing__validate_node, mcp__n8n-mcp-testing__validate_workflow, mcp__plugin_postgres-best-practices_supabase__authenticate, mcp__supabase-telemetry__apply_migration, mcp__supabase-telemetry__create_branch, mcp__supabase-telemetry__delete_branch, mcp__supabase-telemetry__deploy_edge_function, mcp__supabase-telemetry__execute_sql, mcp__supabase-telemetry__generate_typescript_types, mcp__supabase-telemetry__get_advisors, mcp__supabase-telemetry__get_edge_function, mcp__supabase-telemetry__get_logs, mcp__supabase-telemetry__get_project_url, mcp__supabase-telemetry__get_publishable_keys, mcp__supabase-telemetry__list_branches, mcp__supabase-telemetry__list_edge_functions, mcp__supabase-telemetry__list_extensions, mcp__supabase-telemetry__list_migrations, mcp__supabase-telemetry__list_tables, mcp__supabase-telemetry__merge_branch, mcp__supabase-telemetry__rebase_branch, mcp__supabase-telemetry__reset_branch, mcp__supabase-telemetry__search_docs"
model: sonnet model: sonnet
--- ---
You are n8n-mcp-tester, a specialized testing agent for the n8n Model Context Protocol (MCP) server. You validate that MCP tools and functionality work correctly in real-world scenarios after fixes or new features are implemented. You are n8n-mcp-tester, a specialized testing agent for the n8n Model Context Protocol (MCP) server. You validate that MCP tools and functionality work correctly in real-world scenarios after fixes or new features are implemented.
## Your Core Responsibilities ## Your Core Responsibilities

View File

@@ -36,11 +36,11 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -48,7 +48,7 @@ jobs:
- name: Extract metadata - name: Extract metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: | tags: |
@@ -56,7 +56,7 @@ jobs:
type=raw,value=test-amd64 type=raw,value=test-amd64
- name: Build and push Docker image (AMD64 only) - name: Build and push Docker image (AMD64 only)
uses: docker/build-push-action@v5 uses: docker/build-push-action@v7
with: with:
context: . context: .
platforms: linux/amd64 platforms: linux/amd64

View File

@@ -53,7 +53,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -62,11 +62,11 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -74,7 +74,7 @@ jobs:
- name: Extract metadata - name: Extract metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: | tags: |
@@ -85,7 +85,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v7
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
@@ -109,7 +109,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -167,7 +167,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
with: with:
generate_release_notes: true generate_release_notes: true
body: | body: |

View File

@@ -78,15 +78,15 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -94,7 +94,7 @@ jobs:
- name: Extract metadata - name: Extract metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: | tags: |
@@ -107,7 +107,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v7
with: with:
context: . context: .
no-cache: false no-cache: false
@@ -186,15 +186,15 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -202,7 +202,7 @@ jobs:
- name: Extract metadata for Railway - name: Extract metadata for Railway
id: meta-railway id: meta-railway
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway
tags: | tags: |
@@ -215,7 +215,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Railway Docker image - name: Build and push Railway Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v7
with: with:
context: . context: .
file: ./Dockerfile.railway file: ./Dockerfile.railway

View File

@@ -389,7 +389,7 @@ jobs:
echo "Version: $(node -e "console.log(require('./package.json').version)")" echo "Version: $(node -e "console.log(require('./package.json').version)")"
- name: Publish to NPM with retry - name: Publish to NPM with retry
uses: nick-invision/retry@v2 uses: nick-invision/retry@v4
with: with:
timeout_minutes: 5 timeout_minutes: 5
max_attempts: 3 max_attempts: 3
@@ -448,13 +448,13 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -462,7 +462,7 @@ jobs:
- name: Extract metadata for standard image - name: Extract metadata for standard image
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: | tags: |
@@ -472,7 +472,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push standard Docker image - name: Build and push standard Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v7
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
@@ -553,7 +553,7 @@ jobs:
- name: Extract metadata for Railway image - name: Extract metadata for Railway image
id: meta-railway id: meta-railway
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway
tags: | tags: |
@@ -563,7 +563,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Railway Docker image - name: Build and push Railway Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v7
with: with:
context: . context: .
file: ./Dockerfile.railway file: ./Dockerfile.railway

View File

@@ -116,7 +116,7 @@ jobs:
# Upload coverage to Codecov # Upload coverage to Codecov
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
if: always() if: always()
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info files: ./coverage/lcov.info
@@ -250,7 +250,7 @@ jobs:
path: artifacts path: artifacts
- name: Publish test results - name: Publish test results
uses: dorny/test-reporter@v1 uses: dorny/test-reporter@v3
if: always() if: always()
continue-on-error: true continue-on-error: true
with: with:

View File

@@ -129,7 +129,7 @@ jobs:
- name: Create Pull Request - name: Create Pull Request
if: steps.branch.outputs.branch_name != '' if: steps.branch.outputs.branch_name != ''
uses: peter-evans/create-pull-request@v5 uses: peter-evans/create-pull-request@v8
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ steps.branch.outputs.branch_name }} branch: ${{ steps.branch.outputs.branch_name }}

View File

@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [2.45.1] - 2026-04-02
### Fixed
- **Use stdio-wrapper.js as default bin entry point** — the previous entry point (`index.js`) wrote INFO-level logs to stdout, corrupting JSON-RPC MCP transport for stdio-mode users (Fixes #693, Related: #555, #628)
- **Preserve node credentials during full workflow updates** — `n8n_update_full_workflow` now carries forward existing credential references from the server when user-provided nodes omit them, preventing "missing credentials" errors on PUT (Fixes #689)
### Changed
- **Updated publish scripts** to use `stdio-wrapper.js` as the npm bin entry point, ensuring the fix persists across releases
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
## [2.45.0] - 2026-04-01
### Changed
- **Update n8n dependencies** to latest versions:
- `n8n`: 2.13.3 → 2.14.2
- `n8n-core`: 2.13.1 → 2.14.1
- `n8n-workflow`: 2.13.1 → 2.14.1
- `@n8n/n8n-nodes-langchain`: 2.13.1 → 2.14.1
- **Rebuild FTS5 search index** with all 1396 nodes (812 base + 584 community)
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
## [2.44.1] - 2026-04-01
### Security
- **Bump axios** from `^1.11.0` to `^1.14.0` to patch known vulnerability
- **Bump nodemon** from `^3.1.10` to `^3.1.14` to patch transitive dependency vulnerabilities
### Changed
- **Upgrade GitHub Actions** to latest versions across all CI/CD workflows (docker, release, test, update-n8n-deps) — contributed by @salmanmkc in #663
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
## [2.44.0] - 2026-04-01 ## [2.44.0] - 2026-04-01
### Added ### Added

View File

@@ -5,7 +5,7 @@
[![npm version](https://img.shields.io/npm/v/n8n-mcp.svg)](https://www.npmjs.com/package/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) [![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-3336%20passing-brightgreen.svg)](https://github.com/czlonkowski/n8n-mcp/actions) [![Tests](https://img.shields.io/badge/tests-3336%20passing-brightgreen.svg)](https://github.com/czlonkowski/n8n-mcp/actions)
[![n8n version](https://img.shields.io/badge/n8n-2.13.3-orange.svg)](https://github.com/n8n-io/n8n) [![n8n version](https://img.shields.io/badge/n8n-2.14.2-orange.svg)](https://github.com/n8n-io/n8n)
[![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fczlonkowski%2Fn8n--mcp-green.svg)](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp) [![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fczlonkowski%2Fn8n--mcp-green.svg)](https://github.com/czlonkowski/n8n-mcp/pkgs/container/n8n-mcp)
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp) [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)

Binary file not shown.

2281
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "n8n-mcp", "name": "n8n-mcp",
"version": "2.44.0", "version": "2.45.1",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
} }
}, },
"bin": { "bin": {
"n8n-mcp": "./dist/mcp/index.js" "n8n-mcp": "./dist/mcp/stdio-wrapper.js"
}, },
"scripts": { "scripts": {
"build": "tsc -p tsconfig.build.json", "build": "tsc -p tsconfig.build.json",
@@ -140,27 +140,27 @@
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.2.4",
"@vitest/runner": "^3.2.4", "@vitest/runner": "^3.2.4",
"@vitest/ui": "^3.2.4", "@vitest/ui": "^3.2.4",
"axios": "^1.11.0", "axios": "^1.14.0",
"axios-mock-adapter": "^2.1.0", "axios-mock-adapter": "^2.1.0",
"fishery": "^2.3.1", "fishery": "^2.3.1",
"msw": "^2.10.4", "msw": "^2.10.4",
"nodemon": "^3.1.10", "nodemon": "^3.1.14",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vitest": "^3.2.4" "vitest": "^3.2.4"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "1.28.0", "@modelcontextprotocol/sdk": "1.28.0",
"@n8n/n8n-nodes-langchain": "^2.13.1", "@n8n/n8n-nodes-langchain": "^2.14.1",
"@supabase/supabase-js": "^2.57.4", "@supabase/supabase-js": "^2.57.4",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"express": "^5.1.0", "express": "^5.1.0",
"express-rate-limit": "^7.1.5", "express-rate-limit": "^7.1.5",
"form-data": "^4.0.5", "form-data": "^4.0.5",
"lru-cache": "^11.2.1", "lru-cache": "^11.2.1",
"n8n": "^2.13.3", "n8n": "^2.14.2",
"n8n-core": "^2.13.1", "n8n-core": "^2.14.1",
"n8n-workflow": "^2.13.1", "n8n-workflow": "^2.14.1",
"openai": "^4.77.0", "openai": "^4.77.0",
"sql.js": "^1.13.0", "sql.js": "^1.13.0",
"tslib": "^2.6.2", "tslib": "^2.6.2",

View File

@@ -1,6 +1,6 @@
{ {
"name": "n8n-mcp-runtime", "name": "n8n-mcp-runtime",
"version": "2.33.2", "version": "2.45.1",
"description": "n8n MCP Server Runtime Dependencies Only", "description": "n8n MCP Server Runtime Dependencies Only",
"private": true, "private": true,
"dependencies": { "dependencies": {

View File

@@ -35,7 +35,7 @@ node -e "
const pkg = require('./package.json'); const pkg = require('./package.json');
pkg.name = 'n8n-mcp'; pkg.name = 'n8n-mcp';
pkg.description = 'Integration between n8n workflow automation and Model Context Protocol (MCP)'; pkg.description = 'Integration between n8n workflow automation and Model Context Protocol (MCP)';
pkg.bin = { 'n8n-mcp': './dist/mcp/index.js' }; pkg.bin = { 'n8n-mcp': './dist/mcp/stdio-wrapper.js' };
pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' }; pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' };
pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation']; pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation'];
pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en'; pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en';

View File

@@ -68,7 +68,7 @@ pkg.exports = {
import: './dist/index.js' import: './dist/index.js'
} }
}; };
pkg.bin = { 'n8n-mcp': './dist/mcp/index.js' }; pkg.bin = { 'n8n-mcp': './dist/mcp/stdio-wrapper.js' };
pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' }; pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' };
pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation']; pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation'];
pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en'; pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en';

View File

@@ -776,6 +776,28 @@ export async function handleUpdateWorkflow(
const current = await client.getWorkflow(id); const current = await client.getWorkflow(id);
workflowBefore = JSON.parse(JSON.stringify(current)); workflowBefore = JSON.parse(JSON.stringify(current));
// Preserve credentials from current workflow for nodes that don't specify them.
// AI-generated node updates typically omit credential references because they
// aren't included in the context provided to the AI. Without this merge, the
// n8n API rejects the PUT with missing credentials.
if (updateData.nodes && current.nodes) {
const currentById = new Map<string, any>();
const currentByName = new Map<string, any>();
for (const node of current.nodes) {
if (node.id) currentById.set(node.id, node);
currentByName.set(node.name, node);
}
for (const node of updateData.nodes as any[]) {
const hasCredentials = node.credentials && typeof node.credentials === 'object' && Object.keys(node.credentials).length > 0;
if (!hasCredentials) {
const match = (node.id && currentById.get(node.id)) || currentByName.get(node.name);
if (match?.credentials) {
node.credentials = match.credentials;
}
}
}
}
// Create backup before modifying workflow (default: true) // Create backup before modifying workflow (default: true)
if (createBackup !== false) { if (createBackup !== false) {
try { try {

View File

@@ -16,6 +16,12 @@ import { ExecutionStatus } from '@/types/n8n-api';
vi.mock('@/services/n8n-api-client'); vi.mock('@/services/n8n-api-client');
vi.mock('@/services/workflow-validator'); vi.mock('@/services/workflow-validator');
vi.mock('@/database/node-repository'); vi.mock('@/database/node-repository');
vi.mock('@/services/workflow-versioning-service', () => ({
WorkflowVersioningService: vi.fn().mockImplementation(() => ({
createBackup: vi.fn().mockResolvedValue({ versionId: 'v1', versionNumber: 1, pruned: 0 }),
getVersions: vi.fn().mockResolvedValue([]),
})),
}));
vi.mock('@/config/n8n-api', () => ({ vi.mock('@/config/n8n-api', () => ({
getN8nApiConfig: vi.fn() getN8nApiConfig: vi.fn()
})); }));
@@ -1343,4 +1349,142 @@ describe('handlers-n8n-manager', () => {
expect(result.error).toMatch(/mode:\s*'preview'/); expect(result.error).toMatch(/mode:\s*'preview'/);
}); });
}); });
describe('handleUpdateWorkflow - credential preservation', () => {
function mockCurrentWorkflow(nodes: any[]): void {
const workflow = createTestWorkflow({ id: 'wf-1', active: false, nodes });
mockApiClient.getWorkflow.mockResolvedValue(workflow);
mockApiClient.updateWorkflow.mockResolvedValue({ ...workflow, updatedAt: '2024-01-02' });
}
function getSentNodes(): any[] {
return mockApiClient.updateWorkflow.mock.calls[0][1].nodes;
}
it('should preserve credentials from current workflow when update nodes omit them', async () => {
mockCurrentWorkflow([
{
id: 'node-1', name: 'Postgres', type: 'n8n-nodes-base.postgres',
typeVersion: 2, position: [100, 100],
parameters: { operation: 'executeQuery', query: 'SELECT 1' },
credentials: { postgresApi: { id: 'cred-123', name: 'My Postgres' } },
},
{
id: 'node-2', name: 'HTTP Request', type: 'n8n-nodes-base.httpRequest',
typeVersion: 4, position: [300, 100],
parameters: { url: 'https://example.com' },
credentials: { httpBasicAuth: { id: 'cred-456', name: 'Basic Auth' } },
},
{
id: 'node-3', name: 'Set', type: 'n8n-nodes-base.set',
typeVersion: 3, position: [500, 100], parameters: {},
},
]);
await handlers.handleUpdateWorkflow(
{
id: 'wf-1',
nodes: [
{
id: 'node-1', name: 'Postgres', type: 'n8n-nodes-base.postgres',
typeVersion: 2, position: [100, 100],
parameters: { operation: 'executeQuery', query: 'SELECT * FROM users' },
},
{
id: 'node-2', name: 'HTTP Request', type: 'n8n-nodes-base.httpRequest',
typeVersion: 4, position: [300, 100],
parameters: { url: 'https://example.com/v2' },
},
{
id: 'node-3', name: 'Set', type: 'n8n-nodes-base.set',
typeVersion: 3, position: [500, 100], parameters: { mode: 'manual' },
},
],
connections: {},
},
mockRepository,
);
const sentNodes = getSentNodes();
expect(sentNodes[0].credentials).toEqual({ postgresApi: { id: 'cred-123', name: 'My Postgres' } });
expect(sentNodes[1].credentials).toEqual({ httpBasicAuth: { id: 'cred-456', name: 'Basic Auth' } });
expect(sentNodes[2].credentials).toBeUndefined();
});
it('should not overwrite user-provided credentials', async () => {
mockCurrentWorkflow([
{
id: 'node-1', name: 'Postgres', type: 'n8n-nodes-base.postgres',
typeVersion: 2, position: [100, 100], parameters: {},
credentials: { postgresApi: { id: 'cred-old', name: 'Old Postgres' } },
},
]);
await handlers.handleUpdateWorkflow(
{
id: 'wf-1',
nodes: [
{
id: 'node-1', name: 'Postgres', type: 'n8n-nodes-base.postgres',
typeVersion: 2, position: [100, 100], parameters: {},
credentials: { postgresApi: { id: 'cred-new', name: 'New Postgres' } },
},
],
connections: {},
},
mockRepository,
);
const sentNodes = getSentNodes();
expect(sentNodes[0].credentials).toEqual({ postgresApi: { id: 'cred-new', name: 'New Postgres' } });
});
it('should match nodes by name when ids differ', async () => {
mockCurrentWorkflow([
{
id: 'server-id-1', name: 'Gmail', type: 'n8n-nodes-base.gmail',
typeVersion: 2, position: [100, 100], parameters: {},
credentials: { gmailOAuth2: { id: 'cred-gmail', name: 'Gmail' } },
},
]);
await handlers.handleUpdateWorkflow(
{
id: 'wf-1',
nodes: [
{
id: 'client-id-different', name: 'Gmail', type: 'n8n-nodes-base.gmail',
typeVersion: 2, position: [100, 100],
parameters: { resource: 'message' },
},
],
connections: {},
},
mockRepository,
);
const sentNodes = getSentNodes();
expect(sentNodes[0].credentials).toEqual({ gmailOAuth2: { id: 'cred-gmail', name: 'Gmail' } });
});
it('should treat empty credentials object as missing and carry forward', async () => {
mockCurrentWorkflow([
{ id: 'node-1', name: 'Postgres', type: 'n8n-nodes-base.postgres', typeVersion: 2, position: [100, 100], parameters: {}, credentials: { postgresApi: { id: 'cred-123', name: 'My Postgres' } } },
]);
await handlers.handleUpdateWorkflow(
{
id: 'wf-1',
nodes: [
{ id: 'node-1', name: 'Postgres', type: 'n8n-nodes-base.postgres', typeVersion: 2, position: [100, 100], parameters: {}, credentials: {} },
],
connections: {},
},
mockRepository,
);
const sentNodes = getSentNodes();
expect(sentNodes[0].credentials).toEqual({ postgresApi: { id: 'cred-123', name: 'My Postgres' } });
});
});
}); });