gemini-cliのsandboxでAWS MCP Serversを動かしてみて分かったこと
結局、何が分かったのか
セキュリティと安全性を高めるために用意されているsandboxモードですが、それゆえに出来ることが制限される場面がいくつかあり、個別に解決策を考えていく必要があることが分かりました。どのような制限にぶつかり、そしてどう解決したのかを、まずは書いていきます。AWS MCP Serversを起動するための設定や解決策の詳細はその後に書きました。
書き込み可能なディレクトリ
まずは、書き込み権限が与えられるディレクトリが「プロジェクトルート」と「/tmp」に限定されている点です。つまり、ユーザーのホームディレクトリ直下にも書き込みすることが出来ない、ということになっています。ホームディレクトリ直下には、各種ツール類のユーザー単位の設定やキャッシュなどが置かれることが多いため結構な影響が予想されます。
今回のケースだと、以下のディレクトリが既定値ではディレクトリ直下に配置されるため対策が必要となりました。gitの考慮は必須ではないのですが、作業結果をリモートにも保存しておきたければ必要になるので対応しました。
課題 | 既定値 | 解決策 |
---|---|---|
AWSの認証の設定やキャッシュ | ~/.aws | sandbox起動前に取得した一時クレデンシャルを環境変数として渡す。これによりホームディレクトリ直下に設定やキャッシュを保持する必要を回避する。 |
uvxのキャッシュ[1] | ~/.cache/uv | プロジェクトルート直下に設定で変更。もちろんgit管理対象外にする。 |
git config --global | ~/.gitconfig | git config --localで設定することで回避する。 |
環境変数
gemini-cliのsandboxモードは、-sオプションを指定することで有効になります。--sandbox-imageで利用するDockerイメージも指定可能です。ただし、これ以上のオプションが見当たりません。起動されるDockerコンテナに対して柔軟な設定変更は難しい、ということになります。
今回のケースだと「AWSの認証周りをどう解決するか」が頭が痛い問題になりました。いくつかの方法が考えられるのですが、以下のような検討のを経て .env ファイルに書き込んで、環境変数として認証情報を引き渡す方法を採用することにしました。
解決案 | 結論 | 理由 |
---|---|---|
~/.awsディレクトリをマウントする | NG | マウントするための起動オプションがgemini-cliに無い。 |
設定ファイル類をDockerイメージにコピーする | NG | docker buildした環境の設定に固定されてしまう。 |
クレデンシャルを環境変数としてsandbox用のDockerコンテナに渡す | NG | 環境変数指定するための起動オプションがgemini-cliに無い。 |
プロジェクトルート直下の.envファイルにクレデンシャルを書き込む | OK | 実現可能な案が他にないため採用。もちろん.envファイルはgit管理対象外にする。 |
gemini-cli + AWS MCP Servers の実現例
ここからは AWS MCP Servers が動かせる gemini-cli の sandbox の設定例を紹介していきます。今回の記事で登場しているファイルたちの配置は以下の通りです。
プロジェクトルート
├── .cache
│ └── uv
├── .env
├── .gemini
│ ├── sandbox.Dockerfile
│ └── settings.json
├── .git
(省略)
│ └── refs
├── .gitignore
├── build_sandbox_image.sh
└── run_gemini_cli_sandbox.sh
Dockerイメージ
sandbox用のDockerイメージは公式イメージをベースとせずに、Debianをベースイメージにして作ってみました。理由は以下です。
- 何をインストールして設定すればsandboxとして使えるのか知りたかった。
- 公式イメージだと非ルートユーザーのUID周りで気持ち悪い事象が発生した。
2ポチ目の詳細は後にして、まずは作成したDockerfileはこちらです。何をしているかはコメントを見れば分かるかと思います。
# ベースイメージとして最新のDebianを使用
FROM public.ecr.aws/docker/library/debian:bookworm-slim
# 必要なパッケージ、Node.js、gemini-cli、uvをインストールし、不要ファイルを削除
# 各処理の内容を詳細にコメントしています
RUN set -eux; \
\
# 1. パッケージリストの更新とシステムのアップグレード
apt-get update -y; \
apt-get upgrade -y; \
\
# 2. 必要なパッケージのインストール(curl, ca-certificates, git)
apt-get install -y --no-install-recommends \
curl \
ca-certificates \
git; \
\
# 3. Node.js 24.x のインストール(公式スクリプトを利用)
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -; \
apt-get install -y --no-install-recommends nodejs; \
\
# 4. gemini-cli をグローバルインストール(dev依存は除外)
npm install -g --omit=dev @google/gemini-cli; \
\
# 5. uv をインストール(公式スクリプトのためcurl | shを許容)
export UV_INSTALL_DIR=/usr/local/bin; \
curl -LsSf https://astral.sh/uv/install.sh | sh; \
\
# 6. 不要なキャッシュ・一時ファイル・npmキャッシュを削除し、イメージを軽量化
apt-get clean; \
apt-get autoremove -y; \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ~/.npm
# uvのキャッシュディレクトリを環境変数で指定
ENV UV_CACHE_DIR=./.cache/uv
さて、2ポチ目の「気持ち悪い事象」の内容を話しておきます。まず、以下の前提があります。
- 今回の検証作業で利用しているLinux環境へのログインユーザーは user1(UID:1000) です。
- gemini-cliはsandobox環境に非ルートユーザー geminを自動的に作成します。このユーザーのUIDはマウントしたディレクトリのパーミッション等の問題を回避するためにdocker runを実行したホスト側のユーザーと同じUIDであることが推奨されます。gemini-cliの既定値もそうなっています。つまり、gemini(UID:1000) が作成されます。
- 公式イメージには node(UID:1000) が既に存在しています。
結果、なにが起きたかというとUIDが重複しました。
(省略)
node:x:1000:1000::/home/node:/bin/bash
gemini:x:1000:1000::/home/user1:/bin/bash
そして、キモイ現象がこちら。見事に混乱してます。whoamiは上段を、$HOMEは下段の値が反映されていますね。それ以前に「IDなのに重複している」という事態が耐え難いです、、、。
$ whoami
node
$ echo $HOME
/home/user1
というわけで、今回は UID:1000 のユーザーが予め用意されていない debian:bookworm-slim をベースイメージに採用して検証を行いました。
ビルドスクリプト
特に難しいことは無く、普通に docker build するだけで ok です。イメージ名は wanileap-sandbox にしています。由来とかは気にしないでください。
#!/bin/bash
# --- 依存コマンドの存在チェック ---
if ! command -v docker >/dev/null 2>&1; then
echo "Error: docker command not found. Please install Docker." >&2
exit 1
fi
# エラー発生時は即時終了
set -e
# --- Dockerイメージのビルド ---
# wanileap-sandbox環境用のDockerイメージをビルドします。
docker build -t wanileap-sandbox -f .gemini/sandbox.Dockerfile .gemini
実は、環境変数 BUILD_SNADBOX に 1 を設定すると起動時に プロジェクトルート配下に配置したプロジェクト固有の.gemini/sandox.Dockerfile を使ってイメージをビルドしてくれる機能があるのですが、この機能には大きな罠があって、実際に試してみるとこうなります。
$ BUILD_SANDBOX=1 gemini -s
(省略)
hopping into sandbox (command: docker) ...
ERROR: cannot build sandbox using installed gemini binary; run `npm link ./packages/cli` under gemini-cli repo to switch to linked binary.
チョットなに言ってるか分からないですが、要するに gemini-cli のソースコードをダウンロードして、自分で npm build して、npm link して実行できるようにした gemini-cli じゃないとダメ、と言われているらしいです。npm install -g @google/gemini-cli とかいうお手軽インストールの場合はダメだと。なんでよ、、、。
そんなわけで、予め docker build したイメージを起動時の --sandbox-image に指定してあげる方法を利用するのが手軽で良いかと思います。非常にシンプルに書くと以下の感じ。
gemini -s --sandbox-image wanileap-sandbox
起動スクリプト
gemini-cliを起動するのは前述の通り非常にシンプルなのですが、今回は AWS MCP Servers のために AWSの認証情報を環境変数として引き渡したいという要件があります。実装方法としてはプロジェクトルート直下の .env ファイルに書き込む、です。ビルドスクリプトと同様にコメントが充実しているので、何をやっているのかは見ていただければ分かると思います。
#!/bin/bash
# 依存コマンドの存在チェック
for cmd in aws gemini docker; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: $cmd command not found. Please install $cmd." >&2
exit 1
fi
done
# エラー発生時は即時終了
set -e
# --- AWS SSO認証情報の取得と検証 ---
# AWS SSO 認証情報を取得し、.envファイルに書き出します。
# 注意: このスクリプトを実行する前に、ホストマシンで 'aws sso login' を実行し、
# SSOセッションがアクティブであることを確認してください。
echo "Retrieving AWS SSO credentials..."
AWS_CREDENTIALS=$(aws configure export-credentials --format env)
# 認証情報取得に失敗した場合はエラー終了
if [ $? -ne 0 ]; then
echo "Error: Failed to retrieve AWS SSO credentials. Please ensure 'aws sso login' has been executed,"
echo "and that AWS CLI is correctly configured."
exit 1
fi
# 取得した認証情報を.envファイルに保存
cat <<EOF > .env
$AWS_CREDENTIALS
EOF
# .envファイルの内容を一時的に環境変数にも反映(スクリプト内で利用する場合)
eval "$AWS_CREDENTIALS"
# 必須なAWS認証情報が全て揃っているかをチェック
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ] || [ -z "$AWS_SESSION_TOKEN" ]; then
echo "Error: Incomplete AWS credentials retrieved. Please verify your SSO session is active."
exit 1
fi
echo "AWS credentials have been written to .env file."
# --- gemini-cliのサンドボックス起動 ---
# 事前にビルドしたwanileap-sandboxイメージでgemini-cliをサンドボックスモードで起動します。
gemini -s --sandbox-image wanileap-sandbox -y
起動時に -y オプションも指定しています。このオプションは gemini-cli がユーザーへの確認を省略して自動でドンドン処理を進めてくれる設定です。せっかく sandbox を使っているので大胆な設定にしてみました。
なお、-y オプションが指定されていてもユーザーに対して確認が実行されるケースがあります。どのようなケースなのかは、gemini-cli で質問して教えてもらいました。
╭────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > gemini-cliで-yオプションを指定してもユーザーへの確認が行われるのはどのようなケースですか。 │
╰────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ gemini-cliでは、-yオプションを指定した場合でも、ユーザーへの確認が求められるのは、主に以下のような破壊的または不可逆な操作、あるいは重大な影響を及ぼす可能性のある
操作を実行しようとする場合です。
これは、AIエージェントとしての安全性を最優先し、意図しないデータ損失やシステムへの変更を防ぐための設計上の判断です。
具体的には、以下のようなケースが考えられます。
* ファイルの削除や上書きなど、不可逆なファイルシステム変更: 例えば、重要な設定ファイルや大量のファイルを削除する場合など。
* 本番環境へのデプロイやインフラの変更など、システムに重大な影響を与える操作: 誤ったデプロイがサービス停止につながる可能性があるため。
* セキュリティに関わる設定変更や機密情報の取り扱い: 誤操作がセキュリティリスクにつながる可能性があるため。
-yオプションは一般的な確認プロンプトをスキップしますが、上記のような特に慎重な判断が求められる操作においては、最終的なユーザーの明示的な承認を求めるように設計さ
れています。
gemini-cli の設定ファイル
具体的には .gemini/settings.json です。ここに利用したい MCP Server の設定を入れていきます。関連する設定を紹介します。jsonはコメント書けないので、各項目の意図は後ほど説明します。
{
"mcpServers": {
"awslabs.aws-documentation-mcp-server": {
"command": "uvx",
"args": [
"awslabs.aws-documentation-mcp-server@latest"
],
"env": {
"FASTMCP_LOG_LEVEL": "ERROR",
"AWS_DOCUMENTATION_PARTITION": "aws"
},
"disabled": false,
"autoApprove": []
},
(省略 ... 他にもいろいろ設定しました)
"awslabs.cost-explorer-mcp-server": {
"command": "uvx",
"args": [
"awslabs.cost-explorer-mcp-server@latest"
],
"env": {
"FASTMCP_LOG_LEVEL": "ERROR"
},
"disabled": false,
"autoApprove": []
}
},
"fileFiltering": {
"respectGitIgnore": true,
"enableRecursiveFileSearch": true
}
}
awslabs.xxx-server となっているのが各種 AWS MCP Servers です。awslabs.aws-documentation-mcp-server は公開ドキュメントの検索機能を提供する MCP Server なので AWSアカウントへのログインは不要です。一方で awslabs.cost-explorer-mcp-server は個別の AWSアカウントの利用料金を取得してくれる MCP Server なので AWSアカウントへのログインが必要となっています。
なお、ドキュメントを参照すると AWS_PROFILE 環境編集の設定がガイドされています。しかし、今回は AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN 環境変数を利用して一時クレデンシャルを渡す方式を採用したので、AWS_PROFILE を設定するのはダメです。
最後に、fileFiltering は AWS MCP Servers とは直接関係のある設定ではないです。しかし、AWS MCP Servers が利用する uv のキャッシュをプロジェクトルート直下に変更したことに関連した設定です。gemini-cli は起動時にプロジェクト配下にあるファイルをスキャンして文脈の理解をしようとするのですが、この時に .cache ディレクトリの内容が読み取られたとしてもあまり意味がありません。ということで、 respectGitIgnore オプションを true に設定することで、.cache スキャン対象から除外するようにしています。
動作確認の結果
まずは、MCP Server の状態を確認するコマンド打ってみて無事に起動していることを確認します。先ほどの設定は抜粋だったので2つしか登録がありませんでしたが、実際には5つ登録していました。
╭──────────╮
│ > /mcp │
╰──────────╯
ℹ Configured MCP servers:
🟢 awslabs.core-mcp-server - Ready (1 tools)
- prompt_understanding
🟢 awslabs.aws-documentation-mcp-server - Ready (3 tools)
- read_documentation
- search_documentation
- recommend
🟢 awslabs.cdk-mcp-server - Ready (7 tools)
- CDKGeneralGuidance
- ExplainCDKNagRule
- CheckCDKNagSuppressions
- GenerateBedrockAgentSchema
- GetAwsSolutionsConstructPattern
- SearchGenAICDKConstructs
- LambdaLayerDocumentationProvider
🟢 awslabs.cost-analysis-mcp-server - Ready (6 tools)
- analyze_cdk_project
- analyze_terraform_project
- get_pricing_from_web
- get_pricing_from_api
- get_bedrock_patterns
- generate_cost_report
🟢 awslabs.cost-explorer-mcp-server - Ready (7 tools)
- get_today_date
- get_dimension_values
- get_tag_values
- get_cost_forecast
- get_cost_and_usage_comparisons
- get_cost_comparison_drivers
- get_cost_and_usage
最後に MCP Server の力を借りないと回答できない質問を投げて、きちんと貢献してくれるか確認します。
╭─────────────────────────────────────────────────────────────────────────────╮
│ > AWSの過去一年の利用料金の傾向を調べて、改善策があれば提案してください。 │
╰─────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✔ get_today_date (awslabs.cost-explorer-mcp-server MCP Server) {} │
│ │
│ { │
│ "today_date_UTC": "2025-07-02", │
│ "current_month": "2025-07" │
│ } │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ AWSの過去1年間の利用料金の傾向を調べて、改善策を提案しますね。
まず、過去1年間の利用料金データを取得するために、期間を指定する必要があります。本日は2025年7月2日ですので、過去1年間として「2024年7月1日から2025年6月30日」のデ
ータを取得します。
次に、どのような粒度でデータを取得し、どのようにグループ化するかを決めます。
* 粒度 (granularity): 月ごとの傾向を見るために MONTHLY を使用します。
* グループ化 (group_by): どのサービスがコストを占めているかを見るために SERVICE でグループ化します。
* メトリック (metric): UnblendedCost (非ブレンドコスト) を使用します。
これらの情報に基づいて、get_cost_and_usage ツールを使用してデータを取得します。
データ取得後、その傾向を分析し、一般的な改善策を提案します。具体的な改善策は、取得したデータの内容によってより詳細に提案できます。
では、まず過去1年間の月ごとのサービス別コストデータを取得します。
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✔ get_cost_and_usage (awslabs.cost-explorer-mcp-server MCP Server) {"group_by":{"Key":"SERVICE","Type":"DIMENSION"},"date_range":{"start_date":"2024-07-01","end… │
│ │
│ ... first 193 lines hidden ... │
(省略)
│ } │
│ } │
│ } │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ 過去1年間のAWS利用料金データが取得できました。
過去1年間の利用料金傾向の分析
取得したデータから、以下の傾向が見られます。
(省略:金額は秘密です)
これらの改善策は一般的なものですが、具体的な利用状況やアーキテクチャによって最適な方法は異なります。まずはS3とBedrockの利用状況を詳細に確認し、そこから着手する
ことをお勧めします。
無事に、AWS MCP Servers が AWS アカウントに接続して情報を提供していることが確認できました。
おまけ
なお、今回の実装は全て gemini-cli または github copilot にやってもらっています。動かないコードや意図した通りではないコードが生成されるケースも少なからずありましたが、何回か生成とフィードバックのキャッチボールを繰り返すうちに欲しいものが最終的に手に入ったと思います。
ひたすらプロンプトを考えて打ち込む作業ではあったのですが、実質的に生成AIが書いたコードをレビューして生成AIにフィードバックする作業だったように思います。今後は「○○を作りたい!」という揺らがない要件を抱いて、自身の持つ完成イメージに近づけるための具体的で明確な指示とフィードバックを生成AIに出せる人が生成AIと共生しながら生き残っていけるのかもしれませんね。
-
uvはPythonパッケージマネージャーで、MCPサーバーの依存関係管理に使用されます。 ↩︎
Discussion