docs: add MCP integration guide

This commit is contained in:
2026-07-21 15:24:16 +08:00
parent 2d0c740c88
commit 6241fb9d6d
2 changed files with 129 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
# Host-Agent MCP Server Integration
The host-agent process exposes a Streamable HTTP MCP server on the same
port as the local console (default `127.0.0.1:8765`), at path `/mcp`. This
lets any MCP-compatible client — Hermes Agent, Claude Desktop, custom
scripts using the `mcp` Python SDK — drive devices directly through the
same `DeviceManager` the cloud worker uses.
## Prerequisites
- Host-agent built from this repo (see `docs/MACOS_IPHONE_SETUP.md`).
- An MCP client that supports the Streamable HTTP transport (mcp SDK
1.20+ on the client side).
## Get the bearer token
The first time host-agent starts after this feature ships, it generates
a random bearer token and writes it to:
<identity_path.parent>/host_mcp_token.json
(Default: `tasks/host_mcp_token.json` next to `host_identity.json`.)
To print it for copy/paste:
device-host-agent mcp-token
To rotate: delete the file and restart host-agent. Old tokens stop
working immediately.
## Hermes Agent configuration
Add to `~/.hermes/config.yaml`:
```yaml
mcp_servers:
apex_device:
url: "http://127.0.0.1:8765/mcp"
headers:
Authorization: "Bearer <paste-token-here>"
```
Start (or restart) Hermes. Verify by asking Hermes to list devices:
> Use the apex_device MCP to list connected devices.
## Tools exposed
All 11 device tools from `api/mcp.py`:
- `take_screenshot(device_id?)`
- `tap(x, y, device_id?)`
- `swipe(start_x, start_y, end_x, end_y, duration_ms?, device_id?)`
- `input_text(text, device_id?)`
- `launch_app(app_id, device_id?)`
- `find_text(query, device_id?)`
- `find_icon(name, device_id?)`
- `get_ui_tree(device_id?, include_app_info?)`
- `describe_screen(device_id?)`
- `list_devices()`
- `device_status(device_id)`
## Concurrency model
- The cloud worker and MCP clients share the same `DeviceManager`.
- Per-device, session-level locking: the first caller (cloud or MCP) to
touch a device holds it; the other side sees a busy error.
- MCP sessions hold their lock until the session ends OR 60 seconds of
inactivity. Cloud assignments hold theirs until the assignment
terminates.
- The cloud scheduler is told about MCP-held devices via the heartbeat
`mcp_busy_device_ids` field, so it normally won't even try to dispatch
to them. A 30-second window exists between an MCP acquire and the next
heartbeat; during that window cloud may dispatch, and the host-agent
will fail-fast the assignment with `failure_reason="device held by an
active MCP session"`.
## Network binding
The MCP endpoint is bound to the same address as the local console. By
default this is `127.0.0.1` (loopback only). To expose on a different
interface, set `HOST_AGENT_CONSOLE_BIND_HOST` AND
`HOST_AGENT_CONSOLE_ALLOW_NON_LOOPBACK=true` — both are required. This
is the same escape hatch the local console uses; there is no MCP-only
override.
## Error responses
| Condition | HTTP / JSON-RPC | Body |
|---|---|---|
| Missing/wrong bearer token | HTTP 401 | `{"error": "invalid token"}` + `WWW-Authenticate: Bearer` |
| Device busy (cloud) | JSON-RPC `-32000` | `"device X is busy (held by cloud assignment)"`, `data.busy_owner = "cloud_assignment"` |
| Device busy (other MCP) | JSON-RPC `-32000` | `"..."`, `data.busy_owner = "mcp_session:<prefix>"` |
| Unknown device | JSON-RPC `-32602` | `"unknown device: X"` |
| Tool error | JSON-RPC `-32000` | Original exception message |
## Troubleshooting
- **`list_devices` returns `[]`**: no devices registered. Use the local
console at `http://127.0.0.1:8765/` to add one (Login → Devices).
- **`device X is busy` even when cloud console says device is idle**:
check whether another MCP session is holding it. The local console
dashboard shows active MCP sessions and held device_ids.
- **Token verification fails after restart**: confirm you copied the
token from the current `host_mcp_token.json`, not an older one.
Rotation = delete file + restart.
## Out of scope (current version)
- `wait_until_usable` MCP tool: implemented internally but not exposed.
MVP callers must handle busy errors themselves.
- MCP call history in the local console: only current state is surfaced,
not a call log.
- Token rotation CLI: use delete-and-restart for now.
- Non-loopback binding without explicit opt-in.