guohanghui commited on
Commit
d0b41ed
·
verified ·
1 Parent(s): 55b955d

Update graph-theory/mcp_output/mcp_plugin/mcp_service.py

Browse files
graph-theory/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,582 +1,131 @@
1
- import os
2
- import sys
3
- from typing import Dict, Any, List, Optional, Tuple
4
-
5
- source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
6
- if source_path not in sys.path:
7
- sys.path.insert(0, source_path)
8
-
9
  from fastmcp import FastMCP
10
- from graph import Graph, Graph3D
11
 
 
12
  mcp = FastMCP("graph_theory_service")
13
 
14
- # Store graphs by ID
15
- _graphs: Dict[str, Graph] = {}
16
- _graph3d: Dict[str, Graph3D] = {}
17
-
18
-
19
- @mcp.tool(name="get_library_info")
20
- def get_library_info() -> dict:
21
- """
22
- Get information about the graph-theory library.
23
-
24
- Returns:
25
- dict: Version and available functionality.
26
- """
27
- try:
28
- from graph.version import __version__
29
-
30
- return {
31
- "success": True,
32
- "result": {
33
- "library": "graph-theory",
34
- "version": __version__,
35
- "graph_types": ["Graph", "Graph3D"],
36
- "features": [
37
- "shortest_path", "breadth_first_search", "depth_first_search",
38
- "maximum_flow", "minimum_cost_flow", "traveling_salesman",
39
- "topological_sort", "cycle_detection", "components",
40
- "critical_path", "adjacency_matrix"
41
- ],
42
- },
43
- "error": None,
44
- }
45
- except Exception as e:
46
- return {"success": False, "result": None, "error": str(e)}
47
-
48
-
49
- @mcp.tool(name="create_graph")
50
- def create_graph(
51
- graph_id: str,
52
- from_dict: Optional[Dict[str, Dict[str, float]]] = None,
53
- from_list: Optional[List[Tuple]] = None
54
- ) -> dict:
55
- """
56
- Create a new graph.
57
-
58
- Parameters:
59
- graph_id (str): Unique identifier for the graph.
60
- from_dict (Optional[Dict]): Dictionary representation {node: {neighbor: distance}}.
61
- from_list (Optional[List[Tuple]]): List of edges as [(n1, n2, distance), ...].
62
-
63
- Returns:
64
- dict: Success status and graph information.
65
- """
66
- try:
67
- if graph_id in _graphs:
68
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' already exists"}
69
-
70
- g = Graph(from_dict=from_dict, from_list=from_list)
71
- _graphs[graph_id] = g
72
-
73
- return {
74
- "success": True,
75
- "result": {
76
- "graph_id": graph_id,
77
- "nodes": list(g.nodes()),
78
- "edges": list(g.edges()),
79
- "num_nodes": len(list(g.nodes())),
80
- "num_edges": len(list(g.edges())),
81
- },
82
- "error": None,
83
- }
84
- except Exception as e:
85
- return {"success": False, "result": None, "error": str(e)}
86
-
87
-
88
- @mcp.tool(name="add_node")
89
- def add_node(graph_id: str, node_id: str, obj: Any = None) -> dict:
90
- """
91
- Add a node to a graph.
92
-
93
- Parameters:
94
- graph_id (str): ID of the graph.
95
- node_id (str): Unique identifier for the node.
96
- obj (Any): Optional object associated with the node.
97
-
98
- Returns:
99
- dict: Success status.
100
- """
101
- try:
102
- if graph_id not in _graphs:
103
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
104
-
105
- g = _graphs[graph_id]
106
- g.add_node(node_id, obj)
107
-
108
- return {
109
- "success": True,
110
- "result": {"graph_id": graph_id, "node_id": node_id, "message": "Node added"},
111
- "error": None,
112
- }
113
- except Exception as e:
114
- return {"success": False, "result": None, "error": str(e)}
115
-
116
-
117
- @mcp.tool(name="add_edge")
118
- def add_edge(
119
- graph_id: str,
120
- node1: str,
121
- node2: str,
122
- distance: float = 1.0,
123
- bidirectional: bool = False
124
- ) -> dict:
125
  """
126
- Add an edge to a graph.
127
-
128
- Parameters:
129
- graph_id (str): ID of the graph.
130
- node1 (str): Source node.
131
- node2 (str): Destination node.
132
- distance (float): Edge weight/distance.
133
- bidirectional (bool): If True, creates edge in both directions.
134
-
135
- Returns:
136
- dict: Success status.
137
- """
138
- try:
139
- if graph_id not in _graphs:
140
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
141
-
142
- g = _graphs[graph_id]
143
- g.add_edge(node1, node2, distance, bidirectional)
144
-
145
- return {
146
- "success": True,
147
- "result": {
148
- "graph_id": graph_id,
149
- "edge": (node1, node2),
150
- "distance": distance,
151
- "bidirectional": bidirectional,
152
- },
153
- "error": None,
154
- }
155
- except Exception as e:
156
- return {"success": False, "result": None, "error": str(e)}
157
-
158
-
159
- @mcp.tool(name="shortest_path")
160
- def shortest_path(graph_id: str, start: str, end: str) -> dict:
161
- """
162
- Find the shortest path between two nodes.
163
-
164
- Parameters:
165
- graph_id (str): ID of the graph.
166
- start (str): Start node.
167
- end (str): End node.
168
-
169
- Returns:
170
- dict: Distance and path as list of nodes.
171
- """
172
- try:
173
- if graph_id not in _graphs:
174
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
175
-
176
- g = _graphs[graph_id]
177
- distance, path = g.shortest_path(start, end)
178
-
179
- return {
180
- "success": True,
181
- "result": {
182
- "distance": distance,
183
- "path": path,
184
- "num_nodes": len(path),
185
- },
186
- "error": None,
187
- }
188
- except Exception as e:
189
- return {"success": False, "result": None, "error": str(e)}
190
-
191
-
192
- @mcp.tool(name="breadth_first_search")
193
- def breadth_first_search(graph_id: str, start: str, end: str) -> dict:
194
- """
195
- Find path with fewest nodes using BFS.
196
-
197
- Parameters:
198
- graph_id (str): ID of the graph.
199
- start (str): Start node.
200
- end (str): End node.
201
 
202
  Returns:
203
- dict: Number of nodes and path.
204
  """
205
  try:
206
- if graph_id not in _graphs:
207
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
208
-
209
- g = _graphs[graph_id]
210
- nodes, path = g.breadth_first_search(start, end)
211
-
212
- return {
213
- "success": True,
214
- "result": {
215
- "num_nodes": nodes,
216
- "path": path,
217
- },
218
- "error": None,
219
- }
 
 
 
220
  except Exception as e:
221
- return {"success": False, "result": None, "error": str(e)}
222
 
223
-
224
- @mcp.tool(name="depth_first_search")
225
- def depth_first_search(graph_id: str, start: str, end: str) -> dict:
226
  """
227
- Find a path using DFS.
228
-
229
- Parameters:
230
- graph_id (str): ID of the graph.
231
- start (str): Start node.
232
- end (str): End node.
233
 
234
  Returns:
235
- dict: Path as list of nodes.
236
  """
237
  try:
238
- if graph_id not in _graphs:
239
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
240
-
241
- g = _graphs[graph_id]
242
- path = g.depth_first_search(start, end)
243
-
244
- return {
245
- "success": True,
246
- "result": {"path": path},
247
- "error": None,
248
- }
249
  except Exception as e:
250
- return {"success": False, "result": None, "error": str(e)}
251
 
252
-
253
- @mcp.tool(name="maximum_flow")
254
- def maximum_flow(graph_id: str, source: str, sink: str) -> dict:
255
  """
256
- Calculate maximum flow from source to sink.
257
 
258
- Parameters:
259
- graph_id (str): ID of the graph.
260
- source (str): Source node.
261
- sink (str): Sink node.
262
 
263
  Returns:
264
- dict: Maximum flow value and flow graph.
265
  """
266
  try:
267
- if graph_id not in _graphs:
268
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
269
-
270
- g = _graphs[graph_id]
271
- flow_value, flow_graph = g.maximum_flow(source, sink)
272
-
273
- return {
274
- "success": True,
275
- "result": {
276
- "max_flow": flow_value,
277
- "flow_edges": list(flow_graph.edges()) if flow_graph else [],
278
- },
279
- "error": None,
280
- }
281
  except Exception as e:
282
- return {"success": False, "result": None, "error": str(e)}
283
-
284
 
285
- @mcp.tool(name="solve_tsp")
286
- def solve_tsp(graph_id: str, method: str = "2023") -> dict:
287
  """
288
- Solve the Traveling Salesman Problem.
289
 
290
- Parameters:
291
- graph_id (str): ID of the graph.
292
- method (str): Algorithm to use ('greedy', 'bnb', '2023').
 
 
 
293
 
294
  Returns:
295
- dict: Tour length and path.
296
  """
297
  try:
298
- if graph_id not in _graphs:
299
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
300
-
301
- g = _graphs[graph_id]
302
- tour_length, path = g.solve_tsp(method=method)
303
-
304
- return {
305
- "success": True,
306
- "result": {
307
- "tour_length": tour_length,
308
- "path": path,
309
- "method": method,
310
- },
311
- "error": None,
312
- }
313
  except Exception as e:
314
- return {"success": False, "result": None, "error": str(e)}
315
 
316
-
317
- @mcp.tool(name="has_cycles")
318
- def has_cycles(graph_id: str) -> dict:
319
  """
320
- Check if the graph has cycles.
321
 
322
- Parameters:
323
- graph_id (str): ID of the graph.
 
 
324
 
325
  Returns:
326
- dict: Boolean indicating if cycles exist.
327
  """
328
  try:
329
- if graph_id not in _graphs:
330
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
331
-
332
- g = _graphs[graph_id]
333
- has_cycle = g.has_cycles()
334
-
335
- return {
336
- "success": True,
337
- "result": {"has_cycles": has_cycle},
338
- "error": None,
339
- }
340
  except Exception as e:
341
- return {"success": False, "result": None, "error": str(e)}
342
-
343
 
344
- @mcp.tool(name="components")
345
- def components(graph_id: str) -> dict:
346
  """
347
- Find connected components in the graph.
348
 
349
- Parameters:
350
- graph_id (str): ID of the graph.
351
 
352
  Returns:
353
- dict: List of components (each component is a set of nodes).
354
  """
355
  try:
356
- if graph_id not in _graphs:
357
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
358
-
359
- g = _graphs[graph_id]
360
- comps = g.components()
361
-
362
- return {
363
- "success": True,
364
- "result": {
365
- "num_components": len(comps),
366
- "components": [list(comp) for comp in comps],
367
- },
368
- "error": None,
369
- }
370
  except Exception as e:
371
- return {"success": False, "result": None, "error": str(e)}
372
-
373
-
374
- @mcp.tool(name="topological_sort")
375
- def topological_sort(graph_id: str) -> dict:
376
- """
377
- Perform topological sort on a DAG.
378
-
379
- Parameters:
380
- graph_id (str): ID of the graph.
381
-
382
- Returns:
383
- dict: Topologically sorted list of nodes.
384
- """
385
- try:
386
- if graph_id not in _graphs:
387
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
388
-
389
- g = _graphs[graph_id]
390
- sorted_nodes = list(g.topological_sort())
391
-
392
- return {
393
- "success": True,
394
- "result": {"sorted_nodes": sorted_nodes},
395
- "error": None,
396
- }
397
- except Exception as e:
398
- return {"success": False, "result": None, "error": str(e)}
399
-
400
-
401
- @mcp.tool(name="critical_path")
402
- def critical_path(graph_id: str) -> dict:
403
- """
404
- Find the critical path in a project network.
405
-
406
- Parameters:
407
- graph_id (str): ID of the graph.
408
-
409
- Returns:
410
- dict: Critical path information.
411
- """
412
- try:
413
- if graph_id not in _graphs:
414
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
415
-
416
- g = _graphs[graph_id]
417
- result = g.critical_path()
418
-
419
- return {
420
- "success": True,
421
- "result": {"critical_path": result},
422
- "error": None,
423
- }
424
- except Exception as e:
425
- return {"success": False, "result": None, "error": str(e)}
426
-
427
-
428
- @mcp.tool(name="adjacency_matrix")
429
- def adjacency_matrix(graph_id: str) -> dict:
430
- """
431
- Get the adjacency matrix representation of the graph.
432
-
433
- Parameters:
434
- graph_id (str): ID of the graph.
435
-
436
- Returns:
437
- dict: Adjacency matrix as dictionary.
438
- """
439
- try:
440
- if graph_id not in _graphs:
441
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
442
-
443
- g = _graphs[graph_id]
444
- matrix = g.adjacency_matrix()
445
-
446
- return {
447
- "success": True,
448
- "result": {"adjacency_matrix": matrix},
449
- "error": None,
450
- }
451
- except Exception as e:
452
- return {"success": False, "result": None, "error": str(e)}
453
-
454
-
455
- @mcp.tool(name="is_partite")
456
- def is_partite(graph_id: str, n: int = 2) -> dict:
457
- """
458
- Check if graph is n-partite.
459
-
460
- Parameters:
461
- graph_id (str): ID of the graph.
462
- n (int): Number of partitions to check for.
463
-
464
- Returns:
465
- dict: Boolean and partitions if n-partite.
466
- """
467
- try:
468
- if graph_id not in _graphs:
469
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
470
-
471
- g = _graphs[graph_id]
472
- is_n_partite, partitions = g.is_partite(n)
473
-
474
- return {
475
- "success": True,
476
- "result": {
477
- "is_partite": is_n_partite,
478
- "partitions": partitions,
479
- "n": n,
480
- },
481
- "error": None,
482
- }
483
- except Exception as e:
484
- return {"success": False, "result": None, "error": str(e)}
485
-
486
-
487
- @mcp.tool(name="get_graph_info")
488
- def get_graph_info(graph_id: str) -> dict:
489
- """
490
- Get information about a stored graph.
491
-
492
- Parameters:
493
- graph_id (str): ID of the graph.
494
-
495
- Returns:
496
- dict: Graph statistics and properties.
497
- """
498
- try:
499
- if graph_id not in _graphs:
500
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
501
-
502
- g = _graphs[graph_id]
503
- nodes = list(g.nodes())
504
- edges = list(g.edges())
505
-
506
- return {
507
- "success": True,
508
- "result": {
509
- "graph_id": graph_id,
510
- "num_nodes": len(nodes),
511
- "num_edges": len(edges),
512
- "nodes": nodes[:20], # First 20 for brevity
513
- "edges": edges[:20], # First 20 for brevity
514
- "has_cycles": g.has_cycles(),
515
- },
516
- "error": None,
517
- }
518
- except Exception as e:
519
- return {"success": False, "result": None, "error": str(e)}
520
-
521
-
522
- @mcp.tool(name="list_graphs")
523
- def list_graphs() -> dict:
524
- """
525
- List all stored graphs.
526
-
527
- Returns:
528
- dict: List of graph IDs.
529
- """
530
- try:
531
- return {
532
- "success": True,
533
- "result": {
534
- "graphs": list(_graphs.keys()),
535
- "graph3d": list(_graph3d.keys()),
536
- "total": len(_graphs) + len(_graph3d),
537
- },
538
- "error": None,
539
- }
540
- except Exception as e:
541
- return {"success": False, "result": None, "error": str(e)}
542
-
543
-
544
- @mcp.tool(name="delete_graph")
545
- def delete_graph(graph_id: str) -> dict:
546
- """
547
- Delete a stored graph.
548
-
549
- Parameters:
550
- graph_id (str): ID of the graph to delete.
551
-
552
- Returns:
553
- dict: Confirmation of deletion.
554
- """
555
- try:
556
- if graph_id in _graphs:
557
- del _graphs[graph_id]
558
- return {
559
- "success": True,
560
- "result": {"message": f"Graph '{graph_id}' deleted"},
561
- "error": None,
562
- }
563
- elif graph_id in _graph3d:
564
- del _graph3d[graph_id]
565
- return {
566
- "success": True,
567
- "result": {"message": f"Graph3D '{graph_id}' deleted"},
568
- "error": None,
569
- }
570
- else:
571
- return {"success": False, "result": None, "error": f"Graph '{graph_id}' not found"}
572
- except Exception as e:
573
- return {"success": False, "result": None, "error": str(e)}
574
-
575
-
576
- def create_app():
577
- """Create and return FastMCP application instance"""
578
- return mcp
579
 
 
580
 
 
581
  if __name__ == "__main__":
582
- mcp.run(transport="http", host="0.0.0.0", port=8000)
 
 
 
 
 
 
 
 
 
1
  from fastmcp import FastMCP
 
2
 
3
+ # Create the FastMCP service application
4
  mcp = FastMCP("graph_theory_service")
5
 
6
+ @mcp.tool(name="list_graph_methods", description="List all available graph methods")
7
+ def list_graph_methods():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  """
9
+ List all available methods in the graph-theory library.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  Returns:
12
+ dict: A dictionary containing the list of methods and their descriptions.
13
  """
14
  try:
15
+ methods = [
16
+ "add_node",
17
+ "add_edge",
18
+ "del_node",
19
+ "del_edge",
20
+ "shortest_path",
21
+ "breadth_first_search",
22
+ "depth_first_search",
23
+ "maximum_flow",
24
+ "topological_sort",
25
+ "critical_path",
26
+ "network_size",
27
+ "distance_map",
28
+ "degree_of_separation",
29
+ "visualize_graph",
30
+ ]
31
+ return {"success": True, "methods": methods}
32
  except Exception as e:
33
+ return {"success": False, "error": str(e)}
34
 
35
+ @mcp.tool(name="create_graph", description="Create a new graph")
36
+ def create_graph():
 
37
  """
38
+ Create a new graph instance.
 
 
 
 
 
39
 
40
  Returns:
41
+ dict: A dictionary with the graph instance.
42
  """
43
  try:
44
+ from graph.base import BasicGraph
45
+ graph = BasicGraph()
46
+ return {"success": True, "graph": graph}
 
 
 
 
 
 
 
 
47
  except Exception as e:
48
+ return {"success": False, "error": str(e)}
49
 
50
+ @mcp.tool(name="add_node", description="Add a node to the graph")
51
+ def add_node(graph, node, obj=None):
 
52
  """
53
+ Add a node to the graph.
54
 
55
+ Args:
56
+ graph (BasicGraph): The graph instance.
57
+ node (hashable): The node to add.
58
+ obj (optional): An object to associate with the node.
59
 
60
  Returns:
61
+ dict: A dictionary with the success status.
62
  """
63
  try:
64
+ graph.add_node(node, obj)
65
+ return {"success": True}
 
 
 
 
 
 
 
 
 
 
 
 
66
  except Exception as e:
67
+ return {"success": False, "error": str(e)}
 
68
 
69
+ @mcp.tool(name="add_edge", description="Add an edge to the graph")
70
+ def add_edge(graph, node1, node2, value=1, bidirectional=False):
71
  """
72
+ Add an edge to the graph.
73
 
74
+ Args:
75
+ graph (BasicGraph): The graph instance.
76
+ node1 (hashable): The starting node.
77
+ node2 (hashable): The ending node.
78
+ value (int or float): The weight of the edge.
79
+ bidirectional (bool): Whether the edge is bidirectional.
80
 
81
  Returns:
82
+ dict: A dictionary with the success status.
83
  """
84
  try:
85
+ graph.add_edge(node1, node2, value, bidirectional)
86
+ return {"success": True}
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  except Exception as e:
88
+ return {"success": False, "error": str(e)}
89
 
90
+ @mcp.tool(name="shortest_path", description="Find the shortest path between two nodes")
91
+ def shortest_path(graph, start, end):
 
92
  """
93
+ Find the shortest path between two nodes in the graph.
94
 
95
+ Args:
96
+ graph (BasicGraph): The graph instance.
97
+ start (hashable): The starting node.
98
+ end (hashable): The ending node.
99
 
100
  Returns:
101
+ dict: A dictionary with the shortest path and its distance.
102
  """
103
  try:
104
+ distance, path = graph.shortest_path(start, end)
105
+ return {"success": True, "distance": distance, "path": path}
 
 
 
 
 
 
 
 
 
106
  except Exception as e:
107
+ return {"success": False, "error": str(e)}
 
108
 
109
+ @mcp.tool(name="visualize_graph", description="Visualize the graph")
110
+ def visualize_graph(graph):
111
  """
112
+ Visualize the graph using matplotlib.
113
 
114
+ Args:
115
+ graph (BasicGraph): The graph instance.
116
 
117
  Returns:
118
+ dict: A dictionary with the visualization status.
119
  """
120
  try:
121
+ from graph.visuals import plot_graph
122
+ plot_graph(graph)
123
+ return {"success": True}
 
 
 
 
 
 
 
 
 
 
 
124
  except Exception as e:
125
+ return {"success": False, "error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ # Add more tools here following the same pattern
128
 
129
+ # Run the MCP service
130
  if __name__ == "__main__":
131
+ mcp.run()