非エンジニアの「作りたい」と「安全に公開したい」を両立するSandbox MCPを作った

16 min read

目次

  1. 背景:作るのは簡単になったが、公開は難しいまま
  2. 作るのは簡単。でも、安全に公開するのは難しい
  3. UIの一貫性とデータの混在
  4. Sandbox MCP — 「作る」と「公開」の間に立つプラットフォーム
  5. 規模感
  6. できること — Web、API、DB、定期実行まで
  7. フロントエンド — sandbox-ui-kitによる統一デザイン
  8. バックエンド — 自動Dockerfile生成 + Cloud Run
  9. データベース — localStorageとFirestoreの透過的フォールバック
  10. Firestoreの名前空間分離
  11. インフラ — Wildcard DNS + Cloudflare Worker + 自前Git Server
  12. URLの決まり方
  13. Cloudflare Workerによる即時公開
  14. 自前Git Serverで大規模アプリもプッシュできる
  15. なぜ自前のGit Serverなのか
  16. 実際の利用フロー
  17. セキュリティ — 4つの独立したゲート
  18. ① 公開画面のゲート — Cloudflare Worker上の自前OAuth
  19. ② デプロイ操作のゲート — MCPのOAuth
  20. ③ データのゲート — SandboxDBの名前空間分離
  21. ④ 実行権限のゲート — Cloud Run SA + IAM
  22. ログ設計 — アプリが自分のアクセスログをクエリできる
  23. ツール設計 — AIに「正しく使ってもらう」工夫
  24. まとめ

みなさまこんにちは!エアークローゼットでCTOをしているです。

これまでにDB Graph MCP社内MCP群の全体像Biz Graph MCPと、社内向けに作っているMCPサーバーを順に紹介してきました。

今回はその中でもちょっと毛色が違うものを取り上げます。Sandbox MCP ── 非エンジニアの社員がAIと一緒に作ったアプリを、ワンコマンドで社内に安全に公開できるプラットフォームです。

「Claude Codeでアプリを作れるなら、それをそのまま社内に出せばいいじゃん」という話を、安全に実現する仕組みです。

背景:作るのは簡単になったが、公開は難しいまま

Claude CodeをはじめとするAIコーディングエージェントの普及で、いま社内の景色が大きく変わりつつあります。

これまで「アプリを作る」と言うと、エンジニアの仕事でした。要件定義してデザインを起こして、フロントを書いてバックエンドを書いてDBを設計して、CI/CDを組んで本番にデプロイする ── 全部できる人が必要だった。

ところが今は、PdMやデザイナー、CSのメンバーがClaude Codeに「こういう画面を作って」と話しかけて、その場でモックアップが立ち上がる時代です。エアークローゼットでも、

こういった非エンジニアからのアウトプットが、確実に増えてきています。「とりあえずこれで運用してみよう」という話まで出るようになった。

ところが、ここで大きな壁にぶつかります。

作るのは簡単。でも、安全に公開するのは難しい

ローカルで動くものを作るのは、AIのおかげで誰でもできるようになりました。python -m http.server 8000 で立ち上げて自分のMacで見るところまでは、5分もかからない。

でも「これチームに見せたい」「他の人に触ってもらいたい」となった瞬間、ハードルが一気に上がります。

これらを「全部AIに書かせる」ことは原理的にはできます。ただし出来上がりはAI任せ。Cloudflareの設定が間違っていて全世界に公開されていたとか、認証処理がバイパスされていたとか、本番DBに書き込めるサービスアカウントが渡されていたとか ── そういう事故が起きるリスクは、AIがコードを書けば書くほど高まります。

非エンジニアが「ちょっと作ってみたい」と言ったときに、作る側が責任を持つべきことと、プラットフォームが標準で守るべきことを明確に分ける必要があるんです。

加えてもう1つ、地味だけど大事な問題があります。

UIの一貫性とデータの混在

非エンジニアがそれぞれ独立にアプリを作ると、

これが10アプリ20アプリと増えていくと、社内のツール群がカオスになります。利用者は「このツールはどこで作ってるんだっけ?」「このボタンはなんで他と挙動が違うの?」となる。

社内ツールであっても、最低限の統一感は欲しい。デザインも、データの置き場所も。

Sandbox MCP — 「作る」と「公開」の間に立つプラットフォーム

そこで作ったのがSandbox MCPです。

非エンジニアがClaude Codeに「これ作って」と言うだけで、

  1. UI Kitを使った統一デザインのアプリが生成され
  2. ローカルで動作確認でき
  3. ワンコマンドで https://sbx-{nickname}--{app-name}.example.com/ にデプロイされ
  4. Cloudflare Worker上の自前OAuthで社内SSOが強制され
  5. データはFirestoreの専用DBに分離して保存される

── ここまでが、AIとのチャット1セッション内で完結します。

「作った人」が責任を持つのは機能だけ。公開のセキュリティ・データの分離・ドメインとSSL・認証は、Sandbox MCPのプラットフォームが標準で担保します。

System Overview

規模感

リソース 内容
MCPツール 10個(publish, status, schedule, list, delete, write_file, read_file, list_files, init_repo, unschedule)
対応ランタイム Python (Flask + gunicorn), Node.js, 静的HTML/SPA, カスタムDockerfile
URL sbx-{nickname}--{app-name}.example.com(Universal SSLでカバー、ACM不要)
認証 Cloudflare Worker上で動かす自前OAuth (Google Workspace @air-closet.com)
データ Firestore named DB sandbox にnickname × app単位で名前空間分離
インフラ 自前Git Server (GCE) + Cloud Run + Cloudflare Worker + KV
デプロイ時間 通常2〜5分(gitプッシュ 〜 公開URL反映まで)

ここからは、Sandbox MCPの中身を順に見ていきます。

できること — Web、API、DB、定期実行まで

Sandbox MCPは「とりあえず社内に出したい」を網羅できるよう、4種類のアプリ形態に対応しています。

種別 判定 用途
Python .py ファイルあり Flask + gunicornでAPI、画面付き分析ツール
Node.js package.json あり ExpressでAPI + 画面、Bunも可
静的HTML/SPA .html のみ(Python/Nodeなし) nginxで配信、React/Vue dist対応
カスタム Dockerfile を含める 任意のランタイム(Go、Rust、Bun、何でも可)

このどれかであれば、追加の設定なしに sandbox_publish 一発でデプロイされます。

さらに、sandbox_schedule を使えばCloud Schedulerに乗ったバッチアプリも同じ仕組みで動かせます。「毎朝9時にSlackへリスクサマリーを投げる」みたいなものを、ボタン1つでcron化できる。

sandbox_schedule(
  app_name: "risk-alert",
  schedule: "0 9 * * *",
  path: "/api/cron",
  timezone: "Asia/Tokyo"
)

これでCloud Schedulerがアプリの /api/cron を毎朝9時に叩いてくれます。スケジューラの設定UIを開く必要も、cron文法をIaCに書き起こす必要もありません。

フロントエンド — sandbox-ui-kitによる統一デザイン

非エンジニアが作ったアプリでも、社内のツール群として一貫性を持たせたい。これを担うのが sandbox-ui-kit リポジトリです。

mcp-sandbox.example.com/git 上に専用リポジトリを置いてあり、以下を提供しています。

ファイル 内容
sandbox-ui.css デザイントークン + glass morphismコンポーネントスタイル(dark/light対応)
sandbox-ui.js テーマ切替・モーダル・トースト等の汎用JS
sandbox-db.js SandboxDBクライアントSDK(後述)
index.html Storybook形式の全コンポーネントカタログ
README.md 全APIドキュメント

ポイントは、これをAIが読んで活用することを前提に設計していることです。

sandbox_publish ツールのdescriptionには次のように書いてあります。

アプリ作成時はまずread_fileでREADME.mdを読み、UI Kitを活用すること。

Claude Codeは新しいアプリを作るとき、read_file でこのREADME.mdを取得し、自分のアプリにどのCSS/JSを読み込むべきか、どのコンポーネント名を使えばいいかを理解した上でコードを生成します。人間がUIガイドラインを口頭で説明する代わりに、AI向けの「使い方」を一箇所に集約しているわけです。

結果として、誰が(AIと)作ったアプリでも、ボタン・モーダル・フォームの見た目が揃います。

バックエンド — 自動Dockerfile生成 + Cloud Run

「Dockerは書きたくない」「ランタイムの設定を考えたくない」── これも非エンジニアの典型的な要望です。

Sandbox MCPは、ソースファイルの種類を見て自動的にDockerfileを生成します。

// apps/mcp/git-server/src/sandbox/tools.ts
if (hasPy) {
  dockerfile = generatePythonDockerfile(hasRequirements);
  // requirements.txt も自動生成(なければ)
  if (!hasRequirements) {
    await writeFile('requirements.txt', 'flask\ngunicorn\n');
  }
} else if (hasPackageJson) {
  dockerfile = generateNodeDockerfile(true);
} else if (hasHtml) {
  dockerfile = generateStaticDockerfile();
}

例えばPythonアプリは

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=8080
CMD ["python", "-u", "$(ls *.py | head -1)"]

が自動生成され、requirements.txt がなければ flask + gunicorn を勝手に入れてくれます。AIが from flask import Flask で始まるコードを書いても、依存ライブラリが解決されない事故は起きません。

Cloud Runへのデプロイは gcloud run deploy --source を使い、Cloud Buildがイメージを焼いてくれます。アプリ作者は Dockerfile書いてもいいし書かなくてもいい。書かなければ標準が当たる、書けばカスタムできる ── 非エンジニアにもエンジニアにも優しい設計です。

Deploy Flow

データベース — localStorageとFirestoreの透過的フォールバック

「データを保存したい。でもDBの設定はしたくない。」

これに答えるのがSandboxDB SDKです。同じコードが、ローカルでは localStorage、デプロイ後はFirestoreで動きます。

<script src="https://mcp-sandbox.example.com/api/db/sdk.js"></script>
<script type="module">
  const db = new SandboxDB({ token: googleOAuthAccessToken });

  // 保存(どこに保存されるかは hostname で自動判定)
  const { id } = await db.collection('items').add({ name: 'test' });

  // 一覧
  const items = await db.collection('items').get();

  // 1件取得・更新・削除
  await db.collection('items').doc(id).update({ name: 'updated' });
  await db.collection('items').doc(id).delete();
</script>

SDKの中身はこうなっています。

this._isLocal = location.hostname === 'localhost'
              || location.hostname === '127.0.0.1';

async add(data) {
  if (this._db._isLocal) return this._localAdd(data);  // localStorage
  return this._req('', 'POST', data);                  // Firestore REST API
}

localhost で動かしている間はlocalStorageを使い、sbx-*.example.com にデプロイされた瞬間にFirestoreに切り替わる。コード側は一切変更不要です。

これによって、AIと一緒にアプリを開発するときの体験が劇的に良くなります。

Firestoreの名前空間分離

デプロイ後のデータパスは厳密に分離されています。

sandbox_data/{nickname}--{app}/{collection}/{docId}

異なるアプリのデータには物理的に到達できません。同じ人が作った別のアプリ間でも、パスが違うので分離されます。

そして何より重要なのは、Sandbox用に sandbox というnamed databaseを切っていること。社内の他システムが使っている (default) DBとは完全に別のFirestoreデータベースです。Sandboxアプリのコードがどう暴走しても、Sandbox以外のデータには絶対に触れない構造になっています。

インフラ — Wildcard DNS + Cloudflare Worker + 自前Git Server

ここからがSandbox MCPのインフラ的な見どころです。

URLの決まり方

公開URLは

https://sbx-{nickname}--{app-name}.example.com/

の形式です。nicknameMCPのOAuthセッションから自動取得します。Sandbox MCPにGoogleでログインしたときのemailから、Firestoreの users コレクションを引いてnicknameを解決する。利用者は「私は誰」を毎回入力する必要がありません。

r.tsuji@air-closet.com → users[r.tsuji@air-closet.com].nickname → "ryan"

                                  sbx-ryan--todo-app.example.com

補足: users コレクションは別の社内パイプライン(HRシステムやGoogle Workspaceディレクトリと連携した日次バッチ)で事前に社員情報をFirestoreに同期済みです。Sandbox MCP側はそれを参照するだけで、社員マスタを自前で持つ必要はありません。

これが効くのは、URLを見るだけで「誰のアプリか」が分かることです。チーム内で「ryanさんのtodo-app見て」と言うときにURLを読み上げると、自然に作者名が伝わる。社内のオーナーシップが明確になります。

Cloudflare Workerによる即時公開

新しいサブドメインを公開するとき、普通は以下が必要です。

  1. DNSのA/CNAMEレコードを追加
  2. SSL証明書を発行(ACMやLet's Encryptで15〜30分待ち)
  3. ロードバランサやDomainMappingの設定

Sandbox MCPはこれを全部スキップします。仕組みはCloudflareのEdge Router Workerです。

URL Routing

DNSは *.example.com に対するwildcard + Cloudflare proxyで固定されており、Universal SSLが自動で全サブドメインをカバーします。Cloudflare Workerが *.example.com/* の全トラフィックを受け、サブドメインに応じてルーティングします。

ロジックは3段階です。

// apps/worker/edge-router/src/index.ts
export async function handleRequest(request, env) {
  const url = new URL(request.url);

  // ① sbx-* プレフィックス → Sandbox ルーティング
  const sandboxSub = extractSandboxSubdomain(url.hostname);
  if (sandboxSub !== null) {
    return handleSandboxRequest(request, url, sandboxSub, env);
  }

  // ② KV route:{subdomain} に登録済み → Cloud Run proxy
  const subdomain = extractSubdomain(url.hostname);
  if (subdomain) {
    const proxyResponse = await handleCloudRunProxy(request, url, subdomain, env);
    if (proxyResponse) return proxyResponse;
  }

  // ③ 上記以外 → fetch(request) でパススルー
  return fetch(request);
}

sandbox_publish のデプロイ完了時にやっていることは、Cloudflare KVに route:{nickname}/{app} キーを書き込むだけ。これで新しいサブドメインがその瞬間にルーティング可能になります。

await kvPut(`route:${nickname}/${appName}`, serviceUrl);

DNS設定なし、SSL発行待ちなし、IaCデプロイなし。MCPツールの実行中にすべてが完了します。

自前Git Serverで大規模アプリもプッシュできる

実はこの仕組みは、当初はgitをまったく使わない設計で作り始めていました。

主なユーザーがPdMやCSなどビジネスサイドの社員になることを想定していたので、「gitの概念を覚えてもらうのはハードルが高い、MCPツールだけで完結させよう」と考えていたんです。sandbox_write_file でファイルを書いて sandbox_publish でデプロイ ── これで全部済むはず、と。

ところがこのアプローチは、すぐに2つの壁にぶつかりました。

壁1: チャンク分割が頻発する

MCPのツール呼び出しはHTTPリクエストで送られるため、1回のペイロードサイズに上限があります。React/Vueでビルドしたバンドルや、画像を含むSPA、ファイル数が数十個ある業務ツールなどは、そのままだと送れない。sandbox_write_fileappend モードでチャンク分割して送る運用にしたものの、AIが「ファイルAの前半 → ファイルAの後半 → ファイルBの前半 → ...」を繰り返すたびにエラー復旧やリトライが走り、デプロイが不安定になりました。

壁2: トークン消費が膨大

これが本当の問題でした。AIに「このアプリをデプロイして」と頼むと、AIはソースコード全体をMCPツールの引数として送ります。ファイル内容がそのまま会話のコンテキストに乗るため、数千行のアプリだと一気にトークンを食い尽くす。デプロイ1回で数万トークン使うことも珍しくなく、Claude Codeのセッションがあっという間に圧縮対象になります。

さらに、AIは「送ったあと、念のため確認」みたいな挙動で sandbox_read_file で同じファイルをまた読み返したりする。書く・読む・書くを繰り返してトークンが燃えていくわけです。

そこで方針転換して、gitプッシュを併用する設計に切り替えました。gitプッシュなら:

ビジネスサイドの社員が手で git push を叩く想定はないものの、Claude Codeが裏でgitコマンドを実行するのであればハードルにはなりません。利用者は「これ作って・公開して」と言うだけで、AIが必要に応じて git init && git push を勝手に走らせてくれる。

なぜ自前のGit Serverなのか

gitプッシュを採用するとなると、次は「どこにリポジトリを置くか」の問題です。GitHubの組織アカウントを使う案もありましたが、これも見送りました。

非エンジニアの社員も含めて全員にGitHubアカウントを発行・管理するのは、コスト的にも運用的にも割に合わない。1アプリ作るだけのためにGitHubのシート代を払うのは、明らかにオーバーキルです。

幸い、エアークローゼットでは別目的で自前のGit ServerをGCE上に運用していました。社内向けの「コード調査用read-only Git MCP」をホストするためのVMで、リポジトリを /mnt/repos/ 配下にクローンしてある構成です。

ここにGit Smart HTTP Protocolのエンドポイントを足して、sandbox-apps リポジトリを1つ追加するだけで、Sandbox用のgit受け口が完成しました。VMはもともと動いているので増分コストはほぼゼロ、認証は既存のGoogle OAuth基盤に乗せられる、リポジトリ管理はOSのディレクトリ操作だけで済む。新規にインフラを立てるより、既存の社内Git Serverに間借りするほうが圧倒的にシンプルでした。

実際の利用フロー

# 1. MCP ツールで Git URL を取得(nickname は自動)
sandbox_init_repo(app_name: "my-app")
# → https://mcp-sandbox.example.com/git/sandbox/ryan/my-app.git

# 2. ローカルでコミット(AI が裏で実行)
cd ~/my-app/
git init && git add . && git commit -m "init"
git remote add sandbox <返された URL>

# 3. push
git push sandbox main
# Username: oauth2accesstoken
# Password: $(gcloud auth print-access-token)

# 4. デプロイ
sandbox_publish(app_name: "my-app", description: "...")

認証はGoogle OAuthトークンをBasic Authのパスワードに乗せる方式(GCP Source Reposと同じパターン)。@air-closet.com 以外は通りません。GitHubアカウント不要で、社員なら誰でもプッシュできます。

リモートリポジトリは receive.denyCurrentBranch=updateInstead で動作しているため、プッシュと同時にサーバー側のワーキングツリーが更新されます。Cloud Runはこのディレクトリを --source として参照するので、プッシュとpublishの間に余計な手順は要りません。

なお、小さなアプリ(数ファイル・各数百行以下)であれば、引き続き sandbox_write_file 経由でも問題なくデプロイできます。規模に応じてMCP経由 / gitプッシュを使い分ける設計です。

セキュリティ — 4つの独立したゲート

ここまでが「便利に作れる」話。ここからは「安全に公開できる」話です。

冒頭で書いた通り、AIに書かせたコードを人目に晒すのはリスクが高い。だからこそSandbox MCPは、アプリの実装に依存せずに安全性を担保する仕組みを四重に張っています。

Security Layers

① 公開画面のゲート — Cloudflare Worker上の自前OAuth

sbx-*.example.com は、ルーティング用に動かしている同じCloudflare Workerが認証ゲートも兼ねる構成にしています。利用者がアクセスすると、Workerがまず cortex_session Cookieを検証し、未認証ならGoogle Workspace SSO開始用のエンドポイント(auth.example.com/__edge/auth/start)にリダイレクトします。@air-closet.com のアカウントでログインしないとCloud Runに到達できません。

これはアプリの実装に依存しません。AIが認証処理を1行も書いていなくても、Workerが先に止めます。「うっかり公開」が物理的に発生しない構造です。

なぜZeroTrust Accessから自前OAuthへ移行したか

最初はCloudflare ZeroTrust Accessで同じことを実現していました。Cloudflareの管理画面で @air-closet.com ドメイン制限を設定するだけで終わるので、コードをいっさい書かずにSSOゲートが立てられる ── 起動時の選択としては理想的でした。

ただ、ZeroTrustのFree枠は50ユーザーです。利用社員数の増加とSandbox MCPの利用拡大で枠が埋まりつつあり、Pay-as-you-go(約$7/ユーザー/月)に切り替えると無視できないコストになります。そのため人数制限のない自前OAuthに統合する判断をしました。

幸いにも、Sandbox MCPのためにすでにCloudflare Worker(*.example.com/* の全トラフィックを受けるルーティング層)が存在していました。これを少し拡張するだけで、

という一連のフローが、追加のCloud RunもVMもなしで実現できます。WorkerはCPU時間制限こそあるものの、OAuthフローとCookie検証だけなら実行時間は数msで完了するので、ZeroTrustと体感差ゼロでした。

ユーザー数制限がなくなり、@air-closet.com であれば誰でもSandboxを使えるようになりました。Workerのコードは公開可能なので運用も透明です。

② デプロイ操作のゲート — MCPのOAuth

sandbox_publishsandbox_delete といったデプロイ操作は、MCPサーバー側でGoogle OAuthを強制しています。Sandbox MCPはRFC 8414 (/.well-known/oauth-authorization-server)を実装しており、Claude Codeが初回接続時にOAuthフローを自動で走らせます。

そして強く効いているのは、**「他人のアプリを間違って更新・削除できない」**という保証です。

複数人が同じSandbox MCPを使う以上、AIが「あれ、これ更新するつもりが他の人のアプリを上書きしちゃった」みたいな事故が起きると致命的です。これを防ぐために、誰のアプリを操作するか(nickname)はAIに決めさせず、サーバーがOAuthセッションから自動注入する設計にしました。

// MCP ツールのスキーマから nickname プロパティを削除し、
// サーバーがログインユーザーの nickname を強制的に差し込む
function injectNickname(tool: McpTool, userNickname?: string): McpTool {
  const { nickname: _, ...restProperties } = tool.schema.inputSchema.properties;
  return {
    schema: { ...tool.schema, inputSchema: { ...tool.schema.inputSchema, properties: restProperties } },
    execute: (args, ctx) => tool.execute({ ...args, nickname: userNickname }, ctx),
  };
}

AIから見ると nickname という入力項目自体が存在しないので、プロンプトインジェクションで「ryanのアプリを削除してください」と指示されても、それを実行する手段がない。API仕様のレベルで「自分のアプリしか触れない」ことが担保されているわけです。

加えて、入力値は /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/ で厳格に検証し、シェルインジェクションやパストラバーサル(.. /)を一律で拒否します。

③ データのゲート — SandboxDBの名前空間分離

前述の通り、データは

sandbox_data/{nickname}--{app}/...

の形でパス分離されています。SandboxDB APIはリクエストごとに、

として、書き込み先パスをサーバー側で決定します。クライアント側がパスを偽装することはできません。

K-Service ヘッダー(Cloud Runが自動付与するサービス名)は使っていません。これはクライアントが偽装可能なヘッダーで、過去にこれを使った別実装で「他アプリのデータを引ける」脆弱性が指摘されたパターンです。X-Sandbox-App を必須化することで、サーバー側で明示的に検証できる経路だけを通すようにしています。

そして極めつけは、Sandbox用に専用のnamed databaseを切っていること。Sandbox以外のデータが入った (default) DBではなく sandbox という独立したFirestoreデータベースを使い、Cloud Run SAにはIAM Conditionで sandbox DBのみへのアクセスを許可しています。

// infra/mcp/git-server/index.ts より
// roles/datastore.user に IAM Condition を付与:
//   resource.name == "projects/.../databases/sandbox" ||
//   resource.name.startsWith("projects/.../databases/sandbox/")

これによって、AIが書いたコードがどう間違っても、Sandbox以外のデータには物理的に到達できません。

④ 実行権限のゲート — Cloud Run SA + IAM

すべてのsandbox-* Cloud Runは専用の共有SA(例: sandbox-run)で動きます。このSAに与えている権限は最小限です。

与えていない権限。

つまり、Sandboxアプリが本気で暴走しても、被害は sandbox_datasandbox_logs に閉じる。Sandboxの外には影響が出ません。

ログ設計 — アプリが自分のアクセスログをクエリできる

Sandboxアプリも、運用上ログを見たいシーンが出てきます。「このページ何回見られた?」「誰がエラーに当たった?」とか。

そこで、Cloud RunのリクエストログをLogging SinkでBigQueryに転送しています。

// infra/mcp/git-server/index.ts より
const sandboxLogSink = new gcp.logging.ProjectSink('sandbox-logs-sink', {
  destination: `bigquery.googleapis.com/projects/${projectId}/datasets/sandbox_logs`,
  filter: [
    'resource.type="cloud_run_revision"',
    'resource.labels.service_name:"sandbox-"',
    'logName:"run.googleapis.com%2Frequests"',
  ].join(' AND '),
  bigqueryOptions: { usePartitionedTables: true },
});

sandbox_logs データセットはproject ownerのみアクセス可なACLで保護されており(remoteIpやUser-Agent等のPIIが含まれるため)、Sandbox用SAには専用の bigquery.dataViewer を限定付与しています。

これで、アプリ側から自分のアクセスログをBigQueryで集計可能になります。「このアプリの先週の利用者数をSlackに投げる」みたいな運用を、Sandbox内で完結できる。

ツール設計 — AIに「正しく使ってもらう」工夫

最後に、Sandbox MCPのツール定義の話を少しします。MCPの本質的な勝負どころは、ここにあると個人的には思っています。

Sandbox MCPは10個のツールを公開しています。

ツール 用途
sandbox_publish デプロイ開始(非同期)
sandbox_deploy_status デプロイ状況確認
sandbox_init_repo gitプッシュ用リポジトリ初期化
sandbox_write_file ファイル書き込み(overwrite/append)
sandbox_list アプリ一覧
sandbox_delete アプリ削除
sandbox_schedule Cloud Scheduler設定
sandbox_unschedule Cloud Scheduler削除
sandbox_read_file ソースコード読み取り
sandbox_list_files ファイル一覧

AIが「いま何のツールを呼ぶべきか」を正しく判断できるかは、ツールのdescriptionに何を書くかでほぼ決まります。

例えば sandbox_publish のdescriptionには、ツールの機能だけでなく以下を全部書いてあります。

この情報があるから、AIは

  1. ユーザーが「Slackの絵文字スコアを表示するツール作って」と言う
  2. sandbox_publish のdescriptionで「UI KitのREADMEを先に読め」という指示を見る
  3. read_filesandbox-ui-kit/README.md を取得
  4. → ガイドラインに沿ったHTML/CSS/JSを生成
  5. → SandboxDB SDKの使い方もdescriptionに書いてあるのでデータ保存も組み込む
  6. sandbox_publish を呼ぶ

という流れを、ユーザーに何も追加質問せずに自分で組み立てられます。「何ができるか」だけでなく「何をやるべきか」までツール定義に書くのが、AI向け設計のキモです。

逆に、ツール定義をそっけなく書きすぎると、AIは何度も人間に「次は何をすればいいですか?」と聞き返してきます。descriptionは人間向けのドキュメントというより、AI向けのrunbookだと考えるとうまくいきます。

まとめ

Sandbox MCPは、AI時代の社内ツール開発における2つの課題に答えるために作りました。

このギャップを埋めるために、

を実現しました。

作ってみて改めて感じるのは、AIと一緒に開発する時代のプラットフォームの役割は変わってきているということです。これまでのプラットフォームは「人間に使いやすい」を目指していました。これからは「AIに正しく使われる」も目指す必要がある。ツールのdescriptionはAI向けのドキュメントですし、安全性は「AIが間違ったコードを書く」前提で設計しないといけない。

そして同時に、作る側の責任を限定することで、「ちょっと触ってみる」のハードルを徹底的に下げる。これが、非エンジニアの「作りたい」気持ちを業務改善のアウトプットに変えていく入口になります。

次回は、ここまで紹介してきたMCP群の上で実際に動いている社内AI Botの話を書こうと思っています。

この記事が、社内向けプラットフォームの設計に悩んでいる方の参考になれば嬉しいです。

comments (0)

まだコメントはありません。