Skip to main content
Glama
durjoi

BQ Agent Gateway

by durjoi
README.md
# BQ Agent Gateway

A read-only MCP server that connects Claude to BigQuery with layered guardrails
against dangerous and expensive SQL.

## What it blocks

**Static AST validation** (parsed with `sqlglot`, not regex):
- Anything that isn't a `SELECT` (no `INSERT`/`UPDATE`/`DELETE`/`MERGE`/DDL)
- `SELECT *` and `table.*` (but `COUNT(*)` is fine)
- `CROSS JOIN` and implicit cartesian products (comma joins with no `ON`)
- Multiple statements in one call (blocks `SELECT 1; DROP TABLE ...`)
- Queries referencing datasets outside an allowlist
- Missing `LIMIT` (auto-injected by default) and over-large `LIMIT`s

**BigQuery cost gate:**
- A dry-run estimates bytes scanned *before* running; over-threshold queries are rejected
- Execution sets `maximum_bytes_billed` as a hard server-side cap, so even a
  mis-estimate can't overspend — BigQuery kills the job instead
- Row count and wall-clock timeout are capped

## Tools exposed
`list_datasets`, `list_tables`, `describe_table`, `estimate_query_cost`, `run_query`.

## Setup

```bash
pip install -r requirements.txt        # or: uv pip install -r requirements.txt
cp .env.example .env                    # then edit values

# Auth: either set GOOGLE_APPLICATION_CREDENTIALS to a service-account key,
# or run:
gcloud auth application-default login
```

Give the service account only what it needs:
`roles/bigquery.dataViewer` + `roles/bigquery.jobUser`. The dataViewer role
cannot mutate data, so the read-only guarantee is enforced at the IAM layer too,
not just in code.

## Run

```bash
# stdio (for Claude Desktop)
python server.py

# or HTTP
fastmcp run server.py --transport http --port 8000
```

## Register with Claude Desktop

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

```json
{
  "mcpServers": {
    "bq-agent-gateway": {
      "command": "python",
      "args": ["/absolute/path/to/bq-agent-gateway/server.py"],
      "env": {
        "BQ_PROJECT": "my-gcp-project",
        "GOOGLE_APPLICATION_CREDENTIALS": "/absolute/path/to/key.json",
        "BQ_ALLOWED_DATASETS": "analytics,prod_reporting",
        "BQ_MAX_BYTES_SCANNED_GIB": "5"
      }
    }
  }
}
```

Restart Claude Desktop; the BigQuery tools appear in the tools menu.

## Design notes / defense in depth

The guardrails are structured as independent layers so a bypass of one doesn't
defeat the rest:

1. **IAM** — read-only service account can't write regardless of SQL.
2. **AST validation** — shape checks that regex can't do reliably.
3. **Dry-run cost gate** — the real budget protection for BigQuery.
4. **`maximum_bytes_billed`** — hard cap enforced by BigQuery itself.
5. **Row/time caps** — bound the response size and latency.

Toggle any static rule via env vars (see `.env.example`) without touching code.
```