Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ros2-medkit-mcp"
version = "0.6.0"
version = "0.7.0"
description = "MCP server adapter for ros2_medkit SOVD HTTP API"
authors = ["bburda <bartoszburda93@gmail.com>"]
readme = "README.md"
Expand All @@ -15,7 +15,7 @@ pydantic = "^2.10.0"
uvicorn = { version = "^0.34.0", extras = ["standard"] }
starlette = "^0.45.0"
# Distributed via GitHub Releases wheel (no PyPI yet). Replace with version constraint when available.
ros2-medkit-client = {url = "https://github.com/selfpatch/ros2_medkit_clients/releases/download/py-v0.6.0/ros2_medkit_client-0.6.0-py3-none-any.whl"}
ros2-medkit-client = {url = "https://github.com/selfpatch/ros2_medkit_clients/releases/download/py-v0.7.0/ros2_medkit_client-0.7.0-py3-none-any.whl"}

[tool.poetry.group.dev.dependencies]
pytest = "^8.3.0"
Expand Down
2 changes: 1 addition & 1 deletion src/ros2_medkit_mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""ros2_medkit_mcp - MCP adapter for ros2_medkit SOVD HTTP API."""

__version__ = "0.6.0"
__version__ = "0.7.0"
39 changes: 26 additions & 13 deletions src/ros2_medkit_mcp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,28 +477,28 @@ def _validate_relative_uri(uri: str) -> None:
# force-shutdown); the generated modules use underscores.
"lifecycle": {
"get": {
"components": lifecycle.get_components_status,
"apps": lifecycle.get_apps_status,
"components": lifecycle.get_component_status,
"apps": lifecycle.get_app_status,
},
"start": {
"components": lifecycle.put_components_status_start,
"apps": lifecycle.put_apps_status_start,
"components": lifecycle.put_component_status_start,
"apps": lifecycle.put_app_status_start,
},
"restart": {
"components": lifecycle.put_components_status_restart,
"apps": lifecycle.put_apps_status_restart,
"components": lifecycle.put_component_status_restart,
"apps": lifecycle.put_app_status_restart,
},
"force-restart": {
"components": lifecycle.put_components_status_force_restart,
"apps": lifecycle.put_apps_status_force_restart,
"components": lifecycle.put_component_status_force_restart,
"apps": lifecycle.put_app_status_force_restart,
},
"shutdown": {
"components": lifecycle.put_components_status_shutdown,
"apps": lifecycle.put_apps_status_shutdown,
"components": lifecycle.put_component_status_shutdown,
"apps": lifecycle.put_app_status_shutdown,
},
"force-shutdown": {
"components": lifecycle.put_components_status_force_shutdown,
"apps": lifecycle.put_apps_status_force_shutdown,
"components": lifecycle.put_component_status_force_shutdown,
"apps": lifecycle.put_app_status_force_shutdown,
},
},
}
Expand Down Expand Up @@ -1269,12 +1269,25 @@ async def execute_script(
script_id: str,
params: dict[str, Any] | None = None,
entity_type: str = "components",
execution_type: str = "now",
) -> dict[str, Any]:
# The request body is ScriptExecutionRequest: `execution_type` says when to
# run and the script's own inputs live under `parameters`. The shipped
# backend accepts only `now`, which is why it defaults, but a ScriptProvider
# plugin defines its own vocabulary and answers 400 for a value it does not
# know, so the caller can name one.
#
# `is not None` rather than a truth test: `parameters` is forwarded to the
# provider untouched, so an explicitly empty object is a different request
# from an absent one and only the caller knows which they meant.
body: dict[str, Any] = {"execution_type": execution_type}
if params is not None:
body["parameters"] = params
fn = _entity_func("scripts", "execute", entity_type)
kwargs: dict[str, Any] = {
_entity_id_kwarg(entity_type): entity_id,
"script_id": script_id,
"body": params if params else {},
"body": body,
}
return await self._call(fn, **kwargs)

Expand Down
27 changes: 23 additions & 4 deletions src/ros2_medkit_mcp/mcp_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2071,14 +2071,27 @@ async def list_tools() -> list[Tool]:
},
"params": {
"type": "object",
"description": "Optional parameters to pass to the script execution",
"description": (
"Optional parameters for the script, forwarded to the provider"
" untouched. The accepted shape is the script's own, from"
" parameters_schema on the script."
),
},
"entity_type": {
"type": "string",
"enum": ["components", "apps"],
"description": "Entity type",
"default": "components",
},
"execution_type": {
"type": "string",
"description": (
"When to run. The built-in script backend accepts only 'now'; a"
" provider plugin defines its own vocabulary and answers 400 for a"
" value it does not support."
),
"default": "now",
},
},
"required": ["entity_id", "script_id"],
},
Expand Down Expand Up @@ -2485,8 +2498,10 @@ async def list_tools() -> list[Tool]:
"update_config": {
"type": "object",
"description": (
"Update package configuration"
" (e.g., {'name': 'firmware-v2', 'version': '2.0.0',"
"Update package configuration. 'id' is required and becomes"
" the update's path segment; every other key is forwarded to"
" the update backend unchanged"
" (e.g., {'id': 'firmware-v2', 'version': '2.0.0',"
" 'uri': 'https://...'})"
),
},
Expand Down Expand Up @@ -3076,7 +3091,11 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
elif normalized_name == "ros2_medkit_execute_script":
args = ExecuteScriptArgs(**arguments)
result = await client.execute_script(
args.entity_id, args.script_id, args.params, args.entity_type
args.entity_id,
args.script_id,
args.params,
args.entity_type,
args.execution_type,
)
return format_json_response(result)

Expand Down
12 changes: 11 additions & 1 deletion src/ros2_medkit_mcp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,12 +1009,22 @@ class ExecuteScriptArgs(BaseModel):
script_id: str = Field(..., description="The script identifier")
params: dict[str, Any] | None = Field(
default=None,
description="Optional parameters to pass to the script execution",
description=(
"Optional parameters for the script, forwarded to the provider untouched. "
"The accepted shape is the script's own, from parameters_schema on the script."
),
)
entity_type: str = Field(
default="components",
description="Entity type: 'components' or 'apps'",
)
execution_type: str = Field(
default="now",
description=(
"When to run. The built-in script backend accepts only 'now'; a provider "
"plugin defines its own vocabulary and answers 400 for a value it does not support."
),
)


class GetScriptExecutionArgs(BaseModel):
Expand Down
Loading
Loading