@iflow-mcp/jimmy974-n8n-workflow-builder 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/56d11557bccd4af1a295124e12a471e3_process.log +4 -0
- package/Dockerfile +36 -0
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/dist/index.js +161 -0
- package/package.json +18 -0
- package/push_info.json +1 -0
- package/smithery.yaml +13 -0
- package/src/index.js +163 -0
- package/src/index.ts +229 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
[2026-01-12 13:21:25] [SUCCESS] 步骤1:获取项目完成 - Fork并克隆项目成功
|
|
2
|
+
[2026-01-12 13:21:45] [SUCCESS] 步骤2:阅读代码完成 - Node.js项目,MCP服务端项目,使用@modelcontextprotocol/sdk,支持stdio协议
|
|
3
|
+
[2026-01-12 13:22:23] [SUCCESS] 步骤3:本地测试完成 - 修改配置、构建、本地测试通过,发现1个工具:create_workflow
|
|
4
|
+
[2026-01-12 13:22:40] [SUCCESS] 步骤4:创建并推送iflow分支完成 - 已推送到https://github.com/iflow-mcp/jimmy974-n8n-workflow-builder
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
|
|
2
|
+
# Use Node.js as the base image
|
|
3
|
+
FROM node:20-alpine AS builder
|
|
4
|
+
|
|
5
|
+
# Set the working directory in the container
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy the package files and install dependencies
|
|
9
|
+
COPY package.json package-lock.json ./
|
|
10
|
+
RUN npm install
|
|
11
|
+
|
|
12
|
+
# Copy the source code
|
|
13
|
+
COPY src ./src
|
|
14
|
+
COPY tsconfig.json ./
|
|
15
|
+
|
|
16
|
+
# Compile the TypeScript code
|
|
17
|
+
RUN npx tsc
|
|
18
|
+
|
|
19
|
+
# Use a slim Node.js image for the final build
|
|
20
|
+
FROM node:20-slim
|
|
21
|
+
|
|
22
|
+
# Set the working directory in the container
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
|
|
25
|
+
# Copy compiled code from the builder stage
|
|
26
|
+
COPY --from=builder /app/dist ./dist
|
|
27
|
+
|
|
28
|
+
# Install only production dependencies
|
|
29
|
+
COPY package.json package-lock.json ./
|
|
30
|
+
RUN npm install --omit=dev
|
|
31
|
+
|
|
32
|
+
# Expose any necessary ports (assuming 3000 as an example)
|
|
33
|
+
EXPOSE 3000
|
|
34
|
+
|
|
35
|
+
# Command to run the application
|
|
36
|
+
CMD ["node", "dist/index.js"]
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Makafeli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# n8n Workflow Builder MCP Server
|
|
2
|
+
[](https://smithery.ai/server/n8n-workflow-builder)
|
|
3
|
+
|
|
4
|
+
A Model Context Protocol (MCP) server for programmatically creating and managing n8n workflows.
|
|
5
|
+
|
|
6
|
+
<a href="https://glama.ai/mcp/servers/fhoynrlnpp"><img width="380" height="200" src="https://glama.ai/mcp/servers/fhoynrlnpp/badge" alt="n8n Workflow Builder Server MCP server" /></a>
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
- Create workflows with nodes and connections
|
|
10
|
+
- Validate workflow specifications
|
|
11
|
+
- Export complete workflow configurations
|
|
12
|
+
- REST API interface through MCP
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Installing via Smithery
|
|
17
|
+
|
|
18
|
+
To install n8n Workflow Builder for Claude Desktop automatically via [Smithery](https://smithery.ai/server/n8n-workflow-builder):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx -y @smithery/cli install n8n-workflow-builder --client claude
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Manual Installation
|
|
25
|
+
1. Clone the repository:
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/[your-username]/n8n-workflow-builder.git
|
|
28
|
+
cd n8n-workflow-builder
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. Install dependencies:
|
|
32
|
+
```bash
|
|
33
|
+
npm install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
3. Compile TypeScript:
|
|
37
|
+
```bash
|
|
38
|
+
npx tsc
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
4. Start the server:
|
|
42
|
+
```bash
|
|
43
|
+
npm start
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
The server provides a `create_workflow` tool that accepts a workflow specification:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"nodes": [
|
|
53
|
+
{
|
|
54
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
55
|
+
"name": "HTTP Request",
|
|
56
|
+
"parameters": {
|
|
57
|
+
"url": "https://example.com",
|
|
58
|
+
"method": "GET"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"connections": []
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
Add the server to your MCP configuration:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"n8n-workflow-builder": {
|
|
73
|
+
"command": "node",
|
|
74
|
+
"args": ["/path/to/n8n-workflow-builder/dist/index.js"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
12
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
14
|
+
class N8NWorkflowBuilder {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.nodes = [];
|
|
17
|
+
this.connections = [];
|
|
18
|
+
this.nextPosition = { x: 100, y: 100 };
|
|
19
|
+
}
|
|
20
|
+
addNode(nodeType, name, parameters) {
|
|
21
|
+
const node = {
|
|
22
|
+
type: nodeType,
|
|
23
|
+
name: name,
|
|
24
|
+
parameters: parameters,
|
|
25
|
+
position: Object.assign({}, this.nextPosition)
|
|
26
|
+
};
|
|
27
|
+
this.nodes.push(node);
|
|
28
|
+
this.nextPosition.x += 200;
|
|
29
|
+
return name;
|
|
30
|
+
}
|
|
31
|
+
connectNodes(source, target, sourceOutput = 0, targetInput = 0) {
|
|
32
|
+
this.connections.push({
|
|
33
|
+
source_node: source,
|
|
34
|
+
target_node: target,
|
|
35
|
+
source_output: sourceOutput,
|
|
36
|
+
target_input: targetInput
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
exportWorkflow() {
|
|
40
|
+
const workflow = {
|
|
41
|
+
nodes: this.nodes,
|
|
42
|
+
connections: { main: [] }
|
|
43
|
+
};
|
|
44
|
+
for (const conn of this.connections) {
|
|
45
|
+
const connection = {
|
|
46
|
+
node: conn.target_node,
|
|
47
|
+
type: 'main',
|
|
48
|
+
index: conn.target_input,
|
|
49
|
+
sourceNode: conn.source_node,
|
|
50
|
+
sourceIndex: conn.source_output
|
|
51
|
+
};
|
|
52
|
+
workflow.connections.main.push(connection);
|
|
53
|
+
}
|
|
54
|
+
return workflow;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
class N8NWorkflowServer {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.server = new Server({
|
|
60
|
+
name: 'n8n-workflow-builder',
|
|
61
|
+
version: '0.1.0'
|
|
62
|
+
}, {
|
|
63
|
+
capabilities: {
|
|
64
|
+
resources: {},
|
|
65
|
+
tools: {}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
this.setupToolHandlers();
|
|
69
|
+
this.server.onerror = (error) => console.error('[MCP Error]', error);
|
|
70
|
+
}
|
|
71
|
+
setupToolHandlers() {
|
|
72
|
+
this.server.setRequestHandler(ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
return ({
|
|
74
|
+
tools: [{
|
|
75
|
+
name: 'create_workflow',
|
|
76
|
+
description: 'Create and configure n8n workflows programmatically',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
nodes: {
|
|
81
|
+
type: 'array',
|
|
82
|
+
items: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
type: { type: 'string' },
|
|
86
|
+
name: { type: 'string' },
|
|
87
|
+
parameters: { type: 'object' }
|
|
88
|
+
},
|
|
89
|
+
required: ['type', 'name']
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
connections: {
|
|
93
|
+
type: 'array',
|
|
94
|
+
items: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
source: { type: 'string' },
|
|
98
|
+
target: { type: 'string' },
|
|
99
|
+
sourceOutput: { type: 'number', default: 0 },
|
|
100
|
+
targetInput: { type: 'number', default: 0 }
|
|
101
|
+
},
|
|
102
|
+
required: ['source', 'target']
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
required: ['nodes']
|
|
107
|
+
}
|
|
108
|
+
}]
|
|
109
|
+
});
|
|
110
|
+
}));
|
|
111
|
+
this.server.setRequestHandler(CallToolRequestSchema, (request) => __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
if (request.params.name !== 'create_workflow') {
|
|
113
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const builder = new N8NWorkflowBuilder();
|
|
117
|
+
function isWorkflowSpec(obj) {
|
|
118
|
+
return obj &&
|
|
119
|
+
typeof obj === 'object' &&
|
|
120
|
+
Array.isArray(obj.nodes) &&
|
|
121
|
+
obj.nodes.every((node) => typeof node === 'object' &&
|
|
122
|
+
typeof node.type === 'string' &&
|
|
123
|
+
typeof node.name === 'string') &&
|
|
124
|
+
(!obj.connections || (Array.isArray(obj.connections) &&
|
|
125
|
+
obj.connections.every((conn) => typeof conn === 'object' &&
|
|
126
|
+
typeof conn.source === 'string' &&
|
|
127
|
+
typeof conn.target === 'string')));
|
|
128
|
+
}
|
|
129
|
+
const args = request.params.arguments;
|
|
130
|
+
if (!isWorkflowSpec(args)) {
|
|
131
|
+
throw new McpError(ErrorCode.InvalidParams, 'Invalid workflow specification: must include nodes array with type and name properties');
|
|
132
|
+
}
|
|
133
|
+
const { nodes, connections } = args;
|
|
134
|
+
for (const node of nodes) {
|
|
135
|
+
builder.addNode(node.type, node.name, node.parameters || {});
|
|
136
|
+
}
|
|
137
|
+
for (const conn of connections || []) {
|
|
138
|
+
builder.connectNodes(conn.source, conn.target, conn.sourceOutput, conn.targetInput);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
content: [{
|
|
142
|
+
type: 'text',
|
|
143
|
+
text: JSON.stringify(builder.exportWorkflow(), null, 2)
|
|
144
|
+
}]
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new McpError(ErrorCode.InternalError, `Workflow creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
run() {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
const transport = new StdioServerTransport();
|
|
155
|
+
yield this.server.connect(transport);
|
|
156
|
+
console.error('N8N Workflow Builder MCP server running on stdio');
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const server = new N8NWorkflowServer();
|
|
161
|
+
server.run().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iflow-mcp/jimmy974-n8n-workflow-builder",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "node dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"bin": {
|
|
9
|
+
"n8n-workflow-builder": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.0.0",
|
|
16
|
+
"@types/node": "^20.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/push_info.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"push_platform":"github","fork_url":"https://github.com/iflow-mcp/jimmy974-n8n-workflow-builder","fork_branch":"iflow"}
|
package/smithery.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
|
|
2
|
+
|
|
3
|
+
startCommand:
|
|
4
|
+
type: stdio
|
|
5
|
+
configSchema:
|
|
6
|
+
# JSON Schema defining the configuration options for the MCP.
|
|
7
|
+
type: object
|
|
8
|
+
required: []
|
|
9
|
+
properties: {}
|
|
10
|
+
commandFunction:
|
|
11
|
+
# A function that produces the CLI command to start the MCP on stdio.
|
|
12
|
+
|-
|
|
13
|
+
(config) => ({command: 'node', args: ['dist/index.js']})
|
package/src/index.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
14
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
15
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
16
|
+
class N8NWorkflowBuilder {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.nodes = [];
|
|
19
|
+
this.connections = [];
|
|
20
|
+
this.nextPosition = { x: 100, y: 100 };
|
|
21
|
+
}
|
|
22
|
+
addNode(nodeType, name, parameters) {
|
|
23
|
+
const node = {
|
|
24
|
+
type: nodeType,
|
|
25
|
+
name: name,
|
|
26
|
+
parameters: parameters,
|
|
27
|
+
position: Object.assign({}, this.nextPosition)
|
|
28
|
+
};
|
|
29
|
+
this.nodes.push(node);
|
|
30
|
+
this.nextPosition.x += 200;
|
|
31
|
+
return name;
|
|
32
|
+
}
|
|
33
|
+
connectNodes(source, target, sourceOutput = 0, targetInput = 0) {
|
|
34
|
+
this.connections.push({
|
|
35
|
+
source_node: source,
|
|
36
|
+
target_node: target,
|
|
37
|
+
source_output: sourceOutput,
|
|
38
|
+
target_input: targetInput
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
exportWorkflow() {
|
|
42
|
+
const workflow = {
|
|
43
|
+
nodes: this.nodes,
|
|
44
|
+
connections: { main: [] }
|
|
45
|
+
};
|
|
46
|
+
for (const conn of this.connections) {
|
|
47
|
+
const connection = {
|
|
48
|
+
node: conn.target_node,
|
|
49
|
+
type: 'main',
|
|
50
|
+
index: conn.target_input,
|
|
51
|
+
sourceNode: conn.source_node,
|
|
52
|
+
sourceIndex: conn.source_output
|
|
53
|
+
};
|
|
54
|
+
workflow.connections.main.push(connection);
|
|
55
|
+
}
|
|
56
|
+
return workflow;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
class N8NWorkflowServer {
|
|
60
|
+
constructor() {
|
|
61
|
+
this.server = new index_js_1.Server({
|
|
62
|
+
name: 'n8n-workflow-builder',
|
|
63
|
+
version: '0.1.0'
|
|
64
|
+
}, {
|
|
65
|
+
capabilities: {
|
|
66
|
+
resources: {},
|
|
67
|
+
tools: {}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.setupToolHandlers();
|
|
71
|
+
this.server.onerror = (error) => console.error('[MCP Error]', error);
|
|
72
|
+
}
|
|
73
|
+
setupToolHandlers() {
|
|
74
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
return ({
|
|
76
|
+
tools: [{
|
|
77
|
+
name: 'create_workflow',
|
|
78
|
+
description: 'Create and configure n8n workflows programmatically',
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
nodes: {
|
|
83
|
+
type: 'array',
|
|
84
|
+
items: {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
type: { type: 'string' },
|
|
88
|
+
name: { type: 'string' },
|
|
89
|
+
parameters: { type: 'object' }
|
|
90
|
+
},
|
|
91
|
+
required: ['type', 'name']
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
connections: {
|
|
95
|
+
type: 'array',
|
|
96
|
+
items: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
source: { type: 'string' },
|
|
100
|
+
target: { type: 'string' },
|
|
101
|
+
sourceOutput: { type: 'number', default: 0 },
|
|
102
|
+
targetInput: { type: 'number', default: 0 }
|
|
103
|
+
},
|
|
104
|
+
required: ['source', 'target']
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
required: ['nodes']
|
|
109
|
+
}
|
|
110
|
+
}]
|
|
111
|
+
});
|
|
112
|
+
}));
|
|
113
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, (request) => __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
if (request.params.name !== 'create_workflow') {
|
|
115
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const builder = new N8NWorkflowBuilder();
|
|
119
|
+
function isWorkflowSpec(obj) {
|
|
120
|
+
return obj &&
|
|
121
|
+
typeof obj === 'object' &&
|
|
122
|
+
Array.isArray(obj.nodes) &&
|
|
123
|
+
obj.nodes.every((node) => typeof node === 'object' &&
|
|
124
|
+
typeof node.type === 'string' &&
|
|
125
|
+
typeof node.name === 'string') &&
|
|
126
|
+
(!obj.connections || (Array.isArray(obj.connections) &&
|
|
127
|
+
obj.connections.every((conn) => typeof conn === 'object' &&
|
|
128
|
+
typeof conn.source === 'string' &&
|
|
129
|
+
typeof conn.target === 'string')));
|
|
130
|
+
}
|
|
131
|
+
const args = request.params.arguments;
|
|
132
|
+
if (!isWorkflowSpec(args)) {
|
|
133
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, 'Invalid workflow specification: must include nodes array with type and name properties');
|
|
134
|
+
}
|
|
135
|
+
const { nodes, connections } = args;
|
|
136
|
+
for (const node of nodes) {
|
|
137
|
+
builder.addNode(node.type, node.name, node.parameters || {});
|
|
138
|
+
}
|
|
139
|
+
for (const conn of connections || []) {
|
|
140
|
+
builder.connectNodes(conn.source, conn.target, conn.sourceOutput, conn.targetInput);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
content: [{
|
|
144
|
+
type: 'text',
|
|
145
|
+
text: JSON.stringify(builder.exportWorkflow(), null, 2)
|
|
146
|
+
}]
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Workflow creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
151
|
+
}
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
run() {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
157
|
+
yield this.server.connect(transport);
|
|
158
|
+
console.error('N8N Workflow Builder MCP server running on stdio');
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const server = new N8NWorkflowServer();
|
|
163
|
+
server.run().catch(console.error);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import {
|
|
5
|
+
CallToolRequestSchema,
|
|
6
|
+
ListToolsRequestSchema,
|
|
7
|
+
McpError,
|
|
8
|
+
ErrorCode
|
|
9
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
|
|
11
|
+
class N8NWorkflowBuilder {
|
|
12
|
+
private nodes: any[];
|
|
13
|
+
private connections: any[];
|
|
14
|
+
private nextPosition: { x: number, y: number };
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
this.nodes = [];
|
|
18
|
+
this.connections = [];
|
|
19
|
+
this.nextPosition = { x: 100, y: 100 };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
addNode(nodeType: string, name: string, parameters: any) {
|
|
23
|
+
const node = {
|
|
24
|
+
type: nodeType,
|
|
25
|
+
name: name,
|
|
26
|
+
parameters: parameters,
|
|
27
|
+
position: { ...this.nextPosition }
|
|
28
|
+
};
|
|
29
|
+
this.nodes.push(node);
|
|
30
|
+
this.nextPosition.x += 200;
|
|
31
|
+
return name;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
connectNodes(source: string, target: string, sourceOutput = 0, targetInput = 0) {
|
|
35
|
+
this.connections.push({
|
|
36
|
+
source_node: source,
|
|
37
|
+
target_node: target,
|
|
38
|
+
source_output: sourceOutput,
|
|
39
|
+
target_input: targetInput
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
exportWorkflow(): any {
|
|
44
|
+
interface WorkflowConnection {
|
|
45
|
+
node: string;
|
|
46
|
+
type: string;
|
|
47
|
+
index: number;
|
|
48
|
+
sourceNode: string;
|
|
49
|
+
sourceIndex: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface Workflow {
|
|
53
|
+
nodes: any[];
|
|
54
|
+
connections: {
|
|
55
|
+
main: WorkflowConnection[];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const workflow: Workflow = {
|
|
60
|
+
nodes: this.nodes,
|
|
61
|
+
connections: { main: [] }
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
for (const conn of this.connections) {
|
|
65
|
+
const connection: WorkflowConnection = {
|
|
66
|
+
node: conn.target_node,
|
|
67
|
+
type: 'main',
|
|
68
|
+
index: conn.target_input,
|
|
69
|
+
sourceNode: conn.source_node,
|
|
70
|
+
sourceIndex: conn.source_output
|
|
71
|
+
};
|
|
72
|
+
workflow.connections.main.push(connection);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return workflow;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class N8NWorkflowServer {
|
|
80
|
+
private server: Server;
|
|
81
|
+
|
|
82
|
+
constructor() {
|
|
83
|
+
this.server = new Server(
|
|
84
|
+
{
|
|
85
|
+
name: 'n8n-workflow-builder',
|
|
86
|
+
version: '0.1.0'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
capabilities: {
|
|
90
|
+
resources: {},
|
|
91
|
+
tools: {}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
this.setupToolHandlers();
|
|
97
|
+
this.server.onerror = (error) => console.error('[MCP Error]', error);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private setupToolHandlers() {
|
|
101
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
102
|
+
tools: [{
|
|
103
|
+
name: 'create_workflow',
|
|
104
|
+
description: 'Create and configure n8n workflows programmatically',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
nodes: {
|
|
109
|
+
type: 'array',
|
|
110
|
+
items: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
type: { type: 'string' },
|
|
114
|
+
name: { type: 'string' },
|
|
115
|
+
parameters: { type: 'object' }
|
|
116
|
+
},
|
|
117
|
+
required: ['type', 'name']
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
connections: {
|
|
121
|
+
type: 'array',
|
|
122
|
+
items: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
source: { type: 'string' },
|
|
126
|
+
target: { type: 'string' },
|
|
127
|
+
sourceOutput: { type: 'number', default: 0 },
|
|
128
|
+
targetInput: { type: 'number', default: 0 }
|
|
129
|
+
},
|
|
130
|
+
required: ['source', 'target']
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
required: ['nodes']
|
|
135
|
+
}
|
|
136
|
+
}]
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
interface WorkflowNode {
|
|
140
|
+
type: string;
|
|
141
|
+
name: string;
|
|
142
|
+
parameters?: Record<string, any>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface WorkflowConnectionSpec {
|
|
146
|
+
source: string;
|
|
147
|
+
target: string;
|
|
148
|
+
sourceOutput?: number;
|
|
149
|
+
targetInput?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interface WorkflowSpec {
|
|
153
|
+
nodes: WorkflowNode[];
|
|
154
|
+
connections?: WorkflowConnectionSpec[];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
158
|
+
if (request.params.name !== 'create_workflow') {
|
|
159
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const builder = new N8NWorkflowBuilder();
|
|
164
|
+
function isWorkflowSpec(obj: any): obj is WorkflowSpec {
|
|
165
|
+
return obj &&
|
|
166
|
+
typeof obj === 'object' &&
|
|
167
|
+
Array.isArray(obj.nodes) &&
|
|
168
|
+
obj.nodes.every((node: any) =>
|
|
169
|
+
typeof node === 'object' &&
|
|
170
|
+
typeof node.type === 'string' &&
|
|
171
|
+
typeof node.name === 'string'
|
|
172
|
+
) &&
|
|
173
|
+
(!obj.connections || (
|
|
174
|
+
Array.isArray(obj.connections) &&
|
|
175
|
+
obj.connections.every((conn: any) =>
|
|
176
|
+
typeof conn === 'object' &&
|
|
177
|
+
typeof conn.source === 'string' &&
|
|
178
|
+
typeof conn.target === 'string'
|
|
179
|
+
)
|
|
180
|
+
));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const args = request.params.arguments;
|
|
184
|
+
if (!isWorkflowSpec(args)) {
|
|
185
|
+
throw new McpError(
|
|
186
|
+
ErrorCode.InvalidParams,
|
|
187
|
+
'Invalid workflow specification: must include nodes array with type and name properties'
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const { nodes, connections } = args;
|
|
192
|
+
|
|
193
|
+
for (const node of nodes) {
|
|
194
|
+
builder.addNode(node.type, node.name, node.parameters || {});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for (const conn of connections || []) {
|
|
198
|
+
builder.connectNodes(
|
|
199
|
+
conn.source,
|
|
200
|
+
conn.target,
|
|
201
|
+
conn.sourceOutput,
|
|
202
|
+
conn.targetInput
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
content: [{
|
|
208
|
+
type: 'text',
|
|
209
|
+
text: JSON.stringify(builder.exportWorkflow(), null, 2)
|
|
210
|
+
}]
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
throw new McpError(
|
|
214
|
+
ErrorCode.InternalError,
|
|
215
|
+
`Workflow creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async run() {
|
|
222
|
+
const transport = new StdioServerTransport();
|
|
223
|
+
await this.server.connect(transport);
|
|
224
|
+
console.error('N8N Workflow Builder MCP server running on stdio');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const server = new N8NWorkflowServer();
|
|
229
|
+
server.run().catch(console.error);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2016",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"rootDir": "./src"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules"]
|
|
15
|
+
}
|