guohanghui commited on
Commit
cad990b
·
verified ·
1 Parent(s): b8e6b34

Update yt/mcp_output/mcp_plugin/mcp_service.py

Browse files
yt/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,72 +1,404 @@
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
- from yt.loaders import load
11
- from yt.data_objects.static_output import Dataset
12
- from yt.visualization.plot_window import PlotWindow
13
- from yt.fields.field_info_container import FieldInfoContainer
 
14
 
15
  # Create the FastMCP service application
16
  mcp = FastMCP("yt_service")
17
 
18
- @mcp.tool(name="load_dataset", description="Load a dataset from a file.")
19
- def load_dataset(file_path: str) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  """
21
- Load a dataset from a file.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- :param file_path: Path to the dataset file.
24
- :return: Dictionary with success status and dataset object or error message.
 
 
 
 
 
25
  """
26
  try:
27
- ds = load(file_path)
28
- return {"success": True, "result": ds}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  except Exception as e:
30
- return {"success": False, "error": str(e)}
 
31
 
32
- @mcp.tool(name="create_plot_window", description="Create a plot window for a dataset.")
33
- def create_plot_window(ds: Dataset, field: str, axis: int) -> dict:
34
  """
35
- Create a plot window for a dataset.
36
 
37
- :param ds: The dataset object.
38
- :param field: The field to plot.
39
- :param axis: The axis to plot along.
40
- :return: Dictionary with success status and plot window object or error message.
41
  """
42
  try:
43
- pw = PlotWindow(ds, axis, fields=[field])
44
- return {"success": True, "result": pw}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  except Exception as e:
46
- return {"success": False, "error": str(e)}
47
 
48
- @mcp.tool(name="get_field_info", description="Get field information from a dataset.")
49
- def get_field_info(ds: Dataset, field: str) -> dict:
 
50
  """
51
- Get field information from a dataset.
52
 
53
- :param ds: The dataset object.
54
- :param field: The field to get information about.
55
- :return: Dictionary with success status and field info or error message.
56
  """
57
  try:
58
- field_info = ds.field_info[field]
59
- return {"success": True, "result": field_info}
 
 
 
 
 
 
 
 
 
 
 
 
60
  except Exception as e:
61
- return {"success": False, "error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  def create_app() -> FastMCP:
64
  """
65
  Create and return the FastMCP application instance.
66
 
67
- :return: FastMCP instance.
 
68
  """
69
  return mcp
70
-
71
- # Ensure the application is created when the module is imported
72
- app = create_app()
 
1
  import os
2
  import sys
3
+ from typing import List, Optional, Dict, Any
 
 
 
 
4
 
5
  from fastmcp import FastMCP
6
+
7
+ # Import yt from PyPI (will be installed via requirements.txt)
8
+ import yt
9
+ from yt import load
10
+ from yt.units import dimensions
11
 
12
  # Create the FastMCP service application
13
  mcp = FastMCP("yt_service")
14
 
15
+
16
+ @mcp.tool(name="get_yt_version", description="Get yt library version and configuration")
17
+ def get_yt_version() -> dict:
18
+ """
19
+ Get the yt library version and configuration.
20
+
21
+ Returns:
22
+ - dict: Version and configuration information.
23
+ """
24
+ try:
25
+ return {
26
+ "success": True,
27
+ "result": {
28
+ "version": yt.__version__,
29
+ "description": "yt is a toolkit for analyzing and visualizing volumetric data"
30
+ },
31
+ "error": None
32
+ }
33
+ except Exception as e:
34
+ return {"success": False, "result": None, "error": str(e)}
35
+
36
+
37
+ @mcp.tool(name="list_sample_datasets", description="List available sample datasets in yt")
38
+ def list_sample_datasets() -> dict:
39
+ """
40
+ List available sample datasets that can be loaded with yt.load_sample().
41
+
42
+ Returns:
43
+ - dict: List of sample datasets.
44
+ """
45
+ try:
46
+ # Common sample datasets in yt
47
+ datasets = {
48
+ "IsolatedGalaxy": "Isolated galaxy simulation (ENZO)",
49
+ "enzo_tiny_cosmology": "Tiny cosmology dataset (ENZO)",
50
+ "output_00080": "RAMSES output",
51
+ "GasSloshingLowRes": "Gas sloshing simulation",
52
+ "WindTunnel": "Wind tunnel test problem",
53
+ "DD0010": "ENZO data dump",
54
+ "cluster_merger_rockstar_halos": "Cluster merger with Rockstar halos",
55
+ "KelvinHelmholtz_hdf5_chk_0100": "Kelvin-Helmholtz instability (FLASH)",
56
+ }
57
+ return {"success": True, "result": datasets, "error": None}
58
+ except Exception as e:
59
+ return {"success": False, "result": None, "error": str(e)}
60
+
61
+
62
+ @mcp.tool(name="list_frontends", description="List available data frontends in yt")
63
+ def list_frontends() -> dict:
64
+ """
65
+ List available data frontends (simulation code formats) supported by yt.
66
+
67
+ Returns:
68
+ - dict: Dictionary of frontends and descriptions.
69
+ """
70
+ try:
71
+ frontends = {
72
+ "enzo": "Enzo AMR cosmology code",
73
+ "flash": "FLASH multiphysics code",
74
+ "ramses": "RAMSES AMR code",
75
+ "athena": "Athena MHD code",
76
+ "athena_pp": "Athena++ MHD code",
77
+ "gadget": "Gadget N-body/SPH code",
78
+ "tipsy": "Tipsy/ChaNGa SPH code",
79
+ "arepo": "Arepo moving-mesh code",
80
+ "gizmo": "GIZMO meshless code",
81
+ "swift": "SWIFT SPH code",
82
+ "art": "ART cosmology code",
83
+ "chombo": "Chombo AMR library",
84
+ "boxlib": "BoxLib/AMReX framework",
85
+ "fits": "FITS astronomical format",
86
+ "hdf5": "Generic HDF5",
87
+ "stream": "In-memory particle/grid data"
88
+ }
89
+ return {"success": True, "result": frontends, "error": None}
90
+ except Exception as e:
91
+ return {"success": False, "result": None, "error": str(e)}
92
+
93
+
94
+ @mcp.tool(name="list_field_types", description="List common field types in yt")
95
+ def list_field_types() -> dict:
96
+ """
97
+ List common field types available in yt datasets.
98
+
99
+ Returns:
100
+ - dict: Field categories and examples.
101
  """
102
+ try:
103
+ field_types = {
104
+ "gas": {
105
+ "description": "Gas/fluid fields",
106
+ "examples": ["density", "temperature", "pressure", "velocity_x", "velocity_y", "velocity_z", "entropy", "specific_thermal_energy"]
107
+ },
108
+ "particle": {
109
+ "description": "Particle fields (dark matter, stars)",
110
+ "examples": ["particle_mass", "particle_position", "particle_velocity"]
111
+ },
112
+ "derived": {
113
+ "description": "Derived/computed fields",
114
+ "examples": ["cell_volume", "cell_mass", "sound_speed", "mach_number", "magnetic_field_strength"]
115
+ },
116
+ "index": {
117
+ "description": "Grid index fields",
118
+ "examples": ["x", "y", "z", "dx", "dy", "dz", "radius"]
119
+ }
120
+ }
121
+ return {"success": True, "result": field_types, "error": None}
122
+ except Exception as e:
123
+ return {"success": False, "result": None, "error": str(e)}
124
+
125
 
126
+ @mcp.tool(name="list_plot_types", description="List available plot types in yt")
127
+ def list_plot_types() -> dict:
128
+ """
129
+ List available visualization/plot types in yt.
130
+
131
+ Returns:
132
+ - dict: Plot types and descriptions.
133
  """
134
  try:
135
+ plot_types = {
136
+ "SlicePlot": {
137
+ "description": "2D slice through data at a specific location",
138
+ "usage": "yt.SlicePlot(ds, 'z', 'density')"
139
+ },
140
+ "ProjectionPlot": {
141
+ "description": "2D projection (integral) along an axis",
142
+ "usage": "yt.ProjectionPlot(ds, 'z', 'density')"
143
+ },
144
+ "PhasePlot": {
145
+ "description": "2D histogram of two fields colored by third",
146
+ "usage": "yt.PhasePlot(ad, 'density', 'temperature', 'cell_mass')"
147
+ },
148
+ "ProfilePlot": {
149
+ "description": "1D binned profile of fields",
150
+ "usage": "yt.ProfilePlot(ad, 'radius', 'density')"
151
+ },
152
+ "ParticlePlot": {
153
+ "description": "Scatter plot of particle positions",
154
+ "usage": "yt.ParticlePlot(ds, 'particle_position_x', 'particle_position_y')"
155
+ },
156
+ "OffAxisSlicePlot": {
157
+ "description": "Slice along arbitrary vector",
158
+ "usage": "yt.OffAxisSlicePlot(ds, normal, 'density')"
159
+ },
160
+ "OffAxisProjectionPlot": {
161
+ "description": "Projection along arbitrary vector",
162
+ "usage": "yt.OffAxisProjectionPlot(ds, normal, 'density')"
163
+ }
164
+ }
165
+ return {"success": True, "result": plot_types, "error": None}
166
  except Exception as e:
167
+ return {"success": False, "result": None, "error": str(e)}
168
+
169
 
170
+ @mcp.tool(name="list_data_objects", description="List available data selection objects in yt")
171
+ def list_data_objects() -> dict:
172
  """
173
+ List available data selection objects for extracting regions of data.
174
 
175
+ Returns:
176
+ - dict: Data object types and descriptions.
 
 
177
  """
178
  try:
179
+ data_objects = {
180
+ "all_data": {
181
+ "description": "Select entire domain",
182
+ "usage": "ds.all_data()"
183
+ },
184
+ "sphere": {
185
+ "description": "Spherical region",
186
+ "usage": "ds.sphere(center, radius)"
187
+ },
188
+ "region": {
189
+ "description": "Rectangular box region",
190
+ "usage": "ds.region(center, left_edge, right_edge)"
191
+ },
192
+ "disk": {
193
+ "description": "Cylindrical disk region",
194
+ "usage": "ds.disk(center, normal, radius, height)"
195
+ },
196
+ "ray": {
197
+ "description": "1D ray through domain",
198
+ "usage": "ds.ray(start_point, end_point)"
199
+ },
200
+ "slice": {
201
+ "description": "2D slice at fixed coordinate",
202
+ "usage": "ds.slice(axis, coordinate)"
203
+ },
204
+ "covering_grid": {
205
+ "description": "Uniformly spaced grid covering region",
206
+ "usage": "ds.covering_grid(level, left_edge, dims)"
207
+ },
208
+ "arbitrary_grid": {
209
+ "description": "Grid with arbitrary spacing",
210
+ "usage": "ds.arbitrary_grid(left_edge, right_edge, dims)"
211
+ },
212
+ "cut_region": {
213
+ "description": "Region defined by conditional expression",
214
+ "usage": "ad.cut_region(['obj[\"temperature\"] > 1e6'])"
215
+ }
216
+ }
217
+ return {"success": True, "result": data_objects, "error": None}
218
  except Exception as e:
219
+ return {"success": False, "result": None, "error": str(e)}
220
 
221
+
222
+ @mcp.tool(name="list_units", description="List common units and unit systems in yt")
223
+ def list_units() -> dict:
224
  """
225
+ List common units and unit systems available in yt.
226
 
227
+ Returns:
228
+ - dict: Unit categories and examples.
 
229
  """
230
  try:
231
+ units = {
232
+ "length": ["cm", "m", "km", "pc", "kpc", "Mpc", "AU", "ly", "Rsun"],
233
+ "mass": ["g", "kg", "Msun", "Mjup", "Mearth"],
234
+ "time": ["s", "yr", "Myr", "Gyr"],
235
+ "velocity": ["cm/s", "m/s", "km/s", "km/h"],
236
+ "density": ["g/cm**3", "kg/m**3", "Msun/kpc**3"],
237
+ "temperature": ["K", "keV"],
238
+ "energy": ["erg", "J", "eV", "keV"],
239
+ "magnetic": ["gauss", "T"],
240
+ "cgs_units": "Base CGS system (cm, g, s)",
241
+ "mks_units": "Base MKS/SI system (m, kg, s)",
242
+ "galactic_units": "Galactic units (kpc, Msun, Myr)"
243
+ }
244
+ return {"success": True, "result": units, "error": None}
245
  except Exception as e:
246
+ return {"success": False, "result": None, "error": str(e)}
247
+
248
+
249
+ @mcp.tool(name="list_colormaps", description="List recommended colormaps for yt")
250
+ def list_colormaps() -> dict:
251
+ """
252
+ List recommended colormaps for scientific visualization in yt.
253
+
254
+ Returns:
255
+ - dict: Colormap categories and names.
256
+ """
257
+ try:
258
+ colormaps = {
259
+ "yt_native": ["algae", "kamae", "arbre", "octarine", "kelp", "dusk", "B-W LINEAR"],
260
+ "sequential": ["viridis", "plasma", "inferno", "magma", "cividis"],
261
+ "diverging": ["RdBu", "RdYlBu", "coolwarm", "seismic", "bwr"],
262
+ "perceptually_uniform": ["viridis", "plasma", "inferno", "magma", "cividis"],
263
+ "notes": {
264
+ "default": "arbre is the default yt colormap",
265
+ "recommendation": "Use perceptually uniform colormaps for quantitative data",
266
+ "diverging_use": "Diverging colormaps are good for data with a meaningful midpoint"
267
+ }
268
+ }
269
+ return {"success": True, "result": colormaps, "error": None}
270
+ except Exception as e:
271
+ return {"success": False, "result": None, "error": str(e)}
272
+
273
+
274
+ @mcp.tool(name="get_yt_code_example", description="Get example code for common yt operations")
275
+ def get_yt_code_example(operation: str) -> dict:
276
+ """
277
+ Get example code for common yt operations.
278
+
279
+ Parameters:
280
+ - operation: The operation to get example code for.
281
+ Options: "load", "slice", "projection", "profile", "phase", "sphere", "volume_render"
282
+
283
+ Returns:
284
+ - dict: Example code and explanation.
285
+ """
286
+ try:
287
+ examples = {
288
+ "load": {
289
+ "code": """
290
+ import yt
291
+ # Load a dataset
292
+ ds = yt.load("path/to/dataset")
293
+ # Print basic info
294
+ print(ds)
295
+ print(ds.field_list)
296
+ print(ds.derived_field_list)
297
+ """,
298
+ "description": "Load a dataset and inspect its properties"
299
+ },
300
+ "slice": {
301
+ "code": """
302
+ import yt
303
+ ds = yt.load("path/to/dataset")
304
+ # Create a slice plot along z-axis
305
+ slc = yt.SlicePlot(ds, 'z', 'density')
306
+ slc.set_cmap('density', 'viridis')
307
+ slc.annotate_grids() # Show AMR grid structure
308
+ slc.save('slice.png')
309
+ """,
310
+ "description": "Create a 2D slice plot of density"
311
+ },
312
+ "projection": {
313
+ "code": """
314
+ import yt
315
+ ds = yt.load("path/to/dataset")
316
+ # Create a projection (column density)
317
+ prj = yt.ProjectionPlot(ds, 'z', 'density', weight_field='density')
318
+ prj.set_unit('density', 'g/cm**2')
319
+ prj.save('projection.png')
320
+ """,
321
+ "description": "Create a 2D projection plot"
322
+ },
323
+ "profile": {
324
+ "code": """
325
+ import yt
326
+ ds = yt.load("path/to/dataset")
327
+ ad = ds.all_data()
328
+ # Create 1D radial profile
329
+ profile = yt.create_profile(ad, 'radius', 'density', weight_field='cell_mass')
330
+ # Plot it
331
+ plot = yt.ProfilePlot.from_profiles(profile)
332
+ plot.save('profile.png')
333
+ """,
334
+ "description": "Create a 1D radial profile"
335
+ },
336
+ "phase": {
337
+ "code": """
338
+ import yt
339
+ ds = yt.load("path/to/dataset")
340
+ ad = ds.all_data()
341
+ # Create 2D phase plot
342
+ phase = yt.PhasePlot(ad, 'density', 'temperature', 'cell_mass', weight_field=None)
343
+ phase.set_unit('density', 'g/cm**3')
344
+ phase.set_unit('temperature', 'K')
345
+ phase.save('phase.png')
346
+ """,
347
+ "description": "Create a 2D phase diagram"
348
+ },
349
+ "sphere": {
350
+ "code": """
351
+ import yt
352
+ ds = yt.load("path/to/dataset")
353
+ # Create spherical region around center
354
+ center = ds.domain_center
355
+ sp = ds.sphere(center, (100, 'kpc'))
356
+ # Get data from sphere
357
+ density = sp['gas', 'density']
358
+ temperature = sp['gas', 'temperature']
359
+ # Compute quantities
360
+ total_mass = sp.quantities.total_mass()
361
+ """,
362
+ "description": "Extract data from a spherical region"
363
+ },
364
+ "volume_render": {
365
+ "code": """
366
+ import yt
367
+ ds = yt.load("path/to/dataset")
368
+ # Create volume rendering
369
+ sc = yt.create_scene(ds, field='density')
370
+ # Customize transfer function
371
+ source = sc[0]
372
+ source.tfh.set_bounds((1e-30, 1e-23))
373
+ source.tfh.set_log(True)
374
+ # Render and save
375
+ sc.save('volume_render.png', sigma_clip=4)
376
+ """,
377
+ "description": "Create a 3D volume rendering"
378
+ }
379
+ }
380
+
381
+ if operation.lower() in examples:
382
+ return {"success": True, "result": examples[operation.lower()], "error": None}
383
+ else:
384
+ return {
385
+ "success": True,
386
+ "result": {
387
+ "available_operations": list(examples.keys()),
388
+ "message": f"Operation '{operation}' not found. Available: {list(examples.keys())}"
389
+ },
390
+ "error": None
391
+ }
392
+ except Exception as e:
393
+ return {"success": False, "result": None, "error": str(e)}
394
+
395
 
396
  def create_app() -> FastMCP:
397
  """
398
  Create and return the FastMCP application instance.
399
 
400
+ Returns:
401
+ - FastMCP: The FastMCP application instance.
402
  """
403
  return mcp
404
+