cc-libs/.plans/mcp-computer-lua-plan.md

245 lines
6.6 KiB
Markdown

# MCP Computer Lua Plan
Relates to [./mcp-bridge-plan.md](./mcp-bridge-plan.md)
## Goal
Add a TrapOS sandbox program that links a ComputerCraft computer to the host-side MCP bridge over WebSocket.
The first supported command is `ping`, used by the bridge's MCP tool `probe-computers`. The Lua program answers:
```text
pong from <computerId> (Label: <computerLabel>)
```
## Decisions
- Lua program path: `programs/mcp-computer.lua`.
- Package: `trapos-sandbox`.
- The program connects outbound to the bridge's ComputerCraft link port.
- The bridge link default is `ws://<host>:3001`.
- The host-side MCP tool is named `probe-computers` and broadcasts to every connected computer.
- No link auth for the first version.
- Keep the first Lua implementation as a program, not an autostart server, until the bridge loop is proven.
## Background
CC:Tweaked cannot listen on real HTTP/TCP ports with standard APIs. It can connect outward using `http.websocket`. This program is therefore the in-game half of the bridge:
```text
[ CC computer ] --outbound WebSocket--> [ tools/mcp-bridge link port :3001 ]
```
The Lua program is effectively an agent. It identifies itself to the bridge, waits for JSON request frames, executes supported methods, and sends JSON response frames.
## User Interface
Program usage:
```text
mcp-computer <ws-url>
mcp-computer -url <ws-url>
mcp-computer --version
mcp-computer --help
```
Examples:
```text
mcp-computer ws://192.168.1.20:3001
mcp-computer -url ws://mcp-bridge.local:3001
```
The program should print clear status lines:
```text
mcp-computer v0.1.1 connecting to ws://192.168.1.20:3001
linked as 12 (Label: base-turtle)
waiting for requests... Press Ctrl+T to stop.
```
If `http` or `http.websocket` is unavailable, print a clear error explaining that CC:Tweaked HTTP/WebSocket must be enabled.
## Link Protocol
All frames are JSON strings compatible with `textutils.serializeJSON` and `textutils.unserializeJSON`.
### Computer Hello
Sent immediately after opening the websocket:
```json
{
"type": "hello",
"computerId": 12,
"computerLabel": "base-turtle"
}
```
Lua fields:
- `computerId`: `os.getComputerID()`.
- `computerLabel`: `os.getComputerLabel()`, or `nil` encoded as JSON null/omitted depending on `textutils.serializeJSON` behavior.
### Hello OK
Expected from bridge:
```json
{
"type": "hello-ok"
}
```
The first version may continue even if `hello-ok` is not received immediately, but implementation should prefer waiting briefly so connection problems are obvious.
### Probe Request
Received from bridge:
```json
{
"type": "request",
"id": "req-123",
"method": "ping"
}
```
### Probe Response
Sent back by Lua:
```json
{
"type": "response",
"id": "req-123",
"ok": true,
"result": "pong from 12 (Label: base-turtle)"
}
```
Unknown method response:
```json
{
"type": "response",
"id": "req-123",
"ok": false,
"error": "unknown method"
}
```
## Implementation Sections
### 1. Program Skeleton
- Add `programs/mcp-computer.lua`.
- Support `--help`, `-help`, `help`, `--version`, `-version`, and `version`.
- Version output uses `require('/apis/libversion')().forSelf()`.
- Parse URL from first positional argument or `-url <ws-url>`.
- Reject missing URL with usage.
### 2. Connection Handling
- Check `http` exists.
- Check `http.websocket` exists.
- Call `http.websocket(url)`.
- On failure, print the returned error and exit non-zero if possible.
- Send `hello` frame.
- Optionally wait up to a short timeout for `hello-ok`.
### 3. Request Loop
- Receive websocket messages in a loop.
- Decode JSON with `textutils.unserializeJSON` inside `pcall`.
- Ignore or respond with error for malformed messages.
- Handle `terminate` cleanly if using `parallel.waitForAny` around websocket receive and terminate watcher.
- For `request` with `method = 'ping'`, send success response.
- For unknown request methods, send error response.
- Close websocket on exit.
### 4. Pong Formatting
Implement a local function:
```lua
local function formatPong()
local label = os.getComputerLabel();
if not label or label == '' then
label = 'nil';
end
return 'pong from ' .. tostring(os.getComputerID()) .. ' (Label: ' .. tostring(label) .. ')';
end
```
Keep the output exactly aligned with the bridge plan.
### 5. Packaging
- Add `programs/mcp-computer.lua` to `packages/trapos-sandbox/ccpm.json`.
- Bump `trapos-sandbox` version.
- Mirror the version bump in `packages/index.json`.
- Do not add it to `autostart` in the first pass.
- Do not add `trapos-sandbox` as a `trapos` dependency unless explicitly desired later.
## Testing Strategy
### Unit-Style Lua Tests
Prefer moving deterministic protocol helpers into `apis/libmcpcomputer.lua` only if testing the program directly becomes awkward. The minimal testable API could expose:
- `formatPong(osLike)`.
- `handleRequest(request, osLike)`.
- `encode/decode` helpers if needed.
If a helper API is added, include it in `trapos-sandbox` and add tests under `tests/` using `/apis/libtest.lua`.
If keeping everything in one program, at least verify syntax and help/version behavior with CraftOS-PC probes.
### CraftOS-PC Probes
Use automated probes only; do not run `just repl`.
Examples after implementation:
```bash
just trapos-exec 'shell.run("/programs/mcp-computer.lua", "--help")'
just trapos-exec 'shell.run("/programs/mcp-computer.lua", "--version")'
```
For an end-to-end probe, run the TS bridge on localhost and use CraftOS-PC with HTTP local access enabled if the harness permits it. If localhost access is blocked by CC:Tweaked config, document the limitation and rely on an in-game/manual validation for the WebSocket connection.
### Required Repo Checks
After editing Lua or package descriptors:
- `just check`
- `just test` if tests are added or changed.
## Manual Validation
1. Start the TypeScript bridge.
2. On one ComputerCraft computer, run `mcp-computer ws://<bridge-host>:3001`.
3. Confirm the bridge logs the computer registration.
4. Connect an MCP client to the bridge MCP port.
5. Call `probe-computers`.
6. Confirm response includes the computer id and label.
7. Connect a second computer and confirm the tool returns two lines.
## Out Of Scope For First Pass
- Autostart server behavior.
- Reconnect/backoff loop.
- Authentication.
- Commands that mutate the world.
- Turtle/peripheral control.
- MCP protocol implementation inside Lua. Lua only speaks the simple bridge link protocol.
## Deliverables
- `programs/mcp-computer.lua` in `trapos-sandbox`.
- Package version bump for `trapos-sandbox`.
- Optional helper API and tests if implementation benefits from unit coverage.
- Working WebSocket link to `tools/mcp-bridge`.
- Correct response to bridge `ping` requests.