@@ -214,24 +214,34 @@ async def apply_layout(graph_id: str, layout: str) -> Dict[str, Any]:
214
214
raise
215
215
216
216
@mcp .tool ()
217
- async def detect_patterns (graph_id : str ,
218
- analysis_type : str ,
219
- options : Optional [Dict [str , Any ]] = None ,
220
- ctx : Optional [Context ] = None ) -> Dict [str , Any ]:
221
- """Identify patterns, communities, and anomalies within graphs.
222
-
217
+ async def detect_patterns (graph_id : str , ctx : Optional [Context ] = None ) -> Dict [str , Any ]:
218
+ """
219
+ Identify patterns, communities, and anomalies within graphs. Runs all supported analyses and returns a combined report.
220
+
223
221
Args:
224
222
graph_id: ID of the graph to analyze
225
- analysis_type: Type of pattern analysis to perform
226
- options: Additional options for the analysis
227
223
ctx: MCP context for progress reporting
224
+
225
+ Returns:
226
+ Dictionary with results from all analyses that succeeded. Keys may include:
227
+ - degree_centrality
228
+ - betweenness_centrality
229
+ - closeness_centrality
230
+ - communities (if community detection is available)
231
+ - shortest_path (if path finding is possible)
232
+ - path_length
233
+ - anomalies (if anomaly detection is available)
234
+ - errors (dict of analysis_type -> error message)
235
+
236
+ Example:
237
+ result = await mcp.call_tool("detect_patterns", {"graph_id": "graph_1"})
228
238
"""
229
239
try :
230
240
if graph_id not in graph_cache :
231
241
raise ValueError (f"Graph not found: { graph_id } " )
232
242
233
243
if ctx :
234
- await ctx .info (f "Starting { analysis_type } analysis ..." )
244
+ await ctx .info ("Starting pattern detection (all analyses) ..." )
235
245
236
246
graph_data = graph_cache [graph_id ]
237
247
nx_graph = graph_data ["nx_graph" ]
@@ -245,30 +255,47 @@ async def detect_patterns(graph_id: str,
245
255
raise ValueError ("Graph data not available for analysis" )
246
256
247
257
results = {}
248
- if analysis_type == "community_detection" :
249
- # Implement community detection
250
- pass
251
- elif analysis_type == "centrality" :
258
+ errors = {}
259
+
260
+ # Centrality
261
+ try :
252
262
results ["degree_centrality" ] = nx .degree_centrality (nx_graph )
253
263
results ["betweenness_centrality" ] = nx .betweenness_centrality (nx_graph )
254
264
results ["closeness_centrality" ] = nx .closeness_centrality (nx_graph )
255
- elif analysis_type == "path_finding" :
256
- if not options or "source" not in options or "target" not in options :
257
- raise ValueError ("source and target nodes required for path finding" )
258
- try :
259
- path = nx .shortest_path (nx_graph , options ["source" ], options ["target" ])
265
+ except Exception as e :
266
+ errors ["centrality" ] = str (e )
267
+
268
+ # Community detection
269
+ try :
270
+ import community as community_louvain
271
+ partition = community_louvain .best_partition (nx_graph )
272
+ results ["communities" ] = partition
273
+ except Exception as e :
274
+ errors ["community_detection" ] = str (e )
275
+
276
+ # Path finding (try between first two nodes if possible)
277
+ try :
278
+ nodes = list (nx_graph .nodes ())
279
+ if len (nodes ) >= 2 :
280
+ path = nx .shortest_path (nx_graph , nodes [0 ], nodes [1 ])
260
281
results ["shortest_path" ] = path
261
282
results ["path_length" ] = len (path ) - 1
262
- except nx .NetworkXNoPath :
263
- results ["error" ] = "No path exists between specified nodes"
264
- elif analysis_type == "anomaly_detection" :
265
- # Implement anomaly detection
266
- pass
267
- else :
268
- raise ValueError (f"Unsupported analysis type: { analysis_type } " )
269
-
283
+ except Exception as e :
284
+ errors ["path_finding" ] = str (e )
285
+
286
+ # Anomaly detection (placeholder)
287
+ try :
288
+ # Example: nodes with degree 1 as "anomalies"
289
+ anomalies = [n for n , d in nx_graph .degree () if d == 1 ]
290
+ results ["anomalies" ] = anomalies
291
+ except Exception as e :
292
+ errors ["anomaly_detection" ] = str (e )
293
+
294
+ if errors :
295
+ results ["errors" ] = errors
296
+
270
297
if ctx :
271
- await ctx .info ("Analysis complete!" )
298
+ await ctx .info ("Pattern detection complete!" )
272
299
273
300
return results
274
301
except Exception as e :
0 commit comments