guohanghui commited on
Commit
ac29fb2
·
verified ·
1 Parent(s): fbcaeb4

Update sktime/mcp_output/mcp_plugin/mcp_service.py

Browse files
sktime/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,586 +1,157 @@
1
- import os
2
- import sys
3
- from typing import List, Dict, Any, Optional
4
- import json
5
-
6
- # Add source path for local sktime
7
- source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
8
- if source_path not in sys.path:
9
- sys.path.insert(0, source_path)
10
-
11
  from fastmcp import FastMCP
12
- import pandas as pd
13
- import numpy as np
14
-
15
- # Import sktime registry and core functions
16
- from sktime.registry import all_estimators, craft
17
- from sktime.datasets import (
18
- load_airline,
19
- load_longley,
20
- load_lynx,
21
- load_shampoo_sales,
22
- load_gunpoint,
23
- load_arrow_head,
24
- load_italy_power_demand,
25
- )
26
 
27
  # Create the FastMCP service application
28
  mcp = FastMCP("sktime_service")
29
 
30
- # Storage for created estimators
31
- _estimators: Dict[str, Any] = {}
32
- _datasets: Dict[str, Any] = {}
33
-
34
-
35
- @mcp.tool(name="list_forecasters", description="List all available forecasters in sktime")
36
- def list_forecasters(filter_tags: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
37
- """
38
- List all available forecasters in sktime.
39
-
40
- Args:
41
- filter_tags: Optional dictionary of tags to filter forecasters
42
-
43
- Returns:
44
- Dictionary with list of forecaster names and their descriptions
45
- """
46
- try:
47
- estimators = all_estimators(
48
- estimator_types="forecaster",
49
- filter_tags=filter_tags,
50
- as_dataframe=True
51
- )
52
- result = []
53
- for _, row in estimators.iterrows():
54
- result.append({
55
- "name": row["name"],
56
- "class": str(row["estimator"])
57
- })
58
- return {"success": True, "result": result, "error": None}
59
- except Exception as e:
60
- return {"success": False, "result": None, "error": str(e)}
61
-
62
-
63
- @mcp.tool(name="list_classifiers", description="List all available time series classifiers")
64
- def list_classifiers(filter_tags: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
65
- """
66
- List all available time series classifiers in sktime.
67
-
68
- Args:
69
- filter_tags: Optional dictionary of tags to filter classifiers
70
-
71
- Returns:
72
- Dictionary with list of classifier names
73
- """
74
- try:
75
- estimators = all_estimators(
76
- estimator_types="classifier",
77
- filter_tags=filter_tags,
78
- as_dataframe=True
79
- )
80
- result = []
81
- for _, row in estimators.iterrows():
82
- result.append({
83
- "name": row["name"],
84
- "class": str(row["estimator"])
85
- })
86
- return {"success": True, "result": result, "error": None}
87
- except Exception as e:
88
- return {"success": False, "result": None, "error": str(e)}
89
-
90
-
91
- @mcp.tool(name="list_transformers", description="List all available transformers")
92
- def list_transformers(filter_tags: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
93
- """
94
- List all available transformers in sktime.
95
-
96
- Args:
97
- filter_tags: Optional dictionary of tags to filter transformers
98
-
99
- Returns:
100
- Dictionary with list of transformer names
101
- """
102
- try:
103
- estimators = all_estimators(
104
- estimator_types="transformer",
105
- filter_tags=filter_tags,
106
- as_dataframe=True
107
- )
108
- result = []
109
- for _, row in estimators.iterrows():
110
- result.append({
111
- "name": row["name"],
112
- "class": str(row["estimator"])
113
- })
114
- return {"success": True, "result": result, "error": None}
115
- except Exception as e:
116
- return {"success": False, "result": None, "error": str(e)}
117
-
118
-
119
- @mcp.tool(name="create_estimator", description="Create an sktime estimator from a specification string")
120
- def create_estimator(spec: str, estimator_id: str) -> Dict[str, Any]:
121
- """
122
- Create an sktime estimator using the craft function.
123
-
124
- Args:
125
- spec: Specification string for the estimator (e.g., "NaiveForecaster(strategy='last')")
126
- estimator_id: Unique identifier to store the estimator
127
-
128
- Returns:
129
- Dictionary with success status and estimator info
130
- """
131
- try:
132
- estimator = craft(spec)
133
- _estimators[estimator_id] = estimator
134
- return {
135
- "success": True,
136
- "result": {
137
- "estimator_id": estimator_id,
138
- "type": type(estimator).__name__,
139
- "spec": str(estimator)
140
- },
141
- "error": None
142
- }
143
- except Exception as e:
144
- return {"success": False, "result": None, "error": str(e)}
145
-
146
-
147
- @mcp.tool(name="load_dataset", description="Load a built-in sktime dataset")
148
- def load_dataset(dataset_name: str, dataset_id: str) -> Dict[str, Any]:
149
- """
150
- Load a built-in sktime dataset.
151
-
152
- Args:
153
- dataset_name: Name of the dataset (airline, longley, lynx, shampoo_sales, gunpoint, arrow_head, italy_power_demand)
154
- dataset_id: Unique identifier to store the dataset
155
-
156
- Returns:
157
- Dictionary with dataset info
158
- """
159
- try:
160
- loaders = {
161
- "airline": load_airline,
162
- "longley": load_longley,
163
- "lynx": load_lynx,
164
- "shampoo_sales": load_shampoo_sales,
165
- "gunpoint": load_gunpoint,
166
- "arrow_head": load_arrow_head,
167
- "italy_power_demand": load_italy_power_demand,
168
- }
169
-
170
- if dataset_name not in loaders:
171
- return {
172
- "success": False,
173
- "result": None,
174
- "error": f"Unknown dataset: {dataset_name}. Available: {list(loaders.keys())}"
175
- }
176
-
177
- data = loaders[dataset_name]()
178
- _datasets[dataset_id] = data
179
-
180
- # Get info about the dataset
181
- if isinstance(data, tuple):
182
- # Classification dataset (X, y)
183
- X, y = data
184
- info = {
185
- "type": "classification",
186
- "X_shape": str(X.shape) if hasattr(X, 'shape') else str(len(X)),
187
- "y_shape": str(y.shape) if hasattr(y, 'shape') else str(len(y)),
188
- "y_unique": list(np.unique(y)) if hasattr(y, '__iter__') else None
189
- }
190
- else:
191
- # Forecasting dataset (single series)
192
- info = {
193
- "type": "forecasting",
194
- "shape": str(data.shape) if hasattr(data, 'shape') else str(len(data)),
195
- "index_type": type(data.index).__name__ if hasattr(data, 'index') else None,
196
- "head": data.head().to_dict() if hasattr(data, 'head') else str(data[:5])
197
- }
198
-
199
- return {
200
- "success": True,
201
- "result": {
202
- "dataset_id": dataset_id,
203
- "dataset_name": dataset_name,
204
- "info": info
205
- },
206
- "error": None
207
- }
208
- except Exception as e:
209
- return {"success": False, "result": None, "error": str(e)}
210
-
211
-
212
- @mcp.tool(name="create_series_from_list", description="Create a pandas Series from a list of values")
213
- def create_series_from_list(
214
- values: List[float],
215
- dataset_id: str,
216
- freq: Optional[str] = None,
217
- start_date: Optional[str] = None
218
- ) -> Dict[str, Any]:
219
- """
220
- Create a pandas Series from a list of values for use with sktime.
221
-
222
- Args:
223
- values: List of numeric values
224
- dataset_id: Unique identifier to store the series
225
- freq: Optional frequency string (e.g., 'M' for monthly, 'D' for daily)
226
- start_date: Optional start date string (e.g., '2020-01-01')
227
-
228
- Returns:
229
- Dictionary with series info
230
- """
231
- try:
232
- if freq and start_date:
233
- index = pd.date_range(start=start_date, periods=len(values), freq=freq)
234
- else:
235
- index = pd.RangeIndex(start=0, stop=len(values))
236
-
237
- series = pd.Series(values, index=index)
238
- _datasets[dataset_id] = series
239
-
240
- return {
241
- "success": True,
242
- "result": {
243
- "dataset_id": dataset_id,
244
- "length": len(series),
245
- "index_type": type(series.index).__name__,
246
- "head": series.head().to_dict()
247
- },
248
- "error": None
249
- }
250
- except Exception as e:
251
- return {"success": False, "result": None, "error": str(e)}
252
-
253
-
254
- @mcp.tool(name="fit_forecaster", description="Fit a forecaster on time series data")
255
- def fit_forecaster(estimator_id: str, dataset_id: str) -> Dict[str, Any]:
256
- """
257
- Fit a forecaster on time series data.
258
-
259
- Args:
260
- estimator_id: ID of the forecaster to fit
261
- dataset_id: ID of the dataset to fit on
262
-
263
- Returns:
264
- Dictionary with fit status
265
- """
266
- try:
267
- if estimator_id not in _estimators:
268
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
269
- if dataset_id not in _datasets:
270
- return {"success": False, "result": None, "error": f"Dataset '{dataset_id}' not found"}
271
-
272
- estimator = _estimators[estimator_id]
273
- data = _datasets[dataset_id]
274
-
275
- estimator.fit(data)
276
-
277
- return {
278
- "success": True,
279
- "result": {
280
- "estimator_id": estimator_id,
281
- "fitted": True,
282
- "data_length": len(data)
283
- },
284
- "error": None
285
- }
286
- except Exception as e:
287
- return {"success": False, "result": None, "error": str(e)}
288
-
289
-
290
- @mcp.tool(name="predict_forecaster", description="Make predictions with a fitted forecaster")
291
- def predict_forecaster(
292
- estimator_id: str,
293
- forecast_horizon: List[int]
294
- ) -> Dict[str, Any]:
295
- """
296
- Make predictions with a fitted forecaster.
297
-
298
- Args:
299
- estimator_id: ID of the fitted forecaster
300
- forecast_horizon: List of integers representing forecast steps (e.g., [1, 2, 3] for 3-step forecast)
301
-
302
- Returns:
303
- Dictionary with predictions
304
- """
305
- try:
306
- if estimator_id not in _estimators:
307
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
308
-
309
- estimator = _estimators[estimator_id]
310
- predictions = estimator.predict(fh=forecast_horizon)
311
-
312
- return {
313
- "success": True,
314
- "result": {
315
- "estimator_id": estimator_id,
316
- "forecast_horizon": forecast_horizon,
317
- "predictions": predictions.to_dict() if hasattr(predictions, 'to_dict') else predictions.tolist()
318
- },
319
- "error": None
320
- }
321
- except Exception as e:
322
- return {"success": False, "result": None, "error": str(e)}
323
-
324
-
325
- @mcp.tool(name="fit_classifier", description="Fit a time series classifier")
326
- def fit_classifier(estimator_id: str, dataset_id: str) -> Dict[str, Any]:
327
- """
328
- Fit a time series classifier on labeled data.
329
-
330
- Args:
331
- estimator_id: ID of the classifier to fit
332
- dataset_id: ID of the classification dataset (must be a tuple of X, y)
333
-
334
- Returns:
335
- Dictionary with fit status
336
- """
337
- try:
338
- if estimator_id not in _estimators:
339
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
340
- if dataset_id not in _datasets:
341
- return {"success": False, "result": None, "error": f"Dataset '{dataset_id}' not found"}
342
-
343
- estimator = _estimators[estimator_id]
344
- data = _datasets[dataset_id]
345
-
346
- if isinstance(data, tuple):
347
- X, y = data
348
- else:
349
- return {"success": False, "result": None, "error": "Dataset must be a (X, y) tuple for classification"}
350
-
351
- estimator.fit(X, y)
352
-
353
- return {
354
- "success": True,
355
- "result": {
356
- "estimator_id": estimator_id,
357
- "fitted": True,
358
- "n_samples": len(y),
359
- "classes": list(np.unique(y))
360
- },
361
- "error": None
362
- }
363
- except Exception as e:
364
- return {"success": False, "result": None, "error": str(e)}
365
-
366
-
367
- @mcp.tool(name="predict_classifier", description="Make predictions with a fitted classifier")
368
- def predict_classifier(estimator_id: str, dataset_id: str) -> Dict[str, Any]:
369
- """
370
- Make predictions with a fitted time series classifier.
371
-
372
- Args:
373
- estimator_id: ID of the fitted classifier
374
- dataset_id: ID of the dataset to predict on
375
-
376
- Returns:
377
- Dictionary with predictions
378
- """
379
- try:
380
- if estimator_id not in _estimators:
381
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
382
- if dataset_id not in _datasets:
383
- return {"success": False, "result": None, "error": f"Dataset '{dataset_id}' not found"}
384
-
385
- estimator = _estimators[estimator_id]
386
- data = _datasets[dataset_id]
387
-
388
- if isinstance(data, tuple):
389
- X, _ = data
390
- else:
391
- X = data
392
-
393
- predictions = estimator.predict(X)
394
-
395
- return {
396
- "success": True,
397
- "result": {
398
- "estimator_id": estimator_id,
399
- "predictions": predictions.tolist() if hasattr(predictions, 'tolist') else list(predictions)
400
- },
401
- "error": None
402
- }
403
- except Exception as e:
404
- return {"success": False, "result": None, "error": str(e)}
405
-
406
-
407
- @mcp.tool(name="fit_transform", description="Fit and transform data using a transformer")
408
- def fit_transform(estimator_id: str, dataset_id: str, output_id: str) -> Dict[str, Any]:
409
- """
410
- Fit a transformer and transform data.
411
-
412
- Args:
413
- estimator_id: ID of the transformer
414
- dataset_id: ID of the dataset to transform
415
- output_id: ID to store the transformed data
416
-
417
- Returns:
418
- Dictionary with transformation result info
419
- """
420
- try:
421
- if estimator_id not in _estimators:
422
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
423
- if dataset_id not in _datasets:
424
- return {"success": False, "result": None, "error": f"Dataset '{dataset_id}' not found"}
425
-
426
- estimator = _estimators[estimator_id]
427
- data = _datasets[dataset_id]
428
-
429
- transformed = estimator.fit_transform(data)
430
- _datasets[output_id] = transformed
431
-
432
- return {
433
- "success": True,
434
- "result": {
435
- "estimator_id": estimator_id,
436
- "input_id": dataset_id,
437
- "output_id": output_id,
438
- "output_shape": str(transformed.shape) if hasattr(transformed, 'shape') else str(len(transformed)),
439
- "head": transformed.head().to_dict() if hasattr(transformed, 'head') else str(transformed[:5])
440
- },
441
- "error": None
442
- }
443
- except Exception as e:
444
- return {"success": False, "result": None, "error": str(e)}
445
-
446
-
447
- @mcp.tool(name="get_estimator_info", description="Get information about a stored estimator")
448
- def get_estimator_info(estimator_id: str) -> Dict[str, Any]:
449
- """
450
- Get detailed information about a stored estimator.
451
-
452
- Args:
453
- estimator_id: ID of the estimator
454
-
455
- Returns:
456
- Dictionary with estimator information
457
- """
458
- try:
459
- if estimator_id not in _estimators:
460
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
461
-
462
- estimator = _estimators[estimator_id]
463
-
464
- info = {
465
- "estimator_id": estimator_id,
466
- "type": type(estimator).__name__,
467
- "spec": str(estimator),
468
- "tags": estimator.get_tags() if hasattr(estimator, 'get_tags') else {},
469
- "is_fitted": estimator.is_fitted if hasattr(estimator, 'is_fitted') else None
470
- }
471
-
472
- # Get parameters
473
- if hasattr(estimator, 'get_params'):
474
- params = estimator.get_params()
475
- # Convert non-serializable params to strings
476
- info["params"] = {k: str(v) if not isinstance(v, (str, int, float, bool, type(None))) else v
477
- for k, v in params.items()}
478
-
479
- return {"success": True, "result": info, "error": None}
480
- except Exception as e:
481
- return {"success": False, "result": None, "error": str(e)}
482
-
483
-
484
- @mcp.tool(name="get_dataset_info", description="Get information about a stored dataset")
485
- def get_dataset_info(dataset_id: str) -> Dict[str, Any]:
486
- """
487
- Get detailed information about a stored dataset.
488
-
489
- Args:
490
- dataset_id: ID of the dataset
491
-
492
- Returns:
493
- Dictionary with dataset information
494
- """
495
- try:
496
- if dataset_id not in _datasets:
497
- return {"success": False, "result": None, "error": f"Dataset '{dataset_id}' not found"}
498
-
499
- data = _datasets[dataset_id]
500
-
501
- if isinstance(data, tuple):
502
- X, y = data
503
- info = {
504
- "dataset_id": dataset_id,
505
- "type": "classification",
506
- "X_type": type(X).__name__,
507
- "y_type": type(y).__name__,
508
- "X_shape": str(X.shape) if hasattr(X, 'shape') else str(len(X)),
509
- "y_shape": str(y.shape) if hasattr(y, 'shape') else str(len(y)),
510
- "classes": list(np.unique(y))
511
- }
512
- elif hasattr(data, 'shape'):
513
- info = {
514
- "dataset_id": dataset_id,
515
- "type": "series" if len(data.shape) == 1 else "dataframe",
516
- "shape": str(data.shape),
517
- "dtype": str(data.dtype) if hasattr(data, 'dtype') else None,
518
- "index_type": type(data.index).__name__ if hasattr(data, 'index') else None,
519
- "head": data.head().to_dict() if hasattr(data, 'head') else str(data[:5])
520
- }
521
- else:
522
- info = {
523
- "dataset_id": dataset_id,
524
- "type": type(data).__name__,
525
- "length": len(data) if hasattr(data, '__len__') else None
526
- }
527
-
528
- return {"success": True, "result": info, "error": None}
529
- except Exception as e:
530
- return {"success": False, "result": None, "error": str(e)}
531
-
532
-
533
- @mcp.tool(name="delete_estimator", description="Delete a stored estimator")
534
- def delete_estimator(estimator_id: str) -> Dict[str, Any]:
535
- """
536
- Delete a stored estimator.
537
-
538
- Args:
539
- estimator_id: ID of the estimator to delete
540
-
541
- Returns:
542
- Dictionary with deletion status
543
- """
544
- try:
545
- if estimator_id not in _estimators:
546
- return {"success": False, "result": None, "error": f"Estimator '{estimator_id}' not found"}
547
-
548
- del _estimators[estimator_id]
549
- return {"success": True, "result": {"deleted": estimator_id}, "error": None}
550
- except Exception as e:
551
- return {"success": False, "result": None, "error": str(e)}
552
-
553
-
554
- @mcp.tool(name="delete_dataset", description="Delete a stored dataset")
555
- def delete_dataset(dataset_id: str) -> Dict[str, Any]:
556
- """
557
- Delete a stored dataset.
558
-
559
- Args:
560
- dataset_id: ID of the dataset to delete
561
-
562
- Returns:
563
- Dictionary with deletion status
564
- """
565
- try:
566
- if dataset_id not in _datasets:
567
- return {"success": False, "result": None, "error": f"Dataset '{dataset_id}' not found"}
568
-
569
- del _datasets[dataset_id]
570
- return {"success": True, "result": {"deleted": dataset_id}, "error": None}
571
- except Exception as e:
572
- return {"success": False, "result": None, "error": str(e)}
573
-
574
-
575
- def create_app() -> FastMCP:
576
- """
577
- Create and return the FastMCP service instance.
578
-
579
- Returns:
580
- An instance of FastMCP configured with the sktime service tools.
581
- """
582
- return mcp
583
 
 
 
 
 
 
 
 
 
 
 
 
 
584
 
585
  if __name__ == "__main__":
586
- 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("sktime_service")
5
 
6
+ @mcp.tool(name="list_available_datasets", description="List all available sktime datasets")
7
+ def list_available_datasets():
8
+ """List all available datasets in sktime."""
9
+ from sktime.datasets import get_dataset_names
10
+ return get_dataset_names()
11
+
12
+ @mcp.tool(name="load_dataset", description="Load a dataset from sktime")
13
+ def load_dataset(dataset_name: str):
14
+ """Load a dataset by name from sktime.
15
+
16
+ Parameters
17
+ ----------
18
+ dataset_name : str
19
+ The name of the dataset to load.
20
+
21
+ Returns
22
+ -------
23
+ Dataset object.
24
+ """
25
+ from sktime.datasets import load_from_tsfile
26
+ return load_from_tsfile(dataset_name)
27
+
28
+ @mcp.tool(name="time_series_classification", description="Perform time series classification using sktime")
29
+ def time_series_classification(data, classifier_name: str):
30
+ """Perform time series classification.
31
+
32
+ Parameters
33
+ ----------
34
+ data : pd.DataFrame
35
+ Input time series data.
36
+ classifier_name : str
37
+ Name of the classifier to use.
38
+
39
+ Returns
40
+ -------
41
+ Classification results.
42
+ """
43
+ from sktime.classification.all import all_estimators
44
+ from sktime.classification.compose import make_pipeline
45
+
46
+ classifiers = dict(all_estimators())
47
+ if classifier_name not in classifiers:
48
+ raise ValueError(f"Classifier {classifier_name} not found in sktime.")
49
+
50
+ classifier = classifiers[classifier_name]()
51
+ pipeline = make_pipeline(classifier)
52
+ return pipeline.fit_predict(data)
53
+
54
+ @mcp.tool(name="time_series_forecasting", description="Perform time series forecasting using sktime")
55
+ def time_series_forecasting(data, forecaster_name: str, fh):
56
+ """Perform time series forecasting.
57
+
58
+ Parameters
59
+ ----------
60
+ data : pd.DataFrame
61
+ Input time series data.
62
+ forecaster_name : str
63
+ Name of the forecaster to use.
64
+ fh : ForecastingHorizon
65
+ Forecasting horizon.
66
+
67
+ Returns
68
+ -------
69
+ Forecast results.
70
+ """
71
+ from sktime.forecasting.all import all_estimators
72
+ from sktime.forecasting.compose import make_reduction
73
+
74
+ forecasters = dict(all_estimators())
75
+ if forecaster_name not in forecasters:
76
+ raise ValueError(f"Forecaster {forecaster_name} not found in sktime.")
77
+
78
+ forecaster = forecasters[forecaster_name]()
79
+ pipeline = make_reduction(forecaster)
80
+ return pipeline.fit_predict(data, fh)
81
+
82
+ @mcp.tool(name="time_series_transformation", description="Apply transformations to time series data using sktime")
83
+ def time_series_transformation(data, transformer_name: str):
84
+ """Apply transformations to time series data.
85
+
86
+ Parameters
87
+ ----------
88
+ data : pd.DataFrame
89
+ Input time series data.
90
+ transformer_name : str
91
+ Name of the transformer to use.
92
+
93
+ Returns
94
+ -------
95
+ Transformed data.
96
+ """
97
+ from sktime.transformations.all import all_estimators
98
+
99
+ transformers = dict(all_estimators())
100
+ if transformer_name not in transformers:
101
+ raise ValueError(f"Transformer {transformer_name} not found in sktime.")
102
+
103
+ transformer = transformers[transformer_name]()
104
+ return transformer.fit_transform(data)
105
+
106
+ @mcp.tool(name="time_series_clustering", description="Perform time series clustering using sktime")
107
+ def time_series_clustering(data, clusterer_name: str):
108
+ """Perform time series clustering.
109
+
110
+ Parameters
111
+ ----------
112
+ data : pd.DataFrame
113
+ Input time series data.
114
+ clusterer_name : str
115
+ Name of the clusterer to use.
116
+
117
+ Returns
118
+ -------
119
+ Clustering results.
120
+ """
121
+ from sktime.clustering.all import all_estimators
122
+
123
+ clusterers = dict(all_estimators())
124
+ if clusterer_name not in clusterers:
125
+ raise ValueError(f"Clusterer {clusterer_name} not found in sktime.")
126
+
127
+ clusterer = clusterers[clusterer_name]()
128
+ return clusterer.fit_predict(data)
129
+
130
+ @mcp.tool(name="time_series_distance", description="Compute distances between time series using sktime")
131
+ def time_series_distance(data1, data2, distance_name: str):
132
+ """Compute distances between time series.
133
+
134
+ Parameters
135
+ ----------
136
+ data1 : pd.DataFrame
137
+ First time series data.
138
+ data2 : pd.DataFrame
139
+ Second time series data.
140
+ distance_name : str
141
+ Name of the distance metric to use.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ Returns
144
+ -------
145
+ Distance value.
146
+ """
147
+ from sktime.dists_kernels.all import all_estimators
148
+
149
+ distances = dict(all_estimators())
150
+ if distance_name not in distances:
151
+ raise ValueError(f"Distance metric {distance_name} not found in sktime.")
152
+
153
+ distance = distances[distance_name]()
154
+ return distance(data1, data2)
155
 
156
  if __name__ == "__main__":
157
+ mcp.run()