guohanghui commited on
Commit
885eaf6
·
verified ·
1 Parent(s): d0b41ed

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

Browse files
graph-theory/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,131 +1,582 @@
 
 
 
 
 
 
 
 
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()
 
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)