Skip to content

Xcode IDE and Xcode MCP support + Insane refactor of tool/workflow management, discovery and run-time visibility#202

Merged
cameroncooke merged 24 commits intomainfrom
xcode-tools-support
Feb 6, 2026
Merged

Xcode IDE and Xcode MCP support + Insane refactor of tool/workflow management, discovery and run-time visibility#202
cameroncooke merged 24 commits intomainfrom
xcode-tools-support

Conversation

@cameroncooke
Copy link
Owner

@cameroncooke cameroncooke commented Feb 5, 2026

Note

Medium Risk
Touches core tool discovery/visibility and CLI/daemon routing semantics, which can affect which tools are registered and how stateful operations execute; however, much of the diff is documentation/config updates and removal of obsolete build-time generators.

Overview
Adds an optional xcode-ide workflow that proxies Xcode 26+ IDE MCP tools via xcrun mcpbridge, with new dynamic xcode_tools_* tool registration and a dedicated doc (docs/XCODE_IDE_MCPBRIDGE.md).

Refactors tool/workflow discovery and visibility toward YAML manifests (new docs/dev/MANIFEST_FORMAT.md) and updates generated tool docs to reflect new workflows/tools (tool counts, re-org of groups, and new sync_xcode_defaults).

Simplifies CLI execution/routing: updates docs/config to describe stateless tools running directly while stateful tools auto-route through a per-workspace daemon with a 10-minute idle shutdown (configurable via XCODEBUILDMCP_DAEMON_IDLE_TIMEOUT_MS), and removes the old build-time plugin-discovery generator code. Also standardizes dev/test commands and VS Code configs to launch MCP via build/cli.js mcp and updates rp-cli CLI command templates to require explicit -w <window_id> routing + longer timeouts.

Written by Cursor Bugbot for commit 13807c9. This will update automatically on new commits. Configure here.

cameroncooke and others added 14 commits February 4, 2026 17:06
…of truth

- Add YAML manifest system for tools and workflows in manifests/ directory
- Implement manifest loader with schema validation (Zod)
- Add visibility system with predicates for runtime filtering
- Remove legacy codegen (generate-tools-manifest.ts, generate-loaders.ts)
- Remove legacy index.ts workflow exports and re-export tool files
- Add PredicateContext for runtime-aware tool filtering
- Support hideWhenXcodeAgentMode predicate for Xcode IDE conflicts
- Fix CLI next steps to use canonical names from manifests
- Default MCP server to info log level
- Remove fallback in next-steps-renderer (require cliTool from enrichment)
- Update entry points from index.js to cli.js in docs and configs
- Add annotationsSchema with title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint
- Add annotations field to toolManifestEntrySchema
- Update tool-catalog to prefer manifest annotations with module fallback
Migrate all tools from default export objects to named schema handler exports. Add annotations metadata to YAML manifests as single source of truth for tool metadata. Introduce predicates (runningUnderXcodeAgent, requiresXcodeTools) for conditional tool visibility. Remove mandatory workflow selection concept and simplify workflow resolution logic.
Add simulator-resolver.ts utility that resolves simulator names to UDIDs via simctl. Refactor session_set_defaults to auto-resolve simulatorName to simulatorId when only a name is provided. Update bootstrap-runtime to auto-resolve simulator names from config defaults at startup. Expand exclusivePairs handling across tools that accept both simulatorId and simulatorName.
Sync session defaults (scheme, simulator, bundle ID) from Xcode's
UserInterfaceState.xcuserstate file, both at startup and via FSEvents
file watching for real-time updates.

- Parse xcuserstate binary plists via bplist-parser to extract active
  scheme and run destination from NSKeyedArchiver object graph
- Watch xcuserstate for changes using chokidar with debouncing and
  change detection to filter out UI-only writes
- Resolve simulator name and bundle ID asynchronously (non-blocking)
  to avoid delaying session default updates
- Add disableXcodeAutoSync config option: when true, disables file
  watcher and exposes manual sync_xcode_defaults tool instead
- Add xcode-ide-state MCP resource for reading current IDE selection
- Fix mutually exclusive session defaults (simulatorId preferred over
  simulatorName when both come from session defaults)
- Use project/workspace path from config for monorepo disambiguation
Build CLI workflow groups from manifest-exposed CLI workflows instead of all manifest workflows. This removes empty workflow placeholders like xcode-ide when no CLI commands are available.

Clarify xcode-ide help output to state bridge tools are MCP-only and point users to MCP server usage plus Xcode MCP Tools prerequisites.

Co-Authored-By: Claude <noreply@anthropic.com>
Route xcode-ide status/sync/disconnect through a shared tool handler facade that uses the manager when a server instance exists and a standalone bridge client otherwise.

This removes repeated branching logic across handlers and keeps CLI/debug bridge commands usable without requiring server bootstrap state.

Co-Authored-By: Claude <noreply@anthropic.com>
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 5, 2026

Open in StackBlitz

npm i https://pkg.pr.new/cameroncooke/XcodeBuildMCP/xcodebuildmcp@202

commit: 13807c9

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

cameroncooke and others added 3 commits February 6, 2026 11:46
Remove legacy daemon routing knobs and CLI daemon flags so routing is

driven by tool statefulness plus the explicit xcode-ide special case.

Add idle shutdown with an activity lease registry so long-running tools

signal lifecycle activity without hardcoded daemon imports.

Clean manifest files by removing values that match schema defaults and

regenerate generated tool documentation.

Co-Authored-By: Claude <noreply@anthropic.com>
Limit parent-directory fallback discovery for xcuserstate to a configured
search root instead of traversing to filesystem root.

Pass the resolved workspace root from server bootstrap so Xcode IDE sync
and watcher startup only search within the active workspace context.

Add regression coverage for nested cwd discovery and boundary enforcement.

Co-Authored-By: Claude <noreply@anthropic.com>
…atching

- Deduplicate tools in buildToolCatalogFromManifest when the same tool
  appears in multiple workflows, preventing ambiguous resolution via
  kebab-case MCP name lookup and non-deterministic Map overwrites
- Fix screenshot window title matching to use the actual Simulator title
  separator (en-dash/hyphen) instead of bare space, preventing
  false-positives like "iPhone 15" matching "iPhone 15 Pro"
- Remove dead tool-visibility.ts superseded by the predicate system
…pping

- Add explicit cli: names to 6 swift-package manifests
- Remove getBaseToolName() from output.ts and register-tool-commands.ts
- Remove global CLI name uniqueness check from loadManifest
Add cli: names to all 70 tool manifests using human-friendly names
scoped by workflow namespace. Simple names (build, test, list) where
unique within a workflow; platform-qualified names
(start-device-log-capture, get-macos-bundle-id) only where tools
coexist in the same workflow and would otherwise clash.

Move tool catalog deduplication from the builder to the index maps
in createToolCatalog so shared tools still appear in every workflow
they belong to (for CLI grouping) while preventing ambiguous
resolution in the lookup indices.
…t catalog lookup

CLI tool subcommands like `simulator list` were resolving to the wrong
handler because multiple workflows share the same CLI name (e.g. "list",
"build", "test"). The flat byCliName map meant last-inserted won.

Instead of re-resolving by name, pass the ToolDefinition directly from
the workflow-scoped yargs handler via a new invokeDirect() method. For
daemon wire communication, use mcpName (unique within the catalog) instead
of the ambiguous cliName.

Also skip the bridge availability check (xcrun --find mcpbridge) in CLI
mode since xcode-ide has availability.cli: false, avoiding an unwanted
Xcode authorization prompt.
The docs incorrectly stated the predicate depends on both
runningUnderXcode AND xcodeToolsActive. In reality it only checks
runningUnderXcode — Xcode's coding agent always provides native
equivalents, independent of the Xcode Tools bridge (which is for
external agents connecting to Xcode's MCP server).
@cameroncooke cameroncooke merged commit f0bd066 into main Feb 6, 2026
8 checks passed
@cameroncooke cameroncooke deleted the xcode-tools-support branch February 6, 2026 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant