Architecture

Adapter model

mcp-sim follows an adapter pattern with two interface layers:

MCP client → mcp-sim service → Platform adapters (iOS, Android)
                                → Controller adapters (agent-device)

Platform adapters (platforms/)

Implement contract.Platform to expose emulator/simulator lifecycle:

  • List, Start, Stop, State, AwaitReady, Wipe, OpenURL

Built-in: platforms/ios, platforms/android

Controller adapters (controllers/)

Implement contract.Controller to expose verification-layer proxies:

  • Start, Stop, Status

Built-in: controllers/agentdevice

Separation of concerns

mcp-sim owns infrastructure lifecycle only — power, state, data wipe, deep-link launch.

It does NOT ship UI automation tools (tap, screenshot, view hierarchy). That role belongs to Controllers.

This split is load-bearing: platform adapters must not contain tap(), screenshot(), or getTree() methods. See CONTRIBUTING.md.

State machine

A device transitions through states:

Stopped → Booting → Running → Stopped
                ↘ (error) → Error → Stopped

Adapter availability & resilience

Each platform adapter is lazy in registration: at server startup, the
adapter constructor probes for its underlying tooling (xcode-select for iOS,
emulator/adb on PATH for Android, etc.). If the tool isn’t reachable, the
adapter returns nil and the registry simply doesn’t include it. The server
boots normally with whatever subset of platforms was actually detected.

This means the same binary can serve a Swift-only developer (no Android SDK),
an Android-only developer (no Xcode), or a full-stack workflow. To disable a
specific platform explicitly:

MCPSIM_IOS_ENABLED=false
MCPSIM_ANDROID_ENABLED=false
MCPSIM_AGENT_DEVICE_ENABLED=false

When an unavailable platform is requested, the call returns a stable MCP
error code (e.g. unsupported_platform).

Extension guide

See adding-platform.md for implementing a new platform adapter.