Skip to content

Configuration

Every setting the server and the agent read, generated from .env.example, which a test holds to the settings models.

Precedence, highest first: a real environment variable, then .env, then ios-mcp.toml, then the defaults in code. So a shell export or CI setting overrides this file without anyone editing it. Nested settings use a double underscore: IOS_MCP_

__. Every value below is the default unless noted, so an untouched copy of this file changes nothing.

Copy .env.example to .env to change any of them. A value marked not set is commented out there: either the default shown for reference, or an example to fill in.

The base install commits to no vendor; install the matching extra, e.g. uv sync --extra openai.

Setting Value Notes
IOS_AGENT_PROVIDER anthropic Anything init_chat_model accepts. Change the model with the provider.
IOS_AGENT_MODEL claude-opus-5
IOS_AGENT_MAX_TOKENS 16000 Thinking and the reply share this budget.
IOS_AGENT_EFFORT medium Anthropic only, sent as output_config. Skipped for every other provider.
IOS_AGENT_PROMPT_CACHE true Anthropic only. Marks the tools, the system prompt and the transcript for prompt caching. Other providers cache on their own terms and are sent nothing. Under measurement in docs/adr/0022.
IOS_AGENT_TEMPERATURE 0.7 not set Deliberately unset. Claude Opus 5, Opus 4.8, Opus 4.7 and Sonnet 5 reject it with a 400; set it only on a provider that accepts it.
IOS_AGENT_MAX_STEPS 24 Turns before the loop gives up on a goal.
IOS_AGENT_EXTRA {"reasoning_effort": "high"} not set Anything this project has not heard of, passed to the provider untouched. Applied last, so it overrides everything above. JSON.
IOS_AGENT_ROUTE_MODEL gpt-5.4-mini not set Start each run on a smaller model from the same provider, and move to IOS_AGENT_MODEL for the rest of the run at the first sign of trouble. Off by default; measured in docs/adr/0015 at about half the cost, which is a tie with its own bar rather than a reliable halving.
IOS_AGENT_USD_PER_MTOK_IN 5.0 not set What the eval suite prices tokens at, per million. Defaults are Claude Opus 5’s rates; nothing here can know what another vendor charges, so set both whenever the provider or model changes. Read from here like every other setting; they used to be read only from the process environment, which a .env never reaches, so setting them here silently did nothing.
IOS_AGENT_USD_PER_MTOK_OUT 25.0 not set
IOS_AGENT_USD_PER_MTOK_CACHE_READ 0.5 not set What a token read from, or written to, the provider’s prompt cache costs. Unset means Anthropic’s 5-minute rates: a tenth and one and a quarter of the input price. For OpenAI set the read price to its cached-input rate and the write price to the input price, since it charges nothing extra to write.
IOS_AGENT_USD_PER_MTOK_CACHE_WRITE 6.25 not set

Provider API keys are NOT read by this project

Section titled “Provider API keys are NOT read by this project”

Each vendor SDK resolves its own, and it does so better than we could: the Anthropic SDK also accepts an auth token or an ant auth login profile. Listed here only so the full set of knobs is in one place.

Setting Value Notes
ANTHROPIC_API_KEY not set
OPENAI_API_KEY not set
GOOGLE_API_KEY not set
GROQ_API_KEY not set

(ollama runs locally and needs no key)

Off by default; uv sync --extra tracing to use it. Every run becomes OpenTelemetry spans, scrubbed by the session’s redactor before export. See docs/adr/0023.

Setting Value Notes
IOS_AGENT_TRACING off off, otel or langsmith. otel exports over OTLP/HTTP to the endpoint below; langsmith exports the same spans to LangSmith with LANGSMITH_API_KEY.
OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:6006 not set Read by the OTLP exporter itself. A local Phoenix listens on port 6006.
OTEL_EXPORTER_OTLP_HEADERS not set
OTEL_SERVICE_NAME ios-agent not set
LANGSMITH_API_KEY not set
LANGSMITH_PROJECT ios-agent not set
LANGSMITH_ENDPOINT https://api.smith.langchain.com not set

Read SAFETY.md before turning any of this off. Doing so is a reasonable choice for a simulator running tests, and is not a reasonable one for a device carrying someone’s real accounts.

Setting Value Notes
IOS_MCP_POLICY__ENABLED true The gate as a whole: approval, app blocklist, redaction, audit.
IOS_MCP_POLICY__CONFIRM_DESTRUCTIVE true Ask before anything that sends, pays, deletes, confirms or signs out.
IOS_MCP_POLICY__CONFIRM_REACHING_A_PERSON true Ask before a like, a follow, a reply: anything that reaches another person.
IOS_MCP_POLICY__MAX_CONSECUTIVE_FAILURES 5 Halt the session after this many failed actions in a row, or when the screen cycles between the same few states within this many steps.
IOS_MCP_POLICY__LOOP_DETECTION_WINDOW 6
IOS_MCP_POLICY__APP_ALLOWLIST ["com.apple.Preferences"] not set Empty means “anything not blocked”. Set it to restrict the agent to one app.
IOS_MCP_SECRET_ICLOUD_PASSWORD not set Secrets are referenced, never inlined. ios_type_secret resolves a reference from the macOS keychain first, falling back to IOS_MCP_SECRET_<REF_UPPER>. The value never reaches a prompt, a tool result, or the audit trail. security add-generic-password -s ios-mcp -a icloud-password -w
Setting Value Notes
IOS_MCP_DEFAULT_DEVICE not set UDID or a name substring. Unset picks the best ready device, preferring a simulator: acting on a real phone should be deliberate.
IOS_MCP_WDA__BASE_URL not set Point at a WebDriverAgent someone else is running (a device farm, a phone on Wi-Fi, a runner started by hand). When set, nothing is launched or torn down.
IOS_MCP_WDA__HOME not set Where WebDriverAgent is built and looked for. Unset: a clone’s vendor/wda when the server runs from one, else ~/Library/Application Support/ios-mcp/wda. ios-mcp prepare-wda simulator builds into the same place.
IOS_MCP_WDA__BUNDLE_ID com.facebook.WebDriverAgentRunner.xctrunner not set Free Apple accounts cannot sign com.facebook.*, so the runner bundle id is configurable for exactly that reason. Left at this default, it is read from the runner ios-mcp prepare-wda device built.
IOS_MCP_WDA__STARTUP_TIMEOUT_S 90 not set A physical device needs a longer startup budget than a simulator.
IOS_MCP_GOIOS__AUTO_START_TUNNEL true not set A cabled iOS 17+ device needs a go-ios tunnel. The userspace one needs no sudo, so it is started on demand; kernel is sudo ios tunnel start.
IOS_MCP_GOIOS__TUNNEL_MODE userspace not set
IOS_MCP_SIMULATOR__SHOW_WINDOW true simctl boot starts the runtime, not the window: without this a simulator runs headlessly and nothing appears on the Mac. Turn it off for CI, where a window is at best pointless.
IOS_MCP_STABILIZE__MAX_WAIT_S 20 not set A device snapshot was measured at about 3.7s in August 2026 against well under a second on a simulator, so max_wait_s must exceed stable_samples snapshots or a real device times out on every action. Settings root read in 0.4 to 0.6s on the same phone in October; the ceiling is kept for the slow case.
IOS_MCP_STABILIZE__SIGNAL tree not set Settle on screenshots instead of a second tree read: wait until no new frame has appeared for QUIET_S, then read the tree once. A screenshot costs 0.13 to 0.17s on a phone against 1.3 to 1.8s for the tree, and this took 17 to 22% off a phone’s settle time. Anything the frames cannot answer falls back to the tree loop. QUIET_S has to outlast a pause inside a transition (Settings search holds still for 0.42s). See ADR 0019.
IOS_MCP_STABILIZE__QUIET_S 0.6 not set
IOS_MCP_STABILIZE__FRAME_TIMEOUT_S 3 not set
IOS_MCP_SCREENSHOT__MAX_EDGE_PX 1568 not set Screenshots leave the session with their long edge capped at this many pixels, because a native capture (2868 on an iPhone 17 Pro Max) is over the 2000 a vision API accepts once a request carries many images. 0 sends the native size.

These decide what an agent loop costs.

Setting Value Notes
IOS_MCP_DIGEST__TOKEN_BUDGET 1500 Most tokens one screen may cost the model. Over it, the least salient elements are dropped and the digest says it was truncated.
IOS_MCP_SNAPSHOT__MAX_DEPTH 50 Depth 20 silently loses a third of a Settings screen; past 30 is free, because the round trip dominates rather than the traversal.
Setting Value Notes
IOS_MCP_SERVER__TRANSPORT stdio stdio for a local MCP client; http serves on this machine only. See docs/threat-model.md before changing the host.
IOS_MCP_SERVER__PORT 8765 not set
IOS_MCP_LOG_LEVEL INFO Logs go to stderr, because stdout belongs to the protocol.