Add entity lifecycle status MCP tools (apps + components) - #23
Conversation
Expose ros2_medkit_status_get/set for apps and components (start, restart, force-restart, shutdown, force-shutdown), with sovd_* back-compat aliases. Wraps the gateway 0.6.0 lifecycle API; rejects other entity types and unknown actions.
There was a problem hiding this comment.
Pull request overview
This PR adds first-class MCP tool support for the ros2_medkit gateway (v0.6.0) entity lifecycle status API, allowing clients to query readiness state and trigger lifecycle transitions for apps and components (with sovd_* aliases preserved for backward compatibility).
Changes:
- Added lifecycle argument models (
LifecycleEntityType,LifecycleAction,StatusGetArgs,StatusSetArgs) to validate entity-type and action inputs. - Added two new MCP tools (
ros2_medkit_status_get,ros2_medkit_status_set) plussovd_status_get/sovd_status_setaliases, wiring dispatch through to the client. - Implemented lifecycle API support in
SovdClient(get_status,set_status) and added corresponding unit tests and README documentation.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/ros2_medkit_mcp/client.py |
Adds lifecycle endpoint routing, validation, and get_status/set_status client methods backed by generated lifecycle API functions. |
src/ros2_medkit_mcp/models.py |
Introduces Pydantic argument models/enums for lifecycle status tools with strict validation (apps/components + allowed actions). |
src/ros2_medkit_mcp/mcp_app.py |
Registers the new MCP tools, adds aliases, and dispatches tool calls to the new client lifecycle methods. |
tests/test_new_tools.py |
Adds integration-style client tests for lifecycle GET/PUT behavior and input rejection cases. |
tests/test_mcp_app.py |
Adds unit tests for tool alias resolution and Pydantic validation of lifecycle argument models. |
README.md |
Documents the new lifecycle tools and their arguments/return behavior. |
| - `entity_id` (required, string): The entity identifier | ||
| - `action` (required, string): one of `start`, `restart`, `force-restart`, `shutdown`, `force-shutdown` | ||
|
|
||
| **Returns:** `202 Accepted` from `PUT /{entity_type}/{entity_id}/status/{action}` (no body) |
There was a problem hiding this comment.
The 202 here is the happy path only when a LifecycleProvider plugin handles the entity. I ran ghcr.io/selfpatch/ros2_medkit-jazzy:latest and every transition PUT returns 501 [not-implemented] Lifecycle control not available for this entity - the gateway's handle_transition requires a plugin provider and there is no default one, so on a stock gateway status_set always fails while status_get works. Worth one sentence here (and in the status_set tool description, so the model can relay it) - otherwise a first-time user concludes the tool is broken. Also, the MCP tool itself returns {} rendered as JSON, not a raw 202.
A transition PUT is answered by a gateway-side LifecycleProvider plugin and no provider ships with the gateway, so on a stock gateway every status_set call comes back as 501 not-implemented while status_get works. Say so in the README and in the tool description, and describe what the tool actually returns on success: an empty JSON object, not a raw 202. Cover the paths that carried the claim: the missing-provider 501 for all five actions, both entity types against every action so a wrong _ENTITY_FUNC_MAP entry shows as a request to the wrong URL, and the registered MCP handlers - tool registry, schema enums, dispatch and the sovd_* alias - which no test reached before.
models.py switched to StrEnum for FaultStatus; the lifecycle enums are declared the same way so the import stays a single name.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Suppressed comments (3)
README.md:390
- The lifecycle API is introduced specifically with the gateway 0.6.0 contract, so older gateways that do not expose
/{entity_type}/{entity_id}/statuswill return an error for reads as well. Please qualify this statement (for example, “Reading the status works on gateways that expose the 0.6.0 lifecycle API”) so users do not assume compatibility with every gateway version.
Reading the status works on any gateway. Triggering a transition requires the gateway
to have a `LifecycleProvider` plugin registered for the entity; there is no built-in
README.md:392
- The MCP tool does not return the documented
501prefix here:_call_voidstores the status inSovdClientError.status_code, but its message for this error envelope is only[not-implemented] ..., andcall_tooldoes not appendstatus_code. This also conflicts with the exact output documented below on line 411; please describe the actual tool error (or change the formatter if the HTTP status must be exposed).
`501 [not-implemented] Lifecycle control not available for this entity`.
src/ros2_medkit_mcp/mcp_app.py:2643
- This tool description also presents
status_getas working on a stock gateway without stating the required gateway version. Since this client is pinned to and the feature is defined for gateway 0.6.0, older gateways can reject the GET endpoint; qualify the claim here so the tool metadata does not overstate compatibility.
" LifecycleProvider plugin for the entity; there is no"
" built-in provider, so a stock gateway answers every"
" transition with 'not-implemented' while status_get still"
" works. On success the gateway returns a body-less 202 and"
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
README.md:392
SovdClientErrorkeeps the HTTP status instatus_code, butcall_toolformats onlystr(e), so the tool output contains[not-implemented] Lifecycle control not available for this entitywithout the501prefix. This example disagrees with the actual response (and with the wording below); please update the documented error text or include status codes in the formatter.
`501 [not-implemented] Lifecycle control not available for this entity`.
src/ros2_medkit_mcp/mcp_app.py:3265
- At this branch, an unsupported
entity_typeoractionraisesValidationError, but the broad handler below converts it intoInternal error: .... That mislabels invalid user input as a server failure and does not provide the clear rejection promised for lifecycle arguments. Handle validation errors as input errors (or validate these arguments before dispatch) so the response is not labeled internal.
status_get_args = StatusGetArgs(**arguments)
Summary
Add
ros2_medkit_status_getandros2_medkit_status_setMCP tools (withsovd_status_get/sovd_status_setback-compat aliases), wrapping the gateway 0.6.0 entity lifecycle API.status_getreads the readiness status;status_settriggers a transition (start / restart / force-restart / shutdown / force-shutdown). Restricted to apps and components; other entity types and unknown actions are rejected with a clear error.Issue
Type
Testing
In a worktree branched from
origin/main(post-0.6.0-migration):run_tests.py- 184 passed (168 baseline + 16 new lifecycle/tool/arg-model tests)ruff check src/ tests/- cleanruff format --check src/ tests/- cleanmypy src/- cleanChecklist
poetry run ruff check src/ tests/)poetry run ruff format --check src/ tests/)poetry run mypy src/)poetry run python run_tests.py)