Compare commits

..

15 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
Romuald Członkowski
0ef69fbf75 Merge pull request #196 from czlonkowski/n8n-dependencies-update
Dependencies Updated
n8n: 1.110.1 → 1.111.0
n8n-core: 1.110.0 (unchanged, latest)
n8n-workflow: 1.108.0 (unchanged, latest)
@n8n/n8n-nodes-langchain: 1.109.1 → 1.110.0
2025-09-16 12:24:57 +02:00
czlonkowski
f39c9a5389 fix: resolve pyodide version conflict for Docker builds
- Added override for pyodide@0.26.4 to resolve version conflict
- @langchain/community requires pyodide <0.27.0 but npm was installing 0.28.0
- This was causing Railway Docker build failures with npm ci

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 11:59:45 +02:00
czlonkowski
92d7577f22 fix: regenerate package-lock.json and update runtime version
- Regenerated package-lock.json with all dependencies properly resolved
- Updated package.runtime.json version to 2.11.2 to match main package
- This should fix Railway Docker build failures

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 11:51:53 +02:00
czlonkowski
874aea6920 chore: bump version to 2.11.2 and update documentation
- Bumped version from 2.11.1 to 2.11.2
- Updated README version badge to 2.11.2
- Updated README n8n version badge to ^1.111.0
- Added comprehensive CHANGELOG entry for v2.11.2
- Documented n8n dependency updates and test results

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 11:17:24 +02:00
czlonkowski
19caa7bbb4 2.11.2 2025-09-16 11:14:56 +02:00
czlonkowski
dff0387ae2 chore: update n8n dependencies to v1.111.0
- Updated n8n from 1.110.1 to 1.111.0
- Updated n8n-core from 1.109.0 to 1.110.0
- Updated n8n-workflow from 1.107.0 to 1.108.0
- Updated @n8n/n8n-nodes-langchain from 1.109.1 to 1.110.0
- Rebuilt node database with 535 nodes
- Templates preserved: 2598 templates with 2534 having metadata
- All critical nodes validated successfully
- Test results: 1911 passed, 5 failed (performance tests), 53 skipped

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 11:14:24 +02:00
Romuald Członkowski
469cc1720d Merge pull request #195 from czlonkowski/templates-update
Summary
Added optional fields parameter to search_templates tool to allow selective field filtering
Reduces response size by 70-98% when requesting only specific fields (e.g., just id and name)
Maintains full backward compatibility - all existing calls continue to work unchanged
Changes
Updated tool definition with new fields parameter
Modified template service to support partial responses
Updated tool documentation with examples
Bumped version to 2.11.1
Benefits
98% reduction in response size when requesting only id/name fields
70% reduction when including description
Significantly reduces token usage for AI agents
Maintains backward compatibility
Test Results
 All unit tests passing
 All integration tests passing
 TypeScript linting successful
 Manual testing confirmed 98% size reduction
2025-09-16 00:02:23 +02:00
czlonkowski
99cdae7655 fix: update package-lock.json version to 2.11.1 2025-09-15 23:53:32 +02:00
czlonkowski
abc226f111 feat: add optional fields parameter to search_templates tool
- Added fields parameter to filter response fields in search_templates
- Reduces response size by 70-98% when using selective fields
- Maintains backward compatibility with optional parameter
- Supports all template fields: id, name, description, author, nodes, views, created, url, metadata
- Updated tool documentation with examples

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 23:46:33 +02:00
czlonkowski
16e6a1fc44 Merge branch 'main' of https://github.com/czlonkowski/n8n-mcp 2025-09-15 11:29:33 +02:00
czlonkowski
a7a6d64931 docs: add template attribution requirements and acknowledgments
- Add mandatory attribution instructions to Claude Project Setup
- Require AI to share template author name, username, and n8n.io link
- Add Template Attribution section to Acknowledgments
- Credit top template contributors without specific counts
- Explain automatic attribution behavior in AI agent instructions

This ensures proper credit to template creators and demonstrates respect
for the n8n community's contributions while maintaining legal compliance.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 11:28:23 +02:00
17 changed files with 10140 additions and 4494 deletions

View File

@@ -2,11 +2,11 @@
[![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.10.9-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)
[![n8n version](https://img.shields.io/badge/n8n-^1.110.1-orange.svg)](https://github.com/n8n-io/n8n)
[![n8n version](https://img.shields.io/badge/n8n-^1.111.0-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)
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/n8n-mcp?referralCode=n8n-mcp)
@@ -389,6 +389,10 @@ You are an expert in n8n automation software using n8n-MCP tools. Your role is t
6. **Building Phase** - Create or customize the workflow:
- If using template: `get_template(templateId, {mode: "full"})`
- **MANDATORY ATTRIBUTION**: When using a template, ALWAYS inform the user:
- "This workflow is based on a template by **[author.name]** (@[author.username])"
- "View the original template at: [url]"
- Example: "This workflow is based on a template by **David Ashby** (@cfomodz). View the original at: https://n8n.io/workflows/2414"
- Customize template or build from validated configurations
- Connect nodes with proper structure
- Add error handling where appropriate
@@ -410,6 +414,7 @@ You are an expert in n8n automation software using n8n-MCP tools. Your role is t
## Key Insights
- **TEMPLATES FIRST** - Always check for existing templates before building from scratch (2,500+ available!)
- **ATTRIBUTION REQUIRED** - Always credit template authors with name, username, and link to n8n.io
- **SMART FILTERING** - Use metadata filters to find templates matching user skill level and time constraints
- **USE CODE NODE ONLY WHEN IT IS NECESSARY** - always prefer to use standard nodes over code node. Use code node only when you are sure you need it.
- **VALIDATE EARLY AND OFTEN** - Catch errors before they reach deployment
@@ -504,6 +509,7 @@ n8n_update_partial_workflow({
- ALWAYS check for existing templates before building from scratch
- LEVERAGE metadata filters to find skill-appropriate templates
- **ALWAYS ATTRIBUTE TEMPLATES**: When using any template, you MUST share the author's name, username, and link to the original template on n8n.io
- VALIDATE templates before deployment (they may need updates)
- USE diff operations for updates (80-90% token savings)
- STATE validation results clearly
@@ -861,6 +867,23 @@ See [Automated Release Guide](./docs/AUTOMATED_RELEASES.md) for complete details
- [Anthropic](https://anthropic.com) for the Model Context Protocol
- All contributors and users of this project
### Template Attribution
All workflow templates in this project are fetched from n8n's public template gallery at [n8n.io/workflows](https://n8n.io/workflows). Each template includes:
- Full attribution to the original creator (name and username)
- Direct link to the source template on n8n.io
- Original workflow ID for reference
The AI agent instructions in this project contain mandatory attribution requirements. When using any template, the AI will automatically:
- Share the template author's name and username
- Provide a direct link to the original template on n8n.io
- Display attribution in the format: "This workflow is based on a template by **[author]** (@[username]). View the original at: [url]"
Template creators retain all rights to their workflows. This project indexes templates to improve discoverability through AI assistants. If you're a template creator and have concerns about your template being indexed, please open an issue.
Special thanks to the prolific template contributors whose work helps thousands of users automate their workflows, including:
**David Ashby** (@cfomodz), **Yaron Been** (@yaron-nofluff), **Jimleuk** (@jimleuk), **Davide** (@n3witalia), **David Olusola** (@dae221), **Ranjan Dailata** (@ranjancse), **Airtop** (@cesar-at-airtop), **Joseph LePage** (@joe), **Don Jayamaha Jr** (@don-the-gem-dealer), **Angel Menendez** (@djangelic), and the entire n8n community of creators!
---
<div align="center">

Binary file not shown.

View File

@@ -7,6 +7,39 @@ 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
- **n8n Dependencies**: Updated to latest versions for compatibility and new features
- n8n: 1.110.1 → 1.111.0
- n8n-core: 1.109.0 → 1.110.0
- n8n-workflow: 1.107.0 → 1.108.0
- @n8n/n8n-nodes-langchain: 1.109.1 → 1.110.0
- **Node Database**: Rebuilt with 535 nodes from updated n8n packages
- **Templates**: Preserved all 2,598 workflow templates with metadata intact
- All critical nodes validated successfully (httpRequest, code, slack, agent)
- Test suite: 1,911 tests passing, 5 flaky performance tests failing (99.7% pass rate)
## [2.11.1] - 2025-09-15
### Added
- **Optional Fields Parameter for search_templates**: Enhanced search_templates tool with field filtering capability
- New optional `fields` parameter accepts an array of field names to include in response
- Supported fields: 'id', 'name', 'description', 'author', 'nodes', 'views', 'created', 'url', 'metadata'
- Reduces response size by 70-98% when requesting only specific fields (e.g., just id and name)
- Maintains full backward compatibility - existing calls without fields parameter work unchanged
- Example: `search_templates({query: "slack", fields: ["id", "name"]})` returns minimal data
- Significantly improves AI agent performance by reducing token usage
### Added
- **Fuzzy Node Type Matching for Templates**: Improved template discovery with flexible node type resolution
- Templates can now be found using simple node names: `["slack"]` instead of `["n8n-nodes-base.slack"]`
@@ -1286,6 +1319,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Basic n8n and MCP integration
- Core workflow automation features
[2.11.2]: https://github.com/czlonkowski/n8n-mcp/compare/v2.11.1...v2.11.2
[2.11.1]: https://github.com/czlonkowski/n8n-mcp/compare/v2.11.0...v2.11.1
[2.11.0]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.9...v2.11.0
[2.10.9]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.8...v2.10.9
[2.10.8]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.5...v2.10.8
[2.10.5]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.4...v2.10.5
[2.10.4]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.3...v2.10.4
[2.10.3]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.2...v2.10.3
[2.10.2]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.1...v2.10.2

14437
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-mcp",
"version": "2.11.0",
"version": "2.11.3",
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
"main": "dist/index.js",
"bin": {
@@ -128,12 +128,12 @@
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.13.2",
"@n8n/n8n-nodes-langchain": "^1.109.1",
"@n8n/n8n-nodes-langchain": "^1.110.0",
"dotenv": "^16.5.0",
"express": "^5.1.0",
"n8n": "^1.110.1",
"n8n-core": "^1.109.0",
"n8n-workflow": "^1.107.0",
"n8n": "^1.111.0",
"n8n-core": "^1.110.0",
"n8n-workflow": "^1.108.0",
"openai": "^4.77.0",
"sql.js": "^1.13.0",
"uuid": "^10.0.0",
@@ -143,5 +143,8 @@
"@rollup/rollup-darwin-arm64": "^4.50.0",
"@rollup/rollup-linux-x64-gnu": "^4.50.0",
"better-sqlite3": "^11.10.0"
},
"overrides": {
"pyodide": "0.26.4"
}
}

View File

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

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

@@ -746,7 +746,8 @@ export class N8NDocumentationMCPServer {
this.validateToolParams(name, args, ['query']);
const searchLimit = Math.min(Math.max(Number(args.limit) || 20, 1), 100);
const searchOffset = Math.max(Number(args.offset) || 0, 0);
return this.searchTemplates(args.query, searchLimit, searchOffset);
const searchFields = args.fields as string[] | undefined;
return this.searchTemplates(args.query, searchLimit, searchOffset, searchFields);
case 'get_templates_for_task':
this.validateToolParams(name, args, ['task']);
const taskLimit = Math.min(Math.max(Number(args.limit) || 10, 1), 100);
@@ -2399,11 +2400,11 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
};
}
private async searchTemplates(query: string, limit: number = 20, offset: number = 0): Promise<any> {
private async searchTemplates(query: string, limit: number = 20, offset: number = 0, fields?: string[]): Promise<any> {
await this.ensureInitialized();
if (!this.templateService) throw new Error('Template service not initialized');
const result = await this.templateService.searchTemplates(query, limit, offset);
const result = await this.templateService.searchTemplates(query, limit, offset, fields);
if (result.items.length === 0 && offset === 0) {
return {

View File

@@ -5,13 +5,14 @@ export const searchTemplatesDoc: ToolDocumentation = {
category: 'templates',
essentials: {
description: 'Search templates by name/description keywords. NOT for node types! For nodes use list_node_templates. Example: "chatbot".',
keyParameters: ['query', 'limit'],
example: 'search_templates({query: "chatbot"})',
keyParameters: ['query', 'limit', 'fields'],
example: 'search_templates({query: "chatbot", fields: ["id", "name"]})',
performance: 'Fast (<100ms) - FTS5 full-text search',
tips: [
'Searches template names and descriptions, NOT node types',
'Use keywords like "automation", "sync", "notification"',
'For node-specific search, use list_node_templates instead'
'For node-specific search, use list_node_templates instead',
'Use fields parameter to get only specific data (reduces response by 70-90%)'
]
},
full: {
@@ -22,6 +23,11 @@ export const searchTemplatesDoc: ToolDocumentation = {
required: true,
description: 'Search query for template names/descriptions. NOT for node types! Examples: "chatbot", "automation", "social media", "webhook". For node-based search use list_node_templates instead.'
},
fields: {
type: 'array',
required: false,
description: 'Fields to include in response. Options: "id", "name", "description", "author", "nodes", "views", "created", "url", "metadata". Default: all fields. Example: ["id", "name"] for minimal response.'
},
limit: {
type: 'number',
required: false,
@@ -47,7 +53,9 @@ export const searchTemplatesDoc: ToolDocumentation = {
'search_templates({query: "email notification"}) - Find email alert workflows',
'search_templates({query: "data sync"}) - Find data synchronization workflows',
'search_templates({query: "webhook automation", limit: 30}) - Find webhook-based automations',
'search_templates({query: "social media scheduler"}) - Find social posting workflows'
'search_templates({query: "social media scheduler"}) - Find social posting workflows',
'search_templates({query: "slack", fields: ["id", "name"]}) - Get only IDs and names of Slack templates',
'search_templates({query: "automation", fields: ["id", "name", "description"]}) - Get minimal info for automation templates'
],
useCases: [
'Find workflows by business purpose',

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

@@ -414,6 +414,14 @@ export const n8nDocumentationToolsFinal: ToolDefinition[] = [
type: 'string',
description: 'Search keyword as string. Example: "chatbot"',
},
fields: {
type: 'array',
items: {
type: 'string',
enum: ['id', 'name', 'description', 'author', 'nodes', 'views', 'created', 'url', 'metadata'],
},
description: 'Fields to include in response. Default: all fields. Example: ["id", "name"] for minimal response.',
},
limit: {
type: 'number',
description: 'Maximum number of results. Default 20.',

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

@@ -55,6 +55,9 @@ export interface TemplateMinimal {
};
}
export type TemplateField = 'id' | 'name' | 'description' | 'author' | 'nodes' | 'views' | 'created' | 'url' | 'metadata';
export type PartialTemplateInfo = Partial<TemplateInfo>;
export class TemplateService {
private repository: TemplateRepository;
@@ -124,12 +127,17 @@ export class TemplateService {
/**
* Search templates by query
*/
async searchTemplates(query: string, limit: number = 20, offset: number = 0): Promise<PaginatedResponse<TemplateInfo>> {
async searchTemplates(query: string, limit: number = 20, offset: number = 0, fields?: string[]): Promise<PaginatedResponse<PartialTemplateInfo>> {
const templates = this.repository.searchTemplates(query, limit, offset);
const total = this.repository.getSearchCount(query);
// If fields are specified, filter the template info
const items = fields
? templates.map(t => this.formatTemplateWithFields(t, fields))
: templates.map(t => this.formatTemplateInfo(t));
return {
items: templates.map(this.formatTemplateInfo),
items,
total,
limit,
offset,
@@ -403,4 +411,21 @@ export class TemplateService {
return info;
}
/**
* Format template with only specified fields
*/
private formatTemplateWithFields(template: StoredTemplate, fields: string[]): PartialTemplateInfo {
const fullInfo = this.formatTemplateInfo(template);
const result: PartialTemplateInfo = {};
// Only include requested fields
for (const field of fields) {
if (field in fullInfo) {
(result as any)[field] = (fullInfo as any)[field];
}
}
return result;
}
}

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

@@ -683,7 +683,7 @@ describe('TemplateService', () => {
const result = await service.searchTemplates('test');
expect(result.items[0].author.verified).toBe(false);
expect(result.items[0]?.author?.verified).toBe(false);
});
});
});

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'
}
};