Skip to main content
Glama

mcp-data-tools

A governed Model Context Protocol (MCP) server for data-platform operations.

Give an LLM agent the ability to query BigQuery, inspect GCS, trigger Airflow DAGs, and run data-quality checks — without giving it the ability to scan an unbounded table, touch a dataset outside its remit, or run anything but a read-only query. Every tool call is dry-run estimated, checked against an explicit allow-list, capped by a cost/row ceiling, and written to an audit trail before it ever reaches a real backend.

Test Lint Security License: MIT Python 3.11+

Why this exists

Agentic data platforms (NL2SQL assistants, on-call/triage agents, data copilots) all eventually hit the same problem: an LLM agent that can call a real query engine is, by construction, an LLM agent that can be prompt-injected or simply mistaken into running a full-table scan, reading a dataset it shouldn't, or mutating data it was only supposed to read. Most MCP servers for data tools wrap the backend SDK directly and call it done. mcp-data-tools treats the guardrail layer as the actual product:

  • Allow-listed resources. BigQuery tables, GCS buckets, and Airflow DAGs are matched against explicit glob patterns. Nothing is reachable by default.

  • Enforced read-only SQL. A lexical check rejects anything that isn't a bare SELECT/WITH, with comments and string literals stripped first so neither can be used to smuggle a write statement past the check.

  • Two-phase table authorization. A cheap preflight check denies obviously out-of-policy queries before any backend call — so an agent can't even use a dry run to probe the existence/schema of a non-allow-listed table — followed by an authoritative check against the backend's own dry-run result. See docs/SECURITY.md.

  • Real, enforced cost ceilings. max_bytes_billed is passed straight through to the backend's execution API, so it holds even if a stale estimate undercounts a table that grew in the meantime.

  • A fail-closed audit trail. Every decision — allowed or denied — is logged. If the audit sink itself is unavailable, the request is denied rather than let through unaudited.

Related MCP server: @us-all/dbt-mcp

Architecture at a glance

flowchart LR
    Agent[LLM Agent] -->|MCP / JSON-RPC| Server[mcp-data-tools]
    Server --> Guardrails[GuardrailEngine]
    Guardrails --> Audit[(Audit Trail)]
    Server --> BigQuery[(BigQuery)]
    Server --> GCS[(GCS)]
    Server --> Airflow[(Airflow)]

Full component and sequence diagrams, plus the design decisions and trade-offs behind them, are in docs/ARCHITECTURE.md.

Tools exposed

Tool

What it does

Guarded by

bigquery_estimate_cost

Dry-runs SQL, returns projected bytes/cost/tables — no data returned

Read-only check, table allow-list

bigquery_query

Executes a read-only SQL query

Read-only check, table allow-list, byte/row ceiling

data_quality_check

Runs null_rate / uniqueness / freshness / row_count checks against a table

Same as bigquery_query, per check

gcs_inspect_object

Fetches metadata for one GCS object

Bucket allow-list

gcs_list_objects

Lists objects under a prefix

Bucket allow-list

airflow_trigger_dag

Triggers a new Airflow DAG run

DAG-id allow-list

airflow_get_dag_run_status

Fetches a DAG run's status

DAG-id allow-list

Quick start

No cloud credentials required — this runs entirely against in-memory mock adapters, the same ones the test suite uses:

git clone https://github.com/umamahesh-ade/mcp-data-tools.git
cd mcp-data-tools
python -m venv .venv && source .venv/bin/activate
pip install -e .
python examples/quickstart.py

You should see an allowed query, a data-quality report, a denied query (hitting a table outside the allow-list), and the resulting audit trail — all in a few dozen lines of output.

Installation

pip install -e ".[dev]"          # base + testing/linting
pip install -e ".[gcp]"          # + google-cloud-bigquery, google-cloud-storage
pip install -e ".[azure]"        # + Azure Key Vault secret resolution
pip install -e ".[all]"          # everything

Running the real MCP server

cp configs/full-example.yaml configs/my-config.yaml
# edit configs/my-config.yaml: project ids, allow-list patterns, Airflow URL
cp .env.example .env
# fill in AIRFLOW_TOKEN, GOOGLE_APPLICATION_CREDENTIALS, etc.

mcp-data-tools validate-config --config configs/my-config.yaml
mcp-data-tools serve --config configs/my-config.yaml

The server speaks MCP over stdio, so it's meant to be launched by an MCP client host (Claude Desktop, an agent framework's MCP client, etc.), not run standalone as a long-lived network service. See docs/CONFIGURATION.md for every config field.

Wiring into an MCP client

Most MCP client hosts take a command + args:

{
  "mcpServers": {
    "data-tools": {
      "command": "mcp-data-tools",
      "args": ["serve", "--config", "/path/to/configs/my-config.yaml"],
      "env": { "AIRFLOW_TOKEN": "..." }
    }
  }
}

Docker

make docker-build
make docker-run          # docker compose run --rm mcp-data-tools

Testing

make test        # pytest
make test-cov     # pytest with coverage (fails under 85%)

The suite includes a real MCP-protocol integration test (tests/integration/test_mcp_server_e2e.py) built on the official mcp SDK's in-memory client/server harness — it exercises the exact list_tools/call_tool JSON-RPC path a real agent host uses, not a hand-rolled fake.

Examples

FAQ

Does this replace IAM? No. It's a fast, auditable policy layer in front of your backends; the service account/role this server runs as should still be scoped to least privilege. See docs/SECURITY.md for the full defense-in-depth argument.

Can it write data? No tool in this package can execute DDL/DML, delete/write a GCS object, or do anything to Airflow beyond triggering a run and reading status. guardrails.read_only: false is not currently a supported configuration surface for SQL tools.

Does it support Databricks/Snowflake instead of BigQuery? Not yet — the query engine is behind ports.QueryEnginePort, so adding one is a new adapter, not a rewrite. See "Extensibility" in docs/ARCHITECTURE.md.

Why do dry runs still get preflight-checked if they don't cost anything? Because a dry run against BigQuery still reveals whether a table exists and its schema/referenced-table list, which is itself information the allow-list is meant to gate. See docs/SECURITY.md.

Troubleshooting

Symptom

Likely cause

Fix

Server starts with zero tools registered

No bigquery/gcs/airflow section in config

Add the relevant section — see configs/full-example.yaml

ConfigurationError: Environment variable 'X' is not set

A ${ENV:X} placeholder or SecretRef references an unset var

Export it or add it to .env

Every query is denied with "not in the allow-list"

guardrails.allowed_table_patterns doesn't match your tables

Check the exact project.dataset.table string in the denial's audit event

BackendOperationError: google-cloud-bigquery is required...

bigquery section configured but [gcp] extra not installed

pip install -e ".[gcp]"

Docker build fails on pip install

No network egress in your build environment

Use docker build --network=host or a proxy-aware base image

Performance

  • Dry runs are always performed before execution (require_dry_run), so every query pays one extra round-trip to BigQuery — a deliberate trade-off of latency for cost/safety guarantees. See docs/ARCHITECTURE.md for why this isn't optional.

  • The regex-based preflight check (core/sql_tables.py) avoids that round-trip entirely for queries that are obviously out of policy.

  • All adapters use a bounded exponential-backoff retry (core/retry.py) with jitter, so a single transient failure doesn't multiply into a retry storm against the backend.

  • guardrails.max_rows_returned and max_bytes_billed bound both the memory footprint of a single tool call and the backend cost, regardless of how a caller phrases the query.

Security

See SECURITY.md for the threat model, the two-phase SQL authorization design, secret handling, and how to report a vulnerability.

Roadmap

See the [Unreleased] section of CHANGELOG.md: BigQuery-backed audit sink, a Databricks/Delta Lake query-engine adapter, and column/row-level masking policy.

Contributing

See CONTRIBUTING.md for the dev setup, the architecture ground rules new contributions are expected to follow, and the lint/type/security/test gate every PR must pass.

License

MIT

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mahesh-cr-de/mcp-data-tools'

If you have feedback or need assistance with the MCP directory API, please join our Discord server