mcpgraph-ux 0.1.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/LICENSE +21 -0
- package/README.md +106 -0
- package/app/api/graph/route.ts +114 -0
- package/app/api/tools/[toolName]/route.ts +64 -0
- package/app/api/tools/route.ts +32 -0
- package/app/globals.css +26 -0
- package/app/layout.tsx +20 -0
- package/app/page.module.css +94 -0
- package/app/page.tsx +110 -0
- package/components/GraphVisualization.module.css +6 -0
- package/components/GraphVisualization.tsx +276 -0
- package/components/ToolList.module.css +49 -0
- package/components/ToolList.tsx +43 -0
- package/components/ToolTester.module.css +142 -0
- package/components/ToolTester.tsx +283 -0
- package/next.config.js +7 -0
- package/package.json +66 -0
- package/server.ts +47 -0
- package/tsconfig.json +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TeamSpark, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# mcpGraph UX
|
|
2
|
+
|
|
3
|
+
A Next.js application that provides a visual interface for the [mcpGraph](https://github.com/TeamSparkAI/mcpGraph) npm module. This application allows you to:
|
|
4
|
+
|
|
5
|
+
- **Visualize graphs**: View your mcpGraph configuration as an interactive graph using React Flow
|
|
6
|
+
- **List tools**: See all available MCP tools defined in your graph configuration
|
|
7
|
+
- **Test tools**: Execute tools with custom parameters and view results from the exit node
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🎨 **Graph Visualization**: Interactive graph visualization using React Flow, showing all nodes and their connections
|
|
12
|
+
- 🔧 **Tool Testing**: Test each MCP tool with a user-friendly form interface
|
|
13
|
+
- 📊 **Real-time Results**: View execution results directly in the UI
|
|
14
|
+
- 🎯 **Tool Filtering**: Filter graph visualization by selected tool
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- Node.js >= 20.0.0
|
|
19
|
+
- The mcpGraph module (should be in `../mcpGraph` relative to this project)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
1. Install dependencies:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. Ensure the mcpGraph module is built and available at `../mcpGraph`
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
Start the server with a port and path to your mcpGraph YAML configuration file:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run server <port> <config-path>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Examples
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Run on port 3000 with example config
|
|
43
|
+
npm run server 3000 ../mcpGraph/examples/count_files.yaml
|
|
44
|
+
|
|
45
|
+
# Run on port 8080 with custom config
|
|
46
|
+
npm run server 8080 /path/to/your/graph.yaml
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The server will:
|
|
50
|
+
1. Load and validate the mcpGraph configuration
|
|
51
|
+
2. Start the Next.js server on the specified port
|
|
52
|
+
3. Make the configuration available through API routes
|
|
53
|
+
|
|
54
|
+
Once running, open your browser to `http://localhost:<port>` to access the UI.
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
For development with hot-reloading:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm run dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Note: In development mode, you'll need to set the `MCPGRAPH_CONFIG_PATH` environment variable:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
MCPGRAPH_CONFIG_PATH=/path/to/config.yaml npm run dev
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Project Structure
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
.
|
|
74
|
+
├── app/ # Next.js app directory
|
|
75
|
+
│ ├── api/ # API routes
|
|
76
|
+
│ │ ├── tools/ # Tool listing and execution endpoints
|
|
77
|
+
│ │ └── graph/ # Graph data endpoint
|
|
78
|
+
│ ├── layout.tsx # Root layout
|
|
79
|
+
│ ├── page.tsx # Main page
|
|
80
|
+
│ └── globals.css # Global styles
|
|
81
|
+
├── components/ # React components
|
|
82
|
+
│ ├── GraphVisualization.tsx # React Flow graph component
|
|
83
|
+
│ ├── ToolList.tsx # Tool sidebar component
|
|
84
|
+
│ └── ToolTester.tsx # Tool testing form component
|
|
85
|
+
├── server.ts # Custom server entry point
|
|
86
|
+
└── package.json # Dependencies and scripts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## API Endpoints
|
|
90
|
+
|
|
91
|
+
- `GET /api/tools` - List all available tools
|
|
92
|
+
- `GET /api/tools/[toolName]` - Get information about a specific tool
|
|
93
|
+
- `POST /api/tools/[toolName]` - Execute a tool with provided arguments
|
|
94
|
+
- `GET /api/graph` - Get graph structure (nodes and edges) for visualization
|
|
95
|
+
|
|
96
|
+
## Design
|
|
97
|
+
|
|
98
|
+
This application follows the design recommendations from the mcpGraph project:
|
|
99
|
+
|
|
100
|
+
- **React Flow** for graph visualization (as recommended in the design docs)
|
|
101
|
+
- **Next.js** for the web framework
|
|
102
|
+
- **TypeScript** for type safety
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { McpGraphApi } from 'mcpgraph';
|
|
3
|
+
|
|
4
|
+
// Infer NodeDefinition type from the API
|
|
5
|
+
type NodeDefinition = ReturnType<McpGraphApi['getConfig']>['nodes'][number];
|
|
6
|
+
|
|
7
|
+
let apiInstance: McpGraphApi | null = null;
|
|
8
|
+
|
|
9
|
+
function getApi(): McpGraphApi {
|
|
10
|
+
const configPath = process.env.MCPGRAPH_CONFIG_PATH;
|
|
11
|
+
if (!configPath) {
|
|
12
|
+
throw new Error('MCPGRAPH_CONFIG_PATH environment variable is not set');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!apiInstance) {
|
|
16
|
+
apiInstance = new McpGraphApi(configPath);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return apiInstance;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function GET() {
|
|
23
|
+
try {
|
|
24
|
+
const api = getApi();
|
|
25
|
+
const config = api.getConfig();
|
|
26
|
+
|
|
27
|
+
// Transform nodes into React Flow format
|
|
28
|
+
const nodes = config.nodes.map((node: NodeDefinition) => {
|
|
29
|
+
const baseNode = {
|
|
30
|
+
id: node.id,
|
|
31
|
+
type: node.type,
|
|
32
|
+
data: {
|
|
33
|
+
label: node.id,
|
|
34
|
+
nodeType: node.type,
|
|
35
|
+
...node,
|
|
36
|
+
},
|
|
37
|
+
position: { x: 0, y: 0 }, // Will be calculated by layout algorithm
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Add specific data based on node type
|
|
41
|
+
if (node.type === 'entry' || node.type === 'exit') {
|
|
42
|
+
return {
|
|
43
|
+
...baseNode,
|
|
44
|
+
data: {
|
|
45
|
+
...baseNode.data,
|
|
46
|
+
tool: (node as any).tool,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
} else if (node.type === 'mcp') {
|
|
50
|
+
return {
|
|
51
|
+
...baseNode,
|
|
52
|
+
data: {
|
|
53
|
+
...baseNode.data,
|
|
54
|
+
server: (node as any).server,
|
|
55
|
+
tool: (node as any).tool,
|
|
56
|
+
args: (node as any).args,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
} else if (node.type === 'transform') {
|
|
60
|
+
return {
|
|
61
|
+
...baseNode,
|
|
62
|
+
data: {
|
|
63
|
+
...baseNode.data,
|
|
64
|
+
transform: (node as any).transform,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
} else if (node.type === 'switch') {
|
|
68
|
+
return {
|
|
69
|
+
...baseNode,
|
|
70
|
+
data: {
|
|
71
|
+
...baseNode.data,
|
|
72
|
+
conditions: (node as any).conditions,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return baseNode;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Create edges from node.next and switch conditions
|
|
81
|
+
const edges: Array<{ id: string; source: string; target: string; label?: string }> = [];
|
|
82
|
+
|
|
83
|
+
config.nodes.forEach((node: NodeDefinition) => {
|
|
84
|
+
if ('next' in node && node.next) {
|
|
85
|
+
edges.push({
|
|
86
|
+
id: `${node.id}-${node.next}`,
|
|
87
|
+
source: node.id,
|
|
88
|
+
target: node.next,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (node.type === 'switch' && 'conditions' in node) {
|
|
93
|
+
const switchNode = node as any;
|
|
94
|
+
switchNode.conditions.forEach((condition: any, index: number) => {
|
|
95
|
+
edges.push({
|
|
96
|
+
id: `${node.id}-${condition.target}-${index}`,
|
|
97
|
+
source: node.id,
|
|
98
|
+
target: condition.target,
|
|
99
|
+
label: condition.rule ? JSON.stringify(condition.rule) : 'default',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return NextResponse.json({ nodes, edges, tools: config.tools });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('Error getting graph:', error);
|
|
108
|
+
return NextResponse.json(
|
|
109
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
110
|
+
{ status: 500 }
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { McpGraphApi } from 'mcpgraph';
|
|
3
|
+
|
|
4
|
+
let apiInstance: McpGraphApi | null = null;
|
|
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
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function GET(
|
|
20
|
+
request: Request,
|
|
21
|
+
{ params }: { params: { toolName: string } }
|
|
22
|
+
) {
|
|
23
|
+
try {
|
|
24
|
+
const api = getApi();
|
|
25
|
+
const tool = api.getTool(params.toolName);
|
|
26
|
+
|
|
27
|
+
if (!tool) {
|
|
28
|
+
return NextResponse.json(
|
|
29
|
+
{ error: `Tool '${params.toolName}' not found` },
|
|
30
|
+
{ status: 404 }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return NextResponse.json({ tool });
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Error getting tool:', error);
|
|
37
|
+
return NextResponse.json(
|
|
38
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
39
|
+
{ status: 500 }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function POST(
|
|
45
|
+
request: Request,
|
|
46
|
+
{ params }: { params: { toolName: string } }
|
|
47
|
+
) {
|
|
48
|
+
try {
|
|
49
|
+
const api = getApi();
|
|
50
|
+
const body = await request.json();
|
|
51
|
+
const args = body.args || {};
|
|
52
|
+
|
|
53
|
+
const result = await api.executeTool(params.toolName, args);
|
|
54
|
+
|
|
55
|
+
return NextResponse.json({ result });
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Error executing tool:', error);
|
|
58
|
+
return NextResponse.json(
|
|
59
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
60
|
+
{ status: 500 }
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { McpGraphApi } from 'mcpgraph';
|
|
3
|
+
|
|
4
|
+
let apiInstance: McpGraphApi | null = null;
|
|
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
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function GET() {
|
|
20
|
+
try {
|
|
21
|
+
const api = getApi();
|
|
22
|
+
const tools = api.listTools();
|
|
23
|
+
return NextResponse.json({ tools });
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Error listing tools:', error);
|
|
26
|
+
return NextResponse.json(
|
|
27
|
+
{ error: error instanceof Error ? error.message : 'Unknown error' },
|
|
28
|
+
{ status: 500 }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
package/app/globals.css
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
* {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
padding: 0;
|
|
4
|
+
margin: 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
html,
|
|
8
|
+
body {
|
|
9
|
+
max-width: 100vw;
|
|
10
|
+
overflow-x: hidden;
|
|
11
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
12
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
13
|
+
sans-serif;
|
|
14
|
+
-webkit-font-smoothing: antialiased;
|
|
15
|
+
-moz-osx-font-smoothing: grayscale;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
background-color: #f5f5f5;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
a {
|
|
23
|
+
color: inherit;
|
|
24
|
+
text-decoration: none;
|
|
25
|
+
}
|
|
26
|
+
|
package/app/layout.tsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Metadata } from 'next'
|
|
2
|
+
import './globals.css'
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: 'mcpGraph UX',
|
|
6
|
+
description: 'Visual interface for mcpGraph',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function RootLayout({
|
|
10
|
+
children,
|
|
11
|
+
}: {
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
}) {
|
|
14
|
+
return (
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<body>{children}</body>
|
|
17
|
+
</html>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
min-height: 100vh;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.header {
|
|
8
|
+
background-color: #fff;
|
|
9
|
+
border-bottom: 1px solid #e0e0e0;
|
|
10
|
+
padding: 1.5rem 2rem;
|
|
11
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.header h1 {
|
|
15
|
+
font-size: 1.5rem;
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
margin-bottom: 0.25rem;
|
|
18
|
+
color: #333;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.header p {
|
|
22
|
+
color: #666;
|
|
23
|
+
font-size: 0.9rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.main {
|
|
27
|
+
flex: 1;
|
|
28
|
+
display: flex;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sidebar {
|
|
33
|
+
width: 300px;
|
|
34
|
+
background-color: #fff;
|
|
35
|
+
border-right: 1px solid #e0e0e0;
|
|
36
|
+
overflow-y: auto;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.content {
|
|
40
|
+
flex: 1;
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.graphSection {
|
|
47
|
+
flex: 1;
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
border-bottom: 1px solid #e0e0e0;
|
|
51
|
+
background-color: #fafafa;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.graphSection h2 {
|
|
55
|
+
padding: 1rem 1.5rem;
|
|
56
|
+
font-size: 1.1rem;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
color: #333;
|
|
59
|
+
border-bottom: 1px solid #e0e0e0;
|
|
60
|
+
background-color: #fff;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.testerSection {
|
|
64
|
+
background-color: #fff;
|
|
65
|
+
padding: 1.5rem;
|
|
66
|
+
overflow-y: auto;
|
|
67
|
+
max-height: 400px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.testerSection h2 {
|
|
71
|
+
font-size: 1.1rem;
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
margin-bottom: 1rem;
|
|
74
|
+
color: #333;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.loading,
|
|
78
|
+
.error {
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
min-height: 100vh;
|
|
84
|
+
padding: 2rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.error {
|
|
88
|
+
color: #d32f2f;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.error h1 {
|
|
92
|
+
margin-bottom: 1rem;
|
|
93
|
+
}
|
|
94
|
+
|
package/app/page.tsx
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import GraphVisualization from '@/components/GraphVisualization';
|
|
5
|
+
import ToolList from '@/components/ToolList';
|
|
6
|
+
import ToolTester from '@/components/ToolTester';
|
|
7
|
+
import styles from './page.module.css';
|
|
8
|
+
|
|
9
|
+
interface Tool {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
inputSchema: Record<string, unknown>;
|
|
13
|
+
outputSchema?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function Home() {
|
|
17
|
+
const [tools, setTools] = useState<Tool[]>([]);
|
|
18
|
+
const [selectedTool, setSelectedTool] = useState<string | null>(null);
|
|
19
|
+
const [graphData, setGraphData] = useState<{ nodes: any[]; edges: any[] } | null>(null);
|
|
20
|
+
const [loading, setLoading] = useState(true);
|
|
21
|
+
const [error, setError] = useState<string | null>(null);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
// Load tools and graph data
|
|
25
|
+
Promise.all([
|
|
26
|
+
fetch('/api/tools').then(res => res.json()),
|
|
27
|
+
fetch('/api/graph').then(res => res.json()),
|
|
28
|
+
])
|
|
29
|
+
.then(([toolsRes, graphRes]) => {
|
|
30
|
+
if (toolsRes.error) {
|
|
31
|
+
setError(toolsRes.error);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (graphRes.error) {
|
|
35
|
+
setError(graphRes.error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setTools(toolsRes.tools);
|
|
39
|
+
setGraphData({ nodes: graphRes.nodes, edges: graphRes.edges });
|
|
40
|
+
if (toolsRes.tools.length > 0) {
|
|
41
|
+
setSelectedTool(toolsRes.tools[0].name);
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.catch(err => {
|
|
45
|
+
setError(err.message);
|
|
46
|
+
})
|
|
47
|
+
.finally(() => {
|
|
48
|
+
setLoading(false);
|
|
49
|
+
});
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
if (loading) {
|
|
53
|
+
return (
|
|
54
|
+
<div className={styles.container}>
|
|
55
|
+
<div className={styles.loading}>Loading...</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (error) {
|
|
61
|
+
return (
|
|
62
|
+
<div className={styles.container}>
|
|
63
|
+
<div className={styles.error}>
|
|
64
|
+
<h1>Error</h1>
|
|
65
|
+
<p>{error}</p>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className={styles.container}>
|
|
73
|
+
<header className={styles.header}>
|
|
74
|
+
<h1>mcpGraph UX</h1>
|
|
75
|
+
<p>Visual interface for mcpGraph execution</p>
|
|
76
|
+
</header>
|
|
77
|
+
|
|
78
|
+
<div className={styles.main}>
|
|
79
|
+
<div className={styles.sidebar}>
|
|
80
|
+
<ToolList
|
|
81
|
+
tools={tools}
|
|
82
|
+
selectedTool={selectedTool}
|
|
83
|
+
onSelectTool={setSelectedTool}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div className={styles.content}>
|
|
88
|
+
<div className={styles.graphSection}>
|
|
89
|
+
<h2>Graph Visualization</h2>
|
|
90
|
+
{graphData && (
|
|
91
|
+
<GraphVisualization
|
|
92
|
+
nodes={graphData.nodes}
|
|
93
|
+
edges={graphData.edges}
|
|
94
|
+
selectedTool={selectedTool}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
{selectedTool && (
|
|
100
|
+
<div className={styles.testerSection}>
|
|
101
|
+
<h2>Test Tool: {selectedTool}</h2>
|
|
102
|
+
<ToolTester toolName={selectedTool} />
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|