Claude Codeを --dangerously-skip-permissions で自律的に動かしつつ、ホストのMac環境を汚さず、かつXcodeビルドなどMacでしか実行できない操作も可能にする仕組みの解説。
Claude Codeの --dangerously-skip-permissions は強力だが、ホスト環境で直接実行すると以下のリスクがある:
- ファイルシステムへの無制限なアクセス
- 任意のシェルコマンドの実行
- 意図しないシステム設定の変更
一方、iOSアプリ開発ではXcodeのツールチェーン(xcodebuild, xcrun simctl 等)がmacOS上でしか動作しないため、単純にDockerに閉じ込めるとビルドやテストができなくなる。
この仕組みは「Dockerでサンドボックス化しつつ、Macでしかできない操作はMCP経由でリモート実行する」というアプローチでこの問題を解決する。
┌─────────────────────────────────────────────────────────┐
│ Zellij (ターミナルマルチプレクサ) │
│ │
│ ┌──────────────┐ ┌──────────┐ ┌───────────────────┐ │
│ │ Plans │ │ Terminal │ │ Changed Files │ │
│ │ (ホスト側) │ │ (Docker) │ │ (ホスト側) │ │
│ └──────────────┘ └──────────┘ └───────────────────┘ │
│ ┌──────────────────────────┐ ┌───────────────────┐ │
│ │ Claude Code │ │ PR Status │ │
│ │ (Docker内で実行) │ │ (ホスト側) │ │
│ │ --dangerously-skip-perms │ └───────────────────┘ │
│ └────────┬─────────────────┘ │
└───────────┼─────────────────────────────────────────────┘
│ MCP (HTTP)
│ host.docker.internal:19418
▼
┌───────────────────────┐
│ xcode-remote │
│ (macOSホスト上で稼働) │
│ │
│ xcodebuild 実行 │
│ xcodegen 実行 │
└───────────────────────┘
エントリーポイント。origin/main から新しいgit worktreeを作り、開発セッションを起動する。
./scripts/new-worktree # Docker モード(デフォルト)
./scripts/new-worktree --no-docker # ホスト直接モードやっていること:
/usr/share/dict/wordsからランダムな3単語を生成してworktree名にする(例:dungy-turpid-azyme)git worktree addでworktreeを作成direnv allowで環境変数を有効化scripts/docker-claudeを呼び出してDocker環境を起動
worktreeごとに独立したブランチ・Dockerコンテナ・Zellijセッションが作られるため、複数タスクの並行作業が可能。
このスクリプトが仕組みの中核。以下を順番に行う:
.secrets (リポジトリルートに配置、.gitignore対象)
├── CLAUDE_CODE_OAUTH_TOKEN # Claude Code認証トークン
└── CLAUDE_CODE_OAUTH_TOKEN_ISSUED # 発行日(期限管理用)
- トークンが未設定なら
claude setup-tokenを対話的に実行して取得・保存 - 発行から335日以上経過していたら更新を促す(有効期限365日)
.secretsファイルはメインリポジトリ直下に置かれ、全worktreeで共有される
xcode-remoteはHTTPサーバーとして動作し、MCP (Model Context Protocol) 経由で xcodebuild コマンドを実行できるようにする。
localhost:19418でヘルスチェック、未起動なら自動起動XCODE_REMOTE_ALLOWED_PATHSで操作可能なディレクトリを制限(worktrees配下のみ)- ログは
/tmp/xcode-remote.logに出力
docker/mcp.docker.json を動的に生成し、コンテナ内の .mcp.json にマウントする。
{
"mcpServers": {
"xcode-remote": {
"type": "http",
"url": "http://host.docker.internal:19418/mcp"
}
}
}Docker内からホストのxcode-remoteに到達するため、Dockerの特殊DNS host.docker.internal を使用する。
docker run -d --rm \
--name "claude-${WORKTREE_NAME}" \
-v "$PROJECT_DIR:$PROJECT_DIR" \ # プロジェクトディレクトリ(双方向)
-v "$MAIN_GIT_DIR:$MAIN_GIT_DIR" \ # .gitディレクトリ(worktree用)
-v "mcp.docker.json:.mcp.json" \ # MCP設定
-v "settings.docker.json:.claude/settings.json" \ # 権限設定
-v "$HOME/.gitconfig:ro" \ # Git設定(読み取り専用)
-v "$HOME/.ssh:ro" \ # SSH鍵(読み取り専用)
-v "$HOME/.config/gh:ro" \ # GitHub CLI設定(読み取り専用)
-v "$HOME/.claude:ro" \ # Claude設定(読み取り専用)
-e "CLAUDE_CODE_OAUTH_TOKEN=..." \ # 認証トークン
claude-ios-dev sleep infinityポイント:
- プロジェクトディレクトリは同じ絶対パスでマウントする。これによりDocker内とホストでパスが一致し、xcode-remoteに
workingDirectoryをそのまま渡せる - ホストの設定ファイル(
.gitconfig,.ssh,.config/gh)は読み取り専用でマウント - コンテナ名に worktree名を含めることで、複数コンテナの並行運用が可能
テンプレート zellij/claude-dev.docker.kdl 内のプレースホルダを置換し、5ペインのレイアウトで起動する。
| ペイン | 実行環境 | 役割 |
|---|---|---|
| Plans | ホスト | plans/ ディレクトリの変更をリアルタイム表示 |
| Claude Code | Docker | --dangerously-skip-permissions でClaude Code実行 |
| Changed Files | ホスト | git diffのリアルタイム表示 |
| Terminal | Docker | デバッグ用ターミナル |
| PR Status | ホスト | PRのCI状態を表示 |
fswatch や gh コマンドなどホスト側のツールが必要なペインはホスト上で、Claude Codeとそのデバッグ用ターミナルはDocker内で実行する。
FROM node:22-bookworm
RUN apt-get update && apt-get install -y git curl gosu jq
# GitHub CLI
RUN curl -fsSL https://cli.github.com/packages/... && apt-get install -y gh
# Claude Code
RUN npm install -g @anthropic-ai/claude-code
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]最小限の構成。iOS開発ツール(Xcode等)はコンテナに含まれず、すべてxcode-remote MCP経由でホスト側に委譲する。
コンテナ起動時に以下を行う:
- UID/GIDマッピング: ホストの
UID:GIDに合わせたユーザーを作成。ボリュームマウントされたファイルの権限問題を回避 - オンボーディングスキップ:
~/.claude.jsonにhasCompletedOnboarding: trueを書き込み - ホスト設定のコピー:
.claude/ディレクトリをコピーし、プラグインパスをコンテナ内のパスに書き換え - Docker専用CLAUDE.md: ユーザーレベルの
~/.claude/CLAUDE.mdを書き換え、「xcodebuildは使えない、代わりにxcode-remote MCPを使え」とClaude Codeに指示 - 非rootユーザーで実行:
gosuでUID/GIDを切り替えてコマンドを実行
--dangerously-skip-permissions を使いつつ、Claude Codeの設定レベルで操作を制限する。
許可されている操作:
- Git操作(add, commit, push, fetch, merge, reset, rebase, restore)
- ファイル検索・閲覧(cat, grep, find, ls, tree)
- GitHub CLI(
ghコマンド) - xcode-remote MCP(全操作)
.claude/と/tmp/へのファイル書き込み- WebFetch, WebSearch
拒否されている操作:
- Supabase CLIの直接実行(MCP経由のみ許可)
不要になったworktreeを自動削除するユーティリティ。
- 未コミットの変更がないことを確認
- ローカルHEADがリモートにプッシュ済み、またはmainにマージ済みであることを確認
- 条件を満たしたworktreeとそのローカルブランチを削除
この仕組みで最も重要な設計判断の一つが、Docker内とホストで同じ絶対パスを使うこと。
ホスト: /Users/hiragram/Development/optical-note/worktrees/dungy-turpid-azyme/
Docker内: /Users/hiragram/Development/optical-note/worktrees/dungy-turpid-azyme/
これにより:
- Claude Codeが
pwdで取得したパスをそのままxcode-remoteのworkingDirectoryに渡せる - パス変換ロジックが不要
- gitの操作(
.gitディレクトリ参照含む)がそのまま動作
| レイヤー | 制限内容 |
|---|---|
| Docker | ファイルシステムはマウントされたディレクトリのみアクセス可能 |
| xcode-remote | XCODE_REMOTE_ALLOWED_PATHS で操作可能ディレクトリを制限 |
| settings.docker.json | Claude Codeが使えるツール・コマンドをホワイトリストで制限 |
| ボリュームマウント | ホスト設定ファイルは読み取り専用(:ro)でマウント |
--dangerously-skip-permissions はClaude Codeの対話的な権限確認をスキップするだけで、settings.json の allow/deny リストは引き続き有効。Docker自体のサンドボックスとxcode-remoteのパス制限と合わせて、三重の防御になっている。
1. ./scripts/new-worktree を実行
│
2. git worktree作成 (ランダム名のブランチ)
│
3. ./scripts/docker-claude が起動
│
├─ OAuthトークンの確認・取得
├─ xcode-remoteの起動確認
├─ MCP設定ファイルの生成
├─ Dockerイメージのビルド
├─ Dockerコンテナの起動
└─ Zellijセッションの起動
│
├─ [Docker] Claude Code (--dangerously-skip-permissions)
│ │
│ ├─ コード編集・git操作 → Docker内で直接実行
│ └─ ビルド・テスト → xcode-remote MCP経由でホストで実行
│
├─ [Docker] デバッグ用ターミナル
├─ [ホスト] Plans表示
├─ [ホスト] Changed Files表示
└─ [ホスト] PR Status表示
4. Zellijセッションを閉じるとDockerコンテナも自動削除
5. 作業完了後、./scripts/cleanup-worktree で不要なworktreeを掃除
- macOS (Xcodeツールチェーン用)
- Docker Desktop (
host.docker.internalのDNS解決に必要) - Zellij (ターミナルマルチプレクサ)
- direnv (環境変数管理)
- xcode-remote (
$HOME/Development/xcode-remoteに配置) - Claude Code のOAuthトークン(初回起動時に自動取得)
- 1Password CLI (
opコマンド、GEMINI_API_KEYの取得に使用、オプション)
host.docker.internal はDocker Desktopが提供する特殊なDNSで、コンテナからホストマシンにアクセスするために使う。OrbStackなど互換環境でも動作する可能性がある。
HTTPサーバーとして動作し、MCP (Model Context Protocol) のインターフェースで xcodebuild や xcodegen を実行できるツール。Claude Codeが直接呼べるMCPツールとして登録される。
Docker内に閉じ込めているため、仮にClaude Codeが予期しない操作を行っても影響範囲はコンテナ内のマウント済みディレクトリに限定される。加えて settings.docker.json のホワイトリストとxcode-remoteのパス制限で多層的に保護している。ただし、マウントされたプロジェクトディレクトリ内のファイルは書き換え可能であるため、gitで変更を追跡・復元できる状態にしておくことが前提。
はい。worktreeごとに独立したDockerコンテナとZellijセッションが作られるため、./scripts/new-worktree を複数回実行すれば並行して作業できる。xcode-remoteは共有だが、ジョブキューで管理されるため衝突しない。