ds-mcp-flowise 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/README.md +174 -0
- package/data/flowise.db +0 -0
- package/data/nodes.json +23349 -0
- package/data/summary.json +39 -0
- package/data/templates.json +53107 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +679 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
- package/scripts/extract-nodes.ts +368 -0
- package/scripts/prepare-database.ts +230 -0
- package/src/index.ts +768 -0
- package/tsconfig.json +19 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* DS-MCP-FLOWISE
|
|
4
|
+
*
|
|
5
|
+
* MCP server for building and managing Flowise chatflows and agentflows.
|
|
6
|
+
* Provides AI assistants with comprehensive knowledge of Flowise nodes
|
|
7
|
+
* and tools for workflow creation.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* DS-MCP-FLOWISE
|
|
4
|
+
*
|
|
5
|
+
* MCP server for building and managing Flowise chatflows and agentflows.
|
|
6
|
+
* Provides AI assistants with comprehensive knowledge of Flowise nodes
|
|
7
|
+
* and tools for workflow creation.
|
|
8
|
+
*/
|
|
9
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
12
|
+
import initSqlJs from 'sql.js';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
// Find database path
|
|
19
|
+
function findDatabasePath() {
|
|
20
|
+
const possiblePaths = [
|
|
21
|
+
path.join(__dirname, '..', 'data', 'flowise.db'),
|
|
22
|
+
path.join(__dirname, '..', '..', 'data', 'flowise.db'),
|
|
23
|
+
path.join(process.cwd(), 'data', 'flowise.db'),
|
|
24
|
+
];
|
|
25
|
+
for (const p of possiblePaths) {
|
|
26
|
+
if (fs.existsSync(p)) {
|
|
27
|
+
return p;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error('Database not found. Run "npm run extract && npm run prepare-db" first.');
|
|
31
|
+
}
|
|
32
|
+
// Global database instance
|
|
33
|
+
let db;
|
|
34
|
+
// Helper function to execute a query and return results
|
|
35
|
+
function query(sql, params = []) {
|
|
36
|
+
const stmt = db.prepare(sql);
|
|
37
|
+
if (params.length > 0) {
|
|
38
|
+
stmt.bind(params);
|
|
39
|
+
}
|
|
40
|
+
const results = [];
|
|
41
|
+
while (stmt.step()) {
|
|
42
|
+
const row = stmt.getAsObject();
|
|
43
|
+
results.push(row);
|
|
44
|
+
}
|
|
45
|
+
stmt.free();
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
// Helper to get a single row
|
|
49
|
+
function queryOne(sql, params = []) {
|
|
50
|
+
const results = query(sql, params);
|
|
51
|
+
return results.length > 0 ? results[0] : null;
|
|
52
|
+
}
|
|
53
|
+
// Create MCP server
|
|
54
|
+
const server = new Server({
|
|
55
|
+
name: 'ds-mcp-flowise',
|
|
56
|
+
version: '1.0.0',
|
|
57
|
+
}, {
|
|
58
|
+
capabilities: {
|
|
59
|
+
tools: {},
|
|
60
|
+
resources: {},
|
|
61
|
+
prompts: {},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// === TOOL DEFINITIONS ===
|
|
65
|
+
const TOOLS = [
|
|
66
|
+
{
|
|
67
|
+
name: 'list_categories',
|
|
68
|
+
description: 'List all available Flowise node categories with node counts',
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {},
|
|
72
|
+
required: [],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'list_nodes',
|
|
77
|
+
description: 'List Flowise nodes, optionally filtered by category. Use this to discover available nodes for building flows.',
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
category: {
|
|
82
|
+
type: 'string',
|
|
83
|
+
description: 'Filter by category (e.g., "Chat Models", "Vector Stores", "Tools")',
|
|
84
|
+
},
|
|
85
|
+
search: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
description: 'Search nodes by name, label, or description',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
required: [],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'get_node_schema',
|
|
95
|
+
description: 'Get detailed schema for a specific Flowise node including all inputs, their types, and options. Essential for correctly configuring nodes in a flow.',
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
name: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
description: 'The node name (e.g., "chatOpenAI", "pinecone", "conversationChain")',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ['name'],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'search_nodes',
|
|
109
|
+
description: 'Search across all nodes to find relevant components for a task',
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
query: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
description: 'Search query (e.g., "vector database", "openai", "memory")',
|
|
116
|
+
},
|
|
117
|
+
limit: {
|
|
118
|
+
type: 'number',
|
|
119
|
+
description: 'Maximum results to return (default: 10)',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
required: ['query'],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'list_templates',
|
|
127
|
+
description: 'List available marketplace templates (pre-built flows). Use these as examples and starting points.',
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: {
|
|
131
|
+
type: {
|
|
132
|
+
type: 'string',
|
|
133
|
+
enum: ['chatflow', 'agentflow', 'agentflowv2', 'tool'],
|
|
134
|
+
description: 'Filter by template type',
|
|
135
|
+
},
|
|
136
|
+
search: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'Search templates by name or description',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: [],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'get_template',
|
|
146
|
+
description: 'Get a complete marketplace template with all nodes and edges. Use this to understand flow patterns.',
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
name: {
|
|
151
|
+
type: 'string',
|
|
152
|
+
description: 'Template name (e.g., "Conversational Retrieval QA Chain", "Simple RAG")',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
required: ['name'],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'find_compatible_nodes',
|
|
160
|
+
description: 'Find nodes that can connect to a given node based on base classes. Use this to discover what can plug into a specific node.',
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: 'object',
|
|
163
|
+
properties: {
|
|
164
|
+
node_name: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
description: 'The node name to find compatible connections for',
|
|
167
|
+
},
|
|
168
|
+
direction: {
|
|
169
|
+
type: 'string',
|
|
170
|
+
enum: ['inputs', 'outputs'],
|
|
171
|
+
description: 'Find nodes that can connect as inputs or outputs (default: inputs)',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
required: ['node_name'],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'validate_flow',
|
|
179
|
+
description: 'Validate a proposed flow structure. Checks that all nodes exist and connections are valid.',
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
nodes: {
|
|
184
|
+
type: 'array',
|
|
185
|
+
description: 'Array of node objects with id, type/name, and data',
|
|
186
|
+
items: {
|
|
187
|
+
type: 'object',
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
edges: {
|
|
191
|
+
type: 'array',
|
|
192
|
+
description: 'Array of edge objects with source and target',
|
|
193
|
+
items: {
|
|
194
|
+
type: 'object',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
required: ['nodes', 'edges'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'generate_flow_skeleton',
|
|
203
|
+
description: 'Generate a basic flow skeleton for a given use case. Returns a starting point that can be customized.',
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
properties: {
|
|
207
|
+
use_case: {
|
|
208
|
+
type: 'string',
|
|
209
|
+
enum: [
|
|
210
|
+
'simple_chatbot',
|
|
211
|
+
'rag_chatbot',
|
|
212
|
+
'conversational_agent',
|
|
213
|
+
'document_qa',
|
|
214
|
+
'api_agent',
|
|
215
|
+
'multi_agent',
|
|
216
|
+
],
|
|
217
|
+
description: 'The type of flow to generate',
|
|
218
|
+
},
|
|
219
|
+
chat_model: {
|
|
220
|
+
type: 'string',
|
|
221
|
+
description: 'Preferred chat model (e.g., "chatOpenAI", "chatAnthropic")',
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
required: ['use_case'],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
// === TOOL IMPLEMENTATIONS ===
|
|
229
|
+
function listCategories() {
|
|
230
|
+
const categories = query(`
|
|
231
|
+
SELECT name, node_count
|
|
232
|
+
FROM categories
|
|
233
|
+
ORDER BY name
|
|
234
|
+
`);
|
|
235
|
+
return JSON.stringify(categories, null, 2);
|
|
236
|
+
}
|
|
237
|
+
function listNodes(category, search) {
|
|
238
|
+
let sql = 'SELECT name, label, category, description, type FROM nodes';
|
|
239
|
+
const params = [];
|
|
240
|
+
const conditions = [];
|
|
241
|
+
if (category) {
|
|
242
|
+
conditions.push('category = ?');
|
|
243
|
+
params.push(category);
|
|
244
|
+
}
|
|
245
|
+
if (search) {
|
|
246
|
+
conditions.push('(name LIKE ? OR label LIKE ? OR description LIKE ?)');
|
|
247
|
+
const searchTerm = `%${search}%`;
|
|
248
|
+
params.push(searchTerm, searchTerm, searchTerm);
|
|
249
|
+
}
|
|
250
|
+
if (conditions.length > 0) {
|
|
251
|
+
sql += ' WHERE ' + conditions.join(' AND ');
|
|
252
|
+
}
|
|
253
|
+
sql += ' ORDER BY category, label';
|
|
254
|
+
const nodes = query(sql, params);
|
|
255
|
+
return JSON.stringify(nodes, null, 2);
|
|
256
|
+
}
|
|
257
|
+
function getNodeSchema(name) {
|
|
258
|
+
const node = queryOne(`SELECT * FROM nodes WHERE name = ?`, [name]);
|
|
259
|
+
if (!node) {
|
|
260
|
+
return JSON.stringify({ error: `Node "${name}" not found` });
|
|
261
|
+
}
|
|
262
|
+
const inputs = query(`
|
|
263
|
+
SELECT input_name, input_label, input_type, description, optional, default_value, options, additional_params
|
|
264
|
+
FROM node_inputs
|
|
265
|
+
WHERE node_name = ?
|
|
266
|
+
ORDER BY additional_params, input_label
|
|
267
|
+
`, [name]);
|
|
268
|
+
// Parse JSON fields
|
|
269
|
+
const schema = {
|
|
270
|
+
...node,
|
|
271
|
+
base_classes: JSON.parse(node.base_classes || '[]'),
|
|
272
|
+
credential: node.credential ? JSON.parse(node.credential) : null,
|
|
273
|
+
inputs: inputs.map((i) => ({
|
|
274
|
+
name: i.input_name,
|
|
275
|
+
label: i.input_label,
|
|
276
|
+
type: i.input_type,
|
|
277
|
+
description: i.description,
|
|
278
|
+
optional: Boolean(i.optional),
|
|
279
|
+
default: i.default_value,
|
|
280
|
+
options: i.options ? JSON.parse(i.options) : null,
|
|
281
|
+
additionalParams: Boolean(i.additional_params),
|
|
282
|
+
})),
|
|
283
|
+
};
|
|
284
|
+
return JSON.stringify(schema, null, 2);
|
|
285
|
+
}
|
|
286
|
+
function searchNodes(searchQuery, limit = 10) {
|
|
287
|
+
const searchTerm = `%${searchQuery}%`;
|
|
288
|
+
const results = query(`
|
|
289
|
+
SELECT name, label, category, description
|
|
290
|
+
FROM nodes
|
|
291
|
+
WHERE name LIKE ? OR label LIKE ? OR description LIKE ? OR category LIKE ?
|
|
292
|
+
LIMIT ?
|
|
293
|
+
`, [searchTerm, searchTerm, searchTerm, searchTerm, limit]);
|
|
294
|
+
return JSON.stringify(results, null, 2);
|
|
295
|
+
}
|
|
296
|
+
function listTemplates(type, search) {
|
|
297
|
+
let sql = 'SELECT name, description, type, usecases FROM templates';
|
|
298
|
+
const params = [];
|
|
299
|
+
const conditions = [];
|
|
300
|
+
if (type) {
|
|
301
|
+
conditions.push('type = ?');
|
|
302
|
+
params.push(type);
|
|
303
|
+
}
|
|
304
|
+
if (search) {
|
|
305
|
+
conditions.push('(name LIKE ? OR description LIKE ?)');
|
|
306
|
+
const searchTerm = `%${search}%`;
|
|
307
|
+
params.push(searchTerm, searchTerm);
|
|
308
|
+
}
|
|
309
|
+
if (conditions.length > 0) {
|
|
310
|
+
sql += ' WHERE ' + conditions.join(' AND ');
|
|
311
|
+
}
|
|
312
|
+
sql += ' ORDER BY type, name';
|
|
313
|
+
const templates = query(sql, params);
|
|
314
|
+
return JSON.stringify(templates.map((t) => ({
|
|
315
|
+
...t,
|
|
316
|
+
usecases: t.usecases ? JSON.parse(t.usecases) : [],
|
|
317
|
+
})), null, 2);
|
|
318
|
+
}
|
|
319
|
+
function getTemplate(name) {
|
|
320
|
+
const template = queryOne(`SELECT * FROM templates WHERE name = ?`, [name]);
|
|
321
|
+
if (!template) {
|
|
322
|
+
return JSON.stringify({ error: `Template "${name}" not found` });
|
|
323
|
+
}
|
|
324
|
+
return JSON.stringify({
|
|
325
|
+
...template,
|
|
326
|
+
usecases: template.usecases ? JSON.parse(template.usecases) : [],
|
|
327
|
+
nodes: JSON.parse(template.nodes),
|
|
328
|
+
edges: JSON.parse(template.edges),
|
|
329
|
+
}, null, 2);
|
|
330
|
+
}
|
|
331
|
+
function findCompatibleNodes(nodeName, direction = 'inputs') {
|
|
332
|
+
const node = queryOne(`SELECT base_classes FROM nodes WHERE name = ?`, [nodeName]);
|
|
333
|
+
if (!node) {
|
|
334
|
+
return JSON.stringify({ error: `Node "${nodeName}" not found` });
|
|
335
|
+
}
|
|
336
|
+
const baseClasses = JSON.parse(node.base_classes || '[]');
|
|
337
|
+
if (direction === 'inputs') {
|
|
338
|
+
// Find nodes whose outputs (base_classes) match this node's input types
|
|
339
|
+
const inputs = query(`SELECT input_type FROM node_inputs WHERE node_name = ?`, [nodeName]);
|
|
340
|
+
const inputTypes = inputs.map((i) => i.input_type);
|
|
341
|
+
// Find nodes that have these types in their base_classes
|
|
342
|
+
const allNodes = query(`SELECT name, label, category, base_classes FROM nodes`);
|
|
343
|
+
const compatible = allNodes.filter((n) => {
|
|
344
|
+
const nodeBaseClasses = JSON.parse(n.base_classes || '[]');
|
|
345
|
+
return nodeBaseClasses.some(bc => inputTypes.includes(bc));
|
|
346
|
+
}).map((n) => ({
|
|
347
|
+
name: n.name,
|
|
348
|
+
label: n.label,
|
|
349
|
+
category: n.category,
|
|
350
|
+
}));
|
|
351
|
+
return JSON.stringify({ compatible_inputs: compatible }, null, 2);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
// Find nodes that accept this node's output types
|
|
355
|
+
const allInputs = query(`
|
|
356
|
+
SELECT DISTINCT n.name, n.label, n.category, ni.input_type
|
|
357
|
+
FROM nodes n
|
|
358
|
+
JOIN node_inputs ni ON n.name = ni.node_name
|
|
359
|
+
`);
|
|
360
|
+
const compatible = allInputs.filter((i) => baseClasses.includes(i.input_type)).map((i) => ({
|
|
361
|
+
name: i.name,
|
|
362
|
+
label: i.label,
|
|
363
|
+
category: i.category,
|
|
364
|
+
}));
|
|
365
|
+
// Deduplicate
|
|
366
|
+
const unique = Array.from(new Map(compatible.map((c) => [c.name, c])).values());
|
|
367
|
+
return JSON.stringify({ compatible_outputs: unique }, null, 2);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function validateFlow(nodes, edges) {
|
|
371
|
+
const errors = [];
|
|
372
|
+
const warnings = [];
|
|
373
|
+
// Check all nodes exist
|
|
374
|
+
const nodeNames = new Set();
|
|
375
|
+
for (const node of nodes) {
|
|
376
|
+
const nodeName = node.data?.name || node.type || node.name;
|
|
377
|
+
if (!nodeName) {
|
|
378
|
+
errors.push(`Node ${node.id} has no name/type specified`);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const exists = queryOne(`SELECT 1 FROM nodes WHERE name = ?`, [nodeName]);
|
|
382
|
+
if (!exists) {
|
|
383
|
+
errors.push(`Node type "${nodeName}" does not exist`);
|
|
384
|
+
}
|
|
385
|
+
nodeNames.add(node.id);
|
|
386
|
+
}
|
|
387
|
+
// Check all edges reference valid nodes
|
|
388
|
+
for (const edge of edges) {
|
|
389
|
+
if (!nodeNames.has(edge.source)) {
|
|
390
|
+
errors.push(`Edge references non-existent source node: ${edge.source}`);
|
|
391
|
+
}
|
|
392
|
+
if (!nodeNames.has(edge.target)) {
|
|
393
|
+
errors.push(`Edge references non-existent target node: ${edge.target}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return JSON.stringify({
|
|
397
|
+
valid: errors.length === 0,
|
|
398
|
+
errors,
|
|
399
|
+
warnings,
|
|
400
|
+
}, null, 2);
|
|
401
|
+
}
|
|
402
|
+
function generateFlowSkeleton(useCase, chatModel) {
|
|
403
|
+
const model = chatModel || 'chatOpenAI';
|
|
404
|
+
const skeletons = {
|
|
405
|
+
simple_chatbot: {
|
|
406
|
+
description: 'A basic chatbot with memory',
|
|
407
|
+
nodes: [
|
|
408
|
+
{ id: 'chatModel_0', type: model, position: { x: 100, y: 200 } },
|
|
409
|
+
{ id: 'memory_0', type: 'bufferMemory', position: { x: 100, y: 400 } },
|
|
410
|
+
{ id: 'chain_0', type: 'conversationChain', position: { x: 400, y: 300 } },
|
|
411
|
+
],
|
|
412
|
+
edges: [
|
|
413
|
+
{ source: 'chatModel_0', target: 'chain_0' },
|
|
414
|
+
{ source: 'memory_0', target: 'chain_0' },
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
rag_chatbot: {
|
|
418
|
+
description: 'Retrieval Augmented Generation chatbot',
|
|
419
|
+
nodes: [
|
|
420
|
+
{ id: 'chatModel_0', type: model, position: { x: 100, y: 100 } },
|
|
421
|
+
{ id: 'embeddings_0', type: 'openAIEmbeddings', position: { x: 100, y: 300 } },
|
|
422
|
+
{ id: 'vectorStore_0', type: 'pinecone', position: { x: 400, y: 300 } },
|
|
423
|
+
{ id: 'retriever_0', type: 'vectorStoreRetriever', position: { x: 700, y: 300 } },
|
|
424
|
+
{ id: 'memory_0', type: 'bufferMemory', position: { x: 400, y: 500 } },
|
|
425
|
+
{ id: 'chain_0', type: 'conversationalRetrievalQAChain', position: { x: 1000, y: 300 } },
|
|
426
|
+
],
|
|
427
|
+
edges: [
|
|
428
|
+
{ source: 'chatModel_0', target: 'chain_0' },
|
|
429
|
+
{ source: 'embeddings_0', target: 'vectorStore_0' },
|
|
430
|
+
{ source: 'vectorStore_0', target: 'retriever_0' },
|
|
431
|
+
{ source: 'retriever_0', target: 'chain_0' },
|
|
432
|
+
{ source: 'memory_0', target: 'chain_0' },
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
conversational_agent: {
|
|
436
|
+
description: 'Agent with tools and memory',
|
|
437
|
+
nodes: [
|
|
438
|
+
{ id: 'chatModel_0', type: model, position: { x: 100, y: 200 } },
|
|
439
|
+
{ id: 'memory_0', type: 'bufferMemory', position: { x: 100, y: 400 } },
|
|
440
|
+
{ id: 'tool_0', type: 'calculator', position: { x: 400, y: 100 } },
|
|
441
|
+
{ id: 'tool_1', type: 'searchApi', position: { x: 400, y: 300 } },
|
|
442
|
+
{ id: 'agent_0', type: 'conversationalAgent', position: { x: 700, y: 200 } },
|
|
443
|
+
],
|
|
444
|
+
edges: [
|
|
445
|
+
{ source: 'chatModel_0', target: 'agent_0' },
|
|
446
|
+
{ source: 'memory_0', target: 'agent_0' },
|
|
447
|
+
{ source: 'tool_0', target: 'agent_0' },
|
|
448
|
+
{ source: 'tool_1', target: 'agent_0' },
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
document_qa: {
|
|
452
|
+
description: 'Document question answering',
|
|
453
|
+
nodes: [
|
|
454
|
+
{ id: 'chatModel_0', type: model, position: { x: 100, y: 100 } },
|
|
455
|
+
{ id: 'embeddings_0', type: 'openAIEmbeddings', position: { x: 100, y: 300 } },
|
|
456
|
+
{ id: 'docLoader_0', type: 'pdfFile', position: { x: 400, y: 100 } },
|
|
457
|
+
{ id: 'textSplitter_0', type: 'recursiveCharacterTextSplitter', position: { x: 700, y: 100 } },
|
|
458
|
+
{ id: 'vectorStore_0', type: 'memoryVectorStore', position: { x: 400, y: 300 } },
|
|
459
|
+
{ id: 'chain_0', type: 'retrievalQAChain', position: { x: 700, y: 300 } },
|
|
460
|
+
],
|
|
461
|
+
edges: [
|
|
462
|
+
{ source: 'chatModel_0', target: 'chain_0' },
|
|
463
|
+
{ source: 'embeddings_0', target: 'vectorStore_0' },
|
|
464
|
+
{ source: 'docLoader_0', target: 'textSplitter_0' },
|
|
465
|
+
{ source: 'textSplitter_0', target: 'vectorStore_0' },
|
|
466
|
+
{ source: 'vectorStore_0', target: 'chain_0' },
|
|
467
|
+
],
|
|
468
|
+
},
|
|
469
|
+
api_agent: {
|
|
470
|
+
description: 'Agent that can call APIs',
|
|
471
|
+
nodes: [
|
|
472
|
+
{ id: 'chatModel_0', type: model, position: { x: 100, y: 200 } },
|
|
473
|
+
{ id: 'tool_0', type: 'customTool', position: { x: 400, y: 100 } },
|
|
474
|
+
{ id: 'tool_1', type: 'requestsGet', position: { x: 400, y: 300 } },
|
|
475
|
+
{ id: 'agent_0', type: 'openAIFunctionAgent', position: { x: 700, y: 200 } },
|
|
476
|
+
],
|
|
477
|
+
edges: [
|
|
478
|
+
{ source: 'chatModel_0', target: 'agent_0' },
|
|
479
|
+
{ source: 'tool_0', target: 'agent_0' },
|
|
480
|
+
{ source: 'tool_1', target: 'agent_0' },
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
multi_agent: {
|
|
484
|
+
description: 'Multiple agents working together',
|
|
485
|
+
nodes: [
|
|
486
|
+
{ id: 'chatModel_0', type: model, position: { x: 100, y: 200 } },
|
|
487
|
+
{ id: 'supervisor_0', type: 'supervisor', position: { x: 400, y: 200 } },
|
|
488
|
+
{ id: 'worker_0', type: 'worker', position: { x: 700, y: 100 } },
|
|
489
|
+
{ id: 'worker_1', type: 'worker', position: { x: 700, y: 300 } },
|
|
490
|
+
],
|
|
491
|
+
edges: [
|
|
492
|
+
{ source: 'chatModel_0', target: 'supervisor_0' },
|
|
493
|
+
{ source: 'supervisor_0', target: 'worker_0' },
|
|
494
|
+
{ source: 'supervisor_0', target: 'worker_1' },
|
|
495
|
+
],
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
const skeleton = skeletons[useCase];
|
|
499
|
+
if (!skeleton) {
|
|
500
|
+
return JSON.stringify({ error: `Unknown use case: ${useCase}` });
|
|
501
|
+
}
|
|
502
|
+
return JSON.stringify(skeleton, null, 2);
|
|
503
|
+
}
|
|
504
|
+
// === REQUEST HANDLERS ===
|
|
505
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
506
|
+
tools: TOOLS,
|
|
507
|
+
}));
|
|
508
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
509
|
+
const { name, arguments: args } = request.params;
|
|
510
|
+
try {
|
|
511
|
+
let result;
|
|
512
|
+
switch (name) {
|
|
513
|
+
case 'list_categories':
|
|
514
|
+
result = listCategories();
|
|
515
|
+
break;
|
|
516
|
+
case 'list_nodes':
|
|
517
|
+
result = listNodes(args?.category, args?.search);
|
|
518
|
+
break;
|
|
519
|
+
case 'get_node_schema':
|
|
520
|
+
result = getNodeSchema(args?.name);
|
|
521
|
+
break;
|
|
522
|
+
case 'search_nodes':
|
|
523
|
+
result = searchNodes(args?.query, args?.limit);
|
|
524
|
+
break;
|
|
525
|
+
case 'list_templates':
|
|
526
|
+
result = listTemplates(args?.type, args?.search);
|
|
527
|
+
break;
|
|
528
|
+
case 'get_template':
|
|
529
|
+
result = getTemplate(args?.name);
|
|
530
|
+
break;
|
|
531
|
+
case 'find_compatible_nodes':
|
|
532
|
+
result = findCompatibleNodes(args?.node_name, args?.direction);
|
|
533
|
+
break;
|
|
534
|
+
case 'validate_flow':
|
|
535
|
+
result = validateFlow(args?.nodes, args?.edges);
|
|
536
|
+
break;
|
|
537
|
+
case 'generate_flow_skeleton':
|
|
538
|
+
result = generateFlowSkeleton(args?.use_case, args?.chat_model);
|
|
539
|
+
break;
|
|
540
|
+
default:
|
|
541
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
content: [{ type: 'text', text: result }],
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
return {
|
|
549
|
+
content: [{ type: 'text', text: JSON.stringify({ error: String(error) }) }],
|
|
550
|
+
isError: true,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
// === RESOURCES ===
|
|
555
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
556
|
+
resources: [
|
|
557
|
+
{
|
|
558
|
+
uri: 'flowise://categories',
|
|
559
|
+
mimeType: 'application/json',
|
|
560
|
+
name: 'All Categories',
|
|
561
|
+
description: 'List of all Flowise node categories',
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
uri: 'flowise://nodes',
|
|
565
|
+
mimeType: 'application/json',
|
|
566
|
+
name: 'All Nodes',
|
|
567
|
+
description: 'Complete list of Flowise nodes',
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
uri: 'flowise://templates',
|
|
571
|
+
mimeType: 'application/json',
|
|
572
|
+
name: 'All Templates',
|
|
573
|
+
description: 'All marketplace templates',
|
|
574
|
+
},
|
|
575
|
+
],
|
|
576
|
+
}));
|
|
577
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
578
|
+
const { uri } = request.params;
|
|
579
|
+
let content;
|
|
580
|
+
switch (uri) {
|
|
581
|
+
case 'flowise://categories':
|
|
582
|
+
content = listCategories();
|
|
583
|
+
break;
|
|
584
|
+
case 'flowise://nodes':
|
|
585
|
+
content = listNodes();
|
|
586
|
+
break;
|
|
587
|
+
case 'flowise://templates':
|
|
588
|
+
content = listTemplates();
|
|
589
|
+
break;
|
|
590
|
+
default:
|
|
591
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
592
|
+
}
|
|
593
|
+
return {
|
|
594
|
+
contents: [{ uri, mimeType: 'application/json', text: content }],
|
|
595
|
+
};
|
|
596
|
+
});
|
|
597
|
+
// === PROMPTS ===
|
|
598
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
599
|
+
prompts: [
|
|
600
|
+
{
|
|
601
|
+
name: 'build_chatflow',
|
|
602
|
+
description: 'Guide for building a Flowise chatflow',
|
|
603
|
+
arguments: [
|
|
604
|
+
{
|
|
605
|
+
name: 'description',
|
|
606
|
+
description: 'What the chatflow should do',
|
|
607
|
+
required: true,
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
name: 'build_agentflow',
|
|
613
|
+
description: 'Guide for building a Flowise agent flow',
|
|
614
|
+
arguments: [
|
|
615
|
+
{
|
|
616
|
+
name: 'description',
|
|
617
|
+
description: 'What the agent should do',
|
|
618
|
+
required: true,
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
}));
|
|
624
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
625
|
+
const { name, arguments: args } = request.params;
|
|
626
|
+
if (name === 'build_chatflow') {
|
|
627
|
+
return {
|
|
628
|
+
messages: [
|
|
629
|
+
{
|
|
630
|
+
role: 'user',
|
|
631
|
+
content: {
|
|
632
|
+
type: 'text',
|
|
633
|
+
text: `Help me build a Flowise chatflow that: ${args?.description}
|
|
634
|
+
|
|
635
|
+
First, use list_categories to see available node types.
|
|
636
|
+
Then, use search_nodes or list_nodes to find relevant components.
|
|
637
|
+
Use get_node_schema to understand how to configure each node.
|
|
638
|
+
Check list_templates for similar examples.
|
|
639
|
+
Finally, use validate_flow to verify the flow is valid.`,
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
if (name === 'build_agentflow') {
|
|
646
|
+
return {
|
|
647
|
+
messages: [
|
|
648
|
+
{
|
|
649
|
+
role: 'user',
|
|
650
|
+
content: {
|
|
651
|
+
type: 'text',
|
|
652
|
+
text: `Help me build a Flowise agent flow that: ${args?.description}
|
|
653
|
+
|
|
654
|
+
Agent flows use nodes from the "Agent Flows" category.
|
|
655
|
+
Use list_nodes with category="Agent Flows" to see available nodes.
|
|
656
|
+
Use list_templates with type="agentflowv2" for examples.
|
|
657
|
+
Key nodes include: startAgentflow, agentAgentflow, llmAgentflow, toolAgentflow, conditionAgentflow.
|
|
658
|
+
Use get_node_schema to understand each node's configuration.`,
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
665
|
+
});
|
|
666
|
+
// === START SERVER ===
|
|
667
|
+
async function main() {
|
|
668
|
+
// Initialize database
|
|
669
|
+
const SQL = await initSqlJs();
|
|
670
|
+
const dbPath = findDatabasePath();
|
|
671
|
+
const dbBuffer = fs.readFileSync(dbPath);
|
|
672
|
+
db = new SQL.Database(dbBuffer);
|
|
673
|
+
console.error('DS-MCP-FLOWISE server started');
|
|
674
|
+
console.error(`Database loaded from: ${dbPath}`);
|
|
675
|
+
const transport = new StdioServerTransport();
|
|
676
|
+
await server.connect(transport);
|
|
677
|
+
}
|
|
678
|
+
main().catch(console.error);
|
|
679
|
+
//# sourceMappingURL=index.js.map
|