Skip to main content
Glama

bq-cost-guard

An MCP server that estimates the cost of a BigQuery query before you run it. It exposes a single tool, estimate_query_cost, which:

  1. runs a BigQuery dry run (free — no data is read or billed) using your Application Default Credentials,

  2. reports bytes processed and the estimated on-demand cost in USD, and

  3. returns warnings for common sources of wasted spend:

    • SELECT * (and SELECT t.*) — scanning columns you don't need

    • No partition filter — a partitioned table referenced by the query is scanned without a filter on its partition column (or _PARTITIONTIME / _PARTITIONDATE)

Example output

{
  "bytesProcessed": 1099511627776,
  "bytesProcessedHuman": "1.00 TiB",
  "estimatedCostUsd": 6.25,
  "currency": "USD",
  "usdPerTib": 6.25,
  "cacheHit": false,
  "warnings": [
    {
      "type": "SELECT_STAR",
      "message": "Query selects every column (SELECT *). Request only the columns you need to reduce bytes scanned."
    },
    {
      "type": "NO_PARTITION_FILTER",
      "table": "my-proj.sales.orders",
      "partitionColumn": "created_at",
      "message": "`my-proj.sales.orders` is partitioned by the partition column `created_at`, but the query has no filter on it, so the whole table will be scanned. Add a filter on the partition column (or _PARTITIONTIME / _PARTITIONDATE)."
    }
  ]
}

Errors (invalid SQL, missing permissions, ...) are returned as normal tool errors and never crash the server.

Related MCP server: BigQuery FinOps MCP Server

Prerequisites

  • Node.js ≥ 22.18

  • A Google Cloud project with the BigQuery API enabled

  • Permission to run dry runs (bigquery.jobs.create) and to read table metadata (bigquery.tables.get) — the latter is only needed for the partition-filter warnings; if metadata can't be read, the cost estimate still works and the warning is silently skipped

Install

git clone <this repo> bq-cost-guard
cd bq-cost-guard
npm install
npm run build

This produces the executable entry point at dist/src/index.js.

Authentication (Application Default Credentials)

No credentials live in this repository. The server authenticates with Application Default Credentials. Pick one of:

  1. gcloud (recommended for development)

    gcloud auth application-default login
    gcloud config set project your-project-id
  2. Service-account key file (kept outside the repo)

    export GOOGLE_APPLICATION_CREDENTIALS="$HOME/keys/bq-cost-guard-sa.json"

    Point it at a key file stored anywhere outside the checkout — the .gitignore in this repo blocks key files from being committed, but you should still keep keys outside the project tree.

  3. Workload identity — on GCE / GKE / Cloud Run / Cloud Shell, ADC is provided automatically; set GOOGLE_CLOUD_PROJECT if it cannot be inferred.

MCP client configuration

The server communicates over stdio. Replace /absolute/path/to/bq-cost-guard with the real checkout path.

Claude Desktop

Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json, Windows: %APPDATA%\Claude\claude_desktop_config.json):

{
  "mcpServers": {
    "bq-cost-guard": {
      "command": "node",
      "args": ["/absolute/path/to/bq-cost-guard/dist/src/index.js"],
      "env": {
        "GOOGLE_CLOUD_PROJECT": "your-project-id"
      }
    }
  }
}

Claude Code

claude mcp add bq-cost-guard \
  --env GOOGLE_CLOUD_PROJECT=your-project-id \
  -- node /absolute/path/to/bq-cost-guard/dist/src/index.js

Cursor

Add the same block to ~/.cursor/mcp.json (project-level: .cursor/mcp.json):

{
  "mcpServers": {
    "bq-cost-guard": {
      "command": "node",
      "args": ["/absolute/path/to/bq-cost-guard/dist/src/index.js"],
      "env": {
        "GOOGLE_CLOUD_PROJECT": "your-project-id"
      }
    }
  }
}

Any other stdio MCP client can use the same command/args/env shape. If you rely on a key file, add "GOOGLE_APPLICATION_CREDENTIALS": "/abs/path/to/key.json" to env (or export it in the shell the client inherits).

Environment variables

Variable

Required

Default

Purpose

GOOGLE_CLOUD_PROJECT

no

inferred

Default project for the BigQuery client

GOOGLE_APPLICATION_CREDENTIALS

no

—

Absolute path to a service-account key file (outside the repo)

BQ_COST_GUARD_USD_PER_TIB

no

6.25

On-demand price in USD per TiB scanned

Tool reference

estimate_query_cost

Argument

Type

Description

sql

string (required)

BigQuery standard SQL to estimate

Returns bytesProcessed, bytesProcessedHuman, estimatedCostUsd, currency, usdPerTib, cacheHit, and warnings[] (see example above).

How the warnings work

  • SELECT * — heuristic token scan of the SQL with comments and string literals removed; count(*) and friends do not trigger it.

  • NO_PARTITION_FILTER — the tool extracts FROM/JOIN table references (skipping CTEs, subqueries, UNNEST, wildcard and INFORMATION_SCHEMA references), reads each table's metadata to see whether it is partitioned (time- or range-partitioned), and checks the query's WHERE/ON clauses for the partition column or the _PARTITIONTIME/_PARTITIONDATE pseudo columns. Tables whose metadata can't be read are skipped silently.

Both are best-effort heuristics meant to prompt a second look, not a full SQL parser.

Pricing notes

The cost figure is bytesProcessed / 1024^4 × usdPerTib at the on-demand list price (default $6.25/TiB, override with BQ_COST_GUARD_USD_PER_TIB). It ignores reservations/editions and flat-rate pricing, free-tier monthly credits, storage and other charges, and result-cache effects (a cacheHit flag is reported instead). Treat it as an estimate, not a bill.

Development

npm test          # unit tests (BigQuery client fully mocked, no network/GCP access)
npm run typecheck # tsc --noEmit
npm run build     # emit dist/
npm start         # run the built server on stdio

Tests use Node's built-in test runner with the BigQuery client replaced by a mock of the exact client surface used here (query({ dryRun: true }) and dataset().table().getMetadata()), so the suite runs without credentials or network access. The MCP protocol surface is tested end-to-end over an in-memory transport.

Project layout:

src/
  index.ts      # entry point: wires real BigQuery client + stdio transport
  server.ts     # MCP server + estimate_query_cost tool registration
  estimator.ts  # bytes/cost/warnings orchestration
  bigquery.ts   # thin adapter over @google-cloud/bigquery (dry run + metadata)
  sql.ts        # pure SQL text analysis (SELECT *, table refs, partition filters)
  cost.ts       # pricing math and formatting
test/           # unit tests with the BigQuery client mocked

License

MIT

Available Tools

1 tool
estimate_query_costEstimate BigQuery query costA
Read-onlyIdempotent

Runs a BigQuery dry run (free; no data is read or billed) for the given standard SQL and returns bytes processed, the estimated on-demand cost in USD, and warnings such as SELECT * usage or missing filters on partitioned tables.

Inputs: sql (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.2/5.0

    Scored across 1 tool

    Disambiguation5/5

    There is only one tool, so there is no possibility of confusion or misselection. Its purpose (dry-run cost estimation) is unmistakable.

    Naming Consistency5/5

    The single name 'estimate_query_cost' follows a clean verb_noun convention and is descriptive. No inconsistent conventions exist to penalize.

    Tool Count3/5

    A single tool is thin even for a narrow 'cost guard' purpose; there is no way to estimate multiple queries, compare variants, or inspect table metadata. It functions but feels minimal.

    Completeness3/5

    Core dry-run cost estimation is covered with useful warnings, but the surface is a dead end: no schema/table inspection, partition discovery, or cost comparison to support the guard workflow. Notable gaps remain for a cost-management domain.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers