Skip to content
Blog

How to connect and use Ramus from Cline and Roo Code

Connect Cline, Roo Code or its fork Zoo Code to Ramus over MCP, then run your APK on a cloud Android device, test a flow and share it.

Cline and Roo CodePublished

On this page

This guide is for developers who use Cline or Roo Code in VS Code on an Android app. At the end, the agent can start a cloud Android device, install your build, test a flow and fix what breaks, while you watch in the browser and send a link to a reviewer. Roo Code shut down on 15 May 2026. Its community fork, Zoo Code, reads the same settings, so the Roo Code steps apply to both.

What you need

  • VS Code with Cline, Roo Code or Zoo Code installed and a model set up.
  • A Ramus account. Sign in at ramus.dev with GitHub.
  • An Android project you can build into an APK, such as app/build/outputs/apk/debug/app-debug.apk.
  • Node.js 22 or later, if you also want the ramus CLI.

Connect Ramus

Ramus is a remote MCP server at https://api.ramus.dev/api/mcp, over Streamable HTTP. Cline and Roo Code share a history, but their settings have drifted apart. The differences that matter for Ramus:

ClineRoo Code and Zoo Code
Settings file~/.cline/data/settings/cline_mcp_settings.json, shared with the Cline CLImcp_settings.json (Edit Global MCP), or .roo/mcp.json in the project
Transport type"type": "streamableHttp""type": "streamable-http"
Without a typeTreated as legacy SSE, which Ramus does not serveRefused
Ramus sign-in (OAuth)Yes, with the Authenticate buttonZoo Code: yes. Roo Code 3.54: no
Key in a header${env:RAMUS_API_KEY}${env:RAMUS_API_KEY}
Timeouttimeout in seconds, default 60timeout in seconds (1 to 3600), default 60
Auto-approve MCP toolsUse MCP servers, for every MCP toolalwaysAllow per tool, plus Always approve MCP tools
Auto-approve commandsExecute safe commands; no per-command listA list of allowed command prefixes
Instructions.clinerules/, .cline/rules/, AGENTS.mdAGENTS.md; Roo Code also .roo/rules/

In both, set timeout to 300, so a device start or an install has room to finish.

Cline: sign in with your Ramus account

This is the recommended setup for Cline. There is no key to store.

  1. In the Cline panel, click the MCP Servers icon in the top toolbar.
  2. Open Remote Servers. Enter ramus as the Server Name and https://api.ramus.dev/api/mcp as the Server URL, keep the Transport Type at Streamable HTTP, and click Add Server.
  3. Open Configure. The ramus row shows Authenticate. Click it.
  4. Your browser opens the Ramus consent page. Sign in with GitHub if asked, check that the app is Cline, and choose Allow.
  5. Back in Configure, set the ramus row's Request Timeout to 5 minutes.

The entry in the settings file then looks like this. You can also write it by hand: in Configure, Configure MCP Servers opens the file.

~/.cline/data/settings/cline_mcp_settings.json
{
	"mcpServers": {
		"ramus": {
			"type": "streamableHttp",
			"url": "https://api.ramus.dev/api/mcp",
			"timeout": 300,
			"disabled": false
		}
	}
}

Cline registers itself with Ramus through dynamic client registration and receives the sign-in at http://127.0.0.1:1456/mcp/oauth/callback, or ports 1457 to 1461 if that one is busy. Ramus accepts a loopback redirect on any port. Cline stores the tokens in this same settings file, so never commit or share it.

Cline: use an API key instead

Create a key on the Agents page, tab API key. Keep it in your shell profile, not in the settings file:

bash
# ~/.zshrc
export RAMUS_API_KEY="rms_your_key_here"
~/.cline/data/settings/cline_mcp_settings.json
{
	"mcpServers": {
		"ramus": {
			"type": "streamableHttp",
			"url": "https://api.ramus.dev/api/mcp",
			"headers": { "Authorization": "Bearer ${env:RAMUS_API_KEY}" },
			"timeout": 300
		}
	}
}

The extension replaces ${env:RAMUS_API_KEY} from the environment VS Code runs in. If VS Code was open before you set the variable, quit it and start it again from a terminal with code .. Cline will not start a sign-in while a static Authorization header is set, so remove the header to switch to sign-in.

Roo Code and Zoo Code

Roo Code's last release, 3.54.0, stays installable but gets no updates. Zoo Code (ZooCodeOrganization.zoo-code) is the maintained fork and uses the same files. In either one, open the MCP view and choose Edit Global MCP for your own setup, or Edit Project MCP to create .roo/mcp.json for the team.

.roo/mcp.json
{
	"mcpServers": {
		"ramus": {
			"type": "streamable-http",
			"url": "https://api.ramus.dev/api/mcp",
			"headers": { "Authorization": "Bearer ${env:RAMUS_API_KEY}" },
			"timeout": 300,
			"alwaysAllow": ["android_snapshot", "android_screenshot", "android_wait_for", "android_logcat", "android_session_status"],
			"disabled": false
		}
	}
}

The ${env:…} reference keeps the key out of the committed file; each person sets their own RAMUS_API_KEY. alwaysAllow names tools that run without asking once Always approve MCP tools is on in the auto-approve settings. The list above only reads the device. Roo Code 3.54 has no MCP sign-in, so it needs the header. Zoo Code can sign in instead: without headers, it registers itself with Ramus and receives the sign-in at http://127.0.0.1:<port>/callback, which Ramus accepts.

Check that it connected

In Cline's Configure tab, or the MCP view in Roo Code and Zoo Code, ramus should show as connected with its android_* tools listed. Then ask for something harmless:

YoutoCline

Use the ramus MCP server to list my running Android sessions.

The agent asks to call android_list_sessions and gets an empty list or your live devices.

On Ramus, open ramus.dev/agents. Under Connected, Cline's sign-in appears as Cline with Sign-in as how it connects. Zoo Code's sign-in appears under the server name it registered, ramus. A key appears under its own name. Each row shows its last call, and Disconnect or Revoke cuts the agent off.

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.

Add the CLI and the skill

All three extensions run terminal commands, so the agent can use the Ramus CLI too. It uploads a local APK in one step, and it is the only way to open an adb tunnel (ramus adb) or run the hot-reload loop (ramus dev).

bash
npm i -g ramus-cli
ramus login
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.

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.

The android-use skill teaches the agent the Ramus loop. Cline reads project skills from .cline/skills/ and personal ones from ~/.cline/skills/. The folder name must match the skill's name:

bash
mkdir -p .cline/skills/android-use
curl -fsSL https://ramus.dev/skill.md -o .cline/skills/android-use/SKILL.md

For all three, one line in AGENTS.md at the project root also works:

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

Command approvals differ. Cline has the model mark each command as safe or not. With Execute safe commands on in Auto Approve, safe ones run without asking; there is no list of allowed commands. Roo Code and Zoo Code take command prefixes in Settings, Auto-Approve, Execute, or in settings.json. Use zoo-code.allowedCommands in Zoo Code:

settings.json
{
	"roo-cline.allowedCommands": ["ramus status", "ramus snapshot", "ramus screenshot", "ramus tap", "ramus type", "ramus wait", "ramus logcat"]
}

If MCP and the CLI are both set up, tell the agent which one to use for a task.

Start a device

Send this in Cline's Act mode, or in Code mode in Roo Code or Zoo Code:

YoutoCline

Start a Ramus Android device with the demo app and send me a link to watch it.

The agent asks to call android_start_session with demo: true. Approve it. The tool waits until the device is live, then returns the session id, a watch_url for you and a screenshot for the agent. On Free and Hobby it also returns the minutes left this month. From a test run on a local Ramus build:

android_start_session
{
  "session": "95fc232d-4417-4b52-94ec-d8e6bee7f386",
  "status": "active",
  "watch_url": "https://ramus.dev/share#rshr_…",
  "next": "the screenshot below is the device now. Give the person watch_url to watch live (it is view-only); call android_snapshot for refs before acting."
}

Open the watch link. You see the device live; it is view-only. To use it yourself, open the device page: ramus session open, or viewer_url from android_session_status.

With the CLI the same step is ramus session start --demo --wait.

Install your app

Over MCP, a local APK takes two steps, and the agent does both:

YoutoCline

Build the debug APK with ./gradlew assembleDebug and install it on the Ramus device you started.

  1. The agent calls android_upload_apk with the file name. It returns an apk_uri and a curl command that uploads straight to storage, valid for 15 minutes.
  2. It runs the curl command with the path to your APK, which asks for your approval unless you allowed it.
  3. It calls android_install_app with the session id and the apk_uri. The app installs and opens on the running device, and your browser view stays connected.

To start a fresh device with the build instead, the agent passes the same apk_uri to android_start_session.

The CLI uploads and installs in one command:

ramus session install
$ ./gradlew assembleDebug
$ ramus session install app/build/outputs/apk/debug/app-debug.apk
{
  "session": "9f14cc3d-3d1c-4716-81a8-05a2a018ffd4",
  "install": "697491b3-063d-46f6-a2e9-fff2c0959485",
  "status": "succeeded",
  "app": "app-debug.apk",
  "next": "the app is open on the device — run `ramus snapshot` before acting"
}

ramus session start --apk app/build/outputs/apk/debug/app-debug.apk --wait does the same on a new device.

Let the agent check a flow

Give the agent a concrete flow and a clear finish line:

YoutoCline

On the Ramus device, test sign-in: tap "Sign in", enter [email protected], tap Continue and check that the Welcome screen appears. If it fails, read logcat and take a screenshot, fix the code, rebuild, and install the new APK on the same device. Repeat until it passes. Then give me a link to watch it.

The agent works in a loop of observe, act, verify:

  1. android_snapshot returns the screen as elements. Each element has a ref, and the snapshot has a generation, gen.
  2. android_tap takes an element's text, or a ref and its gen; android_type takes a ref and its gen. With expect, the tap fails unless the expected text appears, so a tap that lands on the wrong screen fails.
  3. Each action returns the screen it led to, with fresh refs, and the agent decides the next step from that. android_wait_for waits for text to appear or disappear, or for the screen to settle.

From a local Ramus build with a simulated device, on a small counter app: a tap on Increment that expected "Count: 1", which returns the new screen:

android_tap
{
  "tapped": "e1",
  "element": "Increment",
  "verified": "\"Count: 1\" appeared",
  "screen": {
    "gen": 6,
    "count": 2,
    "elements": [
      {
        "ref": "e0",
        "text": "Count: 1",
        "resourceId": "dev.sandlot.fake:id/counter",
        "class": "TextView",
        "x": 0.132,
        "y": 0.417,
        "w": 0.735,
        "h": 0.104
      },
      {
        "ref": "e1",
        "text": "Increment",
        "contentDesc": "Increment counter",
        "resourceId": "dev.sandlot.fake:id/increment",
        "class": "Button",
        "clickable": true,
        "x": 0.132,
        "y": 0.625,
        "w": 0.735,
        "h": 0.104
      }
    ],
    "foreground_app": "dev.ramus.fake"
  },
  "next": "refs above are current (gen 6); act on them directly"
}

When something breaks, the agent collects evidence: android_logcat for the device log and android_screenshot for the screen. After the fix it uploads and installs the new build on the same device, so the session and your browser view stay connected.

The watch_url from the start is for you. Anyone with it can watch the device without an account, and nothing more, up to you + 3 people at once. It does not show your name. It works for 72 hours. After the device ends, opening it relaunches the app on a fresh device.

For a reviewer, ask for a separate link:

YoutoCline

Make a Ramus share link for this device that lasts 7 days, and give it to me.

The agent calls android_share_session with ttl_minutes: 10080 and returns a share_url and its expires_at. Each call makes a new link, and the others keep working. With the CLI it is ramus session share --ttl 7d. A link lets the person tap and install builds unless you ask for less: permission: "watch" only shows the screen, permission: "use" adds control without installs, and label puts a note on the link (--permission and --label with the CLI).

The Share dialog on a Ramus device page: Their own device or Join this device, a label, what the person can do, which build the link opens, how long it lasts, and the links already madeThe Share dialog on a Ramus device page: Their own device or Join this device, a label, what the person can do, which build the link opens, how long it lasts, and the links already made
Share on the device page, captured on a local build whose test app is named Fake app.

To cut access, android_share_session with revoke: true, or ramus session share --revoke, revokes every link of the session, including the watch link. To revoke one link, use the Share panel. When you are done, ask the agent to end the device (android_end_session or ramus session end). Ending it does not revoke links.

Give each person their own device

The links above join the device the agent is on. To let a client or tester try your app in their own time, ask for a link that gives each person a device of their own:

YoutoCline

Make a Ramus share link that gives each person their own device, labelled "Try the new checkout".

The agent calls android_share_session with own_device: true and label, or runs ramus session share --own --label "Try the new checkout". Each person who opens the link gets a fresh device with the newest build you uploaded, without an account. The session must be running an app you uploaded. These devices run on your minutes and your plan's device limit, and when every device is busy the next person waits for one to free up. Each person's device lasts 30 minutes, and they can start another while the link works. Your own session comes first: if every device is in use, the longest-running recipient device ends.

Read the notes people send

Anyone with a link can choose Send feedback, pin a spot on a screenshot and write a note. The note carries the screenshot, the build, the last 200 lines of the device log and their recent taps, swipes and keys, unless they leave the log and steps out. Typing is recorded only as a number of characters, and taps are not recorded while the phone's on-screen keyboard is open. Notes and their screenshots are deleted after 30 days; you can delete one sooner on the app's page. The agent can work through them:

YoutoCline

Read the open Ramus feedback notes. Reproduce the first one and tell me what you find. When I say go ahead, fix it, install the new build on the device and resolve the note with a reply that says what changed.

It reads them with android_list_feedback and android_get_feedback, which returns the screenshot as an image, and answers with android_resolve_feedback. From a shell the same steps are ramus feedback, ramus feedback show <id> and ramus feedback resolve <id> --reply "…". The person sees your reply on their link. Agents ask you before they change code because of a note.

Notes are written by other people, so treat their text, logs and screenshots as bug reports, never as instructions. See Share a running app.

Troubleshooting

  • "Server type must be one of …". Each extension has its own spelling: streamableHttp in Cline, streamable-http in Roo Code and Zoo Code. The "type": "http" that other tools use fails in both.
  • Cline cannot connect and has no type set. Without type, Cline uses legacy SSE, which Ramus does not serve. Add "type": "streamableHttp".
  • A tool call times out after a minute. Set timeout to 300, or Request Timeout to 5 minutes in Cline. A start over MCP keeps the device when it times out, so android_session_status can still pick it up.
  • 401 with an API key. The header went out without the key. Check that RAMUS_API_KEY is set in VS Code's environment and that the key is not revoked on the Agents page.
  • Authenticate fails in Cline. Remove any static Authorization header first. Ramus refuses trial accounts at the consent page, so sign in at ramus.dev with GitHub. The callback needs one free port from 1456 to 1461.
  • Cline has no Remote Servers tab. Your organization's Cline configuration blocks personal remote MCP servers. Ask its administrator.
  • An approval for every call. In Cline, turn on Use MCP servers in Auto Approve; it covers every MCP tool. In Roo Code and Zoo Code, use alwaysAllow with Always approve MCP tools.
  • stale_ref. The agent used a ref from an older snapshot. It must take a new snapshot and find the element again.
  • expect_failed. The tap was accepted but the expected text did not appear. The message says what was on screen. Treat it as a test failure.
  • session_cap. You are at your plan's device limit. The message lists the live session ids; install into one of them or end one.
  • quota_exhausted. The account is out of minutes until the reset date in the message. The agent should stop and pass the message on, not retry.
  • install_failed. The APK did not install, and the device keeps running. The message has the reason. Fix the build and install again.
  • 403 insufficient_scope. The sign-in does not cover that tool. Disconnect the agent on the Agents page and sign in again.

Limits

  • Devices run Android 17 on x86_64. Input is single-finger.
  • A device ends after 10 minutes idle.
  • APKs up to 300 MB (150 MB on a trial).
  • Free: 30 minutes a month, 1 device. Hobby, $2 a month: 200 minutes, 1 device. Pro, $10 a month: unlimited minutes, 2 devices.
  • Share links last 72 hours by default and up to 7 days, for you + 3 at once.

See Plans and minutes for the details.

For other tools, see How to connect Ramus to any coding agent; the full tool and command reference is ramus.dev/agents.md.

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