flowengine-mcp-app 3.0.0 → 3.0.1
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/README.md +19 -270
- package/bin.js +12 -0
- package/package.json +8 -41
- package/LICENSE +0 -21
- package/build/cli/api.d.ts +0 -19
- package/build/cli/api.d.ts.map +0 -1
- package/build/cli/api.js +0 -62
- package/build/cli/api.js.map +0 -1
- package/build/cli/commands/delete.d.ts +0 -4
- package/build/cli/commands/delete.d.ts.map +0 -1
- package/build/cli/commands/delete.js +0 -16
- package/build/cli/commands/delete.js.map +0 -1
- package/build/cli/commands/deploy.d.ts +0 -24
- package/build/cli/commands/deploy.d.ts.map +0 -1
- package/build/cli/commands/deploy.js +0 -103
- package/build/cli/commands/deploy.js.map +0 -1
- package/build/cli/commands/domain.d.ts +0 -9
- package/build/cli/commands/domain.d.ts.map +0 -1
- package/build/cli/commands/domain.js +0 -27
- package/build/cli/commands/domain.js.map +0 -1
- package/build/cli/commands/env.d.ts +0 -13
- package/build/cli/commands/env.d.ts.map +0 -1
- package/build/cli/commands/env.js +0 -53
- package/build/cli/commands/env.js.map +0 -1
- package/build/cli/commands/list.d.ts +0 -4
- package/build/cli/commands/list.d.ts.map +0 -1
- package/build/cli/commands/list.js +0 -25
- package/build/cli/commands/list.js.map +0 -1
- package/build/cli/commands/login.d.ts +0 -4
- package/build/cli/commands/login.d.ts.map +0 -1
- package/build/cli/commands/login.js +0 -37
- package/build/cli/commands/login.js.map +0 -1
- package/build/cli/commands/logout.d.ts +0 -2
- package/build/cli/commands/logout.d.ts.map +0 -1
- package/build/cli/commands/logout.js +0 -8
- package/build/cli/commands/logout.js.map +0 -1
- package/build/cli/commands/logs.d.ts +0 -4
- package/build/cli/commands/logs.d.ts.map +0 -1
- package/build/cli/commands/logs.js +0 -40
- package/build/cli/commands/logs.js.map +0 -1
- package/build/cli/commands/show.d.ts +0 -4
- package/build/cli/commands/show.d.ts.map +0 -1
- package/build/cli/commands/show.js +0 -13
- package/build/cli/commands/show.js.map +0 -1
- package/build/cli/commands/usage.d.ts +0 -7
- package/build/cli/commands/usage.d.ts.map +0 -1
- package/build/cli/commands/usage.js +0 -16
- package/build/cli/commands/usage.js.map +0 -1
- package/build/cli/config.d.ts +0 -17
- package/build/cli/config.d.ts.map +0 -1
- package/build/cli/config.js +0 -42
- package/build/cli/config.js.map +0 -1
- package/build/cli/pack.d.ts +0 -25
- package/build/cli/pack.d.ts.map +0 -1
- package/build/cli/pack.js +0 -111
- package/build/cli/pack.js.map +0 -1
- package/build/cli/ui.d.ts +0 -23
- package/build/cli/ui.d.ts.map +0 -1
- package/build/cli/ui.js +0 -105
- package/build/cli/ui.js.map +0 -1
- package/build/cli.d.ts +0 -10
- package/build/cli.d.ts.map +0 -1
- package/build/cli.js +0 -251
- package/build/cli.js.map +0 -1
- package/build/client.d.ts +0 -94
- package/build/client.d.ts.map +0 -1
- package/build/client.js +0 -311
- package/build/client.js.map +0 -1
- package/build/index.d.ts +0 -12
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -970
- package/build/index.js.map +0 -1
- package/build/tools/functions.d.ts +0 -14
- package/build/tools/functions.d.ts.map +0 -1
- package/build/tools/functions.js +0 -241
- package/build/tools/functions.js.map +0 -1
- package/build/types.d.ts +0 -155
- package/build/types.d.ts.map +0 -1
- package/build/types.js +0 -5
- package/build/types.js.map +0 -1
- package/build/ui/base.d.ts +0 -8
- package/build/ui/base.d.ts.map +0 -1
- package/build/ui/base.js +0 -425
- package/build/ui/base.js.map +0 -1
- package/build/ui/component-viewer.d.ts +0 -14
- package/build/ui/component-viewer.d.ts.map +0 -1
- package/build/ui/component-viewer.js +0 -678
- package/build/ui/component-viewer.js.map +0 -1
- package/build/ui/dashboard.d.ts +0 -21
- package/build/ui/dashboard.d.ts.map +0 -1
- package/build/ui/dashboard.js +0 -252
- package/build/ui/dashboard.js.map +0 -1
- package/build/ui/demo.d.ts +0 -14
- package/build/ui/demo.d.ts.map +0 -1
- package/build/ui/demo.js +0 -222
- package/build/ui/demo.js.map +0 -1
- package/build/ui/instances.d.ts +0 -17
- package/build/ui/instances.d.ts.map +0 -1
- package/build/ui/instances.js +0 -233
- package/build/ui/instances.js.map +0 -1
- package/build/ui/n8n-viewer.d.ts +0 -12
- package/build/ui/n8n-viewer.d.ts.map +0 -1
- package/build/ui/n8n-viewer.js +0 -371
- package/build/ui/n8n-viewer.js.map +0 -1
- package/build/ui/portals.d.ts +0 -14
- package/build/ui/portals.d.ts.map +0 -1
- package/build/ui/portals.js +0 -184
- package/build/ui/portals.js.map +0 -1
- package/build/ui/ui/components.html +0 -312
- package/build/ui/ui/n8n.html +0 -124
- package/build/ui/ui/portals.html +0 -211
- package/build/ui/widgets.d.ts +0 -17
- package/build/ui/widgets.d.ts.map +0 -1
- package/build/ui/widgets.js +0 -200
- package/build/ui/widgets.js.map +0 -1
- package/build/ui/workflows.d.ts +0 -17
- package/build/ui/workflows.d.ts.map +0 -1
- package/build/ui/workflows.js +0 -217
- package/build/ui/workflows.js.map +0 -1
package/build/index.js
DELETED
|
@@ -1,970 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* FlowEngine MCP Server - Modern MCP Apps Implementation
|
|
4
|
-
* Manage your white-label automation platform from Claude
|
|
5
|
-
*
|
|
6
|
-
* Core Features:
|
|
7
|
-
* 1. Instance Management - Provision and manage FlowEngine instances
|
|
8
|
-
* 2. Client Portals - Monitor and access client portals
|
|
9
|
-
* 3. AI FlowBuilder - Create forms, chatbots, and UI embeds
|
|
10
|
-
*/
|
|
11
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
-
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
14
|
-
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server';
|
|
15
|
-
import { z } from 'zod';
|
|
16
|
-
import { readFile } from 'node:fs/promises';
|
|
17
|
-
import { join } from 'node:path';
|
|
18
|
-
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { dirname } from 'node:path';
|
|
20
|
-
import { createServer } from 'node:http';
|
|
21
|
-
import { randomUUID } from 'node:crypto';
|
|
22
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
23
|
-
import { FlowEngineClient } from './client.js';
|
|
24
|
-
import { registerFunctionsTools } from './tools/functions.js';
|
|
25
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
-
const __dirname = dirname(__filename);
|
|
27
|
-
const UI_DIR = join(__dirname, 'ui', 'ui'); // Vite outputs to build/ui/ui/
|
|
28
|
-
// Environment configuration
|
|
29
|
-
const API_KEY = process.env.FLOWENGINE_API_KEY;
|
|
30
|
-
const BASE_URL = process.env.FLOWENGINE_BASE_URL || 'https://flowengine.cloud';
|
|
31
|
-
const MCP_MODE = (process.env.FLOWENGINE_MCP_MODE || 'all');
|
|
32
|
-
// Per-request API key context (HTTP mode - each user's key is isolated)
|
|
33
|
-
const requestApiKey = new AsyncLocalStorage();
|
|
34
|
-
function ensureClient() {
|
|
35
|
-
const apiKey = requestApiKey.getStore() || API_KEY;
|
|
36
|
-
if (!apiKey) {
|
|
37
|
-
throw new Error('FlowEngine API key not configured. Set FLOWENGINE_API_KEY in your MCP config.');
|
|
38
|
-
}
|
|
39
|
-
return new FlowEngineClient({ apiKey, baseUrl: BASE_URL });
|
|
40
|
-
}
|
|
41
|
-
function parseBody(req) {
|
|
42
|
-
return new Promise((resolve, reject) => {
|
|
43
|
-
let body = '';
|
|
44
|
-
req.on('data', (chunk) => { body += chunk; });
|
|
45
|
-
req.on('end', () => {
|
|
46
|
-
try {
|
|
47
|
-
resolve(body ? JSON.parse(body) : undefined);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
resolve(undefined);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
req.on('error', reject);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
// Store latest tool results for UI to retrieve
|
|
57
|
-
const toolResults = new Map();
|
|
58
|
-
function storeToolResult(type, data) {
|
|
59
|
-
const key = `${type}-${Date.now()}`;
|
|
60
|
-
toolResults.set(key, data);
|
|
61
|
-
// Keep only last 10 results to prevent memory leaks
|
|
62
|
-
if (toolResults.size > 10) {
|
|
63
|
-
const firstKey = toolResults.keys().next().value;
|
|
64
|
-
if (firstKey) {
|
|
65
|
-
toolResults.delete(firstKey);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return key;
|
|
69
|
-
}
|
|
70
|
-
function getLatestToolResult(type) {
|
|
71
|
-
// Get most recent result of given type (or any type if not specified)
|
|
72
|
-
let latest = null;
|
|
73
|
-
let latestKey = '';
|
|
74
|
-
for (const [key, value] of toolResults) {
|
|
75
|
-
if (!type || value.type?.includes(type)) {
|
|
76
|
-
if (!latestKey || key > latestKey) {
|
|
77
|
-
latest = value;
|
|
78
|
-
latestKey = key;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return latest;
|
|
83
|
-
}
|
|
84
|
-
// Initialize MCP Server with modern API
|
|
85
|
-
const server = new McpServer({
|
|
86
|
-
name: 'flowengine-mcp',
|
|
87
|
-
version: '2.0.9',
|
|
88
|
-
}, {
|
|
89
|
-
capabilities: {
|
|
90
|
-
resources: {},
|
|
91
|
-
tools: {},
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
/**
|
|
95
|
-
* ===========================
|
|
96
|
-
* APP RESOURCES (3 Focused UIs)
|
|
97
|
-
* ===========================
|
|
98
|
-
*/
|
|
99
|
-
if (MCP_MODE !== 'functions') {
|
|
100
|
-
// 1. n8n Workflow Preview - ONLY workflow builder
|
|
101
|
-
registerAppResource(server, 'n8n Workflow Preview', 'ui://flowengine/n8n', {
|
|
102
|
-
description: 'n8n workflow builder preview. Simple, focused UI showing only the n8n workflow editor.',
|
|
103
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
104
|
-
}, async () => {
|
|
105
|
-
try {
|
|
106
|
-
let html = await readFile(join(UI_DIR, 'n8n.html'), 'utf-8');
|
|
107
|
-
// Inject latest tool result into HTML as a global variable
|
|
108
|
-
const latestResult = getLatestToolResult('workflow');
|
|
109
|
-
if (latestResult) {
|
|
110
|
-
const dataScript = `<script>window.__flowengineToolResult = ${JSON.stringify(latestResult)};</script>`;
|
|
111
|
-
html = html.replace('</head>', `${dataScript}</head>`);
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
contents: [{
|
|
115
|
-
uri: 'ui://flowengine/n8n',
|
|
116
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
117
|
-
text: html,
|
|
118
|
-
}],
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
return {
|
|
123
|
-
contents: [{
|
|
124
|
-
uri: 'ui://flowengine/n8n',
|
|
125
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
126
|
-
text: `<!DOCTYPE html><html><body><h1>Error loading n8n preview</h1><p>${error.message}</p></body></html>`,
|
|
127
|
-
}],
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
// 2. UI Embed Builder - ONLY component management
|
|
132
|
-
registerAppResource(server, 'UI Embed Builder', 'ui://flowengine/components', {
|
|
133
|
-
description: 'UI embed builder and manager. Simple, focused UI for creating and managing forms, chatbots, and widgets.',
|
|
134
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
135
|
-
}, async () => {
|
|
136
|
-
try {
|
|
137
|
-
let html = await readFile(join(UI_DIR, 'components.html'), 'utf-8');
|
|
138
|
-
// Inject latest tool result into HTML as a global variable
|
|
139
|
-
const latestResult = getLatestToolResult('component');
|
|
140
|
-
if (latestResult) {
|
|
141
|
-
const dataScript = `<script>window.__flowengineToolResult = ${JSON.stringify(latestResult)};</script>`;
|
|
142
|
-
html = html.replace('</head>', `${dataScript}</head>`);
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
contents: [{
|
|
146
|
-
uri: 'ui://flowengine/components',
|
|
147
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
148
|
-
text: html,
|
|
149
|
-
}],
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
return {
|
|
154
|
-
contents: [{
|
|
155
|
-
uri: 'ui://flowengine/components',
|
|
156
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
157
|
-
text: `<!DOCTYPE html><html><body><h1>Error loading component builder</h1><p>${error.message}</p></body></html>`,
|
|
158
|
-
}],
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
// 3. Portals Dashboard - ONLY portal management
|
|
163
|
-
registerAppResource(server, 'Portals Dashboard', 'ui://flowengine/portals', {
|
|
164
|
-
description: 'Portal management dashboard. Simple, focused UI for managing client portals, instances, and storage.',
|
|
165
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
166
|
-
}, async () => {
|
|
167
|
-
try {
|
|
168
|
-
const html = await readFile(join(UI_DIR, 'portals.html'), 'utf-8');
|
|
169
|
-
return {
|
|
170
|
-
contents: [{
|
|
171
|
-
uri: 'ui://flowengine/portals',
|
|
172
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
173
|
-
text: html,
|
|
174
|
-
}],
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
return {
|
|
179
|
-
contents: [{
|
|
180
|
-
uri: 'ui://flowengine/portals',
|
|
181
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
182
|
-
text: `<!DOCTYPE html><html><body><h1>Error loading portals dashboard</h1><p>${error.message}</p></body></html>`,
|
|
183
|
-
}],
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
/**
|
|
188
|
-
* ===========================
|
|
189
|
-
* SIMPLE TOOLS (3 Tools Only)
|
|
190
|
-
* ===========================
|
|
191
|
-
* 1. flowengine_create_workflow - Create n8n workflow, return preview
|
|
192
|
-
* 2. flowengine_create_component - Create UI embed (form/chatbot/widget), return preview(s)
|
|
193
|
-
* 3. flowengine_get_instances - List portals/instances (read-only, links to website)
|
|
194
|
-
*/
|
|
195
|
-
// 1. Create Workflow - Creates n8n workflow and returns preview
|
|
196
|
-
registerAppTool(server, 'flowengine_create_workflow', {
|
|
197
|
-
description: 'Create and show a sample n8n workflow instantly. Returns interactive workflow preview with a pre-made workflow template. Use when user asks to create/show workflows, automations, or n8n examples.',
|
|
198
|
-
inputSchema: {
|
|
199
|
-
name: z.string().describe('Workflow name or description'),
|
|
200
|
-
},
|
|
201
|
-
_meta: {
|
|
202
|
-
ui: {
|
|
203
|
-
resourceUri: 'ui://flowengine/n8n',
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
}, async (args) => {
|
|
207
|
-
const client = ensureClient();
|
|
208
|
-
const name = args.name;
|
|
209
|
-
// Create sample workflow object
|
|
210
|
-
const sampleWorkflow = {
|
|
211
|
-
"name": name || "Sample Workflow",
|
|
212
|
-
"nodes": [
|
|
213
|
-
{
|
|
214
|
-
"parameters": {
|
|
215
|
-
"path": "webhook",
|
|
216
|
-
"responseMode": "onReceived",
|
|
217
|
-
"options": {}
|
|
218
|
-
},
|
|
219
|
-
"id": "webhook-1",
|
|
220
|
-
"name": "Webhook",
|
|
221
|
-
"type": "n8n-nodes-base.webhook",
|
|
222
|
-
"typeVersion": 1,
|
|
223
|
-
"position": [250, 300],
|
|
224
|
-
"webhookId": "sample-webhook"
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
"parameters": {
|
|
228
|
-
"values": {
|
|
229
|
-
"string": [
|
|
230
|
-
{
|
|
231
|
-
"name": "message",
|
|
232
|
-
"value": "Received data from webhook"
|
|
233
|
-
}
|
|
234
|
-
]
|
|
235
|
-
},
|
|
236
|
-
"options": {}
|
|
237
|
-
},
|
|
238
|
-
"id": "set-1",
|
|
239
|
-
"name": "Process Data",
|
|
240
|
-
"type": "n8n-nodes-base.set",
|
|
241
|
-
"typeVersion": 1,
|
|
242
|
-
"position": [450, 300]
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
"parameters": {
|
|
246
|
-
"conditions": {
|
|
247
|
-
"string": [
|
|
248
|
-
{
|
|
249
|
-
"value1": "={{$json.email}}",
|
|
250
|
-
"operation": "isNotEmpty"
|
|
251
|
-
}
|
|
252
|
-
]
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
"id": "if-1",
|
|
256
|
-
"name": "Check Email",
|
|
257
|
-
"type": "n8n-nodes-base.if",
|
|
258
|
-
"typeVersion": 1,
|
|
259
|
-
"position": [650, 300]
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
"parameters": {
|
|
263
|
-
"fromEmail": "noreply@example.com",
|
|
264
|
-
"toEmail": "={{$json.email}}",
|
|
265
|
-
"subject": "Notification",
|
|
266
|
-
"text": "={{$json.message}}"
|
|
267
|
-
},
|
|
268
|
-
"id": "email-1",
|
|
269
|
-
"name": "Send Email",
|
|
270
|
-
"type": "n8n-nodes-base.emailSend",
|
|
271
|
-
"typeVersion": 1,
|
|
272
|
-
"position": [850, 250]
|
|
273
|
-
}
|
|
274
|
-
],
|
|
275
|
-
"connections": {
|
|
276
|
-
"Webhook": {
|
|
277
|
-
"main": [[{ "node": "Process Data", "type": "main", "index": 0 }]]
|
|
278
|
-
},
|
|
279
|
-
"Process Data": {
|
|
280
|
-
"main": [[{ "node": "Check Email", "type": "main", "index": 0 }]]
|
|
281
|
-
},
|
|
282
|
-
"Check Email": {
|
|
283
|
-
"main": [[{ "node": "Send Email", "type": "main", "index": 0 }]]
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
"settings": {
|
|
287
|
-
"executionOrder": "v1"
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
try {
|
|
291
|
-
// Try to save workflow to n8n instance
|
|
292
|
-
const createResult = await client.createWorkflow(sampleWorkflow);
|
|
293
|
-
const workflowPreview = {
|
|
294
|
-
type: 'workflow_preview',
|
|
295
|
-
workflow: sampleWorkflow,
|
|
296
|
-
workflowId: createResult?.workflowId,
|
|
297
|
-
workflowUrl: createResult?.workflowUrl,
|
|
298
|
-
message: `✓ Created workflow: ${sampleWorkflow.name}`,
|
|
299
|
-
nodes: sampleWorkflow.nodes.length,
|
|
300
|
-
description: `Workflow created and saved to ${createResult?.instanceName || 'n8n instance'}. Ready to activate and deploy.`,
|
|
301
|
-
instanceName: createResult?.instanceName,
|
|
302
|
-
};
|
|
303
|
-
// Store for UI to retrieve
|
|
304
|
-
storeToolResult('workflow', workflowPreview);
|
|
305
|
-
return {
|
|
306
|
-
content: [{
|
|
307
|
-
type: 'text',
|
|
308
|
-
text: JSON.stringify(workflowPreview, null, 2),
|
|
309
|
-
}],
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
// Fallback: return sample workflow for preview if creation fails
|
|
314
|
-
console.warn('Failed to create workflow in n8n:', error.message);
|
|
315
|
-
const fallbackWorkflow = {
|
|
316
|
-
type: 'workflow_preview',
|
|
317
|
-
workflow: sampleWorkflow,
|
|
318
|
-
message: `✓ Workflow preview created: ${sampleWorkflow.name}`,
|
|
319
|
-
nodes: sampleWorkflow.nodes.length,
|
|
320
|
-
description: `Preview ready. Note: Workflow could not be saved to n8n (${error.message}). Configure API key to enable persistence.`,
|
|
321
|
-
warning: error.message,
|
|
322
|
-
};
|
|
323
|
-
storeToolResult('workflow', fallbackWorkflow);
|
|
324
|
-
return {
|
|
325
|
-
content: [{
|
|
326
|
-
type: 'text',
|
|
327
|
-
text: JSON.stringify(fallbackWorkflow, null, 2),
|
|
328
|
-
}],
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
// 2. Create Component - Creates UI embed and returns preview(s)
|
|
333
|
-
registerAppTool(server, 'flowengine_create_component', {
|
|
334
|
-
description: 'Create a UI embed (form, chatbot, widget) in FlowEngine and show preview. Use when user asks to create forms, chatbots, contact forms, etc. Returns component preview and workflow preview if component needs automation (like chatbot).',
|
|
335
|
-
inputSchema: {
|
|
336
|
-
type: z.enum(['form', 'chatbot', 'widget']).describe('Component type'),
|
|
337
|
-
name: z.string().describe('Component name or description'),
|
|
338
|
-
config: z.any().optional().describe('Optional configuration'),
|
|
339
|
-
},
|
|
340
|
-
_meta: {
|
|
341
|
-
ui: {
|
|
342
|
-
resourceUri: 'ui://flowengine/components',
|
|
343
|
-
},
|
|
344
|
-
},
|
|
345
|
-
}, async (args) => {
|
|
346
|
-
const client = ensureClient();
|
|
347
|
-
const type = args.type;
|
|
348
|
-
const name = args.name;
|
|
349
|
-
const config = args.config;
|
|
350
|
-
// Create component data
|
|
351
|
-
const componentData = {
|
|
352
|
-
type: type,
|
|
353
|
-
name: name || `Sample ${type}`,
|
|
354
|
-
config: config || {},
|
|
355
|
-
};
|
|
356
|
-
try {
|
|
357
|
-
// Create component in database
|
|
358
|
-
const createResult = await client.createComponent(componentData);
|
|
359
|
-
const componentPreview = {
|
|
360
|
-
type: 'component_preview',
|
|
361
|
-
component: {
|
|
362
|
-
id: createResult?.component?.id,
|
|
363
|
-
...componentData,
|
|
364
|
-
slug: createResult?.component?.slug,
|
|
365
|
-
},
|
|
366
|
-
componentId: createResult?.component?.id,
|
|
367
|
-
componentUrl: createResult?.componentUrl,
|
|
368
|
-
message: `✓ Created ${type}: ${componentData.name}`,
|
|
369
|
-
componentType: type,
|
|
370
|
-
description: `${type.charAt(0).toUpperCase() + type.slice(1)} component created successfully. Ready to customize and embed.`,
|
|
371
|
-
};
|
|
372
|
-
// Store for UI to retrieve
|
|
373
|
-
storeToolResult('component', componentPreview);
|
|
374
|
-
return {
|
|
375
|
-
content: [{
|
|
376
|
-
type: 'text',
|
|
377
|
-
text: JSON.stringify(componentPreview, null, 2),
|
|
378
|
-
}],
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
// Fallback: return component preview if creation fails
|
|
383
|
-
console.warn('Failed to create component:', error.message);
|
|
384
|
-
const fallbackComponent = {
|
|
385
|
-
type: 'component_preview',
|
|
386
|
-
component: {
|
|
387
|
-
id: `component-${Date.now()}`,
|
|
388
|
-
...componentData,
|
|
389
|
-
},
|
|
390
|
-
message: `✓ Component preview created: ${componentData.name}`,
|
|
391
|
-
componentType: type,
|
|
392
|
-
description: `Preview ready. Note: Component could not be saved (${error.message}). Configure API key to enable persistence.`,
|
|
393
|
-
warning: error.message,
|
|
394
|
-
};
|
|
395
|
-
storeToolResult('component', fallbackComponent);
|
|
396
|
-
return {
|
|
397
|
-
content: [{
|
|
398
|
-
type: 'text',
|
|
399
|
-
text: JSON.stringify(fallbackComponent, null, 2),
|
|
400
|
-
}],
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
// 3. Get Instances - Lists portals/instances (read-only, no create/delete)
|
|
405
|
-
registerAppTool(server, 'flowengine_get_instances', {
|
|
406
|
-
description: 'Show list of FlowEngine portals and hosting instances. Use when user asks to see their instances, portals, or hosting. Returns simple list with basic info and links to website. Read-only - all actions (create/delete) link to website.',
|
|
407
|
-
inputSchema: {},
|
|
408
|
-
_meta: {
|
|
409
|
-
ui: {
|
|
410
|
-
resourceUri: 'ui://flowengine/portals',
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
}, async () => {
|
|
414
|
-
try {
|
|
415
|
-
const instances = await ensureClient().getInstances();
|
|
416
|
-
return {
|
|
417
|
-
content: [{
|
|
418
|
-
type: 'text',
|
|
419
|
-
text: JSON.stringify({
|
|
420
|
-
type: 'instances_list',
|
|
421
|
-
instances: instances,
|
|
422
|
-
message: `Found ${instances.length} instance(s). Click links to manage on flowengine.cloud`,
|
|
423
|
-
}, null, 2),
|
|
424
|
-
}],
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
catch (error) {
|
|
428
|
-
return {
|
|
429
|
-
content: [{
|
|
430
|
-
type: 'text',
|
|
431
|
-
text: JSON.stringify({
|
|
432
|
-
type: 'instances_list',
|
|
433
|
-
instances: [],
|
|
434
|
-
error: error.message,
|
|
435
|
-
apiKey: API_KEY ? `${API_KEY.substring(0, 10)}...` : 'NOT SET',
|
|
436
|
-
baseUrl: BASE_URL,
|
|
437
|
-
}, null, 2),
|
|
438
|
-
}],
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
// 4. Get Components - Lists all UI embeds (forms, chatbots, widgets)
|
|
443
|
-
registerAppTool(server, 'flowengine_get_components', {
|
|
444
|
-
description: 'Show list of UI embeds (forms, chatbots, widgets) with share links. Use when user asks to see their components, forms, chatbots, or widgets. Returns embed list with embed codes and share options.',
|
|
445
|
-
inputSchema: {},
|
|
446
|
-
_meta: {
|
|
447
|
-
ui: {
|
|
448
|
-
resourceUri: 'ui://flowengine/components',
|
|
449
|
-
},
|
|
450
|
-
},
|
|
451
|
-
}, async () => {
|
|
452
|
-
try {
|
|
453
|
-
const components = await ensureClient().getWidgets();
|
|
454
|
-
return {
|
|
455
|
-
content: [{
|
|
456
|
-
type: 'text',
|
|
457
|
-
text: JSON.stringify(components, null, 2),
|
|
458
|
-
}],
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
catch (error) {
|
|
462
|
-
// Return empty list if not authenticated
|
|
463
|
-
return {
|
|
464
|
-
content: [{
|
|
465
|
-
type: 'text',
|
|
466
|
-
text: JSON.stringify([], null, 2),
|
|
467
|
-
}],
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
// 5. Deploy Instance - Deploy a docker image or GitHub repo to a hosting instance
|
|
472
|
-
registerAppTool(server, 'flowengine_deploy_instance', {
|
|
473
|
-
description: 'Deploy a Docker image or GitHub repo to a FlowEngine hosting instance. Saves config then triggers deployment. Use when user wants to deploy, redeploy, or change what is running on an instance. Provide EITHER dockerImage OR githubRepo (not both). Port defaults to 3000.',
|
|
474
|
-
inputSchema: {
|
|
475
|
-
instanceId: z.string().describe('Instance ID to deploy to'),
|
|
476
|
-
dockerImage: z.string().optional().describe('Docker image to deploy (e.g. "nginx:latest", "ghcr.io/owner/repo:latest"). Use this OR githubRepo.'),
|
|
477
|
-
githubRepo: z.string().optional().describe('GitHub repo to build and deploy (e.g. "owner/repo"). Requires GitHub App installed. Use this OR dockerImage.'),
|
|
478
|
-
port: z.number().optional().describe('Container port your app listens on (default: 3000)'),
|
|
479
|
-
envVars: z.record(z.string()).optional().describe('Environment variables as key-value pairs (e.g. {"NODE_ENV": "production"})'),
|
|
480
|
-
},
|
|
481
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
482
|
-
}, async (args) => {
|
|
483
|
-
const client = ensureClient();
|
|
484
|
-
const { instanceId, dockerImage, githubRepo, port, envVars } = args;
|
|
485
|
-
if (!dockerImage && !githubRepo) {
|
|
486
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Provide either dockerImage or githubRepo.' }, null, 2) }] };
|
|
487
|
-
}
|
|
488
|
-
if (dockerImage && githubRepo) {
|
|
489
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Provide either dockerImage or githubRepo, not both.' }, null, 2) }] };
|
|
490
|
-
}
|
|
491
|
-
try {
|
|
492
|
-
await client.updateInstanceConfig(instanceId, {
|
|
493
|
-
...(dockerImage ? { dockerImage } : { githubRepo }),
|
|
494
|
-
...(port ? { port } : {}),
|
|
495
|
-
...(envVars ? { envVars } : {}),
|
|
496
|
-
});
|
|
497
|
-
const result = await client.manageInstance(instanceId, 'redeploy');
|
|
498
|
-
return {
|
|
499
|
-
content: [{
|
|
500
|
-
type: 'text',
|
|
501
|
-
text: JSON.stringify({
|
|
502
|
-
success: true,
|
|
503
|
-
instanceId,
|
|
504
|
-
source: githubRepo ? 'github' : 'docker',
|
|
505
|
-
...(githubRepo ? { githubRepo } : { dockerImage }),
|
|
506
|
-
port: port || 3000,
|
|
507
|
-
message: result.message || 'Deployment started. Check status with flowengine_get_instance_status.',
|
|
508
|
-
}, null, 2),
|
|
509
|
-
}],
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
catch (error) {
|
|
513
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
// 5b. Provision n8n - Provision an n8n automation instance on an empty slot
|
|
517
|
-
registerAppTool(server, 'flowengine_provision_n8n', {
|
|
518
|
-
description: 'Provision an n8n automation instance on an empty FlowEngine hosting slot. Use when user wants to deploy n8n to an existing empty instance. This is a full n8n provisioning - separate from docker deployments.',
|
|
519
|
-
inputSchema: {
|
|
520
|
-
instanceId: z.string().describe('Instance ID of the empty slot to provision n8n on'),
|
|
521
|
-
},
|
|
522
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
523
|
-
}, async (args) => {
|
|
524
|
-
const client = ensureClient();
|
|
525
|
-
const { instanceId } = args;
|
|
526
|
-
try {
|
|
527
|
-
const result = await client.deployService(instanceId, 'n8n');
|
|
528
|
-
return {
|
|
529
|
-
content: [{
|
|
530
|
-
type: 'text',
|
|
531
|
-
text: JSON.stringify({
|
|
532
|
-
success: true,
|
|
533
|
-
instanceId,
|
|
534
|
-
serviceType: 'n8n',
|
|
535
|
-
message: result.message || 'n8n provisioning started. Check status with flowengine_get_instance_status.',
|
|
536
|
-
}, null, 2),
|
|
537
|
-
}],
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
// 5c. Provision OpenClaw - Provision an OpenClaw AI agent on an empty slot
|
|
545
|
-
registerAppTool(server, 'flowengine_provision_openclaw', {
|
|
546
|
-
description: 'Provision an OpenClaw AI agent on an empty FlowEngine hosting slot. Requires instance with 30GB+ storage. Use when user wants to deploy OpenClaw to an existing empty instance. This is a full OpenClaw provisioning with browser and gateway components - separate from docker deployments.',
|
|
547
|
-
inputSchema: {
|
|
548
|
-
instanceId: z.string().describe('Instance ID of the empty slot to provision OpenClaw on (must have 30GB+ storage)'),
|
|
549
|
-
},
|
|
550
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
551
|
-
}, async (args) => {
|
|
552
|
-
const client = ensureClient();
|
|
553
|
-
const { instanceId } = args;
|
|
554
|
-
try {
|
|
555
|
-
const result = await client.deployService(instanceId, 'openclaw');
|
|
556
|
-
return {
|
|
557
|
-
content: [{
|
|
558
|
-
type: 'text',
|
|
559
|
-
text: JSON.stringify({
|
|
560
|
-
success: true,
|
|
561
|
-
instanceId,
|
|
562
|
-
serviceType: 'openclaw',
|
|
563
|
-
message: result.message || 'OpenClaw provisioning started. Check status with flowengine_get_instance_status.',
|
|
564
|
-
}, null, 2),
|
|
565
|
-
}],
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
catch (error) {
|
|
569
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
// 5b. List GitHub Repos - List repos available for deployment
|
|
573
|
-
registerAppTool(server, 'flowengine_list_github_repos', {
|
|
574
|
-
description: 'List GitHub repositories accessible for deployment via the connected GitHub App. Use when user wants to deploy from GitHub and needs to choose a repo. Requires GitHub App to be installed on flowengine.cloud.',
|
|
575
|
-
inputSchema: {},
|
|
576
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
577
|
-
}, async () => {
|
|
578
|
-
try {
|
|
579
|
-
const result = await ensureClient().listGithubRepos();
|
|
580
|
-
if (!result.success) {
|
|
581
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error, installUrl: result.installUrl }, null, 2) }] };
|
|
582
|
-
}
|
|
583
|
-
const repos = (result.repos || []).map((r) => ({
|
|
584
|
-
full_name: r.full_name,
|
|
585
|
-
name: r.name,
|
|
586
|
-
private: r.private,
|
|
587
|
-
default_branch: r.default_branch,
|
|
588
|
-
html_url: r.html_url,
|
|
589
|
-
}));
|
|
590
|
-
return {
|
|
591
|
-
content: [{
|
|
592
|
-
type: 'text',
|
|
593
|
-
text: JSON.stringify({ success: true, repos, count: repos.length, tip: 'Use full_name (e.g. "owner/repo") with flowengine_deploy_instance.' }, null, 2),
|
|
594
|
-
}],
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
catch (error) {
|
|
598
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
// 5c. Manage Instance - Start/stop/restart a hosting instance
|
|
602
|
-
registerAppTool(server, 'flowengine_manage_instance', {
|
|
603
|
-
description: 'Start, stop, or restart a FlowEngine hosting instance. Use when user asks to start/stop/restart a container. For deploying a new image or repo, use flowengine_deploy_instance instead.',
|
|
604
|
-
inputSchema: {
|
|
605
|
-
instanceId: z.string().describe('Instance ID'),
|
|
606
|
-
action: z.enum(['start', 'stop', 'restart', 'redeploy']).describe('Action: start (boot stopped container), stop (halt container), restart (stop + start), redeploy (rebuild + restart with current config)'),
|
|
607
|
-
},
|
|
608
|
-
_meta: {
|
|
609
|
-
ui: {
|
|
610
|
-
resourceUri: 'ui://flowengine/portals',
|
|
611
|
-
},
|
|
612
|
-
},
|
|
613
|
-
}, async (args) => {
|
|
614
|
-
const client = ensureClient();
|
|
615
|
-
const { instanceId, action } = args;
|
|
616
|
-
try {
|
|
617
|
-
const result = await client.manageInstance(instanceId, action);
|
|
618
|
-
return {
|
|
619
|
-
content: [{
|
|
620
|
-
type: 'text',
|
|
621
|
-
text: JSON.stringify({ success: true, action, instanceId, message: result.message || `Instance ${action} triggered`, ...result }, null, 2),
|
|
622
|
-
}],
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
catch (error) {
|
|
626
|
-
return {
|
|
627
|
-
content: [{
|
|
628
|
-
type: 'text',
|
|
629
|
-
text: JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
630
|
-
}],
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
// 6. Get Instance Logs - Fetch container logs
|
|
635
|
-
registerAppTool(server, 'flowengine_get_instance_logs', {
|
|
636
|
-
description: 'Get container logs for a FlowEngine hosting instance (docker, OpenClaw, n8n). Returns recent log output. Use when user asks to see logs, debug issues, or check what a container is doing.',
|
|
637
|
-
inputSchema: {
|
|
638
|
-
instanceId: z.string().describe('Instance ID'),
|
|
639
|
-
lines: z.number().optional().describe('Number of log lines to return (default 200, max 1000)'),
|
|
640
|
-
},
|
|
641
|
-
_meta: {
|
|
642
|
-
ui: {
|
|
643
|
-
resourceUri: 'ui://flowengine/portals',
|
|
644
|
-
},
|
|
645
|
-
},
|
|
646
|
-
}, async (args) => {
|
|
647
|
-
const client = ensureClient();
|
|
648
|
-
const { instanceId, lines = 200 } = args;
|
|
649
|
-
try {
|
|
650
|
-
const result = await client.getInstanceLogs(instanceId, lines);
|
|
651
|
-
return {
|
|
652
|
-
content: [{
|
|
653
|
-
type: 'text',
|
|
654
|
-
text: result.logs || '(no logs available)',
|
|
655
|
-
}],
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
catch (error) {
|
|
659
|
-
return {
|
|
660
|
-
content: [{
|
|
661
|
-
type: 'text',
|
|
662
|
-
text: `Error fetching logs: ${error.message}`,
|
|
663
|
-
}],
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
// 7. Get Instance Deployments - Deployment history
|
|
668
|
-
registerAppTool(server, 'flowengine_get_instance_deployments', {
|
|
669
|
-
description: 'Get deployment history for a FlowEngine hosting instance. Shows past deploys with timestamps and status. Use when user asks about deployment history, when something was last deployed, or deployment status.',
|
|
670
|
-
inputSchema: {
|
|
671
|
-
instanceId: z.string().describe('Instance ID'),
|
|
672
|
-
},
|
|
673
|
-
_meta: {
|
|
674
|
-
ui: {
|
|
675
|
-
resourceUri: 'ui://flowengine/portals',
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
}, async (args) => {
|
|
679
|
-
const client = ensureClient();
|
|
680
|
-
const { instanceId } = args;
|
|
681
|
-
try {
|
|
682
|
-
const result = await client.getInstanceDeployments(instanceId);
|
|
683
|
-
return {
|
|
684
|
-
content: [{
|
|
685
|
-
type: 'text',
|
|
686
|
-
text: JSON.stringify({ instanceId, deployments: result.deployments || [], message: result.message }, null, 2),
|
|
687
|
-
}],
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
catch (error) {
|
|
691
|
-
return {
|
|
692
|
-
content: [{
|
|
693
|
-
type: 'text',
|
|
694
|
-
text: JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
695
|
-
}],
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
// 8. Get Instance Status - Live status check
|
|
700
|
-
registerAppTool(server, 'flowengine_get_instance_status', {
|
|
701
|
-
description: 'Get live status of a specific FlowEngine instance. Returns running/stopped/provisioning state and instance details. Use when user asks if an instance is running or wants to check its current state.',
|
|
702
|
-
inputSchema: {
|
|
703
|
-
instanceId: z.string().describe('Instance ID'),
|
|
704
|
-
},
|
|
705
|
-
_meta: {
|
|
706
|
-
ui: {
|
|
707
|
-
resourceUri: 'ui://flowengine/portals',
|
|
708
|
-
},
|
|
709
|
-
},
|
|
710
|
-
}, async (args) => {
|
|
711
|
-
const client = ensureClient();
|
|
712
|
-
const { instanceId } = args;
|
|
713
|
-
try {
|
|
714
|
-
const result = await client.getInstanceStatus(instanceId);
|
|
715
|
-
return {
|
|
716
|
-
content: [{
|
|
717
|
-
type: 'text',
|
|
718
|
-
text: JSON.stringify({
|
|
719
|
-
instanceId,
|
|
720
|
-
status: result.containerStatus || result.status,
|
|
721
|
-
coolifyStatus: result.coolifyStatus,
|
|
722
|
-
instance: result.instance,
|
|
723
|
-
}, null, 2),
|
|
724
|
-
}],
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
catch (error) {
|
|
728
|
-
return {
|
|
729
|
-
content: [{
|
|
730
|
-
type: 'text',
|
|
731
|
-
text: JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
732
|
-
}],
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
// 9. List Instance Backups
|
|
737
|
-
registerAppTool(server, 'flowengine_list_instance_backups', {
|
|
738
|
-
description: 'List backups for an OpenClaw instance. Returns backup history with status, file size, and timestamps.',
|
|
739
|
-
inputSchema: {
|
|
740
|
-
instanceId: z.string().describe('Instance ID'),
|
|
741
|
-
},
|
|
742
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
743
|
-
}, async (args) => {
|
|
744
|
-
const client = ensureClient();
|
|
745
|
-
const { instanceId } = args;
|
|
746
|
-
try {
|
|
747
|
-
const result = await client.listInstanceBackups(instanceId);
|
|
748
|
-
return {
|
|
749
|
-
content: [{
|
|
750
|
-
type: 'text',
|
|
751
|
-
text: JSON.stringify({ instanceId, backups: result.backups || [] }, null, 2),
|
|
752
|
-
}],
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
catch (error) {
|
|
756
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
// 10. Create Instance Backup
|
|
760
|
-
registerAppTool(server, 'flowengine_create_instance_backup', {
|
|
761
|
-
description: 'Create a manual backup of an OpenClaw instance. Backs up Docker volumes to the server. Takes up to a few minutes.',
|
|
762
|
-
inputSchema: {
|
|
763
|
-
instanceId: z.string().describe('Instance ID'),
|
|
764
|
-
},
|
|
765
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
766
|
-
}, async (args) => {
|
|
767
|
-
const client = ensureClient();
|
|
768
|
-
const { instanceId } = args;
|
|
769
|
-
try {
|
|
770
|
-
const result = await client.createInstanceBackup(instanceId);
|
|
771
|
-
return {
|
|
772
|
-
content: [{
|
|
773
|
-
type: 'text',
|
|
774
|
-
text: JSON.stringify(result, null, 2),
|
|
775
|
-
}],
|
|
776
|
-
};
|
|
777
|
-
}
|
|
778
|
-
catch (error) {
|
|
779
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
// 11. Update Instance Config
|
|
783
|
-
registerAppTool(server, 'flowengine_update_instance_config', {
|
|
784
|
-
description: 'Update the docker image, port, or environment variables for a docker/website instance. Does not redeploy automatically - call flowengine_manage_instance with action=redeploy after to apply changes.',
|
|
785
|
-
inputSchema: {
|
|
786
|
-
instanceId: z.string().describe('Instance ID'),
|
|
787
|
-
dockerImage: z.string().optional().describe('New Docker image (e.g. nginx:latest)'),
|
|
788
|
-
githubRepo: z.string().optional().describe('GitHub repo to build from (e.g. "owner/repo"). Requires GitHub App to be installed.'),
|
|
789
|
-
port: z.number().optional().describe('Container port to expose'),
|
|
790
|
-
envVars: z.record(z.string()).optional().describe('Environment variables as key-value object'),
|
|
791
|
-
},
|
|
792
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
793
|
-
}, async (args) => {
|
|
794
|
-
const client = ensureClient();
|
|
795
|
-
const { instanceId, dockerImage, githubRepo, port, envVars } = args;
|
|
796
|
-
try {
|
|
797
|
-
const result = await client.updateInstanceConfig(instanceId, { dockerImage, githubRepo, port, envVars });
|
|
798
|
-
return {
|
|
799
|
-
content: [{
|
|
800
|
-
type: 'text',
|
|
801
|
-
text: JSON.stringify(result, null, 2),
|
|
802
|
-
}],
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
catch (error) {
|
|
806
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
807
|
-
}
|
|
808
|
-
});
|
|
809
|
-
// 12. Update Instance Type - Reclassify between docker and website
|
|
810
|
-
registerAppTool(server, 'flowengine_update_instance_type', {
|
|
811
|
-
description: 'Change the type of a FlowEngine instance between "docker" (full management) and "website" (URL + notes tracking). Use when user wants to reclassify an instance.',
|
|
812
|
-
inputSchema: {
|
|
813
|
-
instanceId: z.string().describe('Instance ID'),
|
|
814
|
-
serviceType: z.enum(['docker', 'website']).describe('New type: "docker" for full management, "website" for URL/notes tracking'),
|
|
815
|
-
},
|
|
816
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
817
|
-
}, async (args) => {
|
|
818
|
-
const client = ensureClient();
|
|
819
|
-
const { instanceId, serviceType } = args;
|
|
820
|
-
try {
|
|
821
|
-
await client.updateInstanceConfig(instanceId, { serviceType });
|
|
822
|
-
return {
|
|
823
|
-
content: [{ type: 'text', text: JSON.stringify({ success: true, instanceId, serviceType, message: `Instance type updated to "${serviceType}"` }, null, 2) }],
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
catch (error) {
|
|
827
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
828
|
-
}
|
|
829
|
-
});
|
|
830
|
-
// 13. Update Instance Domain - Update tracked URL for a website/docker instance
|
|
831
|
-
registerAppTool(server, 'flowengine_update_instance_domain', {
|
|
832
|
-
description: 'Update the tracked URL/domain for a website or docker instance. Updates the instance URL in FlowEngine (does not configure Coolify/DNS - do that separately). Use after setting up a custom domain.',
|
|
833
|
-
inputSchema: {
|
|
834
|
-
instanceId: z.string().describe('Instance ID'),
|
|
835
|
-
domain: z.string().describe('New domain (e.g. "app.yourdomain.com" or "https://app.yourdomain.com")'),
|
|
836
|
-
},
|
|
837
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/portals' } },
|
|
838
|
-
}, async (args) => {
|
|
839
|
-
const client = ensureClient();
|
|
840
|
-
const { instanceId, domain } = args;
|
|
841
|
-
try {
|
|
842
|
-
const result = await client.updateInstanceDomain(instanceId, domain);
|
|
843
|
-
return {
|
|
844
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
catch (error) {
|
|
848
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
849
|
-
}
|
|
850
|
-
});
|
|
851
|
-
// 15. List Enrichment Providers - returns available providers with config schemas
|
|
852
|
-
registerAppTool(server, 'flowengine_list_enrichment_providers', {
|
|
853
|
-
description: 'List available enrichment providers (FlowEngine, Apollo, Bright Data) with their config schemas and connection status. Use this to see which providers the user can configure for visitor intelligence.',
|
|
854
|
-
inputSchema: {},
|
|
855
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/n8n' } },
|
|
856
|
-
}, async () => {
|
|
857
|
-
const client = ensureClient();
|
|
858
|
-
try {
|
|
859
|
-
const result = await client.listEnrichmentProviders();
|
|
860
|
-
storeToolResult('enrichment', result);
|
|
861
|
-
return {
|
|
862
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
catch (error) {
|
|
866
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
// 16. Configure Enrichment - update provider config on a site (NEVER sets enrichment_provider - user choice only)
|
|
870
|
-
registerAppTool(server, 'flowengine_configure_enrichment', {
|
|
871
|
-
description: 'Configure enrichment and auto-push settings for a visitor intel site. Can update provider config, enrichment level, and auto-push to Smartlead/Instantly. IMPORTANT: Cannot change enrichment_provider or deanon_provider - those are user-only choices.',
|
|
872
|
-
inputSchema: {
|
|
873
|
-
siteId: z.string().describe('Visitor site ID'),
|
|
874
|
-
providerConfig: z.record(z.unknown()).optional().describe('Provider-specific configuration options (e.g. Apollo seniority filters, Bright Data dataset ID)'),
|
|
875
|
-
enrichmentLevel: z.enum(['basic', 'standard', 'full']).optional().describe('Enrichment depth: basic (3 contacts), standard (5), full (10)'),
|
|
876
|
-
autoPushTo: z.enum(['smartlead', 'instantly']).optional().describe('Auto-push new leads to this outreach platform. Set to empty string to disable.'),
|
|
877
|
-
autoPushCampaignId: z.string().optional().describe('Campaign ID to push leads to (required if autoPushTo is set)'),
|
|
878
|
-
},
|
|
879
|
-
_meta: { ui: { resourceUri: 'ui://flowengine/n8n' } },
|
|
880
|
-
}, async (args) => {
|
|
881
|
-
const client = ensureClient();
|
|
882
|
-
const { siteId, providerConfig, enrichmentLevel, autoPushTo, autoPushCampaignId } = args;
|
|
883
|
-
try {
|
|
884
|
-
const result = await client.configureEnrichment(siteId, { providerConfig, enrichmentLevel, autoPushTo, autoPushCampaignId });
|
|
885
|
-
return {
|
|
886
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
catch (error) {
|
|
890
|
-
return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error.message }, null, 2) }] };
|
|
891
|
-
}
|
|
892
|
-
});
|
|
893
|
-
// 17. Get Latest Tool Result - Used by UI to fetch the result of the last tool call
|
|
894
|
-
registerAppTool(server, 'flowengine_get_latest_result', {
|
|
895
|
-
description: 'Internal: Get the latest tool result (used by UI to fetch preview data)',
|
|
896
|
-
inputSchema: {},
|
|
897
|
-
_meta: {
|
|
898
|
-
ui: {
|
|
899
|
-
resourceUri: 'ui://flowengine/internal',
|
|
900
|
-
},
|
|
901
|
-
},
|
|
902
|
-
}, async () => {
|
|
903
|
-
const latest = getLatestToolResult();
|
|
904
|
-
if (!latest) {
|
|
905
|
-
return {
|
|
906
|
-
content: [{
|
|
907
|
-
type: 'text',
|
|
908
|
-
text: JSON.stringify({ error: 'No result available' }, null, 2),
|
|
909
|
-
}],
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
return {
|
|
913
|
-
content: [{
|
|
914
|
-
type: 'text',
|
|
915
|
-
text: JSON.stringify(latest, null, 2),
|
|
916
|
-
}],
|
|
917
|
-
};
|
|
918
|
-
});
|
|
919
|
-
} // end MCP_MODE !== 'functions'
|
|
920
|
-
if (MCP_MODE !== 'hosting') {
|
|
921
|
-
registerFunctionsTools(server, {
|
|
922
|
-
baseUrl: BASE_URL,
|
|
923
|
-
getApiKey: () => {
|
|
924
|
-
const apiKey = requestApiKey.getStore() || API_KEY;
|
|
925
|
-
if (!apiKey) {
|
|
926
|
-
throw new Error('FlowEngine API key not configured. Set FLOWENGINE_API_KEY in your MCP config.');
|
|
927
|
-
}
|
|
928
|
-
return apiKey;
|
|
929
|
-
},
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* ===========================
|
|
934
|
-
* SERVER STARTUP
|
|
935
|
-
* ===========================
|
|
936
|
-
*/
|
|
937
|
-
async function main() {
|
|
938
|
-
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : null;
|
|
939
|
-
if (port) {
|
|
940
|
-
// HTTP mode - for Claude.ai online "Add custom connector"
|
|
941
|
-
// API key is read from Authorization: Bearer <fe_...> header per request
|
|
942
|
-
const transport = new StreamableHTTPServerTransport({
|
|
943
|
-
sessionIdGenerator: () => randomUUID(),
|
|
944
|
-
});
|
|
945
|
-
await server.server.connect(transport);
|
|
946
|
-
const httpServer = createServer(async (req, res) => {
|
|
947
|
-
const authHeader = req.headers['authorization'] ?? '';
|
|
948
|
-
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : '';
|
|
949
|
-
let body;
|
|
950
|
-
if (req.method === 'POST') {
|
|
951
|
-
body = await parseBody(req);
|
|
952
|
-
}
|
|
953
|
-
await requestApiKey.run(token, () => transport.handleRequest(req, res, body));
|
|
954
|
-
});
|
|
955
|
-
httpServer.listen(port, () => {
|
|
956
|
-
console.error(`FlowEngine MCP Server running on HTTP port ${port}`);
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
else {
|
|
960
|
-
// Stdio mode - for Claude Desktop / Claude Code CLI
|
|
961
|
-
const transport = new StdioServerTransport();
|
|
962
|
-
await server.server.connect(transport);
|
|
963
|
-
console.error('FlowEngine MCP Server running on stdio');
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
main().catch((error) => {
|
|
967
|
-
console.error('Fatal error in MCP server:', error);
|
|
968
|
-
process.exit(1);
|
|
969
|
-
});
|
|
970
|
-
//# sourceMappingURL=index.js.map
|