BQ 警察からのコスト警告!BigQuery remote MCP serverで『自然言語クエリ』する前に
株式会社 MBK Digital 執行役員 CTO の岩尾です。
本記事は Google Developer Experts Advent Calendar 2025 の 25 日目の記事としてお届けします。
本記事は、最近注目されている BigQuery remote MCP server(以下、単に BigQuery MCP と呼びます)を、「便利そう!」だけで社内にばら撒く前に、データ人材(データエンジニア・データアナリスト・データサイエンティスト)が押さえるべきコストの落とし穴とガードレールをまとめたものです。
自己紹介:I am "BQ 警察"
私は 2018 年頃から BigQuery のコスト適正化を「BQ 警察」として推進してきました。
- [DeNA TechCon 2019] DeNAゲーム事業におけるデータエンジニアの貢献(SlideShare)
- [記事] [DeNA TechCon 2020 ライブ配信] DeNA データプラットフォームにおける自由と統制のバランス(Medium)
Google Cloud Next ’19 in Tokyo では自作の BigQuery Police T シャツを着て登壇したりもしました。

当時から一貫して言っているのはこれです。
BigQuery はとても便利なサービスだが、適切に運用しないと本来不要なコストがかかってしまう。不要なコストは削り、より意味のあることに投資しよう。
そして今、LLM × BigQuery により、再び BigQuery のコストが膨れ上がってしまうリスクが再燃しています。
BigQuery remote MCP server って何?
BigQuery MCP は、Cursor / Gemini CLI / Claude Code などの MCP クライアントから、BigQuery を「ツール呼び出し」として使えるようにする仕組みです。
ざっくり言うと:
- ユーザー:「先月の売上推移を見せて」
- LLM:(内部で)テーブル探索 → スキーマ確認 → SQL 生成 → 実行 → 結果整形
- ユーザー:自然言語で BigQuery が叩けたように見える
この「自然言語でのクエリ実行」が、一番のユースケースであり、一番の地雷原でもあります。
なぜコスト事故が起きるのか
BigQuery(特にオンデマンド課金)で最もありがちな事故は大量スキャンです。
そして LLM は、人間が抑制できていた慎重さを、簡単に踏み越えます。
事故パターン 1:自然言語での分析は「試行回数」が多くなりがち(= クエリ回数が増える)
自然言語 → SQL(Text-to-SQL)は、現実には「一発で正解に着地する」より「会話しながら詰めていく」ことの方が多いです。ユーザーはデータベースを探索しながら質問を更新し、必要に応じて曖昧さを解消しつつ、SQL の実行結果を見て次の問いに進む——という流れになりがちです。
BigQuery MCP を介して BigQuery に接続すると、体感としては「ちょっと会話してるだけ」でも、裏側では SQL 実行が複数回積み重なります。
- JOIN 条件が少し違う
- 期間条件を付け忘れて直す
- 粒度(ユーザー単位 / 日次)を直す
- テーブルや列の想定が違って直す
手書きの SQL なら最初から慎重に条件を詰めますが、自然言語だと「まず試してみる → 結果を見て直す」を繰り返しやすく、結果として微修正したクエリの再実行が増えがちです。
しかも BigQuery MCP 自体には独自のクオータがなく、呼び出し回数に上限がありません。つまり、試行錯誤がそのまま「クエリ回数の増加」につながり得ます。
人間なら「このテーブル重いから、まずサンプルで…」と慎重になるところを、LLM は平然と全体に当てに行きます。
事故パターン 2:自然言語が曖昧だと「意図しない SQL」になりやすい(=結果的にスキャンが広がりやすい)
自然言語→SQL では、曖昧な質問や 情報が足りず答えようがない質問が混ざるのが現実です。そういう入力はモデルにとって難しく、追加情報がないと “もっともらしい SQL” を推測で生成しやすくなります。
この推測がたとえば次の形を含むと、BigQuery ではコスト事故(大量スキャン)に直結します。
- 期間条件がない(全期間を読む)
- 対象母集団が広すぎる(全ユーザー / 全イベントを読む)
- 取りたい列が多い(
SELECT *など) - JOIN が雑で中間結果が膨らむ
厄介なのは「会話としては成立して見える」ことです。
ユーザーは危険なクエリが走ったことに気づきにくいまま次の質問を続け、結果としてクエリの反復=課金の反復になり得ます。
BigQuery MCP はデフォルトで 3 分で止まる? でも安心できない
BigQuery MCP の execute_sql は、デフォルトでクエリ処理時間を 3 分に制限し、3 分を超えるクエリは自動キャンセルされます。
一見「安全そう」に見えますが、これはあくまでタイムアウトであって、課金(スキャン量)を抑制しているわけではありません。
BigQuery(オンデマンド課金)の基本は「処理したバイト数(スキャン量)」課金です。極端な話、BigQuery は並列に速く走るので 3 分以内でも TB 級を読み得ます。加えて、BigQuery のジョブはキャンセルしてもコストが発生する可能性があります。つまり「3 分で止まるから安心」「気づいたら止めれば無傷」とは限りません。
なので、この 3 分制限は「対話体験のタイムアウト(長時間クエリをいつまでも待たない)」として捉えるのがよく、「コスト対策としては別のガードレールが必要」になります。
じゃあどうする?(BQ 警察流:ガードレールの三層構造)
LLM × BigQuery を安全にするには、「人の注意」だけに頼らないことが重要です。
私はガードレールを以下の 3 層で考えています。
- 技術的ガードレール(止める)
- 運用的ガードレール(気づく・追う)
- 体験設計ガードレール(そもそも踏みにくくする)
以下、順にいきます。
1)技術的ガードレール:止める(大量スキャンを失敗させる)
(A)パーティション分割テーブルは「Require partition filter」を必須化する
これは古典的ですが最強です。
- パーティション列に条件がないクエリを エラーにする
- 「うっかり全期間」が技術的に不可能になる
ログ・イベント・明細系の重いテーブルほど有効です。
(B)クエリ前に dry run / 推定スキャン量を必ず見る
BigQuery MCP の execute_sql には、入力パラメータとして dryRun が用意されています。dryRun: true にすると BigQuery はジョブを実行せず、代わりに「処理されるバイト数」などジョブ統計(見積もり)を返します。
つまり MCP 経由でも、自然言語で「まず dry run して推定スキャン量を見てから判断する」というガードレールが作れます。
私もまだ検証中ですが、例えば以下のように指示すると良いでしょう。
- 「まず SQL を作って。最初は必ず dry run だけ実行して、推定スキャン量(処理バイト数)を報告して」
- 「推定スキャン量が X GB を超えるなら、本実行はしない。期間・対象・列を絞る案を出して」
(C)日次クエリ量に “ハードキャップ” をかける
プロジェクトやユーザー単位で、1 日あたりの処理量上限を設定できます。
ただし、上限設定は組織の運用に影響するので、いきなり本番全体に入れるのではなく、
まず LLM 用の隔離プロジェクトで試すのが現実的です。
2)運用的ガードレール:気づく・追う(MCP 経由のクエリを特定する)
(A)MCP 経由のジョブをタグで追える
BigQuery MCP 経由のクエリは、公式ドキュメント上 goog-mcp-server:true を使って識別できると案内されています。(この「タグ」は BigQuery のクエリジョブにラベルとして付与されるため、INFORMATION_SCHEMA では labels(key/value の配列)として拾えます)
これ、めちゃくちゃ重要です。
- 「LLM 経由のクエリだけ」を可視化できる
- どのくらい燃えてるかを後から分析できる
- 事故時の原因追跡ができる
例:INFORMATION_SCHEMA で MCP 経由ジョブの処理量を集計する(サンプル)
(※環境に合わせて region-xxx などを読み替えてください)
-- 過去24時間の MCP 経由ジョブをざっくり集計
SELECT
user_email,
COUNT(*) AS jobs,
SUM(total_bytes_billed) AS total_bytes_billed,
SUM(total_bytes_processed) AS total_bytes_processed
FROM `region-asia-northeast1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE
creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
AND job_type = 'QUERY'
AND EXISTS (
SELECT 1
FROM UNNEST(labels) l
WHERE l.key = 'goog-mcp-server' AND l.value = 'true'
)
GROUP BY user_email
ORDER BY total_bytes_billed DESC;
(B)全体感でコストを管理する(予算アラート / 異常検知 / Billing Export)
請求側から「全体感」を握るのもおすすめです。LLM 経由の利用は気づかないうちに増えやすいので、まず早期検知の仕組みを置きます。
- Budgets(予算)+アラート:予定費用に対して実績を追跡し、しきい値で通知できます。
- Cost Anomalies(費用の異常):普段と違うスパイクを検知して通知できます。
- Billing Export → BigQuery:課金データ(使用量・費用など)を BigQuery に自動エクスポートして、SQL や Looker Studio で分析できます(原因追跡・定点観測に効く)。
3)体験設計ガードレール:そもそも踏みにくくする(MCP クライアント側)
(A)クライアント側で許可するツールだけを絞る(例:Gemini CLI)
MCP は「サーバが公開しているツールを、LLM が呼び出せる」仕組みです。
例えば慣れるまでは、「実行系だけ封印して、探索系だけ許可する」などが可能です。
BigQuery MCP のツールには、テーブル探索・スキーマ確認系(list_dataset_ids / get_table_info など)と、実クエリ実行の execute_sql があります。コスト事故の入口は当然 execute_sql なので、導入初期はここを閉じておくのも良いでしょう。
(B)LLM 用の隔離プロジェクト / 隔離データセットを作る
おすすめはこれです。
- LLM から叩ける BigQuery プロジェクトを分ける
- さらに参照できるデータセットを絞る(必要ならビューで公開)
これだけで事故の発生確率が大幅に下がります。
導入前の最小チェック(これだけ)
ここまでに説明した内容をチェックリストでまとめます。
-
重いテーブルはパーティション必須+
Require partition filterを有効化 -
自然言語クエリは「まず
dryRun」を手順として固定 - LLM 用にプロジェクト/データセットを分離
- 日次の上限(クエリ量/処理量)を設定して「燃え続け」を止める
-
goog-mcp-serverラベルで MCP 経由ジョブを追跡できるようにする - Billing Export を出して、日次・月次の異常を検知できるようにする
おわりに:民主化を守るための「統制」
BigQuery MCP は、正しく使えば データ民主化の強い武器です。
でも、LLM の気軽さは、そのままクエリ乱発や大量スキャンにつながります。
私は BQ 警察として、こう言いたいです。
「便利」はコスト事故の入り口になり得る。
だからこそ、便利さを守るためにガードレールが必要。
安全に、賢く、LLM × BigQuery を楽しんでいきましょう!
Discussion