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

Update sktime/mcp_output/mcp_plugin/mcp_service.py

Browse files
sktime/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,72 +1,586 @@
1
  import os
2
  import sys
 
 
3
 
4
- # Path settings to include the local source directory
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
 
11
- # Import core modules from sktime
12
- from sktime.forecasting.base import BaseForecaster
13
- from sktime.classification.base import BaseClassifier
14
- from sktime.transformations.base import BaseTransformer
 
 
 
 
 
 
 
15
 
16
  # Create the FastMCP service application
17
  mcp = FastMCP("sktime_service")
18
 
19
- @mcp.tool(name="forecasting_tool", description="Tool for forecasting time series data")
20
- def forecasting_tool(forecaster: BaseForecaster, data) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  """
22
- Perform forecasting on the provided time series data using the specified forecaster.
23
 
24
- :param forecaster: An instance of a sktime BaseForecaster.
25
- :param data: The time series data to forecast.
26
- :return: A dictionary with success, result, and error fields.
 
 
27
  """
28
  try:
29
- result = forecaster.fit_predict(data)
 
 
 
 
 
 
 
 
 
 
30
  return {"success": True, "result": result, "error": None}
31
  except Exception as e:
32
  return {"success": False, "result": None, "error": str(e)}
33
 
34
- @mcp.tool(name="classification_tool", description="Tool for classifying time series data")
35
- def classification_tool(classifier: BaseClassifier, data, labels) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  """
37
- Perform classification on the provided time series data using the specified classifier.
38
 
39
- :param classifier: An instance of a sktime BaseClassifier.
40
- :param data: The time series data to classify.
41
- :param labels: The labels for the classification task.
42
- :return: A dictionary with success, result, and error fields.
 
 
43
  """
44
  try:
45
- classifier.fit(data, labels)
46
- predictions = classifier.predict(data)
47
- return {"success": True, "result": predictions, "error": None}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  except Exception as e:
49
  return {"success": False, "result": None, "error": str(e)}
50
 
51
- @mcp.tool(name="transformation_tool", description="Tool for transforming time series data")
52
- def transformation_tool(transformer: BaseTransformer, data) -> dict:
 
 
 
 
53
  """
54
- Apply transformations to the provided time series data using the specified transformer.
55
 
56
- :param transformer: An instance of a sktime BaseTransformer.
57
- :param data: The time series data to transform.
58
- :return: A dictionary with success, result, and error fields.
 
 
 
59
  """
60
  try:
61
- transformed_data = transformer.fit_transform(data)
62
- return {"success": True, "result": transformed_data, "error": None}
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  except Exception as e:
64
  return {"success": False, "result": None, "error": str(e)}
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  def create_app() -> FastMCP:
67
  """
68
  Create and return the FastMCP service instance.
69
 
70
- :return: An instance of FastMCP configured with the sktime service tools.
 
71
  """
72
- return mcp
 
 
 
 
 
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)