A CLI for driving Xcode builds, tests, and launches through the Xcode MCP bridge. It solves two problems when working with AI coding agents:
- Persistent daemon — a shared process holds the MCP session, so Xcode's permission prompt fires once instead of once per agent.
- Build/test queue — serializes Xcode work through a ticketed queue so multiple parallel agents share a single Xcode session without stomping on each other, optimizing compile time.
build,test, andlaunchcommands routed through an Xcode MCP server.- Local daemon with serialized queue, async tickets,
--request-keyreuse, status/wait/cancel, and protocol restart handling. - Persistent MCP session management over stdio or streamable HTTP server configs.
- Raw MCP discovery via
toolsandcall. - Compact rendering for Xcode build and test structured results, with warning and issue controls.
- Configuration via
xcodemcpcli.config.jsonandXCODEMCPCLI_*environment variables.
- Node.js 20 or newer.
- Xcode with the MCP bridge available, usually through
xcrun mcpbridge. - An Xcode project or workspace and scheme configured by env, config file, or flags.
npm install
npm run build
npm linkDuring development you can run commands without linking:
npm run dev -- config
npm run dev -- daemon statusFrom your Xcode project root, run:
xcodemcpcli init --scheme MyApp
xcodemcpcli doctor
xcodemcpcli buildinit writes xcodemcpcli.config.json in the current directory. If the directory contains exactly one .xcworkspace, it uses that; otherwise, if it contains exactly one .xcodeproj, it uses that. If your project has multiple containers, pass the path explicitly:
xcodemcpcli init --scheme MyApp --workspace-path ./MyApp.xcworkspace
# or
xcodemcpcli init --scheme MyApp --project-path ./MyApp.xcodeprojUseful setup flags:
--output <path>writes the config somewhere else. UseXCODEMCPCLI_CONFIG=/path/to/config.jsonwhen running commands from another directory.--configuration <name>sets the Xcode configuration; default isDebug.--derived-data-base-path <path>sets a project-specific DerivedData root.--daemon-state-dir <path>keeps the local daemon socket/pid files with your project.--mcp-url <url>configures a streamable HTTP MCP server instead of the default stdioxcrun mcpbridge.--mcp-command <cmd>and repeated--mcp-arg <arg>customize the stdio MCP command.--forceoverwrites an existing config file.
The generated config is plain JSON; review it before committing it if it contains machine-specific absolute paths.
xcodemcpcli loads built-in generic defaults, then xcodemcpcli.config.json from the current directory (or XCODEMCPCLI_CONFIG), then XCODEMCPCLI_* environment variables. There is no default scheme; pass --scheme, set XCODEMCPCLI_SCHEME, or add scheme to xcodemcpcli.config.json.
Minimal environment setup:
export XCODEMCPCLI_PROJECT_PATH="$PWD/MyApp.xcodeproj"
export XCODEMCPCLI_SCHEME="MyApp"
export XCODEMCPCLI_CONFIGURATION="Debug"Or create xcodemcpcli.config.json:
{
"projectPath": "/path/to/MyApp.xcodeproj",
"scheme": "MyApp",
"configuration": "Debug",
"derivedDataBasePath": "/tmp/MyAppDerivedData",
"mcp": {
"transport": "stdio",
"command": "xcrun",
"args": ["mcpbridge"],
"buildTool": "BuildProject",
"testTool": "RunSomeTests"
},
"daemon": {
"stateDir": "/tmp/xcodemcpcli-state"
}
}Common environment variables:
XCODEMCPCLI_CONFIGXCODEMCPCLI_PROJECT_PATHXCODEMCPCLI_WORKSPACE_PATHXCODEMCPCLI_SCHEMEXCODEMCPCLI_CONFIGURATIONXCODEMCPCLI_DERIVED_DATA_BASE_PATHXCODEMCPCLI_CLONED_SOURCE_PACKAGES_DIR_PATHXCODEMCPCLI_MCP_TRANSPORT(stdioorstreamable-http)XCODEMCPCLI_MCP_URL(sets streamable HTTP transport when provided)XCODEMCPCLI_MCP_COMMAND(defaultxcrun)XCODEMCPCLI_MCP_ARGS(JSON string array or whitespace-separated string; defaultmcpbridge)XCODEMCPCLI_MCP_BUILD_TOOLXCODEMCPCLI_MCP_TEST_TOOL(defaultRunSomeTests)XCODEMCPCLI_MCP_TAB_IDENTIFIERXCODEMCPCLI_MCP_CONNECT_TIMEOUT_MSXCODEMCPCLI_MCP_TOOL_TIMEOUT_MSXCODEMCPCLI_MCP_TEST_TOOL_TIMEOUT_MSXCODEMCPCLI_DAEMON_STATE_DIRXCODEMCPCLI_DAEMON_STARTUP_TIMEOUT_MSXCODEMCPCLI_DAEMON_POLL_INTERVAL_MSXCODEMCPCLI_DAEMON_HEARTBEAT_INTERVAL_MSXCODEMCPCLI_APP_LAUNCH_COMMAND(advanced/testing override; default/usr/bin/open)XCODEMCPCLI_MCP_XCODE_PIDandXCODEMCPCLI_MCP_XCODE_SESSION_ID(forwarded to the MCP bridge asMCP_XCODE_PIDandMCP_XCODE_SESSION_ID)
xcodemcpcli config
xcodemcpcli daemon start
xcodemcpcli doctor
xcodemcpcli tools
xcodemcpcli build
xcodemcpcli launch
xcodemcpcli build --async --request-key my-build
xcodemcpcli job list
xcodemcpcli job status <ticket>
xcodemcpcli job wait <ticket>
xcodemcpcli job cancel <ticket>
xcodemcpcli test AppTests/MyFeatureTests
xcodemcpcli test --only-testing AppTests/MyFeatureTests/testExample
xcodemcpcli tests classes --match MyFeatureTests
xcodemcpcli call XcodeListWindows --input '{}' --yesGlobal flags:
--async: enqueue build/launch/test work and print a job ticket immediately.--request-key <id>: reuse an existing queued/running job for the same logical request; mismatched active reuse fails.--json: print raw JSON output where supported.--refresh: refresh cached MCP tool discovery.--yes: skip confirmation for rawcall.--full-issues: print all build/test issues instead of compact output.--max-issues <n>: cap visible failures/tests in compact output.--max-error-lines <n>: cap visible error lines per failed test.--warnings <none|count|full>: warning display mode.
If your project uses agent instructions, add a short note like this and adjust the config path/request keys for your repo:
Use `xcodemcpcli` for Xcode MCP build/test/launch work. If the config is not in the repo root, set `XCODEMCPCLI_CONFIG=/path/to/xcodemcpcli.config.json`.
Useful commands:
- `xcodemcpcli daemon start` and `xcodemcpcli daemon status` to check the local MCP daemon.
- `xcodemcpcli build --async --request-key project-build`, then `xcodemcpcli job status <ticket>` or `xcodemcpcli job wait <ticket>`.
- `xcodemcpcli launch --async --request-key project-launch`, then wait on the returned ticket.
- `xcodemcpcli tests classes --match SomeTests` to discover tests before running them.
- `xcodemcpcli test --only-testing TargetName/SomeTests/testMethod --async --request-key project-test`, then wait on the returned ticket.
Prefer async build/test/launch when agents may need to poll or coordinate. Use stable `--request-key` values for retryable submissions.All build, launch, test, tools, and call requests go through a Unix socket daemon. The daemon owns the MCP session and serializes Xcode actions so concurrent callers receive distinct tickets rather than racing the same Xcode session.
xcodemcpcli daemon start
xcodemcpcli daemon status
xcodemcpcli job list
xcodemcpcli job wait <ticket>
xcodemcpcli daemon stopbuild, launch, and test without --async still submit a ticket and wait for it, printing reconnect guidance while they poll.
testintentionally requires a selector or--only-testing; full-suite test runs are refused by default.launchruns an MCP build, searches the build result/log/DerivedData for the newest matching.app, terminates older matching DerivedData app processes, and opens the selected bundle locally.- Real daemon integration tests require a fuller MCP SDK fixture. Current tests prioritize TypeScript compile coverage and core unit behavior.