Xcode MCP 本地网关服务

XCodeMCPService 是一个本地 MCP 网关服务,将 xcrun mcpbridge 及其他 stdio MCP 服务器聚合成单个 Streamable HTTP 端点,适用于需要 HTTP 传输、多服务器聚合、集中进程管理或兼容旧版 Xcode 的场景。

  • by ljh740
  • Swift
  • 本地部署
  • 更新于
在 GitHub 查看 ljh740/XCodeMCPService

使用说明

XCodeMCPService

中文文档

Local MCP Gateway — Aggregate xcrun mcpbridge and other stdio MCP servers behind one stable Streamable HTTP endpoint.

Xcode 27+ provides native headless MCP and persistent authorization. If you only need one Xcode connection and your client supports stdio, prefer running xcrun mcpbridge directly and do not install this project. Use this project when you need HTTP transport, multi-server aggregation, centralized process management, or compatibility with older Xcode versions.

MCP Client ──HTTP/POST──▶ XCodeMCPService ──stdio──▶ MCP Server (xcrun mcpbridge)
           ◀──JSON-RPC───                  ◀──pipe──

Do You Need This Project?

Scenario Recommended approach
Xcode 27+, one local agent, client supports stdio Configure xcrun mcpbridge directly
HTTP-only client, or multiple clients need a stable localhost endpoint Use XCodeMCPService
Multiple MCP servers need aggregation, timeouts, restart, and shared logging Use XCodeMCPService
Xcode 26.3+ needs to bind to a running Xcode instance Use XCodeMCPService compatibility mode

Direct Xcode 27+ configuration example:

{
  "mcpServers": {
    "xcode-tools": {
      "command": "xcrun",
      "args": ["mcpbridge"]
    }
  }
}

Enable headless MCP once for the Xcode selected by xcode-select:

sudo xcrun mcp-server enable
xcrun mcp-server status

This project never runs mcp-server enable or enables --unsafe-always-allow-all-agents automatically. Apple recommends unsafe mode only for isolated unattended environments; keep per-agent authorization enabled on a daily workstation.

Features

  • Multi-server aggregation — Manage multiple MCP subprocesses and auto-aggregate downstream capabilities; tool names stay original in single-server mode and switch to namespace prefixes (serverName__toolName) only when multiple servers are configured
  • MCP compatibility — Preserve downstream tool titles, annotations, input/output schemas, icons, metadata, and structured results; optional fields absent in older Xcode versions remain absent
  • Streamable HTTP Transport — Lightweight HTTP server based on NWListener, providing /mcp endpoint, localhost only
  • Session management — Independent session per client with secure token identification
  • Process lifecycle — Crash detection + exponential backoff auto-restart
  • macOS status bar app — Visual service status, one-click start/stop
  • Device unlock reminder — While RunAllTests / RunSomeTests / RunProject target a physical device, the service waits for the build to finish (a new build log in DerivedData), then polls the lock state via devicectl; once the device has stayed locked for 30 seconds, the status bar app shows an Apple Intelligence–style glowing edge and a shaking reminder on every display until the device is unlocked or the call ends or is cancelled

Requirements

  • macOS 15.0+
  • Swift 6.0+
  • Xcode 26.3+ (xcrun mcpbridge support)

Installation

git clone git@github.com:ljh740/XCodeMCPService.git
cd XCodeMCPService

# Build .app (recommended)
bash build-app.sh

# List available identities; durable Xcode 27 trust requires an Apple-issued chain
security find-identity -v -p codesigning

# Apple Development is suitable for a local install; disable distribution timestamps
CODE_SIGN_IDENTITY="Apple Development: Your Name (TEAMID)" \
CODE_SIGN_TIMESTAMP=none \
bash build-app.sh

# Install to Applications
cp -r "build/XCode MCP Service.app" /Applications/

Without CODE_SIGN_IDENTITY, the script discovers valid Apple code-signing identities in the current Keychain search list. It selects the sole candidate Team matching an unexpired signed Xcode grant, or otherwise the sole candidate Team. Multiple eligible Teams without a unique match require an explicit CODE_SIGN_IDENTITY; the script prints a warning and retains ad-hoc signing instead of choosing a Team for you. Within the selected Team, development certificates take precedence, followed by later expiration dates and fingerprint order. Team IDs come from certificate OU fields, not display names.

Discovery uses optional Ruby/OpenSSL support; it installs nothing and does not unlock Keychains or change Xcode permissions. Missing dependencies, inaccessible Keychains, or no eligible certificate fall back to ad-hoc signing. Xcode's mcp-server is optional: without it, only an unambiguous Team can be selected. CI without a signing identity continues to work. Set CODE_SIGN_IDENTITY="-" to skip discovery and explicitly retain ad-hoc signing, or specify an identity for a reproducible signed build. Actual signing failures stop the build instead of silently downgrading. Auto-selected development identities disable timestamps unless CODE_SIGN_TIMESTAMP is explicitly set; Developer ID identities retain secure timestamps by default. Ad-hoc and ordinary self-signed signatures may receive only temporary Xcode grants. For distribution, explicitly choose Developer ID Application. The packaging script preserves the Apple Events entitlement on both executables.

Build output is at build/XCode MCP Service.app, containing:

Component Path Description
Status bar app Contents/MacOS/XCodeMCPStatusBar Main entry, visual status bar management
CLI service Contents/MacOS/XCodeMCPService Command-line service (for background use)

The packaging script also produces:

Artifact Path Description
Disk image build/XCodeMCPService.dmg Recommended macOS release package
SHA-256 build/XCodeMCPService.dmg.sha256 Disk image checksum
Zip archive build/XCodeMCPService.app.zip App archive (automatic identity selection, with ad-hoc fallback)
SHA-256 build/XCodeMCPService.app.zip.sha256 Archive checksum

You can locate standalone binaries with swift build -c release --show-bin-path.

Quick Start

1. Create config file

mkdir -p ~/Library/Application\ Support/XCodeMCPService
{
  "bridge": {
    "port": 13339,
    "host": "127.0.0.1",
    "timeout": 30000,
    "toolTimeouts": {
      "xcode-tools__BuildProject": 2400000,
      "xcode-tools__RunAllTests": 2400000,
      "xcode-tools__RunSomeTests": 2400000
    },
    "capabilityTimeout": 15000,
    "logLevel": "info"
  },
  "servers": [
    {
      "name": "xcode-tools",
      "command": "xcrun",
      "args": ["mcpbridge"],
      "enabled": true
    }
  ]
}

Save as ~/Library/Application Support/XCodeMCPService/config.json.

Without a config file, the service uses built-in defaults (port 13339, auto-start xcrun mcpbridge). The service selects the only running Xcode first; with multiple instances it prefers the active or xcode-select-selected installation. When Xcode 27 headless MCP is enabled, it prefers that connection and intentionally leaves MCP_XCODE_PID unset so persistent agent grants survive service restarts. Older Xcode versions, or Xcode 27 with headless mode disabled, bind to the detected GUI process. When an Xcode launches or quits, the service re-resolves the binding and restarts only servers whose Xcode or connection mode changed, waiting for in-flight tool calls (such as builds or tests) to finish first; the status bar menu can also restart a single server. Set env.DEVELOPER_DIR or env.MCP_XCODE_PID for an explicit override, which also disables this automatic switching. A default config file is created on first launch.

Enable the Xcode 27 headless MCP experience once for the currently selected Xcode:

sudo xcrun mcp-server enable
xcrun mcp-server status

To target another Xcode explicitly, pass only that installation's Developer directory:

sudo env DEVELOPER_DIR="/path/to/Xcode.app/Contents/Developer" xcrun mcp-server enable

2. Start the service

# Via .app (status bar app)
open "/Applications/XCode MCP Service.app"

# Or via CLI
BIN_DIR="$(swift build -c release --show-bin-path)"
"$BIN_DIR/XCodeMCPService"

# Specify config file
"$BIN_DIR/XCodeMCPService" --config /path/to/config.json

# Via environment variable
CONFIG_PATH=/path/to/config.json "$BIN_DIR/XCodeMCPService"

3. Configure MCP client

Add to your MCP client configuration:

{
  "mcpServers": {
    "local-mcp": {
      "type": "http",
      "url": "http://127.0.0.1:13339/mcp"
    }
  }
}

Configuration Reference

BridgeConfig

Field Type Default Description
port Int 13339 HTTP listen port
host String "127.0.0.1" Listen address (127.0.0.1 or localhost only)
timeout Int 30000 Request timeout (ms)
toolTimeouts {String: Int} Three overrides shown above Tool timeout overrides (ms), keyed by server__tool canonical name
capabilityTimeout Int 15000 Per-category capability timeout during startup and reconnect (ms)
logLevel String "info" Log level: debug / info / warn / error

Omitting toolTimeouts enables the three 40-minute defaults, including in existing config files. An explicit table replaces those defaults; {} disables all overrides. Keys use the configured server name even when a single-server client calls an unprefixed tool. Other tools, resources, and prompts retain timeout. Override values must be at least 1000 ms. HTTP waits for the longest configured request timeout plus 5 seconds for the response. Restart the service after changing configuration, and set client tool timeouts to at least 40 minutes for these long tasks.

ServerConfig

Field Type Default Description
name String — Unique server name
command String — Launch command
args [String] [] Command arguments
env {String: String}? nil Environment variables
enabled Bool true Whether enabled

Config file lookup order

  1. --config CLI argument
  2. CONFIG_PATH environment variable
  3. ~/Library/Application Support/XCodeMCPService/config.json
  4. Built-in defaults (auto-written to default path on first launch)

Logs

Log files are stored by date in ~/Library/Application Support/XCodeMCPService/logs/ as yyyy-MM-dd.log. Also output to stderr.

Testing

swift test

Tests cover HTTP parsing/serialization/routing, session management, ResponseQueue, ID mapping, Xcode runtime selection, process lifecycle management, and more.

CI/CD

  • .github/workflows/ci.yml: runs swift build -c release and swift test --parallel on every push and pull request.
  • .github/workflows/release.yml: runs bash build-app.sh on every v* tag push or manual dispatch, then uploads the .app bundle, dmg, zip, and SHA-256 checksum files as workflow artifacts.
  • Tag builds also publish build/XCodeMCPService-<tag>.dmg, build/XCodeMCPService-<tag>.dmg.sha256, build/XCodeMCPService-<tag>.zip, and build/XCodeMCPService-<tag>.zip.sha256 to the matching GitHub Release.

License

MIT License

Acknowledgments