mcpgraph-ux 0.1.1 → 0.1.3
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 +2 -0
- package/app/api/execution/breakpoints/route.ts +106 -0
- package/app/api/execution/context/route.ts +69 -0
- package/app/api/execution/controller/route.ts +121 -0
- package/app/api/execution/history/route.ts +40 -0
- package/app/api/execution/history-with-indices/route.ts +57 -0
- package/app/api/execution/stream/route.ts +34 -0
- package/app/api/graph/route.ts +63 -29
- package/app/api/tools/[toolName]/route.ts +268 -17
- package/app/api/tools/route.ts +3 -15
- package/app/page.module.css +64 -18
- package/app/page.tsx +38 -15
- package/components/DebugControls.module.css +124 -0
- package/components/DebugControls.tsx +209 -0
- package/components/ExecutionHistory.module.css +268 -0
- package/components/ExecutionHistory.tsx +197 -0
- package/components/GraphVisualization.module.css +11 -0
- package/components/GraphVisualization.tsx +350 -21
- package/components/InputForm.module.css +115 -0
- package/components/InputForm.tsx +271 -0
- package/components/ServerDetails.module.css +118 -0
- package/components/ServerDetails.tsx +116 -0
- package/components/TelemetryDashboard.module.css +177 -0
- package/components/TelemetryDashboard.tsx +154 -0
- package/components/ToolList.module.css +2 -2
- package/components/ToolTester.module.css +24 -119
- package/components/ToolTester.tsx +627 -229
- package/package.json +2 -2
- package/server.ts +24 -10
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ A Next.js application that provides a visual interface for the [mcpGraph](https:
|
|
|
6
6
|
- **List tools**: See all available MCP tools defined in your graph configuration
|
|
7
7
|
- **Test tools**: Execute tools with custom parameters and view results from the exit node
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
## Features
|
|
10
12
|
|
|
11
13
|
- 🎨 **Graph Visualization**: Interactive graph visualization using React Flow, showing all nodes and their connections
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getController } from '@/lib/executionController';
|
|
3
|
+
|
|
4
|
+
// Force dynamic rendering
|
|
5
|
+
export const dynamic = 'force-dynamic';
|
|
6
|
+
|
|
7
|
+
// Store breakpoints by executionId (fallback if controller not available)
|
|
8
|
+
const breakpointsStore = new Map<string, string[]>();
|
|
9
|
+
|
|
10
|
+
export async function GET(request: NextRequest) {
|
|
11
|
+
try {
|
|
12
|
+
const executionId = request.nextUrl.searchParams.get('executionId');
|
|
13
|
+
|
|
14
|
+
if (!executionId) {
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: 'Missing executionId parameter' },
|
|
17
|
+
{ status: 400 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const controller = getController(executionId);
|
|
22
|
+
if (controller) {
|
|
23
|
+
// Get breakpoints from controller if available
|
|
24
|
+
const state = controller.getState();
|
|
25
|
+
// Note: mcpGraph doesn't expose breakpoints directly, so we use our store
|
|
26
|
+
const breakpoints = breakpointsStore.get(executionId) || [];
|
|
27
|
+
return NextResponse.json({ breakpoints });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fallback to store
|
|
31
|
+
const breakpoints = breakpointsStore.get(executionId) || [];
|
|
32
|
+
return NextResponse.json({ breakpoints });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Error getting breakpoints:', error);
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
37
|
+
{ status: 500 }
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function POST(request: NextRequest) {
|
|
43
|
+
try {
|
|
44
|
+
const body = await request.json();
|
|
45
|
+
const { executionId, breakpoints } = body;
|
|
46
|
+
|
|
47
|
+
if (!executionId) {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{ error: 'Missing executionId' },
|
|
50
|
+
{ status: 400 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!Array.isArray(breakpoints)) {
|
|
55
|
+
return NextResponse.json(
|
|
56
|
+
{ error: 'breakpoints must be an array' },
|
|
57
|
+
{ status: 400 }
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const controller = getController(executionId);
|
|
62
|
+
if (controller) {
|
|
63
|
+
controller.setBreakpoints(breakpoints);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Store breakpoints
|
|
67
|
+
breakpointsStore.set(executionId, breakpoints);
|
|
68
|
+
|
|
69
|
+
return NextResponse.json({ success: true, breakpoints });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('Error setting breakpoints:', error);
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
74
|
+
{ status: 500 }
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function DELETE(request: NextRequest) {
|
|
80
|
+
try {
|
|
81
|
+
const executionId = request.nextUrl.searchParams.get('executionId');
|
|
82
|
+
|
|
83
|
+
if (!executionId) {
|
|
84
|
+
return NextResponse.json(
|
|
85
|
+
{ error: 'Missing executionId parameter' },
|
|
86
|
+
{ status: 400 }
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const controller = getController(executionId);
|
|
91
|
+
if (controller) {
|
|
92
|
+
controller.clearBreakpoints();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
breakpointsStore.delete(executionId);
|
|
96
|
+
|
|
97
|
+
return NextResponse.json({ success: true });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Error clearing breakpoints:', error);
|
|
100
|
+
return NextResponse.json(
|
|
101
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
102
|
+
{ status: 500 }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getController } from '@/lib/executionController';
|
|
3
|
+
import { getApi } from '@/lib/mcpGraphApi';
|
|
4
|
+
|
|
5
|
+
// Force dynamic rendering
|
|
6
|
+
export const dynamic = 'force-dynamic';
|
|
7
|
+
|
|
8
|
+
export async function GET(request: NextRequest) {
|
|
9
|
+
try {
|
|
10
|
+
const executionId = request.nextUrl.searchParams.get('executionId');
|
|
11
|
+
const nodeId = request.nextUrl.searchParams.get('nodeId');
|
|
12
|
+
const sequenceIdParam = request.nextUrl.searchParams.get('sequenceId');
|
|
13
|
+
|
|
14
|
+
if (!executionId) {
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: 'Missing executionId parameter' },
|
|
17
|
+
{ status: 400 }
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!nodeId || !sequenceIdParam) {
|
|
22
|
+
return NextResponse.json(
|
|
23
|
+
{ error: 'Missing nodeId or sequenceId parameter' },
|
|
24
|
+
{ status: 400 }
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sequenceId = parseInt(sequenceIdParam, 10);
|
|
29
|
+
if (isNaN(sequenceId)) {
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
{ error: 'Invalid sequenceId parameter' },
|
|
32
|
+
{ status: 400 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Try to get controller first (for active executions)
|
|
37
|
+
const controller = getController(executionId);
|
|
38
|
+
let context: Record<string, unknown> | null = null;
|
|
39
|
+
|
|
40
|
+
if (controller) {
|
|
41
|
+
// Use controller's context directly (works for active executions)
|
|
42
|
+
try {
|
|
43
|
+
const state = controller.getState();
|
|
44
|
+
context = state.context.getContextForExecution(sequenceId);
|
|
45
|
+
console.log(`[API] Got context from controller for executionIndex=${sequenceId}, nodeId=${nodeId}:`, context ? 'present' : 'null');
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(`[API] Error getting context from controller:`, error);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
// Fallback to API method (may not work after execution completes)
|
|
51
|
+
const api = getApi();
|
|
52
|
+
if (api) {
|
|
53
|
+
context = api.getContextForExecution(sequenceId);
|
|
54
|
+
console.log(`[API] Got context from API for executionIndex=${sequenceId}, nodeId=${nodeId}:`, context ? 'present' : 'null');
|
|
55
|
+
} else {
|
|
56
|
+
console.error(`[API] No controller or API available`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return NextResponse.json({ context: context || null });
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('Error getting node input context:', error);
|
|
63
|
+
return NextResponse.json(
|
|
64
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
65
|
+
{ status: 500 }
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getController, unregisterController, getAllExecutionIds } from '@/lib/executionController';
|
|
3
|
+
import { sendExecutionEvent, closeExecutionStream } from '@/lib/executionStreamServer';
|
|
4
|
+
import { getApi } from '@/lib/mcpGraphApi';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export async function POST(request: NextRequest) {
|
|
8
|
+
try {
|
|
9
|
+
const body = await request.json();
|
|
10
|
+
const { executionId, action } = body;
|
|
11
|
+
|
|
12
|
+
if (!executionId) {
|
|
13
|
+
return NextResponse.json(
|
|
14
|
+
{ error: 'Missing executionId' },
|
|
15
|
+
{ status: 400 }
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!action || !['pause', 'resume', 'step', 'stop'].includes(action)) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: 'Invalid action. Must be one of: pause, resume, step, stop' },
|
|
22
|
+
{ status: 400 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(`[Controller API] Looking for controller with executionId: ${executionId}`);
|
|
27
|
+
// Force getApi to be called to ensure module is loaded
|
|
28
|
+
const api = getApi();
|
|
29
|
+
console.log(`[Controller API] API instance: ${api ? 'present' : 'null'}`);
|
|
30
|
+
const controller = getController(executionId);
|
|
31
|
+
if (!controller) {
|
|
32
|
+
console.log(`[Controller API] Controller not found for executionId: ${executionId}`);
|
|
33
|
+
// Log all registered executionIds for debugging
|
|
34
|
+
const allIds = getAllExecutionIds();
|
|
35
|
+
console.log(`[Controller API] Currently registered executionIds: ${allIds.length > 0 ? allIds.join(', ') : 'none'}`);
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ error: 'Execution not found or not active' },
|
|
38
|
+
{ status: 404 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
console.log(`[Controller API] Found controller for executionId: ${executionId}, status: ${controller.getState().status}`);
|
|
42
|
+
|
|
43
|
+
const state = controller.getState();
|
|
44
|
+
|
|
45
|
+
switch (action) {
|
|
46
|
+
case 'pause':
|
|
47
|
+
if (state.status !== 'running') {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{ error: `Cannot pause: execution status is ${state.status}` },
|
|
50
|
+
{ status: 400 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
controller.pause();
|
|
54
|
+
// Don't send stateUpdate here - onPause hook will send it
|
|
55
|
+
return NextResponse.json({ success: true, status: 'paused' });
|
|
56
|
+
|
|
57
|
+
case 'resume':
|
|
58
|
+
if (state.status !== 'paused') {
|
|
59
|
+
return NextResponse.json(
|
|
60
|
+
{ error: `Cannot resume: execution status is ${state.status}` },
|
|
61
|
+
{ status: 400 }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
controller.resume();
|
|
65
|
+
// Don't send stateUpdate here - the onResume hook already sends it
|
|
66
|
+
return NextResponse.json({ success: true, status: 'running' });
|
|
67
|
+
|
|
68
|
+
case 'step':
|
|
69
|
+
if (state.status !== 'paused') {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{ error: `Cannot step: execution status is ${state.status}` },
|
|
72
|
+
{ status: 400 }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
await controller.step();
|
|
76
|
+
const newState = controller.getState();
|
|
77
|
+
// Don't send stateUpdate here - the onPause hook already sends the correct stateUpdate
|
|
78
|
+
// when step completes and pauses at the next node
|
|
79
|
+
return NextResponse.json({
|
|
80
|
+
success: true,
|
|
81
|
+
status: newState.status,
|
|
82
|
+
currentNodeId: newState.currentNodeId,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
case 'stop':
|
|
86
|
+
if (state.status !== 'running' && state.status !== 'paused') {
|
|
87
|
+
return NextResponse.json(
|
|
88
|
+
{ error: `Cannot stop: execution status is ${state.status}` },
|
|
89
|
+
{ status: 400 }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Call stop() - this sets status to "stopped" and will cause execution to throw "Execution was stopped"
|
|
94
|
+
controller.stop();
|
|
95
|
+
|
|
96
|
+
// Send stopped event
|
|
97
|
+
sendExecutionEvent(executionId, 'executionStopped', {
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Clean up controller and stream
|
|
102
|
+
unregisterController(executionId);
|
|
103
|
+
closeExecutionStream(executionId);
|
|
104
|
+
|
|
105
|
+
return NextResponse.json({ success: true, status: 'stopped' });
|
|
106
|
+
|
|
107
|
+
default:
|
|
108
|
+
return NextResponse.json(
|
|
109
|
+
{ error: 'Invalid action' },
|
|
110
|
+
{ status: 400 }
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('Error in controller action:', error);
|
|
115
|
+
return NextResponse.json(
|
|
116
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
117
|
+
{ status: 500 }
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getController } from '@/lib/executionController';
|
|
3
|
+
import { getApi } from '@/lib/mcpGraphApi';
|
|
4
|
+
|
|
5
|
+
// Force dynamic rendering
|
|
6
|
+
export const dynamic = 'force-dynamic';
|
|
7
|
+
|
|
8
|
+
export async function GET(request: NextRequest) {
|
|
9
|
+
try {
|
|
10
|
+
const executionId = request.nextUrl.searchParams.get('executionId');
|
|
11
|
+
|
|
12
|
+
if (!executionId) {
|
|
13
|
+
return NextResponse.json(
|
|
14
|
+
{ error: 'Missing executionId parameter' },
|
|
15
|
+
{ status: 400 }
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const controller = getController(executionId);
|
|
20
|
+
if (!controller) {
|
|
21
|
+
return NextResponse.json(
|
|
22
|
+
{ error: 'Execution not found or not active' },
|
|
23
|
+
{ status: 404 }
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Get execution history from the controller's state
|
|
28
|
+
const state = controller.getState();
|
|
29
|
+
const history = state.executionHistory || [];
|
|
30
|
+
|
|
31
|
+
return NextResponse.json({ history });
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('Error getting execution history:', error);
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
36
|
+
{ status: 500 }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getController } from '@/lib/executionController';
|
|
3
|
+
import { getApi } from '@/lib/mcpGraphApi';
|
|
4
|
+
import type { NodeExecutionRecord } from 'mcpgraph';
|
|
5
|
+
|
|
6
|
+
// Force dynamic rendering
|
|
7
|
+
export const dynamic = 'force-dynamic';
|
|
8
|
+
|
|
9
|
+
export async function GET(request: NextRequest) {
|
|
10
|
+
try {
|
|
11
|
+
const executionId = request.nextUrl.searchParams.get('executionId');
|
|
12
|
+
|
|
13
|
+
if (!executionId) {
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ error: 'Missing executionId parameter' },
|
|
16
|
+
{ status: 400 }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const controller = getController(executionId);
|
|
21
|
+
if (!controller) {
|
|
22
|
+
return NextResponse.json(
|
|
23
|
+
{ error: 'Execution not found or not active' },
|
|
24
|
+
{ status: 404 }
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const api = getApi();
|
|
29
|
+
if (!api) {
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
{ error: 'API instance not available' },
|
|
32
|
+
{ status: 500 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Get execution history by iterating through execution records
|
|
37
|
+
// We'll get records until we hit null
|
|
38
|
+
const history: NodeExecutionRecord[] = [];
|
|
39
|
+
let index = 0;
|
|
40
|
+
|
|
41
|
+
while (true) {
|
|
42
|
+
const record = api.getExecutionByIndex(index);
|
|
43
|
+
if (!record) break;
|
|
44
|
+
history.push(record);
|
|
45
|
+
index++;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return NextResponse.json({ history });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error getting execution history with indices:', error);
|
|
51
|
+
return NextResponse.json(
|
|
52
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
53
|
+
{ status: 500 }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
import { registerExecutionStream, unregisterExecutionStream } from '@/lib/executionStreamServer';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export async function GET(request: NextRequest) {
|
|
6
|
+
const executionId = request.nextUrl.searchParams.get('executionId');
|
|
7
|
+
|
|
8
|
+
if (!executionId) {
|
|
9
|
+
return new Response('Missing executionId parameter', { status: 400 });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const stream = new ReadableStream({
|
|
13
|
+
start(controller) {
|
|
14
|
+
// Register stream for this execution
|
|
15
|
+
registerExecutionStream(executionId, controller);
|
|
16
|
+
|
|
17
|
+
// Note: The actual execution will be started via POST /api/tools/[toolName]
|
|
18
|
+
// This stream will receive events from those hooks
|
|
19
|
+
},
|
|
20
|
+
cancel() {
|
|
21
|
+
// Clean up when client disconnects
|
|
22
|
+
unregisterExecutionStream(executionId);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return new Response(stream, {
|
|
27
|
+
headers: {
|
|
28
|
+
'Content-Type': 'text/event-stream',
|
|
29
|
+
'Cache-Control': 'no-cache',
|
|
30
|
+
'Connection': 'keep-alive',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
package/app/api/graph/route.ts
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
2
|
+
import { type NodeDefinition } from 'mcpgraph';
|
|
3
|
+
import { getApi } from '@/lib/mcpGraphApi';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function getApi(): McpGraphApi {
|
|
7
|
-
const configPath = process.env.MCPGRAPH_CONFIG_PATH;
|
|
8
|
-
if (!configPath) {
|
|
9
|
-
throw new Error('MCPGRAPH_CONFIG_PATH environment variable is not set');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (!apiInstance) {
|
|
13
|
-
apiInstance = new McpGraphApi(configPath);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return apiInstance;
|
|
17
|
-
}
|
|
5
|
+
// Force dynamic rendering - this route requires runtime config
|
|
6
|
+
export const dynamic = 'force-dynamic';
|
|
18
7
|
|
|
19
8
|
export async function GET() {
|
|
20
9
|
try {
|
|
@@ -34,39 +23,47 @@ export async function GET() {
|
|
|
34
23
|
position: { x: 0, y: 0 }, // Will be calculated by layout algorithm
|
|
35
24
|
};
|
|
36
25
|
|
|
37
|
-
// Add specific data based on node type
|
|
38
|
-
if (node.type === 'entry'
|
|
26
|
+
// Add specific data based on node type using type guards
|
|
27
|
+
if (node.type === 'entry' && 'tool' in node) {
|
|
39
28
|
return {
|
|
40
29
|
...baseNode,
|
|
41
30
|
data: {
|
|
42
31
|
...baseNode.data,
|
|
43
|
-
tool:
|
|
32
|
+
tool: node.tool,
|
|
44
33
|
},
|
|
45
34
|
};
|
|
46
|
-
} else if (node.type === '
|
|
35
|
+
} else if (node.type === 'exit' && 'tool' in node) {
|
|
47
36
|
return {
|
|
48
37
|
...baseNode,
|
|
49
38
|
data: {
|
|
50
39
|
...baseNode.data,
|
|
51
|
-
|
|
52
|
-
tool: (node as any).tool,
|
|
53
|
-
args: (node as any).args,
|
|
40
|
+
tool: node.tool,
|
|
54
41
|
},
|
|
55
42
|
};
|
|
56
|
-
} else if (node.type === '
|
|
43
|
+
} else if (node.type === 'mcp' && 'server' in node && 'tool' in node && 'args' in node) {
|
|
57
44
|
return {
|
|
58
45
|
...baseNode,
|
|
59
46
|
data: {
|
|
60
47
|
...baseNode.data,
|
|
61
|
-
|
|
48
|
+
server: node.server,
|
|
49
|
+
tool: node.tool,
|
|
50
|
+
args: node.args,
|
|
62
51
|
},
|
|
63
52
|
};
|
|
64
|
-
} else if (node.type === '
|
|
53
|
+
} else if (node.type === 'transform' && 'transform' in node) {
|
|
65
54
|
return {
|
|
66
55
|
...baseNode,
|
|
67
56
|
data: {
|
|
68
57
|
...baseNode.data,
|
|
69
|
-
|
|
58
|
+
transform: node.transform,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
} else if (node.type === 'switch' && 'conditions' in node) {
|
|
62
|
+
return {
|
|
63
|
+
...baseNode,
|
|
64
|
+
data: {
|
|
65
|
+
...baseNode.data,
|
|
66
|
+
conditions: node.conditions,
|
|
70
67
|
},
|
|
71
68
|
};
|
|
72
69
|
}
|
|
@@ -87,8 +84,7 @@ export async function GET() {
|
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
if (node.type === 'switch' && 'conditions' in node) {
|
|
90
|
-
|
|
91
|
-
switchNode.conditions.forEach((condition: any, index: number) => {
|
|
87
|
+
node.conditions.forEach((condition, index: number) => {
|
|
92
88
|
edges.push({
|
|
93
89
|
id: `${node.id}-${condition.target}-${index}`,
|
|
94
90
|
source: node.id,
|
|
@@ -99,7 +95,45 @@ export async function GET() {
|
|
|
99
95
|
}
|
|
100
96
|
});
|
|
101
97
|
|
|
102
|
-
return NextResponse.json({
|
|
98
|
+
return NextResponse.json({
|
|
99
|
+
nodes,
|
|
100
|
+
edges,
|
|
101
|
+
tools: config.tools,
|
|
102
|
+
config: {
|
|
103
|
+
name: config.server.name,
|
|
104
|
+
version: config.server.version,
|
|
105
|
+
description: config.server.description,
|
|
106
|
+
servers: Object.entries(config.servers || {}).map(([name, server]) => {
|
|
107
|
+
const details: {
|
|
108
|
+
name: string;
|
|
109
|
+
type: string;
|
|
110
|
+
command?: string;
|
|
111
|
+
args?: string[];
|
|
112
|
+
cwd?: string;
|
|
113
|
+
url?: string;
|
|
114
|
+
headers?: Record<string, string>;
|
|
115
|
+
} = {
|
|
116
|
+
name,
|
|
117
|
+
type: server.type || 'stdio',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (server.type === 'stdio' || !server.type) {
|
|
121
|
+
details.command = server.command;
|
|
122
|
+
details.args = server.args || [];
|
|
123
|
+
if (server.cwd) {
|
|
124
|
+
details.cwd = server.cwd;
|
|
125
|
+
}
|
|
126
|
+
} else if (server.type === 'sse' || server.type === 'streamableHttp') {
|
|
127
|
+
details.url = server.url;
|
|
128
|
+
if (server.headers) {
|
|
129
|
+
details.headers = server.headers;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return details;
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
136
|
+
});
|
|
103
137
|
} catch (error) {
|
|
104
138
|
console.error('Error getting graph:', error);
|
|
105
139
|
return NextResponse.json(
|