From de256b625e756a233c14a75738a940f059061644 Mon Sep 17 00:00:00 2001 From: Stephen von Takach Date: Tue, 20 Oct 2026 16:32:07 +1100 Subject: [PATCH] feat(mcp): module state and logs as MCP resources Uses the resource templates, subscriptions and long running routes from action-controller 9.3 to bring the module debugging features of the control websocket to MCP clients. - resource templates: `placeos://modules/{id}/state`, `placeos://modules/{id}/state/{key}`, `placeos://systems/{sys_id}/{module_slug}/{key}` and `placeos://modules/{id}/logs{?seconds,level}` - the two state value templates can be subscribed to. Watchers use the websocket's redis subscriptions, so any instance can serve a client, and clients re-read the resource when notified - GET /modules/:id/state/:key/value returns the stored JSON as is, for MCP reads. `state_lookup` is unchanged - GET /modules/:id/logs captures a module's debug output for `seconds` (30s default, 36h max) at or above `level`, streamed over HTTP and spooled to disk for MCP clients, capped at 3 MiB with a truncation marker - module logs need support or admin users, or support subsystem Operate and Read (or Manage) on the module's zones. The websocket `debug` command now requires the same - requires action-controller >= 9.3.0, which no longer issues Mcp-Session-Id Co-Authored-By: Claude Opus 5.5 --- OPENAPI_DOC.yml | 230 +++++++++++++++++- shard.lock | 2 +- shard.yml | 2 +- spec/controllers/modules_spec.cr | 89 +++++++ spec/mcp_resources_spec.cr | 203 ++++++++++++++++ spec/module_logs_spec.cr | 72 ++++++ spec/websocket/session_spec.cr | 21 ++ src/placeos-rest-api/controllers/modules.cr | 78 +++++- src/placeos-rest-api/controllers/systems.cr | 4 +- src/placeos-rest-api/mcp.cr | 25 ++ .../utilities/group_permissions.cr | 20 ++ src/placeos-rest-api/utilities/module_logs.cr | 142 +++++++++++ src/placeos-rest-api/websocket/session.cr | 55 ++--- 13 files changed, 902 insertions(+), 41 deletions(-) create mode 100644 spec/mcp_resources_spec.cr create mode 100644 spec/module_logs_spec.cr create mode 100644 src/placeos-rest-api/utilities/module_logs.cr diff --git a/OPENAPI_DOC.yml b/OPENAPI_DOC.yml index c9138e9f..dc62340d 100644 --- a/OPENAPI_DOC.yml +++ b/OPENAPI_DOC.yml @@ -12823,6 +12823,113 @@ paths: application/json: schema: $ref: '#/components/schemas/PlaceOS.Api.Application.ContentError' + /api/engine/v2/modules/{id}/state/{key}/value: + summary: Modules, running instances of drivers that control individual devices + and services + get: + summary: Returns the value of the requested status variable as JSON, `null` + when it isn't set. + description: 'Returns the value of the requested status variable as JSON, `null` + when it isn''t set. + + Subscribe to the resource to be notified when the value changes' + tags: + - Modules + operationId: PlaceOS::Api::Modules_state_value + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: key + in: path + description: the name of the status variable + example: power + required: true + schema: + type: string + responses: + "200": + description: OK + "409": + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.CommonError' + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.CommonError' + "503": + description: Service Unavailable + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ImageGenError' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ImageGenError' + "404": + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.CommonError' + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ImageGenError' + "422": + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ParameterError' + "502": + description: Bad Gateway + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.CommonError' + "504": + description: Gateway Timeout + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.CommonError' + "406": + description: Not Acceptable + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ContentError' + "408": + description: Request Timeout + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.CommonError' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ParameterError' + "415": + description: Unsupported Media Type + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS.Api.Application.ContentError' /api/engine/v2/modules/{id}/state/{key}: summary: Modules, running instances of drivers that control individual devices and services @@ -12928,6 +13035,124 @@ paths: application/json: schema: $ref: '#/components/schemas/PlaceOS.Api.Application.ContentError' + /api/engine/v2/modules/{id}/logs: + summary: Modules, running instances of drivers that control individual devices + and services + get: + summary: Captures the module's debug output for a period of time, as lines of + description: 'Captures the module''s debug output for a period of time, as lines + of + + `