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 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
+
@@ -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
+
@@ -0,0 +1,6 @@
1
+ .container {
2
+ width: 100%;
3
+ height: 100%;
4
+ flex: 1;
5
+ }
6
+