Skip to content

[Bug]: show_build_settings and list_schemes ignore derivedDataPath (BUILD_DIR mismatch, extra package checkout) #547

Description

@ariccio

Disclosure: This issue was generated by Claude Code, operating fully autonomously in the research and drafting of this issue. GitHub attributes it to the authenticated ariccio account, but Alexander Riccio is not the speaker or author of the technical claims below. His role is to provide the quoted prompts, choose the publication scope, and explicitly approve the exact text after preview.

Bug Description

show_build_settings and list_schemes run xcodebuild without the DerivedData location that the build tools use. The build tools, the test tools' build step and the app-path lookups always pass -derivedDataPath (an explicit derivedDataPath, else a per-project folder under the server's own workspace tree). Both discovery tools pass the project or workspace path; show_build_settings also passes the scheme. Neither accepts derivedDataPath or extraArgs, so session defaults for those fields are filtered out. Reproduced on 2.7.1 with this repository's own example projects:

  1. In the calculator example, show_build_settings reports a BUILD_DIR the server's builds do not use. In example_projects/iOS_Calculator, whose config sets derivedDataPath: ./.build/DerivedData, get_sim_app_path resolves the app path under .build/DerivedData/Build/Products/ (no build is needed; it reads build settings with -derivedDataPath), while show_build_settings returns BUILD_DIR under Xcode's own DerivedData for the workspace. An agent that takes product paths from show_build_settings looks in the wrong folder.
  2. With fresh DerivedData, the first call can create another package checkout. In example_projects/spm, whose config enables project-discovery, a first list_schemes checked out the fixture's one remote dependency, swift-argument-parser, into Xcode's DerivedData for the workspace instead of the derivedDataPath set for the run: 9.4 MiB on this fixture. In a private app with many Swift packages on 2.7.0, the same step left 1.9 GB (7,736 files) of SourcePackages in a fresh DerivedData folder.

Both tools are annotated readOnlyHint: true and openWorldHint: false. #341 (9600359d) already gave the app-path lookups the builds' DerivedData "so follow-up commands inspect the same build location"; these two tools were not part of it. Suggested changes for each tool, supported by command-line experiments, are in fold 3: xcodebuild -list rejects -derivedDataPath but honours -clonedSourcePackagesDirPath.

Debug Output

Doctor output (2.7.1, CLI, redacted mode; process tree, local paths and PATH replaced before posting)
⚙️ MobileBuildMCP Doctor

   Generated: 2026-10-05T19:42:18.596Z
   Server Version: 2.7.1
   Output Mode: Redacted (default)

System Information
  platform: darwin
  release: 25.4.0
  arch: arm64
  cpus: 8 x Apple M2
  memory: 24 GB
  hostname: <redacted>
  username: <redacted>
  homedir: (replaced before posting: local path or process detail)
  tmpdir: (replaced before posting: local path or process detail)

Node.js Information
  version: v24.10.0
  execPath: (replaced before posting: local path or process detail)
  pid: 60200
  ppid: 60194
  platform: darwin
  arch: arm64
  cwd: (replaced before posting: local path or process detail)
  argv: (replaced before posting: local path or process detail)

Process Tree
  (replaced before posting: local path or process detail)

Xcode Information
  version: Xcode 26.6 - Build version 17F113
  path: /Applications/Xcode.app/Contents/Developer
  selectedXcode: /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild
  xcrunVersion: xcrun version 72.

Dependencies
  axe: 1.8.0
  mise: Not found

Environment Variables
  INCREMENTAL_BUILDS_ENABLED: (not set)
  DEVELOPER_DIR: (not set)
  HOME: (replaced before posting: local path or process detail)
  USER: <redacted>
  TMPDIR: (replaced before posting: local path or process detail)
  NODE_ENV: (not set)
  SENTRY_DISABLED: (not set)
  AXE_PATH: (not set)
  MOBILEBUILDMCP_LAUNCH_JSON_WAIT_MS: (not set)
  MOBILEBUILDMCP_DEBUGGER_BACKEND: (not set)
  MOBILEBUILDMCP_UI_DEBUGGER_GUARD_MODE: (not set)
  MOBILEBUILDMCP_SENTRY_DISABLED: true
  MOBILEBUILDMCP_RUNTIME: cli
  MOBILEBUILDMCP_SILENCE_LOGS: true

PATH
  (replaced before posting: local path or process detail)

UI Automation (axe)
  Available: Yes
  UI Automation Supported: Yes
  Simulator Video Capture Supported (AXe >= 1.1.0): Yes
  UI-Debugger Guard Mode: error

Incremental Builds
  Enabled: No
  xcodemake Binary Available: No
  Makefile exists (cwd): (not checked: incremental builds disabled)

Mise Integration
  Running under mise: No
  Mise available: No

Debugger Backend (DAP)
  lldb-dap available: Yes
  Selected backend: dap

Manifest Tool Inventory
  Total Unique Tools: 82
  Workflow Count: 15
  coverage: 2 tools
  debugging: 8 tools
  device: 15 tools
  doctor: 1 tools
  macos: 13 tools
  project-discovery: 5 tools
  project-scaffolding: 2 tools
  session-management: 5 tools
  simulator-management: 10 tools
  simulator: 20 tools
  swift-package: 8 tools
  ui-automation: 14 tools
  utilities: 1 tools
  workflow-discovery: 1 tools
  xcode-ide: 5 tools

Runtime Tool Registration
  Enabled Workflows: 0
  Registered Tools: 0
  Note: Runtime registry unavailable.

Xcode IDE Bridge (mcpbridge)
  Workflow enabled: No
  mcpbridge path: /Applications/Xcode.app/Contents/Developer/usr/bin/mcpbridge
  Xcode running: (unknown)
  Connected: No
  Bridge PID: (none)
  Proxied tools: 0
  Last error: (none)
  Note: Bridge debug tools (status/sync/disconnect) are only registered when debug: true

Tool Availability Summary
  Build Tools: Available
  UI Automation Tools: Available
  Incremental Build Support: Not available

Sentry
  Sentry enabled: Yes

Troubleshooting Tips
  If UI automation tools are not available, install axe: brew tap cameroncooke/axe && brew install axe
  If incremental build support is not available, install xcodemake (https://github.com/cameroncooke/xcodemake) and ensure it is executable and available in your PATH
  To enable xcodemake, set environment variable: export INCREMENTAL_BUILDS_ENABLED=1
  For mise integration, follow instructions in the README.md file

✅ Doctor diagnostics complete

The doctor ran with MOBILEBUILDMCP_SENTRY_DISABLED=true. Its "Sentry enabled: Yes" line reads only SENTRY_DISABLED (doctor.ts#L713), while the runtime gate also reads MOBILEBUILDMCP_SENTRY_DISABLED (sentry.ts#L155-L159).

Editor/Client

MobileBuildMCP CLI 2.7.1 for the reproduction below; first seen through Claude Code (MCP over stdio) on 2.7.0.

MCP Server Version

2.7.1 (main is identical to v2.7.1 on 2026-10-05); first seen on 2.7.0.

LLM

No LLM in the 2.7.1 CLI reproduction; the earlier MCP observation was through Claude Code.

MCP Configuration

None beyond each example project's own .mobilebuildmcp/config.yaml, unmodified, plus MOBILEBUILDMCP_SENTRY_DISABLED=true. Step 3 (run B1 in fold 1) also sets MOBILEBUILDMCP_DERIVED_DATA_PATH, because the spm example's config sets no derivedDataPath.

Steps to Reproduce

  1. In example_projects/iOS_Calculator, run mobilebuildmcp simulator get-app-path --platform 'iOS Simulator' --simulator-name '<an installed simulator>' (or node build/cli.js … from a checkout).
  2. In the same folder, run mobilebuildmcp project-discovery show-build-settings --output json and compare its BUILD_DIR with step 1's appPath.
  3. In a fresh copy of example_projects/spm, create the package workspace its config names (workspacePath: .swiftpm/xcode/package.xcworkspace; the example's .gitignore excludes its contents.xcworkspacedata): a contents.xcworkspacedata holding one <FileRef location = "self:">. Do not open the package in Xcode first, since that can resolve its packages. Then, with no DerivedData yet for that workspace, run MOBILEBUILDMCP_DERIVED_DATA_PATH=<lab>/mcp-dd mobilebuildmcp project-discovery list-schemes; the variable supplies the derivedDataPath that the example's config does not set.
  4. Look for SourcePackages/checkouts/swift-argument-parser in Xcode's DerivedData for that workspace, and under <lab>/mcp-dd.

The runs below also gave each copy a per-user Xcode workspace setting (WorkspaceSettings.xcsettings with DerivedDataLocationStyle = WorkspaceRelativePath and DerivedDataCustomLocation = DerivedData), so Xcode's DerivedData stayed inside the copy. That is Xcode configuration, not MobileBuildMCP configuration; without it, Xcode uses its default DerivedData location.

Expected Behavior

BUILD_DIR points under the .build/DerivedData that get_sim_app_path uses. list_schemes and show_build_settings use the builds' package checkout, creating it there if it is missing, instead of a second one elsewhere.

Actual Behavior

  • Step 1: appPath = <copy>/.build/DerivedData/Build/Products/Debug-iphonesimulator/CalculatorApp.app.
  • Step 2: BUILD_DIR = <copy>/DerivedData/CalculatorApp/Build/Products, Xcode's DerivedData location for CalculatorApp.xcworkspace. The default would be ~/Library/Developer/Xcode/DerivedData/CalculatorApp-<hash>/; here a per-user workspace-relative setting kept it inside the copy.
  • Step 3: 4 schemes listed in 7 s, and a new SourcePackages folder (9.4 MiB, 307 files) appeared in Xcode's DerivedData location for the workspace. Nothing appeared under <lab>/mcp-dd, the derivedDataPath set for the run.
  • For step 3's package workspace, xcodebuild prints no build settings, so show_build_settings returns only xcodebuild's preamble, which names the invocation: xcodebuild -showBuildSettings -workspace <copy>/.swiftpm/xcode/package.xcworkspace -scheme spm-Package, followed by Resolve Package Graph.

Error Messages

None; every command above exits 0.

1. Measurements (2.7.1 CLI, Xcode 26.6 (17F113), macOS 26.4)

Each run used a fresh copy of the example project unless it says otherwise. A per-user WorkspaceSettings.xcsettings (DerivedDataLocationStyle = WorkspaceRelativePath) kept Xcode's DerivedData inside each copy, so nothing was written to ~/Library/Developer/Xcode/DerivedData. Sizes in runs A to C are du -k totals of allocated space in this lab. Row D quotes the earlier clone-aware census of a private project, with the scope of each total.

Run Command Result
A1 get-app-path, iOS_Calculator, its config (derivedDataPath: ./.build/DerivedData) appPath under <copy>/.build/DerivedData/Build/Products/
A2 show-build-settings --output json, same copy and config BUILD_DIR = <copy>/DerivedData/CalculatorApp/Build/Products
B1 list-schemes, spm, MOBILEBUILDMCP_DERIVED_DATA_PATH=<lab>/mcp-dd exit 0 in 7 s; <copy>/DerivedData/spm/SourcePackages created, 9,668 KiB, 307 files; <lab>/mcp-dd never created
B2 show-build-settings, same copy its output names the argv: -showBuildSettings -workspace … -scheme spm-Package, no -derivedDataPath
C1 xcodebuild -list -workspace … -derivedDataPath <dd> exit 64: xcodebuild: error: The flag -scheme, -testProductsPath, or -xctestrun is required when specifying -derivedDataPath.
C2 xcodebuild -list -workspace … -clonedSourcePackagesDirPath <dir> exit 0, 4 schemes; the checkout went to <dir> (9,668 KiB); DerivedData got only 28 KiB of logs
C3 xcodebuild -showBuildSettings -workspace … -scheme spm-Package -skipPackageUpdates exit 0; Fetching from https://github.com/apple/swift-argument-parser.git (cached), Checking out 1.5.1: a full checkout was still created
C4 xcodebuild -showBuildSettings -workspace … -scheme spm-Package -derivedDataPath <dd>, run twice first run: checkout in <dd>/SourcePackages; the shared SwiftPM cache's FETCH_HEAD timestamp advanced during the run and named the GitHub remote (network transfer was not directly observed); second run: 2 s, no fetch or checkout messages logged, +4 KiB
C5 xcodebuild -list -workspace … -clonedSourcePackagesDirPath <dd>/SourcePackages, after C4 2 s, no fetch or checkout messages logged, no growth
D 2.7.0 through the MCP server, 2026-10-02, a private app with many Swift packages list_schemes: 11 s, 1,909,944,320 bytes (7,736 files) of SourcePackages in the workspace's DerivedData; show_build_settings returned a BUILD_DIR there, while the builds used -derivedDataPath <checkout>/<App-dir>/DerivedData. The same -showBuildSettings argv run by hand in a checkout with no per-user DerivedData setting created a new ~/Library/Developer/Xcode/DerivedData/<App>-<hash>/ folder of 1,910,665,216 bytes in total, SourcePackages included (no separate SourcePackages total was taken), even with -disableAutomaticPackageResolution.

If the project is also open in Xcode.app, Xcode's DerivedData for the workspace is Xcode.app's own folder, so the extra checkout is shared with Xcode.app (the sharing #525 asks for, there for the build tools). The BUILD_DIR mismatch and the extra checkout relative to the server's builds remain unless the server's builds are pointed at that same folder, as #525's workaround does by rewriting each build and test call's derivedDataPath.

2. Where the argv is built (v2.7.1)
3. Suggested changes, supported by command-line experiments

Runs C1 to C5 were plain xcodebuild commands, not a modified MobileBuildMCP.

  1. show_build_settings: resolve resolveEffectiveDerivedDataPath({ derivedDataPath, workspacePath, projectPath }) and push -derivedDataPath, as app-path-resolver.ts does. Run C4 (xcodebuild -showBuildSettings … -derivedDataPath <dd>, run twice): the checkout lands in <dd>/SourcePackages, and the second query reuses it. Reuse after an actual build is expected, since the build uses the same path, but was not measured here.
  2. list_schemes: -list rejects -derivedDataPath without a scheme (run C1), but honours -clonedSourcePackagesDirPath (run C2). Passing -clonedSourcePackagesDirPath <effective DerivedData>/SourcePackages reused the checkout that C4's query had made (run C5); reuse of a build's checkout is likewise expected, not measured.
  3. Required for 1 and 2 to honour configuration: add an optional derivedDataPath field to each tool's internal schema and forward it to the resolver. Session defaults are filtered to the internal schema's keys (typed-tool-factory.ts#L118-L136, #L251), and resolveEffectiveDerivedDataPath uses only the value passed to it (derived-data-path.ts#L33-L47). Without the field, 1 and 2 would fall back to the per-project default and still miss a configured path such as the calculator example's ./.build/DerivedData. Per-call overrides are a separate, public-schema choice: the build tools leave derivedDataPath out of their session-aware public schema and include it in the legacy schema used when disableSessionDefaults is set (build_sim.ts#L190-L200, #L297-L300, typed-tool-factory.ts#L154-L159).
4. Annotations: a question rather than a claim
  • show_build_settings.yaml#L12-L16 and list_schemes.yaml#L10-L14 declare readOnlyHint: true, destructiveHint: false, openWorldHint: false.
  • The MCP specification (2026-07-28 schema, unchanged from 2025-06-18 here) defines readOnlyHint as "If true, the tool does not modify its environment", destructiveHint: false as "the tool performs only additive updates", and openWorldHint as "If true, this tool may interact with an "open world" of external entities."
  • A first list_schemes call wrote a package checkout (B1). Separately, a plain xcodebuild query (C4) logged a cached fetch of the package URL and advanced the shared cache's FETCH_HEAD; remote network exchange was not observed. The write matches readOnlyHint: false and destructiveHint: false; whether openWorldHint should also be true is a question about what a first call does with an uncached dependency, which may reach the network (not observed here). After the changes in fold 3, a first call on a checkout that has never been built would still resolve packages and write a checkout, only into the builds' folder. Whether the hints should change then is your call. fix(mcp): make tool approval annotations explicit #297 notes that "Codex now treats missing MCP approval hints as risky defaults", so clients do act on them.
5. Why this looks like an oversight rather than a design choice
  • feat(session): auto-scope DerivedData per workspace/project path #341 ("auto-scope DerivedData per workspace/project path"; 108 changed files, and its commit 9600359d, "fix(build): Scope DerivedData by invocation context", touches 109) propagated derivedDataPath "through app-path lookups and generated next steps so follow-up commands inspect the same build location". It changed app-path-resolver.ts, build-utils.ts and derived-data-path.ts; neither discovery tool appears in its file list or in any of its eight commits.
  • The session-default field is described as the "Default DerivedData path for Xcode build/test/clean tools." (session-defaults-schema.ts#L54-L56); no comment, changelog entry or doc page says the discovery tools should skip it.
  • None of the 21 commits in git log --follow of show_build_settings.ts mentions DerivedData or package resolution.
6. Related issues (no obvious duplicate found in a scan of 210 issue titles on 2026-10-05)

#525 (build tools reusing Xcode.app's DerivedData), #526 (orphaned workspaces never swept), #524 (test-product retention), #340 (closed by #341), #173 (derivedDataPath as a session default).

7. Prompt and publication control

The account owner's instruction for this draft, verbatim: "Draft it and hold it for my review." Publication requires the account owner's explicit approval of the exact final text after preview. The 2.7.0 measurements come from a private project; its paths and names are replaced with placeholders. The 2.7.1 runs used copies of this repository's example projects.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions