260 lines
6.9 KiB
Markdown
260 lines
6.9 KiB
Markdown
# MCP Bridge Plan
|
|
|
|
## Goal
|
|
|
|
Build a small TypeScript bridge that lets MCP clients call tools backed by live ComputerCraft computers.
|
|
|
|
The first MCP tool is `probe-computers`. It broadcasts a probe request to every linked computer and returns one line per response:
|
|
|
|
```text
|
|
pong from <computerId> (Label: <computerLabel>)
|
|
```
|
|
|
|
## Decisions
|
|
|
|
- Host-side implementation lives in `tools/mcp-bridge/`.
|
|
- MCP clients connect on `MCP_PORT`, default `3000`.
|
|
- ComputerCraft computers connect on `CC_LINK_PORT`, default `3001`.
|
|
- The MCP port and ComputerCraft link port are separate listeners.
|
|
- No auth is required for the first bridge implementation.
|
|
- `probe-computers` takes no arguments and probes every currently linked computer.
|
|
- The bridge owns real HTTP/MCP transport. ComputerCraft computers connect outbound over WebSocket.
|
|
|
|
## Background
|
|
|
|
CC:Tweaked computers cannot bind a real TCP/HTTP port with the standard APIs. They can make outbound HTTP/WebSocket connections. Therefore the host bridge exposes real MCP over HTTP and maintains outbound WebSocket links from computers.
|
|
|
|
The current MCP TypeScript SDK situation is split:
|
|
|
|
- `@modelcontextprotocol/sdk` v1 is stable and commonly used by existing examples.
|
|
- Newer docs describe split packages such as `@modelcontextprotocol/server` and `@modelcontextprotocol/node`, but the upstream README says that branch is v2/pre-alpha as of this research.
|
|
|
|
Prefer stable v1 for the first implementation unless opencode compatibility requires the newer packages. Re-check package docs immediately before implementation.
|
|
|
|
## Architecture
|
|
|
|
```text
|
|
[ opencode / MCP client ] --HTTP MCP--> [ tools/mcp-bridge ] --WebSocket--> [ CC computer(s) ]
|
|
:3000 :3001
|
|
```
|
|
|
|
The bridge has two independent surfaces:
|
|
|
|
- MCP listener: streamable HTTP MCP endpoint for agents.
|
|
- Link listener: WebSocket endpoint for ComputerCraft computers.
|
|
|
|
The bridge keeps an in-memory registry of connected computers:
|
|
|
|
```ts
|
|
type ComputerConnection = {
|
|
computerId: number;
|
|
label: string | null;
|
|
ws: WebSocket;
|
|
connectedAt: number;
|
|
lastSeenAt: number;
|
|
};
|
|
```
|
|
|
|
## Link Protocol
|
|
|
|
All ComputerCraft link messages are JSON text frames.
|
|
|
|
### Computer Hello
|
|
|
|
Sent by the Lua program immediately after opening the WebSocket:
|
|
|
|
```json
|
|
{
|
|
"type": "hello",
|
|
"computerId": 12,
|
|
"computerLabel": "base-turtle"
|
|
}
|
|
```
|
|
|
|
Bridge behavior:
|
|
|
|
- Validate `computerId` is a number.
|
|
- Treat missing/empty `computerLabel` as `null`.
|
|
- Register or replace the existing connection for that `computerId`.
|
|
- Reply with `hello-ok`.
|
|
|
|
### Hello OK
|
|
|
|
```json
|
|
{
|
|
"type": "hello-ok"
|
|
}
|
|
```
|
|
|
|
### Probe Request
|
|
|
|
Sent by the bridge when MCP tool `probe-computers` is called:
|
|
|
|
```json
|
|
{
|
|
"type": "request",
|
|
"id": "req-123",
|
|
"method": "ping"
|
|
}
|
|
```
|
|
|
|
### Probe Response
|
|
|
|
Sent by each Lua computer:
|
|
|
|
```json
|
|
{
|
|
"type": "response",
|
|
"id": "req-123",
|
|
"ok": true,
|
|
"result": "pong from 12 (Label: base-turtle)"
|
|
}
|
|
```
|
|
|
|
Error shape, reserved for later commands:
|
|
|
|
```json
|
|
{
|
|
"type": "response",
|
|
"id": "req-123",
|
|
"ok": false,
|
|
"error": "unknown method"
|
|
}
|
|
```
|
|
|
|
## MCP Tool Behavior
|
|
|
|
Tool name: `probe-computers`.
|
|
|
|
Input schema: no arguments.
|
|
|
|
Behavior:
|
|
|
|
1. Snapshot the current connected computer registry.
|
|
2. If none are connected, return `No computers connected.`.
|
|
3. Generate one request id per computer, or one shared batch id with per-computer tracking.
|
|
4. Send a `ping` request to every linked computer.
|
|
5. Wait for responses until either all answer or the probe timeout expires.
|
|
6. Return text with one line per successful response.
|
|
7. Include timeout lines for computers that did not answer.
|
|
|
|
Suggested timeout: `CC_PROBE_TIMEOUT_MS`, default `2000`.
|
|
|
|
Example output:
|
|
|
|
```text
|
|
pong from 12 (Label: base-turtle)
|
|
pong from 13 (Label: miner-1)
|
|
timeout from 14 (Label: farm-turtle)
|
|
```
|
|
|
|
## Project Layout
|
|
|
|
```text
|
|
tools/mcp-bridge/
|
|
package.json
|
|
package-lock.json
|
|
tsconfig.json
|
|
src/
|
|
index.ts
|
|
link-server.ts
|
|
mcp-server.ts
|
|
protocol.ts
|
|
test/
|
|
protocol.test.ts
|
|
probe-computers.test.ts
|
|
```
|
|
|
|
Keep the first implementation minimal. Collapse files if the code is clearer in fewer modules.
|
|
|
|
## Implementation Sections
|
|
|
|
### 1. Tooling Setup
|
|
|
|
- Create `tools/mcp-bridge/package.json`.
|
|
- Use TypeScript with strict checks.
|
|
- Use Node's built-in test runner if practical, or Vitest if SDK/test ergonomics require it.
|
|
- Add scripts:
|
|
- `npm run build`
|
|
- `npm test`
|
|
- `npm run dev`
|
|
- `npm start`
|
|
|
|
### 2. Link Server
|
|
|
|
- Start WebSocket server on `CC_LINK_PORT`.
|
|
- Accept JSON text frames only.
|
|
- Register computers after valid `hello`.
|
|
- Remove computers on close/error.
|
|
- Track pending request resolvers by request id.
|
|
- Ensure malformed frames do not crash the process.
|
|
|
|
### 3. MCP Server
|
|
|
|
- Start MCP HTTP transport on `MCP_PORT`.
|
|
- Register tool `probe-computers`.
|
|
- Implement tool response as MCP text content.
|
|
- Keep ordinary `/health` endpoint available on the MCP listener if the selected transport/framework allows it without fighting the SDK.
|
|
|
|
`/health` response should be normal HTTP, not MCP:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"computers": 2
|
|
}
|
|
```
|
|
|
|
### 4. Configuration
|
|
|
|
Environment variables:
|
|
|
|
- `MCP_HOST`, default `127.0.0.1`.
|
|
- `MCP_PORT`, default `3000`.
|
|
- `CC_LINK_HOST`, default `0.0.0.0`.
|
|
- `CC_LINK_PORT`, default `3001`.
|
|
- `CC_PROBE_TIMEOUT_MS`, default `2000`.
|
|
|
|
### 5. Verification
|
|
|
|
Unit tests:
|
|
|
|
- Protocol validation accepts valid `hello` and rejects invalid frames.
|
|
- `probe-computers` returns `No computers connected.` when registry is empty.
|
|
- `probe-computers` aggregates multiple successful responses.
|
|
- `probe-computers` reports timeout for a connected computer that does not answer.
|
|
|
|
Integration tests:
|
|
|
|
- Start bridge on ephemeral ports.
|
|
- Connect fake WebSocket computer clients.
|
|
- Call the probe path through the MCP handler, or through a thin internal function if the SDK transport is hard to drive in tests.
|
|
|
|
Manual/agent checks:
|
|
|
|
- `npm run build` from `tools/mcp-bridge`.
|
|
- `npm test` from `tools/mcp-bridge`.
|
|
- Use opencode headless mode only for critical MCP compatibility checks after the bridge can run locally.
|
|
|
|
## Open Questions To Re-check During Implementation
|
|
|
|
- Exact HTTP MCP transport API for the chosen stable SDK version.
|
|
- Whether opencode expects streamable HTTP only, SSE compatibility, or can use stdio. This bridge should prioritize HTTP because the requirement is an MCP port.
|
|
- Whether tool names with hyphens are accepted cleanly by the MCP clients we care about. If not, challenge `probe-computers` and use `probe_computers`.
|
|
|
|
## Out Of Scope For First Pass
|
|
|
|
- Authentication and authorization.
|
|
- Persistent computer registry.
|
|
- Commands other than `ping`/`probe-computers`.
|
|
- Multi-bridge clustering.
|
|
- TLS termination. Put the bridge behind a reverse proxy if needed later.
|
|
|
|
## Deliverables
|
|
|
|
- `tools/mcp-bridge/` TypeScript project.
|
|
- MCP HTTP listener on port `3000` by default.
|
|
- Computer WebSocket link listener on port `3001` by default.
|
|
- MCP tool `probe-computers` returning live ComputerCraft pong lines.
|
|
- Tests for protocol parsing and probe aggregation.
|