Compare commits

...

3 Commits

Author SHA1 Message Date
Romuald Członkowski
bac4936c6d fix: add n8n 1.121 availableInMCP and callerPolicy settings support (v2.26.4) (#445)
* fix: add n8n 1.121 availableInMCP and callerPolicy settings support (v2.26.4)

n8n 1.121 introduced a new workflow setting `availableInMCP` (boolean)
that controls whether a workflow is "Available in MCP". The sanitization
whitelist was missing this field, causing it to be silently stripped
during workflow updates.

Changes:
- Added `availableInMCP` to Zod schema in workflowSettingsSchema
- Added `availableInMCP` and `callerPolicy` to safeSettingsProperties whitelist
- Both settings are now preserved during workflow updates
- Settings can be toggled via updateSettings operation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Conceived by Romuald Członkowski - www.aiadvisors.pl/en

* test: update tests for callerPolicy and availableInMCP whitelist changes

Updated 5 tests in n8n-validation.test.ts that expected callerPolicy
to be filtered out. Since callerPolicy and availableInMCP are now
whitelisted (n8n 1.121+), the tests now verify these settings are
preserved during workflow updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-26 20:17:34 +01:00
Romuald Członkowski
25784142fe fix: address tools documentation gaps and outdated references (v2.26.3) (#443) 2025-11-26 00:57:15 +01:00
Romuald Członkowski
f770043d3d Revise quick start section in README.md
Removed quick start instructions and example JSON configuration for n8n-MCP.
2025-11-25 21:31:56 +01:00
16 changed files with 280 additions and 71 deletions

View File

@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.26.4] - 2025-11-26
### 🔧 Fixed
- **n8n 1.121 Compatibility**: Added support for new workflow settings introduced in n8n 1.121
- Added `availableInMCP` (boolean) to settings whitelist - controls "Available in MCP" toggle
- Added `callerPolicy` to settings whitelist - was already in schema but missing from sanitization
- Both settings are now preserved during workflow updates instead of being silently stripped
- Settings can be toggled via `updateSettings` operation: `{type: "updateSettings", settings: {availableInMCP: true}}`
**Conceived by Romuald Członkowski - [AiAdvisors](https://www.aiadvisors.pl/en)**
## [2.26.3] - 2025-11-26
### 🔧 Fixed
- **Tools Documentation Gaps**: Addressed remaining documentation issues after v2.26.2 tool consolidation
- Added missing `n8n_workflow_versions` documentation with all 6 modes (list, get, rollback, delete, prune, truncate)
- Removed non-existent tools (`n8n_diagnostic`, `n8n_list_available_tools`) from documentation exports
- Fixed 10+ outdated tool name references:
- `get_node_essentials``get_node({detail: "standard"})`
- `validate_node_operation``validate_node()`
- `get_minimal``n8n_get_workflow({mode: "minimal"})`
- Added missing `mode` and `verbose` parameters to `n8n_health_check` documentation
- Added missing `mode` parameter to `get_template` documentation (nodes_only, structure, full)
- Updated template count from "399+" to "2,700+" in `get_template`
- Updated node count from "525" to "500+" in `search_nodes`
- Fixed `relatedTools` arrays to remove references to non-existent tools
**Conceived by Romuald Członkowski - [AiAdvisors](https://www.aiadvisors.pl/en)**
## [2.26.2] - 2025-11-25
### 🔧 Fixed

View File

@@ -36,10 +36,6 @@ AI results can be unpredictable. Protect your work!
## 🚀 Quick Start
Get n8n-MCP running in minutes:
[![n8n-mcp Video Quickstart Guide](./thumbnail.png)](https://youtu.be/5CccjiLLyaY?si=Z62SBGlw9G34IQnQ&t=343)
### Option 1: Hosted Service (Easiest - No Setup!) ☁️
**The fastest way to try n8n-MCP** - no installation, no configuration:
@@ -51,21 +47,7 @@ Get n8n-MCP running in minutes:
-**Always up-to-date**: Latest n8n nodes and templates
-**No infrastructure**: We handle everything
Just sign up, get your API key, and add to Claude Desktop:
```json
{
"mcpServers": {
"n8n-mcp": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-remote@latest", "https://mcp.n8n-mcp.com/sse"],
"env": {
"API_KEY": "your-api-key-from-dashboard"
}
}
}
}
```
Just sign up, get your API key, and connect your MCP client.
---
@@ -75,6 +57,10 @@ Prefer to run n8n-MCP yourself? Choose your deployment method:
### Option A: npx (Quick Local Setup) 🚀
Get n8n-MCP running in minutes:
[![n8n-mcp Video Quickstart Guide](./thumbnail.png)](https://youtu.be/5CccjiLLyaY?si=Z62SBGlw9G34IQnQ&t=343)
**Prerequisites:** [Node.js](https://nodejs.org/) installed on your system
```bash

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-mcp",
"version": "2.26.2",
"version": "2.26.4",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

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

View File

@@ -4,7 +4,7 @@ export const searchNodesDoc: ToolDocumentation = {
name: 'search_nodes',
category: 'discovery',
essentials: {
description: 'Text search across node names and descriptions. Returns most relevant nodes first, with frequently-used nodes (HTTP Request, Webhook, Set, Code, Slack) prioritized in results. Searches all 525 nodes in the database.',
description: 'Text search across node names and descriptions. Returns most relevant nodes first, with frequently-used nodes (HTTP Request, Webhook, Set, Code, Slack) prioritized in results. Searches all 500+ nodes in the database.',
keyParameters: ['query', 'mode', 'limit'],
example: 'search_nodes({query: "webhook"})',
performance: '<20ms even for complex queries',
@@ -42,7 +42,7 @@ export const searchNodesDoc: ToolDocumentation = {
'Start with single keywords for broadest results',
'Use FUZZY mode when users might misspell node names',
'AND mode works best for 2-3 word searches',
'Combine with get_node_essentials after finding the right node'
'Combine with get_node after finding the right node'
],
pitfalls: [
'AND mode searches all fields (name, description) not just node names',

View File

@@ -7,9 +7,7 @@ import { validateNodeDoc, validateWorkflowDoc } from './validation';
import { getTemplateDoc, searchTemplatesDoc } from './templates';
import {
toolsDocumentationDoc,
n8nDiagnosticDoc,
n8nHealthCheckDoc,
n8nListAvailableToolsDoc
n8nHealthCheckDoc
} from './system';
import { aiAgentsGuide } from './guides';
import {
@@ -22,7 +20,8 @@ import {
n8nValidateWorkflowDoc,
n8nAutofixWorkflowDoc,
n8nTriggerWebhookWorkflowDoc,
n8nExecutionsDoc
n8nExecutionsDoc,
n8nWorkflowVersionsDoc
} from './workflow_management';
// Combine all tool documentations into a single object
@@ -30,9 +29,7 @@ import {
export const toolsDocumentation: Record<string, ToolDocumentation> = {
// System tools
tools_documentation: toolsDocumentationDoc,
n8n_diagnostic: n8nDiagnosticDoc,
n8n_health_check: n8nHealthCheckDoc,
n8n_list_available_tools: n8nListAvailableToolsDoc,
// Guides
ai_agents_guide: aiAgentsGuide,
@@ -61,7 +58,8 @@ export const toolsDocumentation: Record<string, ToolDocumentation> = {
n8n_validate_workflow: n8nValidateWorkflowDoc,
n8n_autofix_workflow: n8nAutofixWorkflowDoc,
n8n_trigger_webhook_workflow: n8nTriggerWebhookWorkflowDoc,
n8n_executions: n8nExecutionsDoc // Actions replace: n8n_get_execution, n8n_list_executions, n8n_delete_execution
n8n_executions: n8nExecutionsDoc, // Actions replace: n8n_get_execution, n8n_list_executions, n8n_delete_execution
n8n_workflow_versions: n8nWorkflowVersionsDoc // Modes: list, get, rollback, delete, prune, truncate
};
// Re-export types

View File

@@ -1,4 +1,2 @@
export { toolsDocumentationDoc } from './tools-documentation';
export { n8nDiagnosticDoc } from './n8n-diagnostic';
export { n8nHealthCheckDoc } from './n8n-health-check';
export { n8nListAvailableToolsDoc } from './n8n-list-available-tools';
export { n8nHealthCheckDoc } from './n8n-health-check';

View File

@@ -5,8 +5,8 @@ export const n8nHealthCheckDoc: ToolDocumentation = {
category: 'system',
essentials: {
description: 'Check n8n instance health, API connectivity, version status, and performance metrics',
keyParameters: [],
example: 'n8n_health_check({})',
keyParameters: ['mode', 'verbose'],
example: 'n8n_health_check({mode: "status"})',
performance: 'Fast - single API call (~150-200ms median)',
tips: [
'Use before starting workflow operations to ensure n8n is responsive',
@@ -31,7 +31,21 @@ Health checks are crucial for:
- Detecting performance degradation
- Verifying API compatibility before operations
- Ensuring authentication is working correctly`,
parameters: {},
parameters: {
mode: {
type: 'string',
required: false,
description: 'Operation mode: "status" (default) for quick health check, "diagnostic" for detailed debug info including env vars and tool status',
default: 'status',
enum: ['status', 'diagnostic']
},
verbose: {
type: 'boolean',
required: false,
description: 'Include extra details in diagnostic mode',
default: false
}
},
returns: `Health status object containing:
- status: Overall health status ('healthy', 'degraded', 'error')
- n8nVersion: n8n instance version information
@@ -81,6 +95,6 @@ Health checks are crucial for:
'Does not check individual workflow health',
'Health endpoint might be cached - not real-time for all metrics'
],
relatedTools: ['n8n_diagnostic', 'n8n_list_available_tools', 'n8n_list_workflows']
relatedTools: ['n8n_list_workflows', 'n8n_validate_workflow', 'n8n_workflow_versions']
}
};

View File

@@ -4,23 +4,30 @@ export const getTemplateDoc: ToolDocumentation = {
name: 'get_template',
category: 'templates',
essentials: {
description: 'Get complete workflow JSON by ID. Ready to import. IDs from search_templates.',
keyParameters: ['templateId'],
example: 'get_template({templateId: 1234})',
description: 'Get workflow template by ID with configurable detail level. Ready to import. IDs from search_templates.',
keyParameters: ['templateId', 'mode'],
example: 'get_template({templateId: 1234, mode: "full"})',
performance: 'Fast (<100ms) - single database lookup',
tips: [
'Get template IDs from search_templates first',
'Returns complete workflow JSON ready for import into n8n',
'Includes all nodes, connections, and settings'
'Use mode="nodes_only" for quick overview, "structure" for topology, "full" for import',
'Returns complete workflow JSON ready for import into n8n'
]
},
full: {
description: `Retrieves the complete workflow JSON for a specific template by its ID. The returned workflow can be directly imported into n8n through the UI or API. This tool fetches pre-built workflows from the community template library containing 399+ curated workflows.`,
description: `Retrieves the complete workflow JSON for a specific template by its ID. The returned workflow can be directly imported into n8n through the UI or API. This tool fetches pre-built workflows from the community template library containing 2,700+ curated workflows.`,
parameters: {
templateId: {
type: 'number',
required: true,
description: 'The numeric ID of the template to retrieve. Get IDs from search_templates'
},
mode: {
type: 'string',
required: false,
description: 'Response detail level: "nodes_only" (minimal - just node list), "structure" (nodes + connections), "full" (complete workflow JSON, default)',
default: 'full',
enum: ['nodes_only', 'structure', 'full']
}
},
returns: `Returns an object containing:
@@ -39,9 +46,10 @@ export const getTemplateDoc: ToolDocumentation = {
- settings: Workflow configuration (timezone, error handling, etc.)
- usage: Instructions for using the workflow`,
examples: [
'get_template({templateId: 1234}) - Get Slack notification workflow',
'get_template({templateId: 5678}) - Get data sync workflow',
'get_template({templateId: 9012}) - Get AI chatbot workflow'
'get_template({templateId: 1234}) - Get complete workflow (default mode="full")',
'get_template({templateId: 1234, mode: "nodes_only"}) - Get just the node list',
'get_template({templateId: 1234, mode: "structure"}) - Get nodes and connections',
'get_template({templateId: 5678, mode: "full"}) - Get complete workflow JSON for import'
],
useCases: [
'Download workflows for direct import into n8n',

View File

@@ -8,3 +8,4 @@ export { n8nValidateWorkflowDoc } from './n8n-validate-workflow';
export { n8nAutofixWorkflowDoc } from './n8n-autofix-workflow';
export { n8nTriggerWebhookWorkflowDoc } from './n8n-trigger-webhook-workflow';
export { n8nExecutionsDoc } from './n8n-executions';
export { n8nWorkflowVersionsDoc } from './n8n-workflow-versions';

View File

@@ -11,7 +11,7 @@ export const n8nDeleteWorkflowDoc: ToolDocumentation = {
tips: [
'Action is irreversible',
'Deletes all execution history',
'Check workflow first with get_minimal'
'Check workflow first with n8n_get_workflow({mode: "minimal"})'
]
},
full: {
@@ -34,7 +34,7 @@ export const n8nDeleteWorkflowDoc: ToolDocumentation = {
performance: 'Fast operation - typically 50-150ms. May take longer if workflow has extensive execution history.',
bestPractices: [
'Always confirm before deletion',
'Check workflow with get_minimal first',
'Check workflow with n8n_get_workflow({mode: "minimal"}) first',
'Consider deactivating instead of deleting',
'Export workflow before deletion for backup'
],

View File

@@ -0,0 +1,168 @@
import { ToolDocumentation } from '../types';
export const n8nWorkflowVersionsDoc: ToolDocumentation = {
name: 'n8n_workflow_versions',
category: 'workflow_management',
essentials: {
description: 'Manage workflow version history, rollback to previous versions, and cleanup old versions',
keyParameters: ['mode', 'workflowId', 'versionId'],
example: 'n8n_workflow_versions({mode: "list", workflowId: "abc123"})',
performance: 'Fast for list/get (~100ms), moderate for rollback (~200-500ms)',
tips: [
'Use mode="list" to see all saved versions before rollback',
'Rollback creates a backup version automatically',
'Use prune to clean up old versions and save storage',
'truncate requires explicit confirmTruncate: true'
]
},
full: {
description: `Comprehensive workflow version management system. Supports six operations:
**list** - Show version history for a workflow
- Returns all saved versions with timestamps, snapshot sizes, and metadata
- Use limit parameter to control how many versions to return
**get** - Get details of a specific version
- Returns the complete workflow snapshot from that version
- Use to compare versions or extract old configurations
**rollback** - Restore workflow to a previous version
- Creates a backup of the current workflow before rollback
- Optionally validates the workflow structure before applying
- Returns the restored workflow and backup version ID
**delete** - Delete specific version(s)
- Delete a single version by versionId
- Delete all versions for a workflow with deleteAll: true
**prune** - Clean up old versions
- Keeps only the N most recent versions (default: 10)
- Useful for managing storage and keeping history manageable
**truncate** - Delete ALL versions for ALL workflows
- Dangerous operation requiring explicit confirmation
- Use for complete version history cleanup`,
parameters: {
mode: {
type: 'string',
required: true,
description: 'Operation mode: "list", "get", "rollback", "delete", "prune", or "truncate"',
enum: ['list', 'get', 'rollback', 'delete', 'prune', 'truncate']
},
workflowId: {
type: 'string',
required: false,
description: 'Workflow ID (required for list, rollback, delete, prune modes)'
},
versionId: {
type: 'number',
required: false,
description: 'Version ID (required for get mode, optional for rollback to specific version, required for single delete)'
},
limit: {
type: 'number',
required: false,
default: 10,
description: 'Maximum versions to return in list mode'
},
validateBefore: {
type: 'boolean',
required: false,
default: true,
description: 'Validate workflow structure before rollback (rollback mode only)'
},
deleteAll: {
type: 'boolean',
required: false,
default: false,
description: 'Delete all versions for workflow (delete mode only)'
},
maxVersions: {
type: 'number',
required: false,
default: 10,
description: 'Keep N most recent versions (prune mode only)'
},
confirmTruncate: {
type: 'boolean',
required: false,
default: false,
description: 'REQUIRED: Must be true to truncate all versions (truncate mode only)'
}
},
returns: `Response varies by mode:
**list mode:**
- versions: Array of version objects with id, workflowId, snapshotSize, createdAt
- totalCount: Total number of versions
**get mode:**
- version: Complete version object including workflow snapshot
**rollback mode:**
- success: Boolean indicating success
- restoredVersion: The version that was restored
- backupVersionId: ID of the backup created before rollback
**delete mode:**
- deletedCount: Number of versions deleted
**prune mode:**
- prunedCount: Number of old versions removed
- remainingCount: Number of versions kept
**truncate mode:**
- deletedCount: Total versions deleted across all workflows`,
examples: [
'// List version history\nn8n_workflow_versions({mode: "list", workflowId: "abc123", limit: 5})',
'// Get specific version details\nn8n_workflow_versions({mode: "get", versionId: 42})',
'// Rollback to latest saved version\nn8n_workflow_versions({mode: "rollback", workflowId: "abc123"})',
'// Rollback to specific version\nn8n_workflow_versions({mode: "rollback", workflowId: "abc123", versionId: 42})',
'// Delete specific version\nn8n_workflow_versions({mode: "delete", workflowId: "abc123", versionId: 42})',
'// Delete all versions for workflow\nn8n_workflow_versions({mode: "delete", workflowId: "abc123", deleteAll: true})',
'// Prune to keep only 5 most recent\nn8n_workflow_versions({mode: "prune", workflowId: "abc123", maxVersions: 5})',
'// Truncate all versions (dangerous!)\nn8n_workflow_versions({mode: "truncate", confirmTruncate: true})'
],
useCases: [
'Recover from accidental workflow changes',
'Compare workflow versions to understand changes',
'Maintain audit trail of workflow modifications',
'Clean up old versions to save database storage',
'Roll back failed workflow deployments'
],
performance: `Performance varies by operation:
- list: Fast (~100ms) - simple database query
- get: Fast (~100ms) - single row retrieval
- rollback: Moderate (~200-500ms) - includes backup creation and workflow update
- delete: Fast (~50-100ms) - database delete operation
- prune: Moderate (~100-300ms) - depends on number of versions to delete
- truncate: Slow (1-5s) - deletes all records across all workflows`,
modeComparison: `| Mode | Required Params | Optional Params | Risk Level |
|------|-----------------|-----------------|------------|
| list | workflowId | limit | Low |
| get | versionId | - | Low |
| rollback | workflowId | versionId, validateBefore | Medium |
| delete | workflowId | versionId, deleteAll | High |
| prune | workflowId | maxVersions | Medium |
| truncate | confirmTruncate=true | - | Critical |`,
bestPractices: [
'Always list versions before rollback to pick the right one',
'Enable validateBefore for rollback to catch structural issues',
'Use prune regularly to keep version history manageable',
'Never use truncate in production without explicit need',
'Document why you are rolling back for audit purposes'
],
pitfalls: [
'Rollback overwrites current workflow - backup is created automatically',
'Deleted versions cannot be recovered',
'Truncate affects ALL workflows - use with extreme caution',
'Version IDs are sequential but may have gaps after deletes',
'Large workflows may have significant version storage overhead'
],
relatedTools: [
'n8n_get_workflow - View current workflow state',
'n8n_update_partial_workflow - Make incremental changes',
'n8n_validate_workflow - Validate before deployment'
]
}
};

View File

@@ -422,8 +422,8 @@ try {
5. Use descriptive variable names
## Related Tools
- get_node_essentials("nodes-base.code")
- validate_node_operation()
- get_node({nodeType: "nodes-base.code"}) - Get Code node configuration details
- validate_node({nodeType: "nodes-base.code", config: {...}}) - Validate Code node setup
- python_code_node_guide (for Python syntax)`;
}
@@ -691,7 +691,7 @@ except json.JSONDecodeError:
\`\`\`
## Related Tools
- get_node_essentials("nodes-base.code")
- validate_node_operation()
- get_node({nodeType: "nodes-base.code"}) - Get Code node configuration details
- validate_node({nodeType: "nodes-base.code", config: {...}}) - Validate Code node setup
- javascript_code_node_guide (for JavaScript syntax)`;
}

View File

@@ -62,6 +62,7 @@ export const workflowSettingsSchema = z.object({
executionTimeout: z.number().optional(),
errorWorkflow: z.string().optional(),
callerPolicy: z.enum(['any', 'workflowsFromSameOwner', 'workflowsFromAList']).optional(),
availableInMCP: z.boolean().optional(),
});
// Default settings for workflow creation
@@ -181,7 +182,9 @@ export function cleanWorkflowForUpdate(workflow: Workflow): Partial<Workflow> {
'executionTimeout',
'errorWorkflow',
'timezone',
'executionOrder'
'executionOrder',
'callerPolicy',
'availableInMCP',
];
if (cleanedWorkflow.settings && typeof cleanedWorkflow.settings === 'object') {

View File

@@ -403,45 +403,47 @@ describe('n8n-validation', () => {
settings: {
executionOrder: 'v1' as const,
saveDataSuccessExecution: 'none' as const,
callerPolicy: 'workflowsFromSameOwner' as const, // Filtered out (not in OpenAPI spec)
callerPolicy: 'workflowsFromSameOwner' as const, // Now whitelisted (n8n 1.121+)
timeSavedPerExecution: 5, // Filtered out (UI-only property)
},
} as any;
const cleaned = cleanWorkflowForUpdate(workflow);
// Unsafe properties filtered out, safe properties kept
// Unsafe properties filtered out, safe properties kept (callerPolicy now whitelisted)
expect(cleaned.settings).toEqual({
executionOrder: 'v1',
saveDataSuccessExecution: 'none'
saveDataSuccessExecution: 'none',
callerPolicy: 'workflowsFromSameOwner'
});
expect(cleaned.settings).not.toHaveProperty('callerPolicy');
expect(cleaned.settings).not.toHaveProperty('timeSavedPerExecution');
});
it('should filter out callerPolicy (Issue #248 - API limitation)', () => {
it('should preserve callerPolicy and availableInMCP (n8n 1.121+ settings)', () => {
const workflow = {
name: 'Test Workflow',
nodes: [],
connections: {},
settings: {
executionOrder: 'v1' as const,
callerPolicy: 'workflowsFromSameOwner' as const, // Filtered out
callerPolicy: 'workflowsFromSameOwner' as const, // Now whitelisted
availableInMCP: true, // New in n8n 1.121
errorWorkflow: 'N2O2nZy3aUiBRGFN',
},
} as any;
const cleaned = cleanWorkflowForUpdate(workflow);
// callerPolicy filtered out (causes API errors), safe properties kept
// callerPolicy and availableInMCP now whitelisted (n8n 1.121+)
expect(cleaned.settings).toEqual({
executionOrder: 'v1',
callerPolicy: 'workflowsFromSameOwner',
availableInMCP: true,
errorWorkflow: 'N2O2nZy3aUiBRGFN'
});
expect(cleaned.settings).not.toHaveProperty('callerPolicy');
});
it('should filter all settings properties correctly (Issue #248 - API design)', () => {
it('should preserve all whitelisted settings properties including callerPolicy (Issue #248 - updated for n8n 1.121)', () => {
const workflow = {
name: 'Test Workflow',
nodes: [],
@@ -455,14 +457,14 @@ describe('n8n-validation', () => {
saveExecutionProgress: false,
executionTimeout: 300,
errorWorkflow: 'error-workflow-id',
callerPolicy: 'workflowsFromAList' as const, // Filtered out (not in OpenAPI spec)
callerPolicy: 'workflowsFromAList' as const, // Now whitelisted (n8n 1.121+)
availableInMCP: false, // New in n8n 1.121
},
} as any;
const cleaned = cleanWorkflowForUpdate(workflow);
// Safe properties kept, unsafe properties filtered out
// See: https://community.n8n.io/t/api-workflow-update-endpoint-doesnt-support-setting-callerpolicy/161916
// All whitelisted properties kept including callerPolicy and availableInMCP
expect(cleaned.settings).toEqual({
executionOrder: 'v0',
timezone: 'UTC',
@@ -471,9 +473,10 @@ describe('n8n-validation', () => {
saveManualExecutions: false,
saveExecutionProgress: false,
executionTimeout: 300,
errorWorkflow: 'error-workflow-id'
errorWorkflow: 'error-workflow-id',
callerPolicy: 'workflowsFromAList',
availableInMCP: false
});
expect(cleaned.settings).not.toHaveProperty('callerPolicy');
});
it('should handle workflows without settings gracefully', () => {
@@ -494,7 +497,6 @@ describe('n8n-validation', () => {
nodes: [],
connections: {},
settings: {
callerPolicy: 'workflowsFromSameOwner' as const, // Filtered out
timeSavedPerExecution: 5, // Filtered out (UI-only)
someOtherProperty: 'value', // Filtered out
},
@@ -514,19 +516,19 @@ describe('n8n-validation', () => {
connections: {},
settings: {
executionOrder: 'v1' as const, // Whitelisted
callerPolicy: 'workflowsFromSameOwner' as const, // Filtered out
callerPolicy: 'workflowsFromSameOwner' as const, // Now whitelisted (n8n 1.121+)
timezone: 'America/New_York', // Whitelisted
someOtherProperty: 'value', // Filtered out
},
} as any;
const cleaned = cleanWorkflowForUpdate(workflow);
// Should keep only whitelisted properties
// Should keep only whitelisted properties (callerPolicy now whitelisted)
expect(cleaned.settings).toEqual({
executionOrder: 'v1',
callerPolicy: 'workflowsFromSameOwner',
timezone: 'America/New_York'
});
expect(cleaned.settings).not.toHaveProperty('callerPolicy');
expect(cleaned.settings).not.toHaveProperty('someOtherProperty');
});
});