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

7.7 KiB

MCP Computer Lua Plan

Depends on the implemented host bridge in tools/mcp-bridge/.

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:

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 implemented local/dev bridge link default is ws://<host>:3001 (CC_LINK_PORT, default 3001). Production/public bridge deployments use ws://<host>:4243 with CC_LINK_PORT=4243; see ../docs/public-ports.md.
  • 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.
  • The implemented bridge accepts WebSocket connections on any path, so a bare local/dev URL such as ws://<host>:3001 is sufficient.

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:

[ CC computer ] --outbound WebSocket--> [ tools/mcp-bridge link listener :3001 local/dev, :4243 production ]

Production deployments use the public ComputerCraft bridge WebSocket port 4243 instead of the local/dev default 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. The host bridge is already implemented and exposes MCP over plain HTTP JSON-RPC on MCP_PORT while this Lua program only speaks the ComputerCraft WebSocket link protocol.

User Interface

Program usage:

mcp-computer <ws-url>
mcp-computer -url <ws-url>
mcp-computer --version
mcp-computer --help

Examples:

mcp-computer ws://192.168.1.20:3001
mcp-computer -url ws://mcp-bridge.local:3001
mcp-computer ws://public.example.com:4243

The program should print clear status lines:

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.

All frames are JSON strings compatible with textutils.serializeJSON and textutils.unserializeJSON.

Computer Hello

Sent immediately after opening the websocket:

{
  "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. The bridge treats missing, empty, or non-string labels as null.

Hello OK

Expected from bridge:

{
  "type": "hello-ok"
}

The bridge sends hello-ok immediately after a valid hello. The Lua program should wait briefly for this frame so connection or protocol problems are obvious, then proceed only after receiving it.

Probe Request

Received from bridge:

{
  "type": "request",
  "id": "req-123",
  "method": "ping"
}

Probe Response

Sent back by Lua:

{
  "type": "response",
  "id": "req-123",
  "ok": true,
  "result": "pong from 12 (Label: base-turtle)"
}

Unknown method response:

{
  "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:

local function formatPong()
  local label = os.getComputerLabel();
  if not label or label == '' then
    label = 'null';
  end
  return 'pong from ' .. tostring(os.getComputerID()) .. ' (Label: ' .. tostring(label) .. ')';
end

Keep the output aligned with the implemented bridge's formatComputer fallback (null for missing labels) and the MCP tool's expected successful pong lines.

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:

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. The bridge already includes integration-test Lua clients under tools/mcp-bridge/test-integration/lua/ that can be used as protocol references. 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 for local/dev, or mcp-computer ws://<public-host>:4243 for public production.
  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.