はじめに
Claude Code のようなコーディングエージェントに長めのタスクを任せるとき、実行のたびに許可プロンプトへ応答するのは手間が大きいと思います。一方で、許可プロンプトを外した運用(--dangerously-skip-permissions)をホストマシン上で行うのは避けたいところです。
この記事では、devcontainer を使って次の 5 点を満たす実行環境を構築します。
- Claude Code をコンテナ内で実行し、許可プロンプトなしの運用を許容できる隔離を確保する
- 複数リポジトリを横断して参照・編集できる
- エージェントが gh コマンドで PR 作成や issue 操作を行える
- MCP(AI がツールや外部サービスへ接続するための共通規格)で接続できる
- 各リポジトリの開発環境を docker compose で起動できる
前提環境は以下の通りです。
- macOS + Docker Desktop
- VS Code + Dev Containers 拡張
- Claude Code のサブスクリプションまたは API アクセス
構成の設計判断
ここでは、構成要素ごとに何を選び、なぜそうしたかを整理します。
devcontainer は各リポジトリではなく親フォルダに置く
devcontainer の仕組みは単純で、VS Code は「開いたフォルダ直下の .devcontainer/devcontainer.json」を読むだけです。これを利用して、各リポジトリではなく、リポジトリ群を束ねる親フォルダに設定を置きます。
~/Repos/ ├── .devcontainer/ │ └── devcontainer.json ├── product-a/ (git リポジトリ) ├── product-b/ (git リポジトリ) └── product-c/ (git リポジトリ)
VS Code で ~/Repos を開いてコンテナを起動すると、コンテナ内の /workspaces/Repos 配下に全リポジトリがマウントされます。Claude Code から「product-a の API 定義を参照しながら product-b のクライアントを修正する」といったリポジトリ横断の作業がそのまま可能になります。
副次的な利点として、devcontainer の設定がどのリポジトリにも属さないため、チームのリポジトリに個人環境の設定ファイルを持ち込まずに済みます。
Docker 実行は docker-in-docker を選ぶ
コンテナ内で docker compose を使うには、コンテナ内から Docker を操作できる必要があります。devcontainer の公式 feature には 2 つの選択肢があります。
- docker-outside-of-docker(ホストの Docker ソケットをマウントする)
- docker-in-docker(コンテナ内に独立した Docker デーモンを立てる)
今回は docker-in-docker を選びます。ソケットマウントの方が軽量ですが、コンテナ内からホストの Docker を直接操作できてしまうため、「エージェントの実行範囲をコンテナに閉じ込める」という今回の目的と両立しません。docker-in-docker であれば、compose で立ち上がるコンテナ群もすべて devcontainer の内側に閉じます。
MCP は user スコープで登録する
Claude Code の MCP サーバー登録には複数のスコープがあります。よく紹介されるのはリポジトリ直下に .mcp.json を置くプロジェクトスコープですが、これは設定ファイルをリポジトリにコミットしてチームで共有する前提の仕組みです。
今回は user スコープで登録します。設定は Claude Code の設定ディレクトリ側に保存されるため、リポジトリのファイルに影響しません。設定ディレクトリはボリュームで永続化するので、コンテナを作り直しても登録は残ります。
Notion や Figma はいずれも公式のリモート MCP サーバーを提供しているため、コンテナ内にサーバープロセスを立てる必要はなく、URL の登録と OAuth 認証だけで接続できます。
ベースイメージは LTS タグで固定する
ベースイメージには mcr.microsoft.com/devcontainers/base:noble(Ubuntu 24.04 LTS)を使います。
よく見かける base:ubuntu という浮動タグは「その時点の最新 Ubuntu」を指すため、新しいリリースが出た直後は各 feature 側のパッケージ対応が追いついておらず、インストールに失敗することがあります。実際に :ubuntu のまま構築すると、claude-code feature の Node.js 自動インストールや docker-in-docker feature の moby パッケージ導入が失敗するケースがあります。環境の再現性という devcontainer 本来の目的からも、LTS のコードネームで固定しておくのが無難です。
認証情報はボリュームで永続化する
devcontainer はリビルドするとホームディレクトリの内容が消えます。何もしないと、リビルドのたびに Claude Code と gh のサインインをやり直すことになります。
これを避けるため、認証情報の保存先に Docker の名前付きボリュームをマウントします。対象は 3 箇所です。
~/.claude(Claude Code の認証トークン・設定・MCP 登録)~/.config/gh(gh の認証トークン)/var/lib/docker(docker-in-docker のイメージキャッシュ。認証ではありませんが、リビルドごとの pull し直しを避けられます)
完成形の devcontainer.json
~/Repos/.devcontainer/devcontainer.json として以下を配置します。
{ "name": "claude-multi-repo", "image": "mcr.microsoft.com/devcontainers/base:noble", "features": { "ghcr.io/devcontainers/features/node:1": {}, "ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}, "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/docker-in-docker:2": {} }, "mounts": [ "source=claude-config,target=/home/vscode/.claude,type=volume", "source=gh-config,target=/home/vscode/.config/gh,type=volume", "source=dind-data,target=/var/lib/docker,type=volume" ], "containerEnv": { "CLAUDE_CONFIG_DIR": "/home/vscode/.claude" }, "remoteUser": "vscode", "postCreateCommand": "sudo chown -R vscode:vscode /home/vscode/.claude /home/vscode/.config/gh" }
いくつかの行には理由があるので補足します。
node feature を明示的に入れています。 Claude Code は npm 経由でインストールされるため Node.js と npm が必要です。claude-code feature には Node の自動インストール機構がありますが、ベースイメージによっては失敗することがあるため、node feature で確実に入れておきます。feature 間の依存関係は自動で解決され、node が先にインストールされます。
CLAUDE_CONFIG_DIR を設定しています。 Claude Code の設定・認証の保存先をボリュームのマウント先に向けるための環境変数です。これにより、user スコープの MCP 登録を含む状態がリビルド後も保持されます。
postCreateCommand で chown しています。 Docker の名前付きボリュームは、初回作成時に root 所有で初期化されます。そのまま vscode ユーザーで gh auth login などを実行すると、トークンの書き込みが permission denied で失敗します。コンテナ作成後に所有者を付け替えることで、この問題を回避します。冪等な処理なので、何度実行されても害はありません。
remoteUser は非 root にしています。 Claude Code の CLI は root で起動された場合に --dangerously-skip-permissions を拒否する仕様のため、許可プロンプトなしの運用にはこの設定が前提になります。devcontainers の base イメージに用意されている vscode ユーザーをそのまま使います。
構築手順
ここからは実際の手順です。各手順に完了確認のコマンドを添えます。
1. 親フォルダとリポジトリの準備
mkdir -p ~/Repos && cd ~/Repos git clone https://github.com/your-org/product-a.git git clone https://github.com/your-org/product-b.git
clone は HTTPS で行います。コンテナ内の git 認証は後述の gh に統合するため、SSH 鍵をコンテナへ持ち込む必要がなくなります。
2. devcontainer.json の配置
前セクションの内容を ~/Repos/.devcontainer/devcontainer.json として保存します。
cat ~/Repos/.devcontainer/devcontainer.json | python3 -m json.tool
JSON として妥当かをこの時点で確認しておくと、起動時の切り分けが楽になります。
3. コンテナの起動
VS Code で ~/Repos を開き、右下に表示される通知から「Reopen in Container」を選びます。表示されない場合はコマンドパレット(Cmd+Shift+P)から「Dev Containers: Reopen in Container」を実行します。
初回は feature のダウンロードとビルドが走るため、数分から 10 分程度かかります。起動後、コンテナ内のターミナルで確認します。
ls /workspaces/Repos # 全リポジトリが見えること docker ps # docker-in-docker のデーモンが応答すること
4. Claude Code のサインイン
コンテナ内のターミナルで claude を実行し、ブラウザでの認証を完了させます。ブラウザ側は成功しているのにターミナルへ戻らない場合は、ブラウザに表示されるコードをターミナルの入力欄へ手動で貼り付けます(ポートフォワードの経路によってコールバックが届かないことがあります)。
5. gh のセットアップ
gh auth login gh auth setup-git gh auth status # 確認
gh auth setup-git により、git の push / pull も gh の認証で通るようになります。
エージェントに書き込み操作をさせる前提なので、認証には fine-grained personal access token の利用を推奨します。対象リポジトリを限定し、権限は Contents / Pull requests / Issues の Read and write に絞っておくと、意図しない操作があってもトークンのスコープが被害の上限になります。
6. MCP サーバーの登録
user スコープで 2 サービスを登録します。
claude mcp add --scope user --transport http notion https://mcp.notion.com/mcp claude mcp add --scope user --transport http figma https://mcp.figma.com/mcp
登録後、claude を起動して /mcp コマンドを実行すると各サーバーの認証状態が表示されるので、そこから OAuth を通します。
動作確認チェックリスト
環境が意図通りにできているか、以下を一巡して確認します。
- どれかのリポジトリで
docker compose up -dが通り、VS Code のポートフォワード経由でホストのブラウザからアクセスできる gh issue list -R your-org/product-aで参照系が通る。テスト issue の作成とクローズで書き込み系も確認できるとより確実/mcpで 2 サービスが接続済みになっており、たとえば Notion の記事を 1 件取得できる- Claude Code に「product-a と product-b の README を比較して」のようなリポジトリ横断の質問を投げ、両方を読めている
- 「Dev Containers: Rebuild Container」を一度実行し、claude / gh / MCP の認証がすべて残っている
最後の項目はボリューム設定の検証を兼ねています。ここで認証が消えるようであれば、mounts か CLAUDE_CONFIG_DIR の設定を見直してください。運用に入ってから気づくより、この時点で潰しておく方が安全です。
制約と補足
最後に、この構成が何を割り切っているかを書いておきます。
ネットワークの外向き制限は入れていません。 Anthropic の公式リファレンス実装には、許可ドメイン以外への外向き通信を遮断するファイアウォールスクリプトが含まれています。今回は MCP 2 サービス、gh、compose のイメージ取得先まで許可リストを管理する運用コストを考えて省略し、事故対策は PAT の権限スコープなどトークン側に寄せました。より強い隔離が必要な場合は、公式リポジトリの init-firewall.sh を組み込む選択肢があります。
影響範囲は全リポジトリに及びます。 マルチリポジトリを 1 コンテナに載せる構成のため、許可プロンプトなしで運用した場合の影響範囲もマウントした全リポジトリに広がります。信頼できるリポジトリで使うこと、というのは公式ドキュメントの注意書きの通りです。
docker-in-docker はディスクを消費します。 イメージやビルドキャッシュがコンテナ側のボリュームに蓄積するため、ホストの Docker とキャッシュは共有されません。ときどき docker system prune を実行するか、ボリュームのサイズを気にかけておく必要があります。
隔離の強度と日々の使い勝手はトレードオフの関係にあり、この構成はその中間のバランスを取ったものです。運用してみて不安が残る箇所があれば、ファイアウォールの追加やリポジトリの分割など、締める方向の調整はいつでもできます。








