@synergenius/flow-weaver 0.10.9 → 0.10.11
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/dist/cli/commands/mcp-setup.d.ts +45 -0
- package/dist/cli/commands/mcp-setup.js +374 -0
- package/dist/cli/commands/tunnel.d.ts +20 -0
- package/dist/cli/commands/tunnel.js +156 -0
- package/dist/cli/flow-weaver.mjs +4467 -415
- package/dist/cli/index.js +47 -13
- package/dist/mcp/editor-connection.d.ts +11 -9
- package/dist/mcp/editor-connection.js +29 -12
- package/dist/mcp/resources.d.ts +3 -3
- package/dist/mcp/resources.js +5 -5
- package/dist/mcp/server.js +3 -5
- package/dist/mcp/tools-editor.js +22 -22
- package/dist/mcp/types.d.ts +3 -3
- package/package.json +3 -1
package/dist/cli/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { initCommand } from './commands/init.js';
|
|
|
19
19
|
import { watchCommand } from './commands/watch.js';
|
|
20
20
|
import { devCommand } from './commands/dev.js';
|
|
21
21
|
import { listenCommand } from './commands/listen.js';
|
|
22
|
+
import { tunnelCommand } from './commands/tunnel.js';
|
|
22
23
|
import { mcpServerCommand } from '../mcp/server.js';
|
|
23
24
|
import { uiFocusNode, uiAddNode, uiOpenWorkflow, uiGetState, uiBatch } from './commands/ui.js';
|
|
24
25
|
import { grammarCommand } from './commands/grammar.js';
|
|
@@ -34,6 +35,7 @@ import { docsListCommand, docsReadCommand, docsSearchCommand } from './commands/
|
|
|
34
35
|
import { statusCommand } from './commands/status.js';
|
|
35
36
|
import { implementCommand } from './commands/implement.js';
|
|
36
37
|
import { marketInitCommand, marketPackCommand, marketPublishCommand, marketInstallCommand, marketSearchCommand, marketListCommand, } from './commands/market.js';
|
|
38
|
+
import { mcpSetupCommand } from './commands/mcp-setup.js';
|
|
37
39
|
import { logger } from './utils/logger.js';
|
|
38
40
|
import { getErrorMessage } from '../utils/error-utils.js';
|
|
39
41
|
import { DEFAULT_SERVER_URL } from '../defaults.js';
|
|
@@ -257,8 +259,8 @@ program
|
|
|
257
259
|
// Listen command
|
|
258
260
|
program
|
|
259
261
|
.command('listen')
|
|
260
|
-
.description('Connect to
|
|
261
|
-
.option('-s, --server <url>', '
|
|
262
|
+
.description('Connect to Studio and stream integration events as JSON lines')
|
|
263
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
262
264
|
.action(async (options) => {
|
|
263
265
|
try {
|
|
264
266
|
await listenCommand(options);
|
|
@@ -268,11 +270,27 @@ program
|
|
|
268
270
|
process.exit(1);
|
|
269
271
|
}
|
|
270
272
|
});
|
|
273
|
+
// Tunnel command
|
|
274
|
+
program
|
|
275
|
+
.command('tunnel')
|
|
276
|
+
.description('Create a tunnel from cloud Studio to your local development server')
|
|
277
|
+
.requiredOption('-k, --key <apiKey>', 'API key for cloud authentication (fw_xxxx)')
|
|
278
|
+
.option('-c, --cloud <url>', 'Cloud server URL', 'https://flowweaver.dev')
|
|
279
|
+
.option('-s, --server <url>', 'Local server URL', DEFAULT_SERVER_URL)
|
|
280
|
+
.action(async (options) => {
|
|
281
|
+
try {
|
|
282
|
+
await tunnelCommand(options);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
logger.error(`Command failed: ${getErrorMessage(error)}`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
271
289
|
// MCP server command
|
|
272
290
|
program
|
|
273
291
|
.command('mcp-server')
|
|
274
292
|
.description('Start MCP server for Claude Code integration')
|
|
275
|
-
.option('-s, --server <url>', '
|
|
293
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
276
294
|
.option('--stdio', 'Run in MCP stdio mode (skip interactive registration)')
|
|
277
295
|
.action(async (options) => {
|
|
278
296
|
try {
|
|
@@ -283,12 +301,28 @@ program
|
|
|
283
301
|
process.exit(1);
|
|
284
302
|
}
|
|
285
303
|
});
|
|
286
|
-
//
|
|
287
|
-
|
|
304
|
+
// MCP setup command
|
|
305
|
+
program
|
|
306
|
+
.command('mcp-setup')
|
|
307
|
+
.description('Configure MCP server for AI coding tools (Claude, Cursor, VS Code, Windsurf, Codex, OpenClaw)')
|
|
308
|
+
.option('--tool <tools...>', 'Specific tools to configure (claude, cursor, vscode, windsurf, codex, openclaw)')
|
|
309
|
+
.option('--all', 'Configure all detected tools without prompting')
|
|
310
|
+
.option('--list', 'List detected tools without configuring')
|
|
311
|
+
.action(async (options) => {
|
|
312
|
+
try {
|
|
313
|
+
await mcpSetupCommand(options);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
logger.error(`Command failed: ${getErrorMessage(error)}`);
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// UI command group (send commands to Studio)
|
|
321
|
+
const uiCmd = program.command('ui').description('Send commands to Studio');
|
|
288
322
|
uiCmd
|
|
289
323
|
.command('focus-node <nodeId>')
|
|
290
|
-
.description('Select and center a node in
|
|
291
|
-
.option('-s, --server <url>', '
|
|
324
|
+
.description('Select and center a node in Studio')
|
|
325
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
292
326
|
.action(async (nodeId, options) => {
|
|
293
327
|
try {
|
|
294
328
|
await uiFocusNode(nodeId, options);
|
|
@@ -301,7 +335,7 @@ uiCmd
|
|
|
301
335
|
uiCmd
|
|
302
336
|
.command('add-node <nodeTypeName>')
|
|
303
337
|
.description('Add a node at viewport center')
|
|
304
|
-
.option('-s, --server <url>', '
|
|
338
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
305
339
|
.action(async (nodeTypeName, options) => {
|
|
306
340
|
try {
|
|
307
341
|
await uiAddNode(nodeTypeName, options);
|
|
@@ -313,8 +347,8 @@ uiCmd
|
|
|
313
347
|
});
|
|
314
348
|
uiCmd
|
|
315
349
|
.command('open-workflow <filePath>')
|
|
316
|
-
.description('Open a workflow file in
|
|
317
|
-
.option('-s, --server <url>', '
|
|
350
|
+
.description('Open a workflow file in Studio')
|
|
351
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
318
352
|
.action(async (filePath, options) => {
|
|
319
353
|
try {
|
|
320
354
|
await uiOpenWorkflow(filePath, options);
|
|
@@ -326,8 +360,8 @@ uiCmd
|
|
|
326
360
|
});
|
|
327
361
|
uiCmd
|
|
328
362
|
.command('get-state')
|
|
329
|
-
.description('Return current workflow state from
|
|
330
|
-
.option('-s, --server <url>', '
|
|
363
|
+
.description('Return current workflow state from Studio')
|
|
364
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
331
365
|
.action(async (options) => {
|
|
332
366
|
try {
|
|
333
367
|
await uiGetState(options);
|
|
@@ -340,7 +374,7 @@ uiCmd
|
|
|
340
374
|
uiCmd
|
|
341
375
|
.command('batch <json>')
|
|
342
376
|
.description('Execute a batch of commands with auto-snapshot rollback')
|
|
343
|
-
.option('-s, --server <url>', '
|
|
377
|
+
.option('-s, --server <url>', 'Studio URL', DEFAULT_SERVER_URL)
|
|
344
378
|
.action(async (json, options) => {
|
|
345
379
|
try {
|
|
346
380
|
await uiBatch(json, options);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AckResponse, EditorConnectionOptions } from './types.js';
|
|
2
2
|
import type { EventBuffer } from './event-buffer.js';
|
|
3
3
|
/**
|
|
4
|
-
* Manages a WebSocket connection to
|
|
4
|
+
* Manages a WebSocket connection to Flow Weaver Studio via socket.io.
|
|
5
5
|
* Supports sending commands and batches with request/response correlation,
|
|
6
6
|
* and forwards incoming `fw:` and `integration:` events to an {@link EventBuffer}.
|
|
7
7
|
*/
|
|
@@ -11,40 +11,42 @@ export declare class EditorConnection {
|
|
|
11
11
|
private buffer;
|
|
12
12
|
private ioFactory;
|
|
13
13
|
private ackTimeout;
|
|
14
|
+
private hasLoggedDisconnect;
|
|
15
|
+
private wasConnected;
|
|
14
16
|
/**
|
|
15
|
-
* @param serverUrl - The base URL of the
|
|
17
|
+
* @param serverUrl - The base URL of the Studio WebSocket server.
|
|
16
18
|
* @param buffer - The event buffer to push incoming events into.
|
|
17
19
|
* @param options - Optional connection configuration (custom io factory, ack timeout).
|
|
18
20
|
*/
|
|
19
21
|
constructor(serverUrl: string, buffer: EventBuffer, options?: EditorConnectionOptions);
|
|
20
22
|
/**
|
|
21
|
-
* Establishes a WebSocket connection to
|
|
23
|
+
* Establishes a WebSocket connection to Studio's `/integrations` namespace.
|
|
22
24
|
* Cleans up any previous connection first. Incoming `fw:` and `integration:` events
|
|
23
25
|
* are automatically forwarded to the event buffer.
|
|
24
26
|
* @param log - Optional logging callback for connection lifecycle events.
|
|
25
27
|
*/
|
|
26
28
|
connect(log?: (msg: string) => void): void;
|
|
27
|
-
/** Whether the socket is currently connected to
|
|
29
|
+
/** Whether the socket is currently connected to Studio. */
|
|
28
30
|
get isConnected(): boolean;
|
|
29
31
|
/**
|
|
30
|
-
* Sends a single command to
|
|
32
|
+
* Sends a single command to Studio and waits for an acknowledgement.
|
|
31
33
|
* Returns an error response if not connected or if the ack times out.
|
|
32
34
|
* @param action - The command action name (e.g. "get-state", "add-node").
|
|
33
35
|
* @param params - Parameters for the command.
|
|
34
|
-
* @returns
|
|
36
|
+
* @returns Studio's acknowledgement response.
|
|
35
37
|
*/
|
|
36
38
|
sendCommand(action: string, params: Record<string, unknown>): Promise<AckResponse>;
|
|
37
39
|
/**
|
|
38
|
-
* Sends a batch of commands to
|
|
40
|
+
* Sends a batch of commands to Studio as a single request and waits for acknowledgement.
|
|
39
41
|
* Returns an error response if not connected or if the ack times out.
|
|
40
42
|
* @param commands - Array of commands, each with an action name and optional params.
|
|
41
|
-
* @returns
|
|
43
|
+
* @returns Studio's acknowledgement response for the entire batch.
|
|
42
44
|
*/
|
|
43
45
|
sendBatch(commands: Array<{
|
|
44
46
|
action: string;
|
|
45
47
|
params?: Record<string, unknown>;
|
|
46
48
|
}>): Promise<AckResponse>;
|
|
47
|
-
/** Disconnects from
|
|
49
|
+
/** Disconnects from Studio and releases the socket. */
|
|
48
50
|
disconnect(): void;
|
|
49
51
|
}
|
|
50
52
|
//# sourceMappingURL=editor-connection.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { io as socketIO } from 'socket.io-client';
|
|
2
2
|
/**
|
|
3
|
-
* Manages a WebSocket connection to
|
|
3
|
+
* Manages a WebSocket connection to Flow Weaver Studio via socket.io.
|
|
4
4
|
* Supports sending commands and batches with request/response correlation,
|
|
5
5
|
* and forwards incoming `fw:` and `integration:` events to an {@link EventBuffer}.
|
|
6
6
|
*/
|
|
@@ -10,8 +10,10 @@ export class EditorConnection {
|
|
|
10
10
|
buffer;
|
|
11
11
|
ioFactory;
|
|
12
12
|
ackTimeout;
|
|
13
|
+
hasLoggedDisconnect = false;
|
|
14
|
+
wasConnected = false;
|
|
13
15
|
/**
|
|
14
|
-
* @param serverUrl - The base URL of the
|
|
16
|
+
* @param serverUrl - The base URL of the Studio WebSocket server.
|
|
15
17
|
* @param buffer - The event buffer to push incoming events into.
|
|
16
18
|
* @param options - Optional connection configuration (custom io factory, ack timeout).
|
|
17
19
|
*/
|
|
@@ -22,7 +24,7 @@ export class EditorConnection {
|
|
|
22
24
|
this.ackTimeout = options?.ackTimeout ?? 10_000;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
|
-
* Establishes a WebSocket connection to
|
|
27
|
+
* Establishes a WebSocket connection to Studio's `/integrations` namespace.
|
|
26
28
|
* Cleans up any previous connection first. Incoming `fw:` and `integration:` events
|
|
27
29
|
* are automatically forwarded to the event buffer.
|
|
28
30
|
* @param log - Optional logging callback for connection lifecycle events.
|
|
@@ -44,12 +46,27 @@ export class EditorConnection {
|
|
|
44
46
|
reconnectionAttempts: Infinity,
|
|
45
47
|
});
|
|
46
48
|
this.socket.on('connect', () => {
|
|
47
|
-
|
|
49
|
+
const msg = this.wasConnected
|
|
50
|
+
? `Reconnected to Studio at ${this.serverUrl}`
|
|
51
|
+
: `Connected to Studio at ${this.serverUrl}`;
|
|
52
|
+
log?.(msg);
|
|
53
|
+
this.hasLoggedDisconnect = false;
|
|
54
|
+
this.wasConnected = true;
|
|
48
55
|
this.buffer.push('mcp:status', { status: 'connected', server: this.serverUrl });
|
|
49
56
|
});
|
|
50
57
|
if (log) {
|
|
51
|
-
this.socket.on('disconnect', (
|
|
52
|
-
|
|
58
|
+
this.socket.on('disconnect', () => {
|
|
59
|
+
if (!this.hasLoggedDisconnect) {
|
|
60
|
+
log('Studio disconnected');
|
|
61
|
+
this.hasLoggedDisconnect = true;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
this.socket.on('connect_error', () => {
|
|
65
|
+
if (!this.hasLoggedDisconnect) {
|
|
66
|
+
log(`Studio not reachable at ${this.serverUrl}`);
|
|
67
|
+
this.hasLoggedDisconnect = true;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
53
70
|
}
|
|
54
71
|
this.socket.onAny((event, data) => {
|
|
55
72
|
if (event.startsWith('fw:') || event.startsWith('integration:')) {
|
|
@@ -57,16 +74,16 @@ export class EditorConnection {
|
|
|
57
74
|
}
|
|
58
75
|
});
|
|
59
76
|
}
|
|
60
|
-
/** Whether the socket is currently connected to
|
|
77
|
+
/** Whether the socket is currently connected to Studio. */
|
|
61
78
|
get isConnected() {
|
|
62
79
|
return this.socket?.connected ?? false;
|
|
63
80
|
}
|
|
64
81
|
/**
|
|
65
|
-
* Sends a single command to
|
|
82
|
+
* Sends a single command to Studio and waits for an acknowledgement.
|
|
66
83
|
* Returns an error response if not connected or if the ack times out.
|
|
67
84
|
* @param action - The command action name (e.g. "get-state", "add-node").
|
|
68
85
|
* @param params - Parameters for the command.
|
|
69
|
-
* @returns
|
|
86
|
+
* @returns Studio's acknowledgement response.
|
|
70
87
|
*/
|
|
71
88
|
async sendCommand(action, params) {
|
|
72
89
|
if (!this.socket) {
|
|
@@ -90,10 +107,10 @@ export class EditorConnection {
|
|
|
90
107
|
});
|
|
91
108
|
}
|
|
92
109
|
/**
|
|
93
|
-
* Sends a batch of commands to
|
|
110
|
+
* Sends a batch of commands to Studio as a single request and waits for acknowledgement.
|
|
94
111
|
* Returns an error response if not connected or if the ack times out.
|
|
95
112
|
* @param commands - Array of commands, each with an action name and optional params.
|
|
96
|
-
* @returns
|
|
113
|
+
* @returns Studio's acknowledgement response for the entire batch.
|
|
97
114
|
*/
|
|
98
115
|
async sendBatch(commands) {
|
|
99
116
|
if (!this.socket) {
|
|
@@ -116,7 +133,7 @@ export class EditorConnection {
|
|
|
116
133
|
this.socket.emit('integration:batch', { requestId, commands });
|
|
117
134
|
});
|
|
118
135
|
}
|
|
119
|
-
/** Disconnects from
|
|
136
|
+
/** Disconnects from Studio and releases the socket. */
|
|
120
137
|
disconnect() {
|
|
121
138
|
this.socket?.disconnect();
|
|
122
139
|
this.socket = null;
|
package/dist/mcp/resources.d.ts
CHANGED
|
@@ -2,12 +2,12 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import type { EditorConnection } from './editor-connection.js';
|
|
3
3
|
import type { EventBuffer } from './event-buffer.js';
|
|
4
4
|
/**
|
|
5
|
-
* Registers MCP resources that expose
|
|
5
|
+
* Registers MCP resources that expose Studio state and event data.
|
|
6
6
|
* Registers two resources:
|
|
7
7
|
* - `fw://events` - read-only peek at the event buffer.
|
|
8
|
-
* - `fw://state` - current
|
|
8
|
+
* - `fw://state` - current Studio/workflow state fetched via the Studio connection.
|
|
9
9
|
* @param mcp - The MCP server instance to register resources on.
|
|
10
|
-
* @param connection - The
|
|
10
|
+
* @param connection - The Studio WebSocket connection used to query state.
|
|
11
11
|
* @param buffer - The event buffer to read events from.
|
|
12
12
|
*/
|
|
13
13
|
export declare function registerResources(mcp: McpServer, connection: EditorConnection, buffer: EventBuffer): void;
|
package/dist/mcp/resources.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registers MCP resources that expose
|
|
2
|
+
* Registers MCP resources that expose Studio state and event data.
|
|
3
3
|
* Registers two resources:
|
|
4
4
|
* - `fw://events` - read-only peek at the event buffer.
|
|
5
|
-
* - `fw://state` - current
|
|
5
|
+
* - `fw://state` - current Studio/workflow state fetched via the Studio connection.
|
|
6
6
|
* @param mcp - The MCP server instance to register resources on.
|
|
7
|
-
* @param connection - The
|
|
7
|
+
* @param connection - The Studio WebSocket connection used to query state.
|
|
8
8
|
* @param buffer - The event buffer to read events from.
|
|
9
9
|
*/
|
|
10
10
|
export function registerResources(mcp, connection, buffer) {
|
|
@@ -16,13 +16,13 @@ export function registerResources(mcp, connection, buffer) {
|
|
|
16
16
|
},
|
|
17
17
|
],
|
|
18
18
|
}));
|
|
19
|
-
mcp.resource('state', 'fw://state', { description: 'Current
|
|
19
|
+
mcp.resource('state', 'fw://state', { description: 'Current Studio/workflow state' }, async () => {
|
|
20
20
|
if (!connection.isConnected) {
|
|
21
21
|
return {
|
|
22
22
|
contents: [
|
|
23
23
|
{
|
|
24
24
|
uri: 'fw://state',
|
|
25
|
-
text: JSON.stringify({ error: 'Not connected to
|
|
25
|
+
text: JSON.stringify({ error: 'Not connected to Studio' }),
|
|
26
26
|
},
|
|
27
27
|
],
|
|
28
28
|
};
|
package/dist/mcp/server.js
CHANGED
|
@@ -3,7 +3,6 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { DEFAULT_SERVER_URL } from '../defaults.js';
|
|
4
4
|
import { EventBuffer } from './event-buffer.js';
|
|
5
5
|
import { EditorConnection } from './editor-connection.js';
|
|
6
|
-
import { offerClaudeRegistration } from './auto-registration.js';
|
|
7
6
|
import { registerEditorTools } from './tools-editor.js';
|
|
8
7
|
import { registerQueryTools } from './tools-query.js';
|
|
9
8
|
import { registerTemplateTools } from './tools-template.js';
|
|
@@ -52,7 +51,7 @@ export async function startMcpServer(options) {
|
|
|
52
51
|
// Use injected deps for testing, or create real ones
|
|
53
52
|
const buffer = options._testDeps?.buffer ?? new EventBuffer(undefined, undefined, filterFromEnv);
|
|
54
53
|
const connection = options._testDeps?.connection ?? new EditorConnection(serverUrl, buffer);
|
|
55
|
-
// Connect to
|
|
54
|
+
// Connect to Studio (non-blocking)
|
|
56
55
|
if (!options._testDeps) {
|
|
57
56
|
const log = options.stdio
|
|
58
57
|
? (msg) => process.stderr.write(msg + '\n')
|
|
@@ -91,9 +90,8 @@ export async function mcpServerCommand(options) {
|
|
|
91
90
|
? (msg) => process.stderr.write(msg + '\n')
|
|
92
91
|
: (msg) => process.stdout.write(msg + '\n');
|
|
93
92
|
if (!options.stdio) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
log(`Starting MCP server (editor: ${serverUrl})...`);
|
|
93
|
+
log('Tip: run "flow-weaver mcp-setup" to register with your AI tools.');
|
|
94
|
+
log(`Starting MCP server (Studio: ${serverUrl})...`);
|
|
97
95
|
}
|
|
98
96
|
await startMcpServer(options);
|
|
99
97
|
if (!options.stdio) {
|
package/dist/mcp/tools-editor.js
CHANGED
|
@@ -4,8 +4,8 @@ import { executeWorkflowFromFile } from './workflow-executor.js';
|
|
|
4
4
|
import { AgentChannel } from './agent-channel.js';
|
|
5
5
|
import { storePendingRun, getPendingRun, removePendingRun, listPendingRuns } from './run-registry.js';
|
|
6
6
|
/**
|
|
7
|
-
* Unwrap
|
|
8
|
-
*
|
|
7
|
+
* Unwrap Studio ack responses to flatten double-nested results.
|
|
8
|
+
* Studio returns { requestId, success, result: { actualData } },
|
|
9
9
|
* we extract the `result` field to avoid double-nesting in MCP output.
|
|
10
10
|
*/
|
|
11
11
|
function unwrapAckResult(ack) {
|
|
@@ -15,25 +15,25 @@ function unwrapAckResult(ack) {
|
|
|
15
15
|
return ack;
|
|
16
16
|
}
|
|
17
17
|
export function registerEditorTools(mcp, connection, buffer) {
|
|
18
|
-
mcp.tool('fw_check_events', 'Get buffered
|
|
18
|
+
mcp.tool('fw_check_events', 'Get buffered Studio events. Returns and clears the event buffer unless peek=true.', { peek: z.boolean().optional().describe('If true, read events without clearing the buffer') }, async (args) => {
|
|
19
19
|
const events = args.peek ? buffer.peek() : buffer.drain();
|
|
20
20
|
return makeToolResult(events);
|
|
21
21
|
});
|
|
22
|
-
mcp.tool('fw_get_state', 'Get the current
|
|
22
|
+
mcp.tool('fw_get_state', 'Get the current Studio/workflow state from Flow Weaver.', {}, async () => {
|
|
23
23
|
if (!connection.isConnected) {
|
|
24
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
24
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
25
25
|
}
|
|
26
26
|
const result = await connection.sendCommand('get-state', {});
|
|
27
27
|
return makeToolResult(unwrapAckResult(result));
|
|
28
28
|
});
|
|
29
|
-
mcp.tool('fw_focus_node', 'Select and center a node in
|
|
29
|
+
mcp.tool('fw_focus_node', 'Select and center a node in Flow Weaver Studio.', { nodeId: z.string().describe('The ID of the node to focus') }, async (args) => {
|
|
30
30
|
if (!connection.isConnected) {
|
|
31
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
31
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
32
32
|
}
|
|
33
33
|
const result = await connection.sendCommand('focus-node', { nodeId: args.nodeId });
|
|
34
34
|
return makeToolResult(unwrapAckResult(result));
|
|
35
35
|
});
|
|
36
|
-
mcp.tool('fw_add_node', 'Add a new node to the workflow in
|
|
36
|
+
mcp.tool('fw_add_node', 'Add a new node to the workflow in Flow Weaver Studio.', {
|
|
37
37
|
nodeTypeName: z.string().describe('The name of the node type to add'),
|
|
38
38
|
nodeTypeDefinition: z
|
|
39
39
|
.record(z.unknown())
|
|
@@ -41,7 +41,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
41
41
|
.describe('Optional node type definition object'),
|
|
42
42
|
}, async (args) => {
|
|
43
43
|
if (!connection.isConnected) {
|
|
44
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
44
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
45
45
|
}
|
|
46
46
|
const params = { nodeTypeName: args.nodeTypeName };
|
|
47
47
|
if (args.nodeTypeDefinition) {
|
|
@@ -50,19 +50,19 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
50
50
|
const result = await connection.sendCommand('add-node', params);
|
|
51
51
|
return makeToolResult(unwrapAckResult(result));
|
|
52
52
|
});
|
|
53
|
-
mcp.tool('fw_open_workflow', 'Open a workflow file in
|
|
53
|
+
mcp.tool('fw_open_workflow', 'Open a workflow file in Flow Weaver Studio.', { filePath: z.string().describe('The path to the workflow file to open') }, async (args) => {
|
|
54
54
|
if (!connection.isConnected) {
|
|
55
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
55
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
56
56
|
}
|
|
57
57
|
const result = await connection.sendCommand('open-workflow', { filePath: args.filePath });
|
|
58
58
|
return makeToolResult(unwrapAckResult(result));
|
|
59
59
|
});
|
|
60
|
-
mcp.tool('fw_send_command', 'Send a generic command to
|
|
60
|
+
mcp.tool('fw_send_command', 'Send a generic command to Flow Weaver Studio.', {
|
|
61
61
|
action: z.string().describe('The command action name'),
|
|
62
62
|
params: z.record(z.unknown()).optional().describe('Optional parameters for the command'),
|
|
63
63
|
}, async (args) => {
|
|
64
64
|
if (!connection.isConnected) {
|
|
65
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
65
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
66
66
|
}
|
|
67
67
|
const result = await connection.sendCommand(args.action, args.params ?? {});
|
|
68
68
|
return makeToolResult(unwrapAckResult(result));
|
|
@@ -76,7 +76,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
76
76
|
.describe('Array of commands to execute as a batch'),
|
|
77
77
|
}, async (args) => {
|
|
78
78
|
if (!connection.isConnected) {
|
|
79
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
79
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
80
80
|
}
|
|
81
81
|
const result = await connection.sendBatch(args.commands);
|
|
82
82
|
return makeToolResult(unwrapAckResult(result));
|
|
@@ -84,7 +84,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
84
84
|
// --- New tools ---
|
|
85
85
|
mcp.tool('fw_remove_node', 'Remove a node and its connections from the workflow.', { nodeName: z.string().describe('The name/ID of the node to remove') }, async (args) => {
|
|
86
86
|
if (!connection.isConnected) {
|
|
87
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
87
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
88
88
|
}
|
|
89
89
|
const result = await connection.sendCommand('remove-node', { nodeName: args.nodeName });
|
|
90
90
|
return makeToolResult(unwrapAckResult(result));
|
|
@@ -101,7 +101,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
101
101
|
.describe('Connection specification'),
|
|
102
102
|
}, async (args) => {
|
|
103
103
|
if (!connection.isConnected) {
|
|
104
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
104
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
105
105
|
}
|
|
106
106
|
const bridgeAction = args.action === 'add' ? 'add-connection' : 'remove-connection';
|
|
107
107
|
const result = await connection.sendCommand(bridgeAction, {
|
|
@@ -111,7 +111,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
111
111
|
});
|
|
112
112
|
mcp.tool('fw_undo_redo', 'Undo or redo the last workflow change.', { action: z.enum(['undo', 'redo']).describe('Whether to undo or redo') }, async (args) => {
|
|
113
113
|
if (!connection.isConnected) {
|
|
114
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
114
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
115
115
|
}
|
|
116
116
|
const result = await connection.sendCommand(args.action, {});
|
|
117
117
|
return makeToolResult(unwrapAckResult(result));
|
|
@@ -123,7 +123,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
123
123
|
filePath: z
|
|
124
124
|
.string()
|
|
125
125
|
.optional()
|
|
126
|
-
.describe('Path to workflow file. When provided, compiles and executes directly (no
|
|
126
|
+
.describe('Path to workflow file. When provided, compiles and executes directly (no Studio needed)'),
|
|
127
127
|
workflowName: z
|
|
128
128
|
.string()
|
|
129
129
|
.optional()
|
|
@@ -134,7 +134,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
134
134
|
.optional()
|
|
135
135
|
.describe('Include execution trace events (default: true)'),
|
|
136
136
|
}, async (args, extra) => {
|
|
137
|
-
// When filePath is provided, compile and execute directly (no
|
|
137
|
+
// When filePath is provided, compile and execute directly (no Studio needed)
|
|
138
138
|
if (args.filePath) {
|
|
139
139
|
try {
|
|
140
140
|
const channel = new AgentChannel();
|
|
@@ -197,9 +197,9 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
197
197
|
return makeErrorResult(code, message);
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
-
// No filePath: delegate to
|
|
200
|
+
// No filePath: delegate to Studio via Socket.io (existing behavior)
|
|
201
201
|
if (!connection.isConnected) {
|
|
202
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
202
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
203
203
|
}
|
|
204
204
|
const result = await connection.sendCommand('execute-workflow', args.params ?? {});
|
|
205
205
|
return makeToolResult(unwrapAckResult(result));
|
|
@@ -247,7 +247,7 @@ export function registerEditorTools(mcp, connection, buffer) {
|
|
|
247
247
|
});
|
|
248
248
|
mcp.tool('fw_get_workflow_details', 'Get full workflow structure including nodes, connections, types, and positions.', {}, async () => {
|
|
249
249
|
if (!connection.isConnected) {
|
|
250
|
-
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to
|
|
250
|
+
return makeErrorResult('EDITOR_NOT_CONNECTED', 'Not connected to Studio. Is Studio running?');
|
|
251
251
|
}
|
|
252
252
|
const result = await connection.sendCommand('get-workflow-details', {});
|
|
253
253
|
return makeToolResult(unwrapAckResult(result));
|
package/dist/mcp/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { EventBuffer } from './event-buffer.js';
|
|
|
3
3
|
import type { EditorConnection } from './editor-connection.js';
|
|
4
4
|
/** Options for initializing the MCP server. */
|
|
5
5
|
export interface McpServerOptions {
|
|
6
|
-
/** WebSocket server URL for
|
|
6
|
+
/** WebSocket server URL for Studio connection. */
|
|
7
7
|
server?: string;
|
|
8
8
|
/** Whether to use stdio transport instead of SSE. */
|
|
9
9
|
stdio?: boolean;
|
|
@@ -27,7 +27,7 @@ export interface RegistrationDeps {
|
|
|
27
27
|
/** Resolve the path to the Flow Weaver CLI executable. */
|
|
28
28
|
resolveCliPath: () => string;
|
|
29
29
|
}
|
|
30
|
-
/** Acknowledgement response returned by
|
|
30
|
+
/** Acknowledgement response returned by Studio after processing a command. */
|
|
31
31
|
export interface AckResponse {
|
|
32
32
|
/** Unique identifier correlating the response to its originating request. */
|
|
33
33
|
requestId: string;
|
|
@@ -47,7 +47,7 @@ export interface BufferedEvent {
|
|
|
47
47
|
/** ISO 8601 timestamp of when the event was buffered. */
|
|
48
48
|
timestamp: string;
|
|
49
49
|
}
|
|
50
|
-
/** Options for configuring the
|
|
50
|
+
/** Options for configuring the Studio WebSocket connection. */
|
|
51
51
|
export interface EditorConnectionOptions {
|
|
52
52
|
/** Custom socket.io factory, primarily used for injecting mocks in tests. */
|
|
53
53
|
ioFactory?: typeof socketIO;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.11",
|
|
4
4
|
"description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -139,12 +139,14 @@
|
|
|
139
139
|
"socket.io-client": "^4.8.0",
|
|
140
140
|
"source-map": "^0.7.6",
|
|
141
141
|
"ts-morph": "^21.0.1",
|
|
142
|
+
"ws": "^8.19.0",
|
|
142
143
|
"zod": "^3.22.4"
|
|
143
144
|
},
|
|
144
145
|
"devDependencies": {
|
|
145
146
|
"@types/js-yaml": "^4.0.9",
|
|
146
147
|
"@types/node": "^20.11.0",
|
|
147
148
|
"@types/react": "^19.0.0",
|
|
149
|
+
"@types/ws": "^8.18.1",
|
|
148
150
|
"@vitest/coverage-v8": "^4.0.18",
|
|
149
151
|
"esbuild": "^0.27.2",
|
|
150
152
|
"prettier": "^3.1.1",
|