Xcode's MCP server, shipped in Xcode 26.3 and still there in Xcode 27, is a fine default. If you're happy with it, keep it. If you want to trim context, send the questions an agent asks constantly (a build setting, a named test run, a symbol search) to xcodebuild, xcrun simctl and grep, and keep MCP for what it does better: filtered string catalogs, directory listings, compiler flags, crash logs, device automation, and anything with no CLI equivalent. See Apple's Xcode 27 release notes for what's changed.
What I measured
Thirteen query pairs, CLI vs MCP, run against four apps: Rain Check (a small app of mine), WordPress-iOS, Signal-iOS and Firefox for iOS. All four are live clones, and every number is from an actual run, nothing extrapolated. Every CLI number is filtered: -quiet, piped, grepped. Tokens were counted with tiktoken1. A dash means I couldn't measure it, not zero2.
| Query | App | CLI | MCP | Note |
|---|---|---|---|---|
| One build setting's value | Rain Check | 8 | 11,946 | CLI wins everywhere; MCP has no per-key filter anywhere. Firefox: 450 settings, 11,995 tokens unfiltered vs. 13 grepped, ~923×. Narrower than Rain Check's ~1,493×, but still three to four orders of magnitude on every app |
| WordPress-iOS | 14 | 12,558 | ||
| Signal-iOS | 12 | 12,064 | ||
| Firefox for iOS | 13 | 11,995 | ||
| Find a symbol in one file | Rain Check | 24 | 67 | CLI wins everywhere it runs |
| WordPress-iOS | 14 | 59 | ||
| Signal-iOS | 9 | 51 | ||
| Firefox for iOS | 20 | 49 | ||
| Read a 21-line file range | Rain Check | 217 | 402 | CLI wins everywhere it runs |
| WordPress-iOS | 142 | 312 | ||
| Signal-iOS | 104 | 277 | ||
| Firefox for iOS | 149 | 316 | ||
| Full clean build, success only | Rain Check | 5 | 164 | CLI wins on the three that build. Firefox's CLI build succeeded; MCP's BuildProject failed on package resolution (see Method). A capability gap, not a cost gap |
| WordPress-iOS | 5 | 77 | ||
| Signal-iOS | 5 | 77 | ||
| Firefox for iOS | 5 | — | ||
| List schemes | Rain Check | 237 | 154 | Flips per app depending on what "list schemes" actually costs each side: Signal-iOS's CocoaPods has no Swift package graph to resolve, so CLI wins there; Firefox's 34 schemes (Client project plus BrowserKit) cost CLI less than MCP's more heavily annotated response |
| WordPress-iOS | 1,294 | 427 | ||
| Signal-iOS | 147 | 283 | ||
| Firefox for iOS | 806 | 1,103 | ||
| Run one named test | Rain Check | 93 | 7,259 | CLI numbers assume you already know the exact identifier, which flatters CLI: on WordPress-iOS, -only-testing: took two failed attempts by hand (wrong scope, wrong test plan) before a clean 7-token pass line; MCP's GetTestList gave the exact targetName/identifier pair and RunSomeTests worked first try, at the cost of that separate discovery call (see the day-long tally below the table). Firefox's CLI number is big, but that's what it measured: its first test-target build let a full pass of Swift 6 concurrency warnings through -quiet before the pass/fail line. MCP couldn't attempt it, same package-resolution failure as the build row |
| WordPress-iOS | 7 | 338 | ||
| Signal-iOS | 106 | 339 | ||
| Firefox for iOS | 19,006 | — | ||
| List targets (name + type) | Rain Check | 78 | 310 | CLI wins everywhere it runs; Signal-iOS's 8 targets cost MCP nearly as much as WordPress-iOS's 16, and Firefox's 23 targets cost more still |
| WordPress-iOS | 92 | 952 | ||
| Signal-iOS | 37 | 622 | ||
| Firefox for iOS | 108 | 1,358 | ||
Find *ViewController.swift files | Rain Check | 48 | 85 | Flips on two of three bigger apps: WordPress-iOS (154 matches) and Signal-iOS (257, using its own *Controller.swift convention) favor MCP's truncated, metadata-light list over CLI's full paths. Firefox flips back: 192 matches, but its longer nested paths (BrowserKit sub-packages) make CLI's raw list cheaper than MCP's JSON wrapper plus package-dependency metadata |
| WordPress-iOS | 2,287 | 2,071 | ||
| Signal-iOS | 3,940 | 1,756 | ||
| Firefox for iOS | 1,362 | 1,849 | ||
ls a directory (comparable file count) | Rain Check | 1,389 | 227 | Not a fair fight anywhere: ls -la dumps metadata nobody asked for. Dirs picked for ~42-54 files each: WordPress-iOS 44, Signal-iOS's SignalServiceKit/Contacts 47, Firefox's Client/Frontend/Browser 54 |
| WordPress-iOS | 1,505 | 253 | ||
| Signal-iOS | 1,598 | 283 | ||
| Firefox for iOS | 1,815 | 1,027 | ||
| One file's compiler flags | Rain Check | 415 | 86 | There's no dedicated CLI command for this on any app. The workaround is grepping OTHER_SWIFT_FLAGS out of the same unfiltered build-settings dump row 1 already paid for. Cheap when that dump is small (Signal-iOS), expensive when it isn't (WordPress-iOS's dump has deep DerivedData module-map paths baked in). Firefox's dash here is my miss, not an impossibility: its build succeeded, so the dump existed, I just didn't capture it before cleaning up the clone. Its file had no per-file flags set at all, and even that empty MCP answer still cost 83 tokens of wrapping JSON |
| WordPress-iOS | 265 | 88 | ||
| Signal-iOS | 54 | 88 | ||
| Firefox for iOS | — | 83 | ||
Translation state of a .xcstrings catalog | Rain Check | 475,528 | 359 | Neither Signal-iOS nor Firefox has a single .xcstrings file — both still use older localization formats (95 .strings files on Signal-iOS alone; Firefox uses .strings/.xliff). That's how those projects are set up, not a hole in my search |
| WordPress-iOS | 8,375 | 57 | ||
| Signal-iOS | — | — | ||
| Firefox for iOS | — | — | ||
| List run destinations | Rain Check | 125–452 | 651 | Flips per app. The CLI cost tracks how many devices and simulators were registered on the machine when I measured, which was fewer for Signal-iOS than for WordPress-iOS. On Firefox it flips the other way: MCP's grouped, flagged listing beats CLI's raw destination dump |
| WordPress-iOS | 1,461 | 726 | ||
| Signal-iOS | 465 | 875 | ||
| Firefox for iOS | 982 | 813 | ||
| Build failure (one compile error) | Rain Check | 140 | 204–434 | Nearly a tie on WordPress-iOS: its error surfaced in 13 separate filtered error: lines (one syntax mistake cascades into repeated diagnostics across a large shared module), closing most of the usual CLI advantage. Signal-iOS's throwaway error was a single clean type mismatch. Firefox's MCP side never reached the injected error, same package-resolution failure as its build row |
| WordPress-iOS | 626 | 648 | ||
| Signal-iOS | 36 | 141 | ||
| Firefox for iOS | 43 | — |
Over a day, the two unfiltered calls are what add up. I modeled 12 fix/verify cycles shaped like my Rain Check work: defaulting to MCP cost 181,750 tokens, CLI-first cost 16,429, about 11×, a gap of 165,321 tokens a day. On WordPress-iOS, which is over twice the size, live calls gave 10.4× at 30 cycles a day, so the gap doesn't grow much with the codebase. The cost that matters is context, not dollars: one build-setting call alone is 6% of a 200k window.
Where MCP is the better pick
- A real filter.
StringCatalogRead,GetBuildLogby severity,GetFileCompilerFlags. The CLI has to dump and grep; MCP answers the question. - Structured data, or no CLI equivalent. Crash logs, device automation, live preview rendering, test discovery.
GetTestListalso gave me the exact test identifier first try on WordPress-iOS where-only-testing:took two failed guesses by hand. - Fragile multi-step CLI workflows. Quoting and escaping Swift source by hand is where a plain shell gets error-prone.
- Long sessions. The tool schemas cost ~2,400 tokens once. In a persistent session that amortizes to nothing.
Where the CLI is the better pick
- One build setting.
xcodebuild -showBuildSettings | grep KEY. - A named test run when you already know the identifier:
-only-testing:Target/Class,-quiet, read the pass line. - Symbol searches and file ranges.
grep -nandsed -nare a few tokens each. - Build success or failure.
-quietplusgrep error:is 5 tokens for a clean build.
One caveat if you're on a large SPM-heavy app: on Firefox for iOS, every read-only MCP call worked, but BuildProject and RunSomeTests failed on package resolution where plain xcodebuild in the same clone built fine. Worth checking on your own project before picking a default.
Mixing them
My CLAUDE.md used to say "prefer the Xcode MCP tools for anything they cover." It now says: CLI and grep for build settings, named test runs and symbol searches; MCP for anything with a filter, structured output, or no CLI equivalent; quiet output by default; batch edits before building. If you'd rather stay MCP-only, the one habit worth changing is not calling GetTestList before every run. Write the identifier by hand, or grep the file it writes to disk.
Most public comparisons3 measure MCP against unfiltered xcodebuild output, where MCP wins easily. Against filtered CLI it's closer, and it depends on the question. Either one is a reasonable default. Pick whichever fits how you work and borrow the other where it's cheaper.
Your mix of calls will differ from mine, so don't take these numbers as yours. The full dataset (all 13 queries across all four apps) is the table above. I'll publish the scripts too, so you can point them at your own repo.
- tiktoken's
cl100k_baseencoding, not Anthropic's real tokenizer, so treat the ratios as the result and the absolute numbers as approximate. ↩ - Firefox's MCP build path fails on its own package graph, and two of the four apps predate the
.xcstringsformat. ↩ - Sentry, XcodeBuildMCP, this walkthrough. ↩