Skip to content
Blog

How to connect and use Ramus from GitHub Copilot

Connect GitHub Copilot in VS Code to Ramus, then run your APK on a cloud Android device, let the agent test a flow and share the device.

GitHub CopilotPublished

On this page

This guide is for developers who use GitHub Copilot's agent in VS Code on an Android app. At the end, Copilot can start a cloud Android device, install your build, test a flow and fix what breaks. You watch the device in your browser, take over from the device page when you want, and send a link to a reviewer.

What you need

  • VS Code with GitHub Copilot, signed in to GitHub.
  • On Copilot Business or Copilot Enterprise, an administrator must turn on the MCP servers in Copilot policy. It is off by default. Copilot Free, Pro, Pro+ and Max are not governed by it.
  • 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. Copilot connects to it over Streamable HTTP. Nothing runs on your machine.

The file VS Code reads depends on the agent harness. Since VS Code 1.143, new chat sessions use the Copilot harness, the runtime the Copilot CLI also uses. It reads ~/.copilot/mcp-config.json for you and .mcp.json at the repository root for your team. Both use a top-level mcpServers object. VS Code's own format, .vscode/mcp.json or the user mcp.json with a top-level servers object, still works and is passed on to Copilot sessions, except entries that prompt for input with ${input:…}.

Sign in with your Ramus account

VS Code signs in to Ramus with your account, so there is no key to store.

  1. Open the Command Palette and run MCP: Add Server.
  2. Choose HTTP (HTTP or Server-Sent Events) and enter https://api.ramus.dev/api/mcp.
  3. Enter ramus as the server ID.
  4. Choose Global, then Copilot Global.

Copilot Global writes the server to ~/.copilot/mcp-config.json. Add timeout, in milliseconds, to that entry, or write it yourself: the Copilot runtime gives an MCP call 30 seconds by default, and a device start can wait longer.

~/.copilot/mcp-config.json
{
	"mcpServers": {
		"ramus": {
			"type": "http",
			"url": "https://api.ramus.dev/api/mcp",
			"tools": ["*"],
			"timeout": 300000
		}
	}
}

For a team, put the same mcpServers block in .mcp.json at the repository root and commit it. It holds no secret. Each person signs in with their own Ramus account.

The first time the agent needs Ramus, VS Code asks you to sign in for the ramus server, in a dialog or in the chat. Accept it. Your browser opens the Ramus consent page. Sign in with GitHub if asked, check that the app is Visual Studio Code, and choose Allow. The browser hands the sign-in back to VS Code and the tools load.

The Ramus consent page: Connect Visual Studio Code to Ramus, with what it will be able to do, Deny and AllowThe Ramus consent page: Connect Visual Studio Code to Ramus, with what it will be able to do, Deny and Allow
Ramus asks once. VS Code keeps the sign-in and refreshes it.

VS Code signs in as the client https://vscode.dev/oauth/client-metadata.json. The browser returns to http://127.0.0.1:33418/ (another port if that one is busy), or to https://vscode.dev/redirect in a remote window. Ramus accepts both.

This terminal command adds the server to your VS Code user profile instead. It sets no timeout, so prefer the file above if device starts time out.

bash
code --add-mcp '{"name":"ramus","type":"http","url":"https://api.ramus.dev/api/mcp"}'

Or use an API key

Use a key where nobody can complete a browser sign-in. It works in both harnesses. Create one on the Agents page, tab API key. Keep it in your shell profile, not in a file you commit:

bash
# ~/.zshrc
export RAMUS_API_KEY="rms_your_key_here"

Then add Ramus with the key in the header, with the same timeout:

~/.copilot/mcp-config.json
{
	"mcpServers": {
		"ramus": {
			"type": "http",
			"url": "https://api.ramus.dev/api/mcp",
			"headers": { "Authorization": "Bearer ${RAMUS_API_KEY}" },
			"tools": ["*"],
			"timeout": 300000
		}
	}
}

The Copilot format expands ${NAME} in header values, from the environment VS Code runs in. If VS Code was already open when you set the variable, quit it and start it again from a terminal with code ..

Check that it connected

In VS Code, run Chat: Open Customizations and open MCP Servers. ramus should be running. Its menu has Show Output for errors, and the Tools tab lists the android_* tools. MCP: List Servers gives the same actions from the Command Palette.

Then ask for something harmless:

YoutoGitHub Copilot

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

The agent calls android_list_sessions and gets an empty list or your live devices.

On Ramus, open ramus.dev/agents. Under Connected, a sign-in appears as Visual Studio Code with Sign-in as how it connects, and a key appears under its own name. Both show their last call. Disconnect or Revoke cuts the agent off. To sign out on the VS Code side, run MCP: List Servers, pick ramus and choose Sign Out.

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

Copilot's agent runs terminal commands, so it can use the Ramus CLI as well. The CLI 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 status confirms it. RAMUS_API_KEY in the environment wins over the sign-in.

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

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

For a lighter touch, add one line to AGENTS.md or .github/copilot-instructions.md. Copilot reads both:

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

VS Code asks before running commands its rules do not auto-approve. To let the agent observe and drive the device without a prompt each time, add a rule for those ramus commands. Starts, installs and shares still ask.

settings.json
{
	"chat.tools.terminal.autoApprove": {
		"/^ramus (status|snapshot|screenshot|tap|type|swipe|press|wait|logcat)\\b/": true
	}
}

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

Copilot cloud agent on GitHub

The cloud agent (formerly the coding agent) works in its own environment on GitHub, not on your machine. Instead of setting up an emulator there, it can drive a Ramus device with the CLI. Create a key on the Agents page and add it in the repository settings as an Agents secret named RAMUS_API_KEY; it reaches the agent's shell as an environment variable. Add the AGENTS.md line above. If the agent's firewall blocks a ramus command, add the host it names to the repository's custom allowlist.

For MCP there, add Ramus under Settings, Copilot, MCP servers, with the key in an Agents secret named COPILOT_MCP_RAMUS_API_KEY. The cloud agent does not support OAuth for remote servers.

json
{
	"mcpServers": {
		"ramus": {
			"type": "http",
			"url": "https://api.ramus.dev/api/mcp",
			"headers": { "Authorization": "Bearer $COPILOT_MCP_RAMUS_API_KEY" },
			"tools": ["*"]
		}
	}
}

Copilot in Visual Studio and JetBrains IDEs

Copilot in Visual Studio 17.14 and later and in JetBrains IDEs also reads an mcp.json with a top-level servers object. JetBrains IDEs take headers under requestInit.headers. This guide does not cover their sign-in. If it fails, use the API key header.

Start a device

YoutoGitHub Copilot

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

Copilot calls android_start_session with demo: true. VS Code asks you to approve the call. Approve it once, or for the session, the workspace or always. The tool waits until the device is live, then returns the session id, a watch_url for you and a screenshot. 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. VS Code renders MCP Apps in chat by default, and Ramus attaches one to its screenshot results, so the device's screen may also appear in the conversation.

With the CLI the same step is one command:

bash
ramus session start --demo --wait

Install your app

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

YoutoGitHub Copilot

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. VS Code's default rules always ask before curl, so you see the upload before it happens.
  3. It calls android_install_app with the session id and the apk_uri. The app installs and opens on the device that is already running, 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, on a new device or a running one:

ramus session start
$ ramus session start --apk app/build/outputs/apk/debug/app-debug.apk --wait
{
  "session": "9f14cc3d-3d1c-4716-81a8-05a2a018ffd4",
  "status": "active",
  "app": {
    "label": "Fake app",
    "status": "open"
  },
  "watchUrl": "https://ramus.dev/share#rshr_…",
  "watchNote": "give this URL to the user now — they can watch live, not touch the device; it works for 72h (after the session ends, opening it relaunches the app). `ramus session share --permission use` makes a link that can control it",
  "idleTtlSec": 600
}

This output comes from a local Ramus build with a simulated device, whose test app is named Fake app.

bash
ramus session install --session <id> app/build/outputs/apk/debug/app-debug.apk

Let the agent check a flow

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

YoutoGitHub Copilot

On the Ramus device, test sign-in: tap "Sign in", enter reviewer@example.com, 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 show me the final screen and 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.

When something breaks, the agent collects evidence: android_logcat for the device log and android_screenshot for the screen, which only the agent sees. android_show_screen puts the screen in the chat for you. 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"
}

After the fix it uploads and installs the new build on the same device, so the session, your browser view and any reviewer 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:

YoutoGitHub Copilot

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:

YoutoGitHub Copilot

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:

YoutoGitHub Copilot

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

  • ramus is missing in a Copilot session. Check that the format matches the file: mcpServers in ~/.copilot/mcp-config.json or .mcp.json, servers in .vscode/mcp.json. Entries that use ${input:…} are not passed to Copilot sessions. Workspace files load only in a trusted workspace.
  • MCP is unavailable on a work account. On Copilot Business or Enterprise, ask an administrator to enable MCP servers in Copilot. If the enterprise uses an allowlist, it needs an entry such as { "serverUrl": "https://api.ramus.dev/api/mcp" }. In the official MCP Registry, Ramus is dev.ramus/android.
  • Sign-in does not finish. Ramus refuses trial accounts at the consent page, so sign in at ramus.dev with GitHub first. To start over, run MCP: List Servers, pick ramus, choose Sign Out, then call a tool again.
  • 401 with an API key. The header was sent without the key. The Copilot format expands ${RAMUS_API_KEY}; ${env:RAMUS_API_KEY} is VS Code's format. VS Code needs the variable in its own environment. Show Output on the server shows the error.
  • A start times out. Set "timeout": 300000 as above. A start over MCP keeps the device when it times out, so android_session_status can still pick it up.
  • "Cannot have more than 128 tools per request." Ramus adds 24 tools. Turn off servers or tools you do not need in the Tools tab.
  • An approval for every call. Pick a wider scope in the approval dialog, or review approvals with Chat: Manage Tool Approval.
  • 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. Sign out of ramus 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.