# dreambot-mcp

MCP server that lets an AI agent **play Old School RuneScape** by driving a local **DreamBot** client.
It exposes 100+ tools (`dreambot_*` / `osrs_*`) and talks to the DreamBot **bridge** over HTTP
(`127.0.0.1:8765`). Pure Node, **no runtime dependencies**.

```
Agent ⟷ dreambot-mcp (this) ⟷ DreamBot bridge (HTTP 8765) ⟷ DreamBot client ⟷ OSRS
```

> The Java **bridge** (the DreamBot script) lives in a separate project
> (`…/dreambot-mcp` bridge repo). This project is **only the MCP server**. The bridge must be running
> (DreamBot → script "DreamBot MCP Bridge" → Running) for any tool to work.

## Transports

| Transport | When | Resilience |
|---|---|---|
| **stdio** (default) | agent spawns it as a child process | a per-call timeout drops the whole pipe → all tools gone until session restart |
| **HTTP** (`--http`) | long-lived server, shared by agents, service/Docker | a timed-out call no longer kills everything; the client reconnects |

### Run — stdio (default)
```
node src/dreambot-mcp.js
```

### Run — HTTP (recommended for long autonomous sessions)
```
MCP_TRANSPORT=http MCP_HTTP_PORT=8787 node src/dreambot-mcp.js
# or:  node src/dreambot-mcp.js --http
```
Serves MCP Streamable-HTTP at `http://127.0.0.1:8787/mcp`.

## Configure your agent

**Claude Code (HTTP):**
```
claude mcp add --transport http dreambot http://127.0.0.1:8787/mcp
```
**Claude Code (stdio):** point `.mcp.json` at `node …/src/dreambot-mcp.js` (keep `"timeout": 900000`).

**Codex (`~/.codex/config.toml`):**
```toml
[mcp_servers.dreambot]
command = "node"
args = ['D:\dreambot-osrs\mcp-server\src\dreambot-mcp.js']   # stdio
env = { DREAMBOT_BRIDGE_URL = "http://127.0.0.1:8765" }
```
(For HTTP with Codex, use its remote-server config if supported by your version.)

## Environment

| Var | Default | Purpose |
|---|---|---|
| `DREAMBOT_BRIDGE_URL` | `http://127.0.0.1:8765` | DreamBot bridge HTTP base URL |
| `MCP_TRANSPORT` | `stdio` | `http` to run the HTTP transport |
| `MCP_HTTP_PORT` | `8787` | HTTP transport port |
| `MCP_HTTP_HOST` | `127.0.0.1` | HTTP transport bind host |
| `DREAMBOT_DATA_DIR` | `<project>/data` | persistent data root (saved routines under `data/routines`) |

## Persistent data
Saved routines live in `data/routines/*.json`. Point `DREAMBOT_DATA_DIR` at a host path / Docker volume
to persist them across restarts and image rebuilds.

## Long Calls
Avoid holding a single MCP request open past the client timeout. Use non-blocking modes:

- `dreambot_run_routine { background:true }` -> poll `dreambot_get_routine_status`.
- `dreambot_run_task_plan { background:true }` -> poll `dreambot_get_background_job_status`.
- `osrs_wintertodt_round { background:true }` -> poll `dreambot_get_background_job_status`.
- `dreambot_take_break` returns immediately by default; poll `dreambot_humanizer_status` until `onBreak:false`.
  Pass `wait:true` only for legacy blocking behavior.

## Precision / Diagnostics
For pathing-sensitive mechanics, inspect before clicking:

- `dreambot_inspect_tile` returns reachability, collision flag, screen/minimap visibility, bounds, and objects on a tile.
- `dreambot_rotate_to_tile` / `dreambot_get_camera` make screen-tile clicks easier to frame.
- `dreambot_click_tile_precise` forces optional walk/run state and verifies progress/arrival.
- `dreambot_mouse_status` / `dreambot_click_screen` are expert fallbacks for interfaces that widgets cannot expose.
- `dreambot_entity_diagnostics` explains target reachability before repeated/ambiguous entity interactions.
- `dreambot_entity_target_token` -> `dreambot_interact_target` is the preferred exact-target flow for NPCs, objects,
  and ground items.
- `dreambot_shop_diagnostics` -> `dreambot_shop_buy` is the preferred shop flow.
- `dreambot_ge_buy_safe` is the preferred GE buy flow. It hard-guards price, coins, F2P/members access, open slots,
  and before/after GE snapshots. Keep raw `dreambot_ge_open` + `dreambot_ge_buy` for dev/debug only.

## Validate (no live bridge needed)
```
npm run smoke     # spawn server, initialize + tools/list -> TOOLS_COUNT
npm run verify    # module wiring (catches silent missing-dep ReferenceError)
node src/_test-http.js   # exercise the HTTP transport end-to-end (initialize/tools.list/tools.call)
```
A live call against a running bridge:
```
node src/_test-call.js dreambot_health '{}'
```

## Docker (optional)
```
docker build -t dreambot-mcp .
docker run --rm -p 8787:8787 \
  -e MCP_TRANSPORT=http -e MCP_HTTP_HOST=0.0.0.0 \
  -e DREAMBOT_BRIDGE_URL=http://host.docker.internal:8765 \
  -v dreambot_data:/app/data \
  dreambot-mcp
```
> The bridge runs on the **host** (inside DreamBot), so from the container use
> `host.docker.internal` to reach `8765`.
