expo-mcp
MCP server for Expo/React Native app automation with Maestro integration.
Features
- Session-Based Architecture:
start_session launches Expo, binds a device, and acquires a lease — no manual device ID management
- Device Lease with TTL: 2-minute lease auto-renewed on every device tool call; expires after inactivity so other instances can use the device
- Cross-Instance Coordination: Multiple MCP instances can run simultaneously without device conflicts
- Expo Dev Server Management: Start/stop/reload Expo development server
- Maestro Integration: Full UI automation tools (tap, input, screenshot, etc.)
Installation
As a Claude Code Plugin (Recommended)
Two commands, then restart:
# 1. Install the plugin. Just dismiss the "Expo App Directory" prompt
# (or leave it empty) — the next step configures it for you.
/plugin marketplace add DaveDev42/expo-mcp
/plugin install expo-mcp --scope project
# 2. One-shot installer. Runs environment checks, auto-detects the Expo
# app directory, and writes the userConfig directly into
# .claude/settings.json. No /plugin UI round-trip needed.
/expo-mcp:install # auto-detect
/expo-mcp:install apps/mobile # monorepo: pass the path explicitly
Then restart Claude Code and all tools, agents, and skills are ready.
Installer flags:
/expo-mcp:install apps/mobile --global # write to ~/.claude/settings.json
/expo-mcp:install --scaffold-maestro # also create a starter maestro/
/expo-mcp:install --skip-doctor # skip prerequisite checks
The installer runs bundled Node scripts (doctor.mjs, detect-app-dir.mjs, scaffold-maestro.mjs) from the plugin directory. Claude Code will prompt you to approve each one the first time it runs — approve them to continue.
If you'd rather pre-approve the scripts (no prompts), add this to .claude/settings.local.json in your project — replacing <PATH> with the absolute path shown by Claude Code the first time each script runs:
{
"permissions": {
"allow": [
"Bash(node <PATH>/doctor.mjs:*)",
"Bash(node <PATH>/detect-app-dir.mjs:*)",
"Bash(node <PATH>/scaffold-maestro.mjs:*)"
]
}
}
Installing the plugin automatically wires up:
- The
expo MCP server (no manual .mcp.json needed)
- A QA agent (
qa) for automated mobile app testing
- A flow writer agent (
flow-writer) for creating Maestro YAML test flows
- A usage guide skill (
/expo-guide) with tool reference and best practices
- A validation hook that warns on QA PASS verdicts without execution evidence
As an MCP Server Only
This project is distributed only through GitHub (the expo-mcp name on npm belongs to a different, unrelated package — do not use it). Run it via the GitHub reference:
npx -y github:DaveDev42/expo-mcp
Usage with Claude Code
Manual MCP Setup
If not using the plugin, add to your .mcp.json:
{
"mcpServers": {
"expo": {
"command": "npx",
"args": ["-y", "github:DaveDev42/expo-mcp"]
}
}
}
Monorepo Setup
Use a positional argument to specify the app directory:
{
"mcpServers": {
"expo": {
"command": "npx",
"args": ["-y", "github:DaveDev42/expo-mcp", "apps/mobile"]
}
}
}
Specific Device
Pin a specific simulator or emulator with --device-id:
{
"mcpServers": {
"expo": {
"command": "npx",
"args": ["-y", "github:DaveDev42/expo-mcp", "--device-id=6D192F60-1234-5678-ABCD-000000000000"]
}
}
}
Tool Filtering
Exclude specific tools with --exclude-tools:
{
"mcpServers": {
"expo": {
"command": "npx",
"args": ["-y", "github:DaveDev42/expo-mcp", "apps/mobile", "--exclude-tools=list_devices"]
}
}
}
Or expose only specific tools with --tools:
{
"mcpServers": {
"expo": {
"command": "npx",
"args": ["-y", "github:DaveDev42/expo-mcp", "--tools=start_session,stop_session,take_screenshot"]
}
}
}
CLI Reference
Usage: expo-mcp [app-dir] [options]
Arguments:
app-dir Path to Expo app directory (default: cwd)
Options:
--device-id=<id> Specific device to use (iOS simulator UUID or Android serial)
--exclude-tools=tool1,tool2 Exclude specific tools from the MCP server
--tools=tool1,tool2 Only expose specific tools
-h, --help Show help message
-v, --version Show version number
Quick Start
# 1. Start session (launches Expo + binds device + acquires lease)
start_session({ target: "ios-simulator" })
# 2. Use tools directly (no device_id needed!)
take_screenshot()
tap_on({ text: "Login" })
input_text({ text: "hello@example.com" })
press_key({ key: "Enter" })
scroll({ direction: "down" })
swipe({ direction: "left" })