👮

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 警察」として推進してきました。

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. 技術的ガードレール(止める)
  2. 運用的ガードレール(気づく・追う)
  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 経由の利用は気づかないうちに増えやすいので、まず早期検知の仕組みを置きます。

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 を楽しんでいきましょう!

株式会社 MBK デジタル

Discussion