---
title: "Test your iOS app with Codex | Mobster"
description: "Add Mobster's MCP server to OpenAI's Codex so it checks the iOS screens it writes on a headless simulator, with pass or fail from accessibility assertions."
canonical: "https://mobster.dev/blog/test-ios-app-with-codex"
last-updated: "2026-10-08"
---

Add Mobster's MCP server to OpenAI's Codex so it checks the iOS screens it writes on a headless simulator, with pass or fail from accessibility assertions.

# Test your iOS app with Codex

Add Mobster's MCP server to OpenAI's Codex so it checks the iOS screens it writes on a headless simulator, with pass or fail from accessibility assertions.

7 October 2026 (updated 8 October 2026) 5 min read The Mobster team

- [Codex](https://mobster.dev/blog/tags/codex)
- [iOS testing](https://mobster.dev/blog/tags/testing)
- [MCP](https://mobster.dev/blog/tags/mcp)

Codex writes Swift well and builds it with `xcodebuild` without complaint. It can’t look at the result: “the paywall works” means “the paywall compiles”. Mobster gives Codex a headless iOS simulator, an outline of every screen, and a verdict it didn’t write, through one local MCP server.

## Add Mobster to Codex

Install Mobster CLI on a Mac with Apple silicon, macOS 15 or later, and Xcode with an iOS Simulator runtime, then add the server:

```
curl -fsSL https://mobster.dev/install.sh | sh
mobster mcp install codex
```

The second line runs `codex mcp add mobster -- /Users/you/.local/bin/mobster mcp`, which writes `~/.codex/config.toml`:

```
[mcp_servers.mobster]command = "/Users/you/.local/bin/mobster"args = ["mcp"]
```

`mobster mcp` is a local stdio server. Codex starts it as a child process on your Mac, and nothing goes through a relay.

On 7 October 2026 a real `codex exec` session (Codex 0.161.0) connected to this server and called its `status` tool. The verify loop below uses the same plain MCP tools. If something doesn’t work, [open an issue](https://github.com/RadishSoftware/mobster/issues) with what Codex printed.

The [Codex integration page](https://mobster.dev/integrations/codex) keeps this setup current, including how to give the server a model key for Smart mode.

## It fits Codex’s timeout

Codex gives each tool call 60 seconds by default, and no Mobster call needs more: every tool returns within 45 seconds, and `wait` within 50. Longer work carries on under a `run_id`. The first run on a Mac creates the simulator, boots it and builds WebDriverAgent, so `verify_start` returns `status: "preparing"` and Codex calls `wait` until the run is ready. To do the slow part ahead of time:

```
mobster sim doctor --fix
```

That creates Mobster’s own simulator, boots it headless and builds WebDriverAgent once per Xcode version. Mobster never opens Simulator.app and never touches the simulators you created. On an M5 Max (28 September 2026, n = 2), a first run including that preparation took 77.7 s and 103.6 s, and warm runs took 6.8 s and 8.6 s.

## Tell Codex how to verify

Codex reads `AGENTS.md`. Put the verify loop there, with your scheme and app names:

```
## 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.
```

The build needs no signing team, and Mobster keeps `build/` and `runs/` out of your commits with a `.mobster/.gitignore`.

## The key-less loop catches what a glance misses

In the key-less loop Codex drives with its own model, and Mobster calls no model and needs no key. Mobster runs the app, reports the screen and judges. `verify_start` fixes the assertions before Codex touches the app. A real check of Daybreak’s reminder setting, the sample app in the CLI’s repository, as tool calls:

```
{"tool": "verify_start", "arguments": {  "app_path": "/abs/path/examples/ios/Daybreak/.build/Build/Products/Debug-iphonesimulator/Daybreak.app",  "launch_args": ["-DaybreakSkipOnboarding", "YES"], "open_url": "daybreak://settings",  "steps": ["Turn on Daily reminder.", "Relaunch the app and open Settings again."],  "expect": [{"value": {"id": "daily_reminder"}, "equals": true}]}}{"tool": "tap", "arguments": {"run_id": "…", "target": {"id": "daily_reminder"}}}{"tool": "relaunch", "arguments": {"run_id": "…"}}{"tool": "open_url", "arguments": {"run_id": "…", "url": "daybreak://settings"}}{"tool": "verify_finish", "arguments": {"run_id": "…"}}
```

It passes. Launched with `-DaybreakBug reminder-not-saved` as well, the switch is off again after the relaunch, and the check fails with `value id=daily_reminder == true failed: was off`. That’s the bug a quick look misses: the switch turns on, and the setting doesn’t survive a relaunch.

## The verdict tells Codex what to fix

`verify_finish` returns one of four verdicts, each with an exit code for scripts: `passed` (0), `failed` (1), `needs_review` (2) and `couldnt_run` (3). With it comes the result JSON: `summary` names the first failing assertion, `reason` carries a `fix` sentence when there is one, and each failed assertion’s `observed` says what was on screen instead, with up to 5 near misses. On Daybreak’s planted `missing-plan` bug:

```
✗ failed  The paywall shows three plans with Annual at $39.99 a year  (18.2 s)
  ✓ text "Choose your plan"
  ✗ count id=/^plan_/ == 3: found 2: plan_weekly, plan_monthly
  ✗ value id=plan_annual == "$39.99 / year": not found; closest: "plan_monthly"
  ✓ visible label=Restore Purchases role=button
  ✓ no_text "Loading"
```

The count line names the two plans it found, so Codex goes straight to the code that drops the third: `ForEach(Plan.all.prefix(2))`.

## Keep the check

`save_check` with a `name` saves the run’s check as `.mobster/checks/<name>.yaml`, plain YAML you can read and commit. The [Checks reference](https://docs.mobster.dev/checks) has every field. A launch-only check reruns with no model and no key, so it suits a script:

```
mobster verify --check .mobster/checks/paywall.yaml --json > result.json
case $? in
  0) echo "paywall ok" ;;
  1) jq -r '.summary, .report' result.json; exit 1 ;;
  *) jq -r '.reason.message' result.json; exit 1 ;;
esac
```

A check with steps reruns through Codex again, or through Smart, Mobster’s own agent, on your OpenAI or Anthropic key with the spend capped by `--max-usd` (default $0.25).

## Give Codex a simulator

Mobster CLI is free and open source under MIT:

```
curl -fsSL https://mobster.dev/install.sh | sh
mobster sim doctor --fix
mobster mcp install codex
```

Mobster for Mac adds guided setup for your iPhone, a live view and approvals, for [$34.99 one time](https://mobster.dev/#pricing).

The full tool list is in the [MCP reference](https://docs.mobster.dev/mcp-server), [Getting started with verify](https://docs.mobster.dev/verify) runs a first check on the sample app, and [Give Claude Code a real iPhone](https://mobster.dev/blog/claude-code-real-iphone) covers the phone side.

## Questions

- ### Can Codex use the iOS Simulator?
  Codex can build your app with xcodebuild, but it can't see a simulator's screen on its own. With Mobster's MCP server it gets a headless simulator that Mobster manages, and tools to read the screen, tap, type and swipe.
- ### Do Mobster's tools fit inside Codex's tool timeout?
  Yes. Codex's default tool timeout is 60 seconds, and every Mobster tool call returns within 45 seconds, wait within 50. Longer work, such as a first simulator boot, carries on under a run_id that the agent polls with wait.
- ### Can I rerun a check without Codex?
  Yes. Save it with save_check or --save and run mobster verify --check. A launch-only check calls no model, and the exit code is the verdict. A check with steps needs Codex again or Smart on your own key.

## Sources

We read each of these on 8 October 2026. Reviewed by Andy Guo on 8 October 2026. Corrections are welcome at the [GitHub repo](https://github.com/RadishSoftware/mobster/issues).

- [Codex docs: Model Context Protocol](https://learn.chatgpt.com/docs/extend/mcp)
- [Codex docs: AGENTS.md](https://learn.chatgpt.com/docs/agent-configuration/agents-md)
- [Mobster docs: MCP server](https://docs.mobster.dev/mcp-server)
- [Mobster docs: add Mobster to your agent](https://docs.mobster.dev/agents)
- [Mobster docs: checks](https://docs.mobster.dev/checks)
- [Mobster docs: simulators](https://docs.mobster.dev/simulators)
- [Mobster docs: CLI reference (mobster verify)](https://docs.mobster.dev/cli)

## Try it on your own app

Mobster CLI is free and open source under MIT. One line installs it; then add it to your agent.

```
curl -fsSL https://mobster.dev/install.sh | sh
```

[Connect your agent](https://mobster.dev/integrations) [Developers](https://mobster.dev/developers) [Mobster for Mac](https://mobster.dev/#pricing)

## Related posts

- 7 October 2026 7 min read
  ### [Give Claude Code a real iPhone](https://mobster.dev/blog/claude-code-real-iphone)
  Claude Code can write a SwiftUI screen but can't see it. Mobster's MCP server lets it read each screen by name, drive it, and prove it works with a verdict.
- 7 October 2026 6 min read
  ### [How to test an iOS app with an AI agent](https://mobster.dev/blog/ai-e2e-tests-ios-assertions)
  Let a model drive your iOS app and never let it judge. Mobster decides pass or fail from accessibility tree assertions on one settled screen, with proof.
- 7 October 2026 4 min read
  ### [Maestro vs XCUITest vs Mobster: when to use which](https://mobster.dev/blog/maestro-vs-xcuitest-vs-mobster)
  XCUITest, Maestro and Mobster all check iOS screens. If a coding agent writes yours, Mobster is built for it: plain-English checks, verdicts from assertions.
