@@ -79,7 +79,6 @@ def clear_session(self, session_id: str) -> None:
7979
8080def get_from_leader (endpoint : str ) -> dict :
8181 """Wrapper around `get_from_leader` to handle errors and callback checks."""
82- logger .debug (endpoint )
8382 try :
8483 r = _get_from_leader (endpoint )
8584 r .raise_for_status ()
@@ -102,7 +101,6 @@ def get_from_leader(endpoint: str) -> dict:
102101
103102def post_into_leader (endpoint : str , json : dict | None = None ) -> dict :
104103 """Wrapper around `post_into_leader` to handle errors."""
105- logger .debug (endpoint , json )
106104 try :
107105 r = _post_into_leader (endpoint , json = json )
108106 r .raise_for_status ()
@@ -139,8 +137,6 @@ def patch_into_leader(endpoint: str, json: dict | None = None) -> dict:
139137# ---------------------------------------------------------------------------
140138# MCP **tools** (thin wrappers around existing REST routes)
141139# ---------------------------------------------------------------------------
142-
143-
144140@mcp .tool ()
145141def list_experiments () -> dict :
146142 """List all experiments (name, creation timestamp, description, hours since creation)."""
@@ -149,7 +145,7 @@ def list_experiments() -> dict:
149145
150146@mcp .tool ()
151147def list_workers (active_only : bool ) -> dict :
152- """Return the cluster inventory. If *active_only*, filter by `is_active`."""
148+ """Return the cluster inventory (aka workers) . If *active_only*, filter by `is_active`."""
153149 workers = get_from_leader ("/api/workers" )
154150 return {"workers" : [w for w in workers if w .get ("is_active" )] if active_only else workers }
155151
@@ -163,7 +159,7 @@ def list_workers_experiment_assignments(active_only: bool) -> dict:
163159
164160@mcp .tool ()
165161def list_jobs_available (unit : str ) -> dict :
166- """Return the unit's available jobs that can be run and settings that can be viewed or changed. Can use "$broadcast" for all units."""
162+ """Return the unit/worker 's available jobs that can be run and settings that can be viewed or changed. Can use "$broadcast" for all units."""
167163 return get_from_leader (f"/api/units/{ unit } /jobs/discover" )
168164
169165
@@ -177,7 +173,7 @@ def run_job(
177173 env : Dict [str , str ] | None = None ,
178174 config_overrides : List [List [str ]] | None = None ,
179175) -> dict :
180- """Launch *job* on *unit* within *experiment* via leader REST API. Can use "$broadcast" for all units."""
176+ """Launch *job* on *unit/worker * within *experiment* via leader REST API. Can use "$broadcast" for all units."""
181177 payload = {
182178 "options" : options or {},
183179 "args" : args or [],
@@ -192,7 +188,7 @@ def run_job(
192188
193189@mcp .tool ()
194190def update_job (unit : str , job : str , experiment : str , settings : dict [str , Any ]) -> dict :
195- """Update settings for a job on a unit within an experiment. Can use "$broadcast" for all units."""
191+ """Update settings for a job on a unit/worker within an experiment. Can use "$broadcast" for all units."""
196192 return patch_into_leader (
197193 f"/api/workers/{ unit } /jobs/update/job_name/{ job } /experiments/{ experiment } " ,
198194 json = {"settings" : settings },
@@ -201,7 +197,7 @@ def update_job(unit: str, job: str, experiment: str, settings: dict[str, Any]) -
201197
202198@mcp .tool ()
203199def stop_job (unit : str , job : str , experiment : str ) -> dict :
204- """Stop *job* on *unit*; optionally scope to *experiment*. Can use "$broadcast" for all units."""
200+ """Stop *job* on *unit/worker *; optionally scope to *experiment*. Can use "$broadcast" for all units."""
205201 endpoint = f"/api/workers/{ unit } /jobs/stop/job_name/{ job } /experiments/{ experiment } "
206202 return post_into_leader (endpoint )
207203
@@ -214,13 +210,13 @@ def stop_all_jobs_in_experiment(experiment: str) -> dict:
214210
215211@mcp .tool ()
216212def stop_all_jobs_on_unit (unit : str , experiment : str ) -> dict :
217- """Stop all jobs on a specific unit for a given experiment. Can use "$broadcast" for all units."""
213+ """Stop all jobs on a specific unit/worker for a given experiment. Can use "$broadcast" for all units."""
218214 return post_into_leader (f"/api/workers/{ unit } /jobs/stop/experiments/{ experiment } " )
219215
220216
221217@mcp .tool ()
222218def running_jobs (unit : str ) -> dict :
223- """Return list of running jobs on *unit*. Can use "$broadcast" for all units."""
219+ """Return list of running jobs on *unit/worker *. Can use "$broadcast" for all units."""
224220 return get_from_leader (f"/api/workers/{ unit } /jobs/running" )
225221
226222
@@ -238,13 +234,13 @@ def blink(unit: str) -> dict:
238234
239235@mcp .tool ()
240236def reboot_unit (unit : str ) -> dict :
241- """Reboot a specific unit. Can use "$broadcast" for all units."""
237+ """Reboot a specific unit/worker . Can use "$broadcast" for all units."""
242238 return post_into_leader (f"/api/units/{ unit } /system/reboot" )
243239
244240
245241@mcp .tool ()
246242def shutdown_unit (unit : str ) -> dict :
247- """Shutdown a specific unit. Can use "$broadcast" for all units."""
243+ """Shutdown a specific unit/worker . Can use "$broadcast" for all units."""
248244 return post_into_leader (f"/api/units/{ unit } /system/shutdown" )
249245
250246
@@ -307,18 +303,39 @@ def get_time_series_column(experiment: str, data_source: str, column: str, lookb
307303
308304
309305@mcp .tool ()
306+ def list_experiment_profiles () -> dict :
307+ """List available experiment profiles (filename, fullpath, and parsed metadata)."""
308+ return get_from_leader ("/api/contrib/experiment_profiles" )
309+
310+
311+ @mcp .tool ()
312+ def run_experiment_profile (
313+ unit : str ,
314+ profile : str ,
315+ experiment : str ,
316+ dry_run : bool = False ,
317+ ) -> dict :
318+ """Execute an experiment profile on a unit/worker within an experiment. Can use "$broadcast" for all units. Optionally dry-run."""
319+ options = {"dry-run" : None } if dry_run else {}
320+ args = ["execute" , profile , experiment ]
321+ return run_job (unit , "experiment_profile" , experiment , options = options , args = args )
322+
323+
324+ # MCP **resources** for config: list of config.ini files, config contents, and unit configuration
325+ # ---------------------------------------------------------------------------
326+ @mcp .resource (path = "configs" , name = "list_config_inis" )
310327def list_config_inis () -> dict :
311328 """List available config.ini files (global and unit-specific)."""
312329 return get_from_leader ("/api/configs" )
313330
314331
315- @mcp .tool ( )
332+ @mcp .resource ( path = "configs/{filename}" , name = "get_config_ini" )
316333def get_config_ini (filename : str ) -> dict :
317334 """Retrieve the contents of a specific config.ini file."""
318335 return get_from_leader (f"/api/configs/{ filename } " )
319336
320337
321- @mcp .tool ( )
338+ @mcp .resource ( path = "units/{unit}/configuration" , name = "get_unit_configuration" )
322339def get_unit_configuration (unit : str ) -> dict :
323340 """Get merged configuration for a given unit (global and unit-specific)."""
324341 return get_from_leader (f"/api/units/{ unit } /configuration" )
0 commit comments