Local MCP server for iOS Simulator and macOS app automation with a native Swift bridge
npm install mcp-baepsae

> Baepsae (Vinous-throated Parrotbill) — A tiny Korean bird. Round, chubby, and constantly hopping around chirping. Known for its grit — even when a little bird tries to keep up with a stork, it never gives up. This project is small too, but it pecks away at your simulators tirelessly.
Local MCP server for iOS Simulator and macOS app automation with a TypeScript MCP layer and a Swift native bridge.
한국어 문서는 README-KR.md를 참고하세요.
- Prerequisites
- Install
- MCP Setup (Recommended)
- Client Matrix
- For LLM
- Manual Setup (Fallback)
- Project Structure
- Commands
- MCP Tool Status
- Troubleshooting
- macOS 14+
- Xcode + iOS Simulator
- Node.js 18+
- Swift 6+
Accessibility permission is required for UI automation features (describe_ui, tap by ID).
1. Open System Settings > Privacy & Security > Accessibility.
2. Enable your terminal (Terminal, iTerm2, VSCode) or command runner (node, openclaw).
3. If the app is missing, click + and add it manually.
``bashRun directly without installing
npx mcp-baepsae@latest
> On macOS, the Swift native binary is built automatically during install. If Swift is not available, the server still works with simctl-based features.
$3
`bash
git clone https://github.com/oozoofrog/mcp-baepsae.git
cd mcp-baepsae
npm install
npm run build
`MCP Setup (Recommended)
Use the installer script that calls each client's MCP install flow directly.
`bash
bash scripts/install.sh --tool all
`Client Matrix
| Client | Install Path | Script Target | Notes |
|---|---|---|---|
| Claude Code |
claude mcp add | --tool claude-code | Supports multi-scope via --claude-scope |
| Claude Desktop | claude mcp add --scope user | --tool claude-desktop | User-level registration |
| Codex CLI | codex mcp add | --tool codex-cli | Also works for Codex Desktop settings |
| Codex Desktop | Codex CLI MCP config | --tool codex-desktop | Uses same MCP registry as CLI |
| OpenCode | Global config (~/.config/opencode/opencode.json) | --tool opencode | Auto-updated by installer |
| Gemini | gemini mcp add | --tool gemini | User scope setup |
| Google Antigravity | Gemini-compatible MCP flow | --tool antigravity | Uses Gemini MCP command path |
| GitHub Copilot | copilot or gh copilot session | --tool copilot | Interactive/session-based setup |For LLM
If you are an LLM agent setting up this MCP server, here is everything you need:
$3
`bash
Register baepsae as an MCP server for all supported clients
bash scripts/install.sh --tool all
`If you installed via npm instead of cloning the repo, use npx:
`bash
Claude Code
claude mcp add baepsae -- npx -y mcp-baepsae@latestCodex CLI
codex mcp add baepsae -- npx -y mcp-baepsae@latest
`$3
`bash
Preview commands without executing
bash scripts/install.sh --tool all --dry-runVerify environment and dependencies
bash scripts/install.sh --tool all --doctorUnregister from all clients
bash scripts/install.sh --tool all --uninstall
`$3
The installer supports multiple runtimes via
--runtime:| Flag | Command | When to use |
|---|---|---|
|
--runtime node (default) | node dist/index.js | Local source build |
| --runtime npx | npx -y mcp-baepsae@latest | npm registry, no global install |
| --runtime bunx | bunx mcp-baepsae@latest | Bun users |
| --runtime global | mcp-baepsae | After npm install -g mcp-baepsae |Manual Setup (Fallback)
Use this when you do not want to run
scripts/install.sh.$3
`bash
Claude Code
claude mcp add baepsae -- npx -y mcp-baepsae@latestCodex CLI
codex mcp add baepsae -- npx -y mcp-baepsae@latestGemini CLI
gemini mcp add --scope user --transport stdio baepsae npx -y mcp-baepsae@latest
`$3
`bash
Claude Code (project)
claude mcp add --scope project --env="BAEPSAE_NATIVE_PATH=/ABS/PATH/native/.build/release/baepsae-native" baepsae -- node /ABS/PATH/dist/index.jsCodex CLI
codex mcp add baepsae --env BAEPSAE_NATIVE_PATH=/ABS/PATH/native/.build/release/baepsae-native -- node /ABS/PATH/dist/index.jsGemini CLI
gemini mcp add --scope user --transport stdio -e BAEPSAE_NATIVE_PATH=/ABS/PATH/native/.build/release/baepsae-native baepsae node /ABS/PATH/dist/index.js
`Project Structure
- MCP server:
src/index.ts
- Native binary project: native/
- Native binary output: native/.build/release/baepsae-native
- Tests: tests/mcp.contract.test.mjs, tests/mcp.real.test.mjsCommands
`bash
npm run build # Build TypeScript + native Swift binary
npm test # Contract/integration tests
npm run test:real # Real simulator smoke test (requires booted simulator)
npm run verify # test + test:real
npm run setup:mcp # Alias for scripts/install.sh
`MCP Tool Status
32 tools implemented end-to-end, organized by platform:
$3
| Tool | Description |
|---|---|
|
list_simulators | List available iOS simulators |
| screenshot | Capture simulator screen |
| record_video | Record simulator screen |
| stream_video | Stream video frames |
| open_url | Open URL in simulator (Safari/Deep Link) |
| install_app | Install .app or .ipa |
| launch_app | Launch app by Bundle ID |
| terminate_app | Terminate running app |
| uninstall_app | Uninstall app |
| button | Hardware buttons (home, lock, side, siri, apple-pay) |
| gesture | Preset gestures (scroll, swipe-edge) |$3
| Tool | Description |
|---|---|
|
list_apps | List running macOS apps |
| scroll | Scroll wheel events |
| menu_action | Execute menu bar actions |
| get_focused_app | Get focused app info |$3
| Tool | Description |
|---|---|
|
describe_ui | Accessibility tree (pagination, filter, subtree, summary) |
| search_ui | Search UI elements by text/ID/label |
| tap | Tap by coordinates or element ID/label (supports double-click) |
| type_text | Type text input |
| swipe | Swipe gesture |
| key | HID keycode input |
| key_sequence | Sequential keycode input |
| key_combo | Modifier + key combination |
| touch | Touch down/up events |
| right_click | Right-click by ID/label or coordinates |
| drag_drop | Drag and drop |
| clipboard | Read/write clipboard |
| list_windows | List app windows |
| activate_app | Bring app to foreground |
| screenshot_app | Capture app window screenshot |$3
| Tool | Description |
|---|---|
|
baepsae_help | Show help info |
| baepsae_version | Show version info |Usage Examples
Open a URL (iOS Simulator):
`javascript
// Open Naver Mobile
open_url({ udid: "...", url: "https://m.naver.com" })
`Manage Apps (iOS Simulator):
`javascript
// Install an app
install_app({ udid: "...", path: "/path/to/App.app" })// Launch Safari
launch_app({ udid: "...", bundleId: "com.apple.mobilesafari" })
// Terminate Safari
terminate_app({ udid: "...", bundleId: "com.apple.mobilesafari" })
`macOS App Automation:
`javascript
// List running macOS apps
list_apps({})// Take screenshot of a macOS app
screenshot_app({ bundleId: "com.apple.Safari" })
`Troubleshooting
-
Invalid environment variable format on Claude setup:
- Use current script (scripts/install.sh) or claude mcp add --env="KEY=value" ... format.
- Missing native binary error:
- Run npm run build and confirm native/.build/release/baepsae-native exists.
- OpenCode does not show baepsae:
- Re-run bash scripts/install.sh --tool opencode --skip-install --skip-build and check ~/.config/opencode/opencode.json.
- Copilot not auto-registered:
- Copilot MCP flow is interactive/session-based. Re-run installer with --interactive.
- Real smoke test skipped:
- Boot an iOS simulator first, then run npm run test:real`.