Skip to content
Blog

How to connect Ramus to any coding agent

Connect Ramus to any coding agent over remote MCP or the ramus CLI, with setup notes for Amp, Kiro, JetBrains, Warp and cloud agents.

Any coding agentPublished

On this page

Ramus gives your coding agent an Android emulator in the cloud. Your agent installs your APK, reads the screen, taps through a flow and collects screenshots and logs, and you get a link to watch the device. You take over from your device page. This page covers the three ways to connect any agent, what it can do once connected, and setup notes for tools that do not have their own guide yet.

Pick your tool

Each guide takes one tool from nothing to a shared link: connect Ramus, start a device, install your APK, let the agent check a flow, and send a reviewer a link to the device.

Amp, Kiro, JetBrains AI Assistant, Junie and Warp are under Other tools. Codex cloud, Cursor cloud agents, Claude Code on the web, GitHub Copilot coding agent, Jules and Devin are under Cloud agents.

Three ways to connect

WayUse it whenYou need
Remote MCP with a Ramus sign-inYour tool supports MCP sign-in (OAuth) and its callback is an https:// or loopback addressA Ramus account, signed in with GitHub
Remote MCP with an API keyYour tool cannot sign in, its callback uses a custom scheme such as Cursor's, or it runs unattendedA key from the Agents page
The ramus CLI and the android-use skillYour agent can run shell commandsNode.js 22 or later, and ramus login or RAMUS_API_KEY

An agent with a shell can use both: MCP tools for the device, and the CLI for uploads, adb and hot reload.

Remote MCP with a Ramus sign-in

The server is https://api.ramus.dev/api/mcp. It speaks Streamable HTTP and takes every message as a POST. It has no SSE stream, so when your tool asks for a transport, choose HTTP or Streamable HTTP. Most tools accept this shape:

mcp.json
{
  "mcpServers": {
    "ramus": {
      "url": "https://api.ramus.dev/api/mcp"
    }
  }
}

The file and the URL key differ between tools (url, serverUrl, httpUrl, or url with "type": "http"); each guide above gives the exact one.

The first request has no credential, so Ramus answers 401 with a WWW-Authenticate header that points your tool at Ramus's sign-in. Your tool opens ramus.dev in your browser, you sign in with GitHub, check what the tool asks for and choose Allow. The tool then refreshes its token by itself.

The Ramus consent page: Connect Claude Code to Ramus, published by claude.ai, returning to localhost, listing what it will be able to do, with Deny and AllowThe Ramus consent page: Connect Claude Code to Ramus, published by claude.ai, returning to localhost, listing what it will be able to do, with Deny and Allow
The consent page names the tool, who published it, where it sends you back, and what it can do.

Ramus identifies your tool by its client metadata document or by dynamic client registration, and sends you back only to an address the tool registered:

  • https:// callbacks are accepted, such as https://claude.ai/api/mcp/auth_callback.
  • Loopback callbacks are accepted on any port: http://127.0.0.1:<port>/… and http://[::1]:<port>/… for every tool, http://localhost:<port>/… for tools without a client secret.
  • Custom URI schemes are refused, such as warp://mcp/oauth2callback. A tool whose only callback is a custom scheme needs an API key.

Accounts made with ramus trial cannot sign in.

Remote MCP with an API key

On the Agents page, choose the API key tab, name a key and create it. The key starts with rms_, works for your whole account and lasts 30 days. Send it in the Authorization header:

mcp.json
{
  "mcpServers": {
    "ramus": {
      "url": "https://api.ramus.dev/api/mcp",
      "headers": {
        "Authorization": "Bearer ${RAMUS_API_KEY}"
      }
    }
  }
}

Many tools expand an environment variable in a header, each with its own syntax (${RAMUS_API_KEY}, ${env:RAMUS_API_KEY}, {env:RAMUS_API_KEY}). Where a tool cannot, put the key in its user-level config, outside the repository. Never commit a key or paste it into the chat.

To check a key from a terminal, list the tools:

list the tools with a key
$ curl -s https://api.ramus.dev/api/mcp \
    -H "Authorization: Bearer $RAMUS_API_KEY" \
    -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools | length'
24

Tools that only start local servers

A tool that only runs local (stdio) servers reaches Ramus through mcp-remote, a bridge that runs locally and forwards each call:

mcp.json
{
  "mcpServers": {
    "ramus": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.ramus.dev/api/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "Bearer rms_your_key_here"
      }
    }
  }
}

Leave no space after the colon and keep Bearer inside the variable: some tools split arguments on spaces. --header-file <path> reads the header from a file instead, which keeps the key out of the process list. Without a header, mcp-remote signs in to Ramus on http://localhost:<port>/oauth/callback, which Ramus accepts, and keeps its tokens in ~/.mcp-auth.

The ramus CLI and the android-use skill

If your agent can run shell commands, the CLI does everything the MCP tools do and a few things they cannot. Install it and sign in:

bash
npm i -g ramus-cli
ramus login

ramus login opens your browser. Check that the page names your machine and choose Allow. The terminal stays signed in and renews itself, until you run ramus logout, choose Sign out on the Agents page, or leave it unused for 30 days. Over SSH it gives you a code to approve on any device. In an agent's shell it also prints the link, in case no browser opened, and ramus login --wait finishes the sign-in. RAMUS_API_KEY in the environment wins over the sign-in. Without an account, ramus trial gives a 15-minute key.

ramus login
$ ramus login

Opening your browser to sign in to Ramus…
(On another device, or no browser? Press Ctrl-C and run `ramus login --device`.)

{
  "login": "ok",
  "account": "@blogger",
  "terminal": "Mac",
  "origin": "https://api.ramus.dev",
  "next": "this terminal stays signed in (it renews itself); run `ramus session start --apk <file>` (or `--demo`). Terminals are listed at https://ramus.dev/agents"
}
The Ramus page Sign in the Ramus CLI on Mac, listing what the terminal will be able to do and how long it stays signed in, with Deny and AllowThe Ramus page Sign in the Ramus CLI on Mac, listing what the terminal will be able to do and how long it stays signed in, with Deny and Allow
Check that it names your machine and that you just ran ramus login, then choose Allow.

Then give your agent the android-use skill, which teaches it to read the screen, act and verify. ramus skill install saves it for Claude Code and ramus skill install --codex for Codex. For other tools, save ramus.dev/skill.md as android-use/SKILL.md in the agent's skills folder, such as .agents/skills/ or .kiro/skills/. An agent without skills gets the same instructions from one line in AGENTS.md:

Test Android changes on Ramus: https://ramus.dev/agents.md

ramus status tells you whether the key works and lists your live sessions.

However you connect, your agent appears under Connected on the Agents page after its first call. Then ask it for something small:

Youtoyour agent

Start a Ramus device, install my app and take a screenshot of its first screen.

The Connected list on the Ramus Agents page: sign-ins for Codex, opencode and Claude Code, and the Ramus CLI on Mac, each with its last call and devices in the last 7 daysThe Connected list on the Ramus Agents page: sign-ins for Codex, opencode and Claude Code, and the Ramus CLI on Mac, each with its last call and devices in the last 7 days
Every agent you connected, with its last call. Disconnect, Sign out or Revoke cuts it off.

What your agent can do

Ramus lists 24 MCP tools, or 27 on accounts with app snapshots. Device tools take the session id that android_start_session returns.

GroupToolWhat it does
Sessionandroid_start_sessionStarts a device from an uploaded APK, a stored or catalog app, a pull request preview, the demo app, or blank; returns the session id, a watch_url and a screenshot
android_session_statusThe session's status, the app on screen and any app Android ended
android_list_sessionsYour live sessions
android_end_sessionEnds the session and frees the device
Installandroid_upload_apkA one-time upload: an apk_uri and a curl command, valid 15 minutes
android_install_appInstalls and opens an APK on the running device; viewers stay connected
android_app_launchBrings the app back to the foreground and returns the screen
Observeandroid_snapshotThe UI tree, with a ref for each element and a gen for this observation
android_screenshotA JPEG of the screen for the agent
android_wait_forWaits for text to appear or go, or for the screen to settle
Actandroid_tapTaps an element by its text, a ref or a point, and returns the screen it leads to; expect fails the call when the text does not appear
android_typeTypes into a field, reads the value back and returns the screen
android_swipeSwipes between two points and returns the screen
android_pressPresses BACK, HOME, ENTER and other keys, and returns the screen
Evidenceandroid_logcatRecent device log lines, with a filter
android_show_screenShows you the screen in the chat
Sharingandroid_share_sessionCreates a share link that can watch, use or install, or that gives each person their own device; or revokes every link for the session
Feedbackandroid_list_feedbackThe notes people sent through your share links
android_get_feedbackOne note with its screenshot, device log and repro steps
android_resolve_feedbackResolves a note, with a reply the person sees on their link
Camera and micandroid_camera_feedPuts an image on the back camera, such as a QR code
android_mic_feedPlays a WAV file into the microphone
Pull requestsandroid_install_github_appStarts the Ramus GitHub App install; you open the setup link
android_install_statusChecks whether that install finished
App snapshotsandroid_save_app_snapshotSaves the whole device state under a label
android_list_app_snapshotsLists saved snapshots and whether each is ready
android_restore_app_snapshotStarts a new session from a saved snapshot

Every action returns the screen it led to, with fresh refs, so your agent decides the next step from that result. An older ref fails with stale_ref instead of tapping the wrong thing.

What only the CLI does

  • Upload and install in one command: ramus session start --apk <file> --wait and ramus session install <file>. Over MCP a local APK takes two steps: your agent runs the curl command android_upload_apk returns, then passes the apk_uri on. A chat client without a shell starts from an attached APK (ChatGPT), a stored app or a pull request preview.
  • adb: ramus adb opens a local adb tunnel to the device.
  • Hot reload: ramus dev runs a reload loop for Expo, React Native, Flutter and Gradle apps.
  • Stored apps: ramus apps list and ramus apps download.

Other tools

These tools have no guide of their own yet. Each section gives the config, how the tool signs in, and the one thing most likely to trip you up, checked against the vendor's documentation on 29 October 2026.

Amp

Amp reads amp.mcpServers from ~/.config/amp/settings.json, or from .amp/settings.json for one workspace. amp mcp add ramus https://api.ramus.dev/api/mcp adds it for you.

~/.config/amp/settings.json
{
  "amp.mcpServers": {
    "ramus": {
      "url": "https://api.ramus.dev/api/mcp"
    }
  }
}

The next time you start Amp, it opens Ramus's sign-in in your browser. Amp uses a client metadata document where the server supports one, as Ramus does, and its callback is http://localhost:8976/oauth/callback, which Ramus accepts. For a key, add "headers": { "Authorization": "Bearer ${RAMUS_API_KEY}" }; Amp expands ${VAR}. amp mcp doctor shows each server's state, and amp mcp oauth logout ramus makes Amp ask you to sign in again.

A server in .amp/settings.json runs only after amp mcp approve ramus, and Amp's cloud orbs cannot open a browser sign-in, so use the header there. Amp runs commands, reads AGENTS.md and loads skills from .agents/skills/, so the CLI path works too.

Kiro

Kiro reads ~/.kiro/settings/mcp.json for every workspace and .kiro/settings/mcp.json for one; the workspace file wins. The command palette opens either (Kiro: Open user MCP config (JSON)).

~/.kiro/settings/mcp.json
{
  "mcpServers": {
    "ramus": {
      "url": "https://api.ramus.dev/api/mcp"
    }
  }
}

When you save, Kiro connects and opens the sign-in in your browser. It registers itself with dynamic client registration and listens on http://localhost:<random port>/oauth/callback, or on an oauth.redirectUri you set on 127.0.0.1 or localhost. Ramus accepts both. The MCP servers tab in the Kiro panel shows the connection, and Output → Kiro - MCP Logs shows errors. Tools listed in autoApprove skip the prompt.

For a key, add "headers": { "Authorization": "Bearer ${RAMUS_API_KEY}" }. Kiro expands only variables you approve in its Mcp Approved Env Vars setting, so add RAMUS_API_KEY there first. Kiro runs commands, reads AGENTS.md and loads skills from .kiro/skills/.

JetBrains AI Assistant and Junie

AI Assistant. Open Settings | Tools | AI Assistant | Model Context Protocol (MCP), choose +, then HTTP, and enter:

json
{
  "mcpServers": {
    "ramus": {
      "url": "https://api.ramus.dev/api/mcp",
      "headers": {
        "Authorization": "Bearer rms_your_key_here"
      }
    }
  }
}

The Status column shows the connection, and Reconnect retries. AI Assistant cannot sign in to a remote MCP server yet (JetBrains issue LLM-25012 is open), so use a key, or add Ramus as a local server through mcp-remote without a header, which signs in on a loopback callback.

Junie. The IDE plugin and the Junie CLI read ~/.junie/mcp/mcp.json for all projects and .junie/mcp/mcp.json for one. Use the JSON above in the user-level file; Junie's docs do not describe variables in headers. To sign in instead, leave out headers, run /mcp in the Junie CLI, choose Ramus (it shows Authorization required), then Authorize. The IDE plugin sets up servers that need a sign-in only through the CLI. If the sign-in does not complete, use the key.

Junie loads at most 100 tools across all servers, and asks before each MCP call unless you allow it under Action Allowlist.

Warp

In the Warp app, open Settings > Agents > MCP servers, choose + Add and the URL option. Warp's sign-in calls back to warp://mcp/oauth2callback, a custom scheme Ramus refuses, so send a key:

json
{
  "ramus": {
    "url": "https://api.ramus.dev/api/mcp",
    "headers": {
      "Authorization": "Bearer rms_your_key_here"
    }
  }
}

The MCP servers page shows whether Ramus is running, and View Logs shows errors. Those logs can contain your key, so remove it before you share them. The Warp Agent CLI (servers in ~/.warp_cli/.mcp.json on macOS) signs in on http://127.0.0.1:<port>/mcp/oauth2callback, which Ramus accepts.

Warp is a terminal, so the CLI path is the shortest: npm i -g ramus-cli, ramus login, and the line in AGENTS.md, which Warp reads as project rules.

Cloud agents

Cloud agents run in containers without KVM, so they cannot run an Android emulator. They drive a Ramus device with the CLI instead:

  1. Create a key on the Agents page, API key tab.
  2. Add it to the platform as RAMUS_API_KEY (table below).
  3. Install the CLI in the platform's setup script: npm i -g ramus-cli.
  4. Add Test Android changes on Ramus: https://ramus.dev/agents.md to AGENTS.md, or CLAUDE.md for Claude Code.
  5. If the platform limits network access, allow the hosts below.
PlatformWhere RAMUS_API_KEY goesNetwork
Codex cloudThe environment's Environment variables. Not Secrets: Codex removes those before the agent runs.Agent internet access is off by default. Turn it on with the hosts below and allow all HTTP methods, because the CLI sends POST requests.
Cursor cloud agentsSecrets on the Cloud Agents dashboard, type Environment Variable or Runtime SecretOpen by default. In an allowlist mode, add the hosts below.
Claude Code on the webThe cloud environment's Environment variables (.env format; anyone who uses the environment can read them)Trusted by default. Choose Custom, list the hosts below and keep the default list.
GitHub Copilot coding agentA secret under Settings → Secrets and variables → Agents, not named COPILOT_MCP_… (only MCP servers see those)Settings → Copilot → Internet access → Custom allowlist. An entry covers its subdomains, so write fleet.ramus.dev without the *..
JulesThe repository's environment variables, turned on when you start the taskNot documented
DevinSecrets in Devin's settings; tell Devin the variable name, since it binds secrets to the commands that need themNot documented
HostUsed for
registry.npmjs.orgInstalling ramus-cli
api.ramus.devEvery CLI command
*.fleet.ramus.devThe device connection: snapshots, taps, screenshots and logs
*.s3.us-east-va.io.cloud.ovh.usAPK uploads, which go straight to object storage
ramus.devagents.md and skill.md

Ask the agent to put the watchUrl from ramus session start in its report, so you can watch the device. To take over, open the device page (ramus session open).

Security

Ramus has three kinds of credentials:

  • A sign-in belongs to one tool and gets only what the MCP tools use: mcp:use, session:read, session:write, share:write, repository:read for pull request previews and upload:write for APK uploads, plus openid, profile, email and offline_access for the refresh token. It can never open an adb tunnel, change repository settings or manage the GitHub App installation.
  • A terminal sign-in from ramus login belongs to one terminal. It can also open adb tunnels. It renews itself and ends after 30 days unused.
  • An API key works for your whole account from any tool or machine, adb tunnels included. It lasts 30 days, and an account holds at most 10.

Remove any of them on the Agents page: Disconnect stops a sign-in at once, including tokens the tool already holds, Sign out ends a terminal's sign-in, and Revoke stops a key. Keep keys in environment variables or user-level config, never in a URL, a committed file or a chat message. Your agent never needs to see a key.

Share links are credentials too. The watch link your agent gets lets anyone who opens it watch the device, without an account, and after the session ends it starts a fresh device until it expires (72 hours by default). It is view-only and does not show your name. Send it to people, not public channels. ramus session share --revoke, or android_share_session with revoke: true, revokes every link for the session; ending the session does not. For a link that can control the device, use ramus session share --permission use, or android_share_session with permission: "use". A link holds you + 3 people on one device. With --own (MCP own_device: true), each person gets their own device on your minutes, for 30 minutes. See Security for more.

Troubleshooting

  • 401 with a WWW-Authenticate header. Ramus got no usable credential. A tool that supports sign-in starts it from this header; one that does not needs a key. With a key, check that the header reads exactly Authorization: Bearer rms_…, that your tool expanded the variable (a literal ${RAMUS_API_KEY} is refused), and that the key is still listed on the Agents page.
a request without a credential
$ curl -s -o /dev/null -D - https://api.ramus.dev/api/mcp \
    -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
HTTP/2 401
content-type: application/json
www-authenticate: Bearer resource_metadata="https://api.ramus.dev/.well-known/oauth-protected-resource/api/mcp", scope="mcp:use session:read session:write share:write repository:read upload:write offline_access"
  • Sign-in fails before the consent page. Registration fails with invalid_redirect_uri, or the browser lands on ramus.dev/login?error=invalid_redirect. Your tool's callback is a custom scheme or an address it did not register. Use a key, or mcp-remote.
  • "trial accounts cannot connect apps". You are signed in to a ramus trial account. Sign in to Ramus with GitHub and connect again.
  • 405 "this server has no SSE stream". Your tool is set to SSE, or opened a stream with GET. Choose HTTP or Streamable HTTP.
  • Too many tools. Some tools cap the tools they load across all servers; Junie stops at 100. Turn off servers or tools you do not use.
  • android_start_session times out. It waits up to 50 seconds, but some tools give up on any call sooner. The device keeps starting either way: ask your agent to pass a smaller timeout_sec and poll android_session_status, or find the session with android_list_sessions. The CLI waits 100 seconds; --keep-on-timeout keeps a slow start.
  • 403 insufficient_scope naming upload:write. Your sign-in predates APK uploads over MCP. Disconnect it on the Agents page and sign in again.
  • quota_exhausted or session_cap. You are out of minutes, or at your plan's device limit. That decision is yours, so your agent should stop and pass on the message and its upgrade link. At the device limit, install into the running session instead. See Plans and minutes.

For every command, error code and limit, read the agent reference and the CLI docs.

Give your agent a device.Keep a hand on it.