guohanghui commited on
Commit
3ab06f9
·
verified ·
1 Parent(s): f2dfbb5

Update MONAI/mcp_output/mcp_plugin/mcp_service.py

Browse files
MONAI/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,133 +1,429 @@
 
 
 
 
 
 
 
 
 
1
  from fastmcp import FastMCP
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  # Create the FastMCP service application
4
  mcp = FastMCP("monai_service")
5
 
6
- @mcp.tool(name="load_medical_dataset", description="Load a medical imaging dataset using MONAI")
7
- def load_medical_dataset(dataset_path: str) -> dict:
 
8
  """
9
- Load a medical imaging dataset using MONAI.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  Parameters:
12
- - dataset_path: Path to the dataset.
 
 
 
 
13
 
14
  Returns:
15
- - dict: Information about the loaded dataset.
16
  """
17
  try:
18
- from monai.data import Dataset
19
- from monai.transforms import LoadImaged
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- data = [{"image": dataset_path}]
22
- transforms = LoadImaged(keys=["image"])
23
- dataset = Dataset(data, transforms)
24
 
25
  return {
26
  "success": True,
27
- "dataset": str(dataset)
 
 
 
 
 
 
 
 
 
 
28
  }
29
  except Exception as e:
30
- return {"success": False, "error": str(e)}
31
 
32
- @mcp.tool(name="train_segmentation_model", description="Train a segmentation model using MONAI")
33
- def train_segmentation_model(config: dict) -> dict:
 
 
 
 
 
 
34
  """
35
- Train a segmentation model using MONAI.
36
 
37
  Parameters:
38
- - config: Configuration dictionary for training.
 
 
 
39
 
40
  Returns:
41
- - dict: Training results.
42
  """
43
  try:
44
- from monai.engines import SupervisedTrainer
45
- from monai.transforms import Compose
 
 
 
 
46
 
47
- # Example: Initialize trainer with config
48
- trainer = SupervisedTrainer(**config)
49
- trainer.run()
50
 
51
  return {
52
  "success": True,
53
- "message": "Training completed successfully."
 
 
 
 
 
 
 
 
54
  }
55
  except Exception as e:
56
- return {"success": False, "error": str(e)}
 
57
 
58
- @mcp.tool(name="evaluate_model", description="Evaluate a trained model using MONAI")
59
- def evaluate_model(model_path: str, test_data: list) -> dict:
 
 
 
 
60
  """
61
- Evaluate a trained model using MONAI.
62
 
63
  Parameters:
64
- - model_path: Path to the trained model.
65
- - test_data: Test dataset.
 
66
 
67
  Returns:
68
- - dict: Evaluation metrics.
69
  """
70
  try:
71
- from monai.engines import SupervisedEvaluator
72
 
73
- evaluator = SupervisedEvaluator(model_path=model_path, data=test_data)
74
- metrics = evaluator.run()
 
 
 
 
 
75
 
76
  return {
77
  "success": True,
78
- "metrics": metrics
 
 
 
 
79
  }
80
  except Exception as e:
81
- return {"success": False, "error": str(e)}
 
82
 
83
- @mcp.tool(name="apply_transforms", description="Apply MONAI transforms to medical images")
84
- def apply_transforms(image_path: str, transforms: list) -> dict:
 
 
 
 
85
  """
86
- Apply MONAI transforms to medical images.
87
 
88
  Parameters:
89
- - image_path: Path to the image.
90
- - transforms: List of transforms to apply.
 
91
 
92
  Returns:
93
- - dict: Transformed image data.
94
  """
95
  try:
96
- from monai.transforms import Compose
 
 
 
97
 
98
- composed_transforms = Compose(transforms)
99
- transformed_image = composed_transforms(image_path)
 
 
 
 
 
 
 
 
 
 
100
 
101
  return {
102
  "success": True,
103
- "transformed_image": transformed_image
 
 
 
 
104
  }
105
  except Exception as e:
106
- return {"success": False, "error": str(e)}
 
107
 
108
- @mcp.tool(name="visualize_segmentation", description="Visualize segmentation results using MONAI")
109
- def visualize_segmentation(image_path: str, segmentation_path: str) -> dict:
 
 
 
 
 
 
110
  """
111
- Visualize segmentation results using MONAI.
112
 
113
  Parameters:
114
- - image_path: Path to the original image.
115
- - segmentation_path: Path to the segmentation result.
 
 
 
116
 
117
  Returns:
118
- - dict: Visualization status.
119
  """
120
  try:
121
- from monai.visualize import blend_images
122
 
123
- blended_image = blend_images(image_path, segmentation_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  return {
126
  "success": True,
127
- "blended_image": blended_image
 
 
 
 
 
 
 
 
128
  }
129
  except Exception as e:
130
- return {"success": False, "error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  def create_app() -> FastMCP:
133
  """
 
1
+ import os
2
+ import sys
3
+ from typing import List, Optional, Dict, Any
4
+
5
+ # Add the local source directory to sys.path
6
+ source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
7
+ if source_path not in sys.path:
8
+ sys.path.insert(0, source_path)
9
+
10
  from fastmcp import FastMCP
11
+ import numpy as np
12
+
13
+ # Import MONAI modules
14
+ import monai
15
+ from monai.transforms import (
16
+ Compose, LoadImage, EnsureChannelFirst, ScaleIntensity,
17
+ NormalizeIntensity, Resize, RandRotate, RandFlip, ToTensor
18
+ )
19
+ from monai.networks.nets import UNet, VNet, AttentionUnet, SegResNet, DenseNet121
20
+ from monai.losses import DiceLoss, DiceCELoss, FocalLoss, TverskyLoss
21
+ from monai.metrics import DiceMetric, MeanIoU, HausdorffDistanceMetric
22
+ from monai.data import decollate_batch
23
+ from monai.inferers import sliding_window_inference
24
 
25
  # Create the FastMCP service application
26
  mcp = FastMCP("monai_service")
27
 
28
+
29
+ @mcp.tool(name="get_monai_info", description="Get MONAI library information and configuration")
30
+ def get_monai_info() -> dict:
31
  """
32
+ Get MONAI library version and system configuration.
33
+
34
+ Returns:
35
+ - dict: MONAI version and configuration info.
36
+ """
37
+ try:
38
+ config = monai.config.get_config_values()
39
+ return {
40
+ "success": True,
41
+ "result": {
42
+ "version": monai.__version__,
43
+ "config": {k: str(v) for k, v in config.items()}
44
+ },
45
+ "error": None
46
+ }
47
+ except Exception as e:
48
+ return {"success": False, "result": None, "error": str(e)}
49
+
50
+
51
+ @mcp.tool(name="create_unet_model", description="Create and initialize a UNet model")
52
+ def create_unet_model(
53
+ spatial_dims: int = 3,
54
+ in_channels: int = 1,
55
+ out_channels: int = 2,
56
+ channels: List[int] = None,
57
+ strides: List[int] = None
58
+ ) -> dict:
59
+ """
60
+ Create a UNet model for medical image segmentation.
61
 
62
  Parameters:
63
+ - spatial_dims: Number of spatial dimensions (2 or 3).
64
+ - in_channels: Number of input channels.
65
+ - out_channels: Number of output channels/classes.
66
+ - channels: Feature channels per layer (default: [16, 32, 64, 128, 256]).
67
+ - strides: Strides per layer (default: [2, 2, 2, 2]).
68
 
69
  Returns:
70
+ - dict: Model information and parameter count.
71
  """
72
  try:
73
+ if channels is None:
74
+ channels = [16, 32, 64, 128, 256]
75
+ if strides is None:
76
+ strides = [2, 2, 2, 2]
77
+
78
+ model = UNet(
79
+ spatial_dims=spatial_dims,
80
+ in_channels=in_channels,
81
+ out_channels=out_channels,
82
+ channels=channels,
83
+ strides=strides,
84
+ num_res_units=2
85
+ )
86
 
87
+ total_params = sum(p.numel() for p in model.parameters())
88
+ trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
 
89
 
90
  return {
91
  "success": True,
92
+ "result": {
93
+ "model_type": "UNet",
94
+ "spatial_dims": spatial_dims,
95
+ "in_channels": in_channels,
96
+ "out_channels": out_channels,
97
+ "channels": channels,
98
+ "strides": strides,
99
+ "total_parameters": total_params,
100
+ "trainable_parameters": trainable_params
101
+ },
102
+ "error": None
103
  }
104
  except Exception as e:
105
+ return {"success": False, "result": None, "error": str(e)}
106
 
107
+
108
+ @mcp.tool(name="create_segresnet_model", description="Create a SegResNet model")
109
+ def create_segresnet_model(
110
+ spatial_dims: int = 3,
111
+ in_channels: int = 1,
112
+ out_channels: int = 2,
113
+ init_filters: int = 16
114
+ ) -> dict:
115
  """
116
+ Create a SegResNet model for medical image segmentation.
117
 
118
  Parameters:
119
+ - spatial_dims: Number of spatial dimensions (2 or 3).
120
+ - in_channels: Number of input channels.
121
+ - out_channels: Number of output channels/classes.
122
+ - init_filters: Initial number of filters.
123
 
124
  Returns:
125
+ - dict: Model information and parameter count.
126
  """
127
  try:
128
+ model = SegResNet(
129
+ spatial_dims=spatial_dims,
130
+ in_channels=in_channels,
131
+ out_channels=out_channels,
132
+ init_filters=init_filters
133
+ )
134
 
135
+ total_params = sum(p.numel() for p in model.parameters())
 
 
136
 
137
  return {
138
  "success": True,
139
+ "result": {
140
+ "model_type": "SegResNet",
141
+ "spatial_dims": spatial_dims,
142
+ "in_channels": in_channels,
143
+ "out_channels": out_channels,
144
+ "init_filters": init_filters,
145
+ "total_parameters": total_params
146
+ },
147
+ "error": None
148
  }
149
  except Exception as e:
150
+ return {"success": False, "result": None, "error": str(e)}
151
+
152
 
153
+ @mcp.tool(name="compute_dice_score", description="Compute Dice score between prediction and ground truth")
154
+ def compute_dice_score(
155
+ prediction: List[List[List[float]]],
156
+ ground_truth: List[List[List[float]]],
157
+ include_background: bool = False
158
+ ) -> dict:
159
  """
160
+ Compute Dice similarity coefficient between prediction and ground truth.
161
 
162
  Parameters:
163
+ - prediction: Predicted segmentation as nested list (2D or 3D).
164
+ - ground_truth: Ground truth segmentation as nested list.
165
+ - include_background: Whether to include background class.
166
 
167
  Returns:
168
+ - dict: Dice score for each class.
169
  """
170
  try:
171
+ import torch
172
 
173
+ pred_tensor = torch.tensor(prediction).unsqueeze(0).unsqueeze(0).float()
174
+ gt_tensor = torch.tensor(ground_truth).unsqueeze(0).unsqueeze(0).float()
175
+
176
+ dice_metric = DiceMetric(include_background=include_background, reduction="mean")
177
+ dice_metric(y_pred=pred_tensor, y=gt_tensor)
178
+ dice_score = dice_metric.aggregate().item()
179
+ dice_metric.reset()
180
 
181
  return {
182
  "success": True,
183
+ "result": {
184
+ "dice_score": dice_score,
185
+ "include_background": include_background
186
+ },
187
+ "error": None
188
  }
189
  except Exception as e:
190
+ return {"success": False, "result": None, "error": str(e)}
191
+
192
 
193
+ @mcp.tool(name="compute_loss", description="Compute segmentation loss")
194
+ def compute_loss(
195
+ prediction: List[List[List[float]]],
196
+ ground_truth: List[List[List[float]]],
197
+ loss_type: str = "dice"
198
+ ) -> dict:
199
  """
200
+ Compute segmentation loss between prediction and ground truth.
201
 
202
  Parameters:
203
+ - prediction: Predicted logits as nested list.
204
+ - ground_truth: Ground truth labels as nested list.
205
+ - loss_type: Type of loss ('dice', 'dice_ce', 'focal', 'tversky').
206
 
207
  Returns:
208
+ - dict: Loss value.
209
  """
210
  try:
211
+ import torch
212
+
213
+ pred_tensor = torch.tensor(prediction).unsqueeze(0).unsqueeze(0).float()
214
+ gt_tensor = torch.tensor(ground_truth).unsqueeze(0).unsqueeze(0).float()
215
 
216
+ if loss_type == "dice":
217
+ loss_fn = DiceLoss(sigmoid=True)
218
+ elif loss_type == "dice_ce":
219
+ loss_fn = DiceCELoss(sigmoid=True)
220
+ elif loss_type == "focal":
221
+ loss_fn = FocalLoss()
222
+ elif loss_type == "tversky":
223
+ loss_fn = TverskyLoss(sigmoid=True)
224
+ else:
225
+ return {"success": False, "result": None, "error": f"Unknown loss type: {loss_type}"}
226
+
227
+ loss_value = loss_fn(pred_tensor, gt_tensor).item()
228
 
229
  return {
230
  "success": True,
231
+ "result": {
232
+ "loss_type": loss_type,
233
+ "loss_value": loss_value
234
+ },
235
+ "error": None
236
  }
237
  except Exception as e:
238
+ return {"success": False, "result": None, "error": str(e)}
239
+
240
 
241
+ @mcp.tool(name="apply_intensity_transforms", description="Apply intensity transforms to an image")
242
+ def apply_intensity_transforms(
243
+ image: List[List[List[float]]],
244
+ normalize: bool = True,
245
+ scale_intensity: bool = False,
246
+ target_min: float = 0.0,
247
+ target_max: float = 1.0
248
+ ) -> dict:
249
  """
250
+ Apply intensity transforms to a medical image.
251
 
252
  Parameters:
253
+ - image: Input image as nested list (2D or 3D).
254
+ - normalize: Whether to normalize intensity (zero mean, unit std).
255
+ - scale_intensity: Whether to scale intensity to target range.
256
+ - target_min: Minimum value for scaling.
257
+ - target_max: Maximum value for scaling.
258
 
259
  Returns:
260
+ - dict: Transformed image statistics.
261
  """
262
  try:
263
+ img_array = np.array(image, dtype=np.float32)
264
 
265
+ transforms_list = []
266
+ applied_transforms = []
267
+
268
+ if normalize:
269
+ transforms_list.append(NormalizeIntensity())
270
+ applied_transforms.append("NormalizeIntensity")
271
+
272
+ if scale_intensity:
273
+ transforms_list.append(ScaleIntensity(minv=target_min, maxv=target_max))
274
+ applied_transforms.append(f"ScaleIntensity({target_min}, {target_max})")
275
+
276
+ if transforms_list:
277
+ transform = Compose(transforms_list)
278
+ result = transform(img_array)
279
+ else:
280
+ result = img_array
281
 
282
  return {
283
  "success": True,
284
+ "result": {
285
+ "applied_transforms": applied_transforms,
286
+ "output_shape": list(result.shape),
287
+ "output_min": float(result.min()),
288
+ "output_max": float(result.max()),
289
+ "output_mean": float(result.mean()),
290
+ "output_std": float(result.std())
291
+ },
292
+ "error": None
293
  }
294
  except Exception as e:
295
+ return {"success": False, "result": None, "error": str(e)}
296
+
297
+
298
+ @mcp.tool(name="resize_image", description="Resize a medical image using MONAI")
299
+ def resize_image(
300
+ image: List[List[List[float]]],
301
+ spatial_size: List[int],
302
+ mode: str = "trilinear"
303
+ ) -> dict:
304
+ """
305
+ Resize a medical image to target spatial size.
306
+
307
+ Parameters:
308
+ - image: Input image as nested list (2D or 3D).
309
+ - spatial_size: Target spatial size [H, W] or [D, H, W].
310
+ - mode: Interpolation mode ('nearest', 'bilinear', 'trilinear').
311
+
312
+ Returns:
313
+ - dict: Resized image info.
314
+ """
315
+ try:
316
+ img_array = np.array(image, dtype=np.float32)
317
+ original_shape = img_array.shape
318
+
319
+ # Add channel dimension if needed
320
+ if len(img_array.shape) == len(spatial_size):
321
+ img_array = img_array[np.newaxis, ...]
322
+
323
+ resize_transform = Resize(spatial_size=spatial_size, mode=mode)
324
+ resized = resize_transform(img_array)
325
+
326
+ return {
327
+ "success": True,
328
+ "result": {
329
+ "original_shape": list(original_shape),
330
+ "target_size": spatial_size,
331
+ "output_shape": list(resized.shape),
332
+ "interpolation_mode": mode
333
+ },
334
+ "error": None
335
+ }
336
+ except Exception as e:
337
+ return {"success": False, "result": None, "error": str(e)}
338
+
339
+
340
+ @mcp.tool(name="get_network_architectures", description="Get details about available network architectures")
341
+ def get_network_architectures() -> dict:
342
+ """
343
+ Get detailed information about available MONAI network architectures.
344
+
345
+ Returns:
346
+ - dict: Network architecture details.
347
+ """
348
+ try:
349
+ architectures = {
350
+ "UNet": {
351
+ "description": "U-Net architecture for semantic segmentation",
352
+ "use_case": "General medical image segmentation",
353
+ "parameters": ["spatial_dims", "in_channels", "out_channels", "channels", "strides"]
354
+ },
355
+ "VNet": {
356
+ "description": "V-Net for volumetric medical image segmentation",
357
+ "use_case": "3D medical image segmentation",
358
+ "parameters": ["spatial_dims", "in_channels", "out_channels"]
359
+ },
360
+ "AttentionUnet": {
361
+ "description": "Attention U-Net with attention gates",
362
+ "use_case": "Segmentation with attention mechanism",
363
+ "parameters": ["spatial_dims", "in_channels", "out_channels", "channels", "strides"]
364
+ },
365
+ "SegResNet": {
366
+ "description": "ResNet-based encoder-decoder for segmentation",
367
+ "use_case": "Medical image segmentation with residual connections",
368
+ "parameters": ["spatial_dims", "in_channels", "out_channels", "init_filters"]
369
+ },
370
+ "SwinUNETR": {
371
+ "description": "Swin Transformer based U-Net",
372
+ "use_case": "State-of-the-art medical image segmentation",
373
+ "parameters": ["img_size", "in_channels", "out_channels", "feature_size"]
374
+ },
375
+ "DenseNet121": {
376
+ "description": "DenseNet for classification",
377
+ "use_case": "Medical image classification",
378
+ "parameters": ["spatial_dims", "in_channels", "out_channels"]
379
+ }
380
+ }
381
+
382
+ return {"success": True, "result": architectures, "error": None}
383
+ except Exception as e:
384
+ return {"success": False, "result": None, "error": str(e)}
385
+
386
+
387
+ @mcp.tool(name="create_densenet_classifier", description="Create a DenseNet classifier model")
388
+ def create_densenet_classifier(
389
+ spatial_dims: int = 3,
390
+ in_channels: int = 1,
391
+ out_channels: int = 2
392
+ ) -> dict:
393
+ """
394
+ Create a DenseNet121 classifier for medical image classification.
395
+
396
+ Parameters:
397
+ - spatial_dims: Number of spatial dimensions (2 or 3).
398
+ - in_channels: Number of input channels.
399
+ - out_channels: Number of output classes.
400
+
401
+ Returns:
402
+ - dict: Model information.
403
+ """
404
+ try:
405
+ model = DenseNet121(
406
+ spatial_dims=spatial_dims,
407
+ in_channels=in_channels,
408
+ out_channels=out_channels
409
+ )
410
+
411
+ total_params = sum(p.numel() for p in model.parameters())
412
+
413
+ return {
414
+ "success": True,
415
+ "result": {
416
+ "model_type": "DenseNet121",
417
+ "spatial_dims": spatial_dims,
418
+ "in_channels": in_channels,
419
+ "out_channels": out_channels,
420
+ "total_parameters": total_params
421
+ },
422
+ "error": None
423
+ }
424
+ except Exception as e:
425
+ return {"success": False, "result": None, "error": str(e)}
426
+
427
 
428
  def create_app() -> FastMCP:
429
  """