Skip to content

opencode and the iOS simulator: MCP setup

Add Mobster to opencode.json as a local MCP server with a one-array command, set a safe timeout, and let opencode check your iOS app on a simulator.

The short version, with Mobster for Mac in Applications

Add the mobster block below to ~/.config/opencode/opencode.json, or opencode.json in a project.

What opencode gets back from mobster verify on your app: the lesson and its “Great job!” pass, but the streak stays at 3, so the check fails.

opencode can write and build your iOS code from the terminal. With Mobster’s MCP server added, it can also run the app on a headless iOS simulator Mobster manages, read each screen as a list of controls by name, and get a verdict of passed or failed from assertions on the accessibility tree, with frames and a report. The same server can drive a real iPhone over USB.

You need an Apple silicon Mac with macOS 15 or later and Xcode with an iOS Simulator runtime. A real iPhone also needs Developer Mode and the WebDriverAgent runner, set up in Mobster for Mac (Device setup).

Install Mobster for Mac

Get Mobster for Mac, drag it to Applications and open it. Its MCP server is the agent inside the app, so the command every client starts is this path, with mcp:

/Applications/Mobster.app/Contents/MacOS/mobster-agent

The first check creates Mobster’s own simulator, boots it headless and builds WebDriverAgent, once per Xcode version. Mobster never touches your own simulators: each one it creates has a name that starts with “Mobster”.

Add the server

opencode reads opencode.json at the project root and ~/.config/opencode/opencode.json for every project, merges them, and gives the project’s file the last word. Its MCP block differs from most clients in three ways: the key is mcp, a local server needs "type": "local", and command is one array that holds the arguments too.

{  "$schema": "https://opencode.ai/config.json",  "mcp": {    "mobster": {      "type": "local",      "command": ["/Applications/Mobster.app/Contents/MacOS/mobster-agent", "mcp"],      "enabled": true,      "timeout": 120000    }  }}

The timeout is there on purpose. opencode’s docs describe it as the time allowed for fetching a server’s tools, 5 seconds by default, and its config schema as a timeout for every request to the server. Mobster’s calls take up to 45 seconds, and wait up to 50, so a 5-second value copied from an example would cut them off. 120,000 ms is safe under every reading. opencode mcp add walks you through the same thing interactively.

Key-less checks need no key: opencode drives with its own model, and Mobster judges. For Smart, where Mobster runs the steps itself on your OpenAI or Anthropic key, add Mobster’s env file to the array. Mobster expands the ~ itself:

"command": ["/Applications/Mobster.app/Contents/MacOS/mobster-agent", "mcp", "--env-file", "~/.config/mobster/agent.env"]

Check it’s connected

opencode mcp list

mobster should be listed. Then, in a session:

Call mobster's status tool and tell me what it says.

status names Mobster’s version, the simulator setup, whether Smart is available and why not if it’s off, and where runs go.

A first check: a lesson, with Smart

Most integration pages here show the key-less loop, where the agent taps through the app itself. opencode can also hand the whole flow to Mobster. With a key in the env file, the server offers a verify tool: Mobster performs the steps on your key, then decides the verdict from the assertions, exactly as in a key-less run.

Say your app has lessons and a daily streak. As a prompt:

Build my app for the simulator, then use mobster's verify tool to finish the last lesson.Expect "Lesson complete" and the streak at 4 days.

opencode calls verify with the app, the steps and the expectations, plus a spend cap:

{"tool": "verify", "arguments": {  "app_path": "/abs/path/YourApp/.mobster/build/Build/Products/Debug-iphonesimulator/YourApp.app",  "launch_env": {"STREAK_DAYS": "3"}, "open_url": "yourapp://lesson/last",  "steps": ["Pick the word that fills the blank, then tap Check.", "Tap Continue until the lesson ends."],  "expect": [{"text": "Lesson complete"}, {"value": {"id": "streak_days"}, "equals": 4}],  "max_usd": 0.15}}

max_usd defaults to 0.25 and can go up to 1.00; max_seconds defaults to 180. A run that finishes within 40 seconds returns its result. A longer one returns {"status": "running", "run_id": …}, and opencode calls wait. What the model says about its own run is never an input to the verdict: the verdict comes from the two assertions on a settled read of the accessibility tree. Smart sends each screen’s image and accessibility text to OpenAI or Anthropic, whichever key it runs on.

Put the rule in AGENTS.md

opencode reads project instructions from AGENTS.md, and /init creates or updates one. Add:

## Verifying UI changesAfter you change a screen in this iOS app, verify it before you say it works.1. Build for the simulator:   xcodebuild -scheme <Scheme> -destination 'generic/platform=iOS Simulator' -derivedDataPath .mobster/build CODE_SIGNING_ALLOWED=NO build2. Call mobster's verify_start with app_path set to the absolute path of   .mobster/build/Build/Products/Debug-iphonesimulator/<App>.app, the steps in plain English, and expect:   what must be true when the steps are done.3. Drive with screen, tap, type_text and swipe, then call verify_finish.4. If the verdict is failed, fix the code and verify again. Report the verdict and the report path.

Use a real iPhone

list_devices names every device Mobster can use. With a USB iPhone’s name as device and no run_id, screen, tap, type_text, swipe, alert, open_url, launch_app and home act on the phone itself. Mobster holds the phone’s lock until stop with the device, or 90 idle seconds.

This is where opencode needs one more setting. It starts from permissive defaults, so it calls tools without asking unless a rule says otherwise. Its permissions are keyed by tool name, and it names MCP tools with the server’s prefix. Make Mobster’s acting tools ask before you connect a phone:

{  "permission": {    "mobster_tap": "ask",    "mobster_type_text": "ask",    "mobster_swipe": "ask",    "mobster_alert": "ask",    "mobster_open_url": "ask",    "mobster_launch_app": "ask",    "mobster_home": "ask",    "mobster_use_code": "ask",    "mobster_set_clipboard": "ask",    "mobster_install_app": "ask"  }}

Then limit the server to the phones you mean, since a rule covers a tool, not a device:

"command": ["/Applications/Mobster.app/Contents/MacOS/mobster-agent", "mcp", "--allow-device", "Test iPhone"]

Any other real device is listed as not_allowed and refused. Mobster’s MCP annotations mark which tools only read, and its instructions tell the agent to ask you before anything that sends, buys, posts or deletes on a real iPhone. Don’t rely on instructions alone: the permission rules are what make opencode stop. A locked phone stops the call at once, and Mobster never enters a passcode.

Troubleshooting

SymptomFix
opencode doesn’t load mobsterUse the mcp key, "type": "local", and one command array: ["/Applications/Mobster.app/Contents/MacOS/mobster-agent", "mcp"]
Calls fail after a few secondsA small timeout is cutting them off. Set "timeout": 120000
The first verify_start returns status: "preparing"The simulator is booting and WebDriverAgent is building. opencode calls wait
No verify toolSmart is off. status says why: no key, a missing env file, or --keyless
couldnt_runreason.class and reason.fix in the result say what to do (Checks)

The MCP reference lists every tool. AI end-to-end tests for iOS explains how the verdict is decided.

Questions

  • What timeout should opencode use for Mobster?

    Set "timeout" to 120000 on the server. opencode's docs describe the field as a 5-second timeout for fetching tools, and its config schema as a timeout for every server request. Mobster's calls take up to 50 seconds, and 120 seconds covers both readings.

  • Does opencode ask before it calls Mobster's tools?

    Not by default. opencode starts from permissive defaults where most permissions are allow. Set Mobster's acting tools, such as mobster_tap and mobster_type_text, to ask before you point it at a real iPhone.

  • Why doesn't opencode load the server?

    Check the shape. opencode's key is mcp, not mcpServers, the server needs "type": "local", and command is one array with the arguments in it, ["mobster", "mcp"].

Sources

We read each of these on 8 October 2026. Reviewed by Andy Guo on . Corrections are welcome at the GitHub repo.

opencode’s logo is a trademark of opencode, shown only to name the product. Mobster isn’t affiliated with or endorsed by opencode.