Skip to content

feat(ai/cli): device-tools MCP server — Claude Code drives the emulator, no API key - #46

Merged
dev-josias merged 2 commits into
mainfrom
feat/device-mcp
Jul 1, 2026
Merged

feat(ai/cli): device-tools MCP server — Claude Code drives the emulator, no API key#46
dev-josias merged 2 commits into
mainfrom
feat/device-mcp

Conversation

@dev-josias

Copy link
Copy Markdown
Contributor

What

Lets the Claude Code CLI backend drive the embedded Android device — screenshot → reason → tap — on the machine's claude login, no API key. A new in-process MCP server (ai/cli/device_server.rs) exposes UMIDE's device tools to Claude, so the same in-panel session that edits code can also operate the emulator.

Requested to make the product usable key-free (the device-driving demo/QA loop otherwise required the BYO-key LLM path).

Verified live (real Pixel_9a, machine claude login, no API key)

Two #[ignore] tests (run with --ignored):

  • live_device_mcp_drives_the_device — hits the MCP server over HTTP exactly as Claude does: tools/list → 7 tools; device_screenshot → an 856 KB base64 PNG; describe_ui → live uiautomator parse; device_tap(540,1200) → tap executed.
  • live_claude_drives_device_via_mcp — spawns the real claude CLI against the server and asserts it drove the device:
    claude exit=0, called tools:
      ["ToolSearch", "mcp__umide-device__device_screenshot", "mcp__umide-device__device_tap"]
    
    Claude screenshotted the device, reasoned, and tapped — no API key.

Plus 4 pure unit tests (tool catalogue, keycode map, MCP-name, ToolOutput→MCP). clippy/fmt clean on Windows.

How

  • device_server.rs mirrors permission_server.rs (a tiny_http Streamable-HTTP MCP server: initialize/tools/list/tools/call/ping).
  • Tools: device_screenshot, describe_ui, device_tap, device_swipe, device_type, device_key, device_logs. Each reuses the built-in agent's device plumbing — the ai.rs free fns (android_screenshot/adb_input/android_describe_ui/android_logs), reached via super::super:: (no ai.rs visibility change), so behaviour is identical and inherits the fix(ai): run adb directly (argv) so device tools work on Windows #41 argv-direct adb path.

Remaining: app wiring (small, in ai/cli/ai.rs — the Mac's area)

The server is proven; to expose it from the in-panel Claude Code backend (not just the tests), four mapped seams remain:

  1. runner.rs — start DeviceServer in CliRunner::run and merge its entry into the single --mcp-config JSON; add a device-serial field to CliRunner.
  2. ai.rs — add a selected_device param to spawn_cli_turn, forward to CliRunner::new.
  3. ai_assistant_view.rs:824 — pass active_device.get_untracked() (mirrors the LLM path at :821).
  4. permission_server.rs — add the read-only device tools (…device_screenshot/…describe_ui/…device_logs) to is_read_only; writes keep prompting.

Coordinating on the board since that's the Mac's active area.

Adds an in-process MCP server (mirrors permission_server.rs — a tiny_http
Streamable-HTTP server) that exposes UMIDE's emulator device tools to the
Claude Code CLI backend, so the in-panel session can drive the Android device
(screenshot → reason → tap) on the machine's `claude` login — no API key.

Tools: device_screenshot, describe_ui, device_tap, device_swipe, device_type,
device_key, device_logs. Each reuses the built-in agent's device plumbing —
the ai.rs free fns (android_screenshot/adb_input/android_describe_ui/
android_logs), reached via `super::super::` (ai/cli is a descendant of ai, so
no visibility change to ai.rs) — inheriting the #41 argv-direct adb path.

Verified live on a real Pixel_9a (ignored test `live_device_mcp_drives_the_
device`, run with --ignored): over HTTP exactly as Claude Code calls it —
tools/list → 7 tools; device_screenshot → an 856 KB base64 PNG; describe_ui →
live uiautomator parse; device_tap(540,1200) → tap executed on the device.
Four pure unit tests cover the tool catalogue, keycode map, MCP-name and
ToolOutput→MCP conversion.

This is the standalone core; the app wiring (start the server in CliRunner,
merge it into --mcp-config, thread the viewed device's serial, auto-allow the
read tools in the permission bridge) follows.
@dev-josias
dev-josias merged commit ce2e8ad into main Jul 1, 2026
7 checks passed
dev-josias added a commit that referenced this pull request Jul 1, 2026
- README + docs/index.html: fuller emulator hardware controls
  (Home/Back/Recents/Power, volume, rotate, keyboard, screenshot) and the
  key-free CLI-agent angle (no API key; uses your existing login), plus a
  README bullet for the new chat sessions.
- CLAUDE.md Current status: add a "Landed since #41" summary (input-channel
  reconnect #37, cmd.exe fixes #40/#41, DeviceInfo.serial #44, device-tools MCP
  core #46, AI panel redesign + sessions #48/#52, Volume/Rotate #49, per-device
  gRPC port #51). Device-MCP app wiring is noted as a handed-off follow-up (not
  advertised as shipped since it isn't wired yet).
dev-josias added a commit that referenced this pull request Jul 1, 2026
Context told the agent it's a mobile IDE; this lets it act on that. Wires
Windows's device_server (#46) into the Claude Code backend so the in-panel,
key-free session can screenshot/tap/read-logs on the running emulator — the
see->act->verify loop, end to end.

The 5 seams:
- runner.rs: CliRunner gains a `serial`; run() starts a DeviceServer alongside
  the permission bridge (Claude only, best-effort); build_args merges its
  mcp_config_entry into the single --mcp-config object (--strict-mcp-config
  requires one JSON) and appends DEVICE_NOTE so the agent knows the tools exist.
- ai.rs: spawn_cli_turn takes selected_device, resolves the Android serial, and
  passes it to CliRunner::new.
- ai_assistant_view.rs: the Launch::Cli arm forwards active_device (mirrors LLM).
- permission_server.rs: adds mcp_config_entry(); auto-allows the device *read*
  tools (device_screenshot/describe_ui/device_logs) while writes
  (tap/swipe/type/key) still prompt via the approval card.

Compiles + clippy clean; 24 CLI unit tests green. Live device-driving verified by
the Windows session on the Pixel per HANDOFF (claude 401 on this Mac blocks a
local live run).
dev-josias added a commit that referenced this pull request Jul 1, 2026
… emulator (#53)

* feat(cli): give the Claude Code backend UMIDE's mobile-first context

The CLI agents felt generic — Claude Code only knew "you're inside the UMIDE
editor" plus the approval model, with no idea it's a mobile IDE. It'd act like a
generic code agent pointed at a folder.

Add a UMIDE_CONTEXT preamble (mirroring the built-in SYSTEM_PROMPT) to Claude's
--append-system-prompt in both write and read-only modes: UMIDE is a Rust IDE for
cross-platform MOBILE dev (React Native / Flutter), the open project is a mobile
app (prefer mobile idioms/tooling), it embeds a live Android emulator (+ iOS sim
on macOS), and changes are meant to be seen and tested on the running device in a
see->act->verify loop.

Claude Code is the key-free default (auto-picked when no API key), so this is the
path most users get. Codex (prompt-on-stdin, context via AGENTS.md) and Gemini
(no append flag) have no clean system-prompt hook here — context for those is a
follow-up. Build + clippy clean; CLI-args tests green.

* feat(cli): wire the device-MCP so Claude Code can drive the emulator

Context told the agent it's a mobile IDE; this lets it act on that. Wires
Windows's device_server (#46) into the Claude Code backend so the in-panel,
key-free session can screenshot/tap/read-logs on the running emulator — the
see->act->verify loop, end to end.

The 5 seams:
- runner.rs: CliRunner gains a `serial`; run() starts a DeviceServer alongside
  the permission bridge (Claude only, best-effort); build_args merges its
  mcp_config_entry into the single --mcp-config object (--strict-mcp-config
  requires one JSON) and appends DEVICE_NOTE so the agent knows the tools exist.
- ai.rs: spawn_cli_turn takes selected_device, resolves the Android serial, and
  passes it to CliRunner::new.
- ai_assistant_view.rs: the Launch::Cli arm forwards active_device (mirrors LLM).
- permission_server.rs: adds mcp_config_entry(); auto-allows the device *read*
  tools (device_screenshot/describe_ui/device_logs) while writes
  (tap/swipe/type/key) still prompt via the approval card.

Compiles + clippy clean; 24 CLI unit tests green. Live device-driving verified by
the Windows session on the Pixel per HANDOFF (claude 401 on this Mac blocks a
local live run).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant