BigQuery Readonly MCP
Provides read-only access to Google BigQuery, allowing listing of projects, datasets, tables, schema retrieval, dry-run estimates, and safe SELECT query execution with configurable limits and error handling.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@BigQuery Readonly MCPRun a query to get the top 10 products by sales from dataset retail.orders"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
BigQuery Readonly MCP
BigQuery Readonly MCP is a FastAPI-based Custom MCP server for safely querying BigQuery from ChatGPT. It is designed as a reusable internal analytics MCP: each tool accepts project_id. The initial validation project was ice-sh; the current pilot default project is ice-mp.
Scope
GitHub repository:
Growth-Management/bigquery-readonly-mcpCloud Run deploy project:
ice-shInitial BigQuery validation project:
ice-shCurrent pilot default BigQuery project:
ice-mpInitial pilot user:
sinohara@impress.co.jpAllowed email domain:
impress.co.jpAllowed project IDs: empty for the current pilot, so the approved user may specify any project readable through their own BigQuery IAM
Allowed dataset IDs: empty by default, so BigQuery IAM controls dataset access
Default
maximumBytesBilled: 1GBDefault
max_results: 1000Default query timeout: 60 seconds
Current session backend: Firestore persistence enabled, with
SESSION_TTL_SECONDS=3600
Related MCP server: Google Sheets MCP Server
Security Model
The MCP server uses Google OAuth and BigQuery calls run with the authenticated user's access token. It does not use a service account to impersonate users for BigQuery query execution.
Users should receive the minimum required IAM permissions:
roles/bigquery.jobUseron the project where jobs are createdroles/bigquery.dataVieweron only the datasets they may inspect
The server enforces readonly SQL before execution. run_readonly_query and dry_run_query only allow one SELECT or WITH statement and reject DML, DDL, EXPORT, LOAD, grants, revokes, and procedure calls.
The server can also enforce application-side allowlists:
ALLOWED_PROJECT_IDS: when set, tool calls for projects outside the list are rejected before any BigQuery API call is made. When empty, project access is governed by the logged-in user's BigQuery IAM.ALLOWED_DATASET_IDS: when set,list_datasetsis filtered andlist_tables/get_table_schemacalls outside the list are rejected before any BigQuery API call is made.ALLOWED_USER_EMAILS: when set, tool calls from users outside the list are rejected before any BigQuery API call is made.
Allowlist rejections are recorded in audit logs with success=false and a rejection_reason.
MCP Tools
list_projectslist_datasetslist_tablesget_table_schemadry_run_queryrun_readonly_query
All tools accept project_id where applicable. If omitted, the current pilot default is ice-mp.
Endpoints
GET /healthzGET /healthPOST /mcpGET /oauth/authorizeGET /oauth/callbackPOST /oauth/tokenGET /.well-known/oauth-authorization-server
Use /health for Cloud Run external health checks. Cloud Run can reserve /healthz before requests reach the container, causing Google Frontend 404 responses even when the FastAPI app is healthy.
Local Development
Create a local environment and install dependencies:
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txtSet local environment variables. Do not commit real secrets. Local development may use memory; the pilot deployment uses Firestore.
export BASE_URL="http://localhost:8080"
export GOOGLE_OAUTH_CLIENT_ID="..."
export GOOGLE_OAUTH_CLIENT_SECRET="..."
export SESSION_SECRET="replace-with-random-value"
export ALLOWED_DOMAIN="impress.co.jp"
export DEFAULT_PROJECT_ID="ice-mp"
export ALLOWED_PROJECT_IDS=""
export ALLOWED_DATASET_IDS=""
export ALLOWED_USER_EMAILS="sinohara@impress.co.jp"
export MAXIMUM_BYTES_BILLED="1073741824"
export MAX_RESULTS="1000"
export QUERY_TIMEOUT_SECONDS="60"
export SESSION_TTL_SECONDS="3600"
export SESSION_STORE_BACKEND="memory"
export FIRESTORE_SESSION_COLLECTION="bigquery_mcp_sessions"ALLOWED_DATASET_IDS is a comma-separated list of project_id:dataset_id values, for example:
export ALLOWED_DATASET_IDS="ice-mp:example_datamart,ice-mp:example_source"Run the app:
uvicorn app.main:app --host 0.0.0.0 --port 8080 --reloadRun tests:
pytestOAuth Setup
Configure a Google OAuth Web application in ice-sh:
Consent screen: internal or equivalent organization-limited configuration
Authorized domain:
impress.co.jpRedirect URI:
https://<cloud-run-url>/oauth/callbackScopes:
openidemailprofilehttps://www.googleapis.com/auth/bigquery.readonly
Store these values in Secret Manager rather than the repository:
google-oauth-client-idgoogle-oauth-client-secretbigquery-mcp-session-secret
For MCP clients such as ChatGPT custom connectors, this service acts as the OAuth server and bridges to Google OAuth internally:
Authorization endpoint:
https://<cloud-run-url>/oauth/authorizeToken endpoint:
https://<cloud-run-url>/oauth/tokenMetadata endpoint:
https://<cloud-run-url>/.well-known/oauth-authorization-serverMCP endpoint:
https://<cloud-run-url>/mcp
The OAuth callback /oauth/callback is the Google OAuth redirect URI. MCP clients should be configured with their own callback URL in the client UI when prompted; the service preserves that callback through the OAuth state and returns an authorization code to the MCP client.
Session Storage
The current pilot deployment uses Firestore-backed session storage:
SESSION_STORE_BACKEND=firestore
FIRESTORE_SESSION_COLLECTION=bigquery_mcp_sessions
SESSION_TTL_SECONDS=3600Firestore persistence keeps a post-login MCP session available across Cloud Run instance restarts, scale-out, and new revisions. This was validated on 2026-06-08: a session document was created in Firestore, the access token was stored only as encrypted ciphertext, and the same mcp_session cookie continued to work after new Cloud Run revisions and GitHub Actions deploys.
Security behavior:
Access tokens are encrypted before being stored in Firestore.
Encryption uses AES-GCM with a key derived from
SESSION_SECRET.SESSION_SECRETrotation intentionally invalidates existing persisted sessions.Invalid or undecryptable session documents are deleted and treated as logged out.
Firestore persistence does not grant BigQuery access. BigQuery calls still use the logged-in user's OAuth token and IAM permissions.
Operational limit:
The server currently stores Google access tokens, not refresh tokens.
Google access tokens can expire before a longer application session TTL.
For this reason, the production pilot pins
SESSION_TTL_SECONDS=3600.Longer sessions require refresh-token support, including encrypted refresh-token storage, expiry handling, and re-login fallback.
Short-lived OAuth authorization requests and authorization codes remain in memory, so a login flow that overlaps a revision restart may still need to be retried. This does not affect already-created Firestore MCP sessions.
Cloud Run Deployment
The GitHub Actions workflow expects these GitHub Secrets:
GCP_PROJECT_ID:ice-shGCP_WORKLOAD_IDENTITY_PROVIDERGCP_DEPLOY_SERVICE_ACCOUNTBASE_URL: deployed Cloud Run URL or custom domain
The deploy service account needs only deployment permissions, such as Artifact Registry write and Cloud Run deploy permissions. It is not used for BigQuery query execution.
Current pilot deployment settings:
DEFAULT_PROJECT_ID=ice-mpALLOWED_PROJECT_IDS=ALLOWED_DATASET_IDS=ALLOWED_USER_EMAILS=sinohara@impress.co.jpMAXIMUM_BYTES_BILLED=1073741824MAX_RESULTS=1000QUERY_TIMEOUT_SECONDS=60SESSION_STORE_BACKEND=firestoreFIRESTORE_SESSION_COLLECTION=bigquery_mcp_sessionsSESSION_TTL_SECONDS=3600
Deployment is managed per GCP project. The Cloud Run deployment project is ice-sh; the current default BigQuery project is ice-mp, but project access is governed by the logged-in user's BigQuery IAM because ALLOWED_PROJECT_IDS is empty.
See docs/cloud-run.md for the full Phase 5 deployment procedure, including required APIs, Artifact Registry, Secret Manager, manual deploy, /health, Cloud Run session storage, and Cloud Logging checks.
See docs/github-actions-deploy.md for the preferred GitHub Actions deployment path using Workload Identity Federation.
See docs/phase-7-ice-sh-validation.md for the Phase 7 validation record.
See docs/rollout-policy.md for the Phase 8 rollout policy covering rollout patterns, allowlists, per-project deployment ownership, validation, audit retention, session persistence, and follow-up hardening.
See docs/all-project-iam-user-pilot.md for the approved pilot change that leaves project access to the named user's BigQuery IAM while keeping ALLOWED_USER_EMAILS=sinohara@impress.co.jp.
Initial Validation On ice-sh
Use a user account in the allowed domain with the required BigQuery IAM permissions, then verify:
MCP connects to
/mcpGoogle OAuth login succeeds
dataset list can be retrieved from
ice-shtable list can be retrieved
schema can be retrieved
dry run returns estimated bytes processed
SELECTexecutes within limitsDML and DDL are rejected before BigQuery execution
unauthorized projects return Access Denied
audit logs are emitted to Cloud Logging
Current status as of 2026-06-04: Phase 7 validation is complete. ice-sh readonly checks, SQL guard checks, Cloud Logging audit checks, and unauthorized-project rejection have been verified. The unauthorized-project check used bq-mcp-access-denied-test-2 with a project-level IAM Deny policy for sinohara@impress.co.jp; MCP dry_run_query returned HTTP 403 Forbidden.
Current Pilot
Current status as of 2026-06-09: pilot operation is enabled for sinohara@impress.co.jp only. ALLOWED_PROJECT_IDS is empty after security-owner approval, so the named user may use any BigQuery project readable through their own Google account IAM. DEFAULT_PROJECT_ID=ice-mp keeps ordinary use oriented to the pilot project. ALLOWED_DATASET_IDS is empty, so dataset access is governed by the logged-in user's BigQuery IAM. Firestore-backed MCP session persistence is enabled and validated with SESSION_TTL_SECONDS=3600.
Before expanding beyond the initial pilot user, repeat the rollout checklist in docs/rollout-policy.md and decide whether ALLOWED_PROJECT_IDS, ALLOWED_DATASET_IDS, or additional named-user restrictions are needed.
Audit Logging
Every tool call writes a single-line JSON audit event to stdout. Cloud Run ingests stdout into Cloud Logging, where the message, event_type, severity, and tool-specific fields can be filtered.
Cloud Logging is the required audit source. BigQuery audit dataset export has been evaluated as an optional Phase 8 P2 hardening path, recommended through a Cloud Logging Log Router sink when retention, reporting, or dashboard requirements justify it. Query history UI is deferred until exported audit data and administrator review requirements are confirmed.
Each audit event includes:
severitymessagetimestampevent_typeuser_emailtoolproject_iddatasettablebytes_processedsuccesserrorrejection_reasonfor failed or rejected calls
rejection_reason uses these categories:
project_not_allowed:project_idis outsideALLOWED_PROJECT_IDS.dataset_not_allowed:project_id:dataset_idis outsideALLOWED_DATASET_IDSfor dataset-targeting metadata tools.user_not_allowed: user email is outsideALLOWED_USER_EMAILS.sql_not_allowed: SQL guard rejected a non-readonly or unsafe query.bigquery_iam_denied: BigQuery returned a permission-denied response, including HTTP 403.bigquery_api_error: BigQuery or HTTP API failed for a non-403 API reason.execution_error: internal execution failed outside the known categories.
Some fields may be empty when the tool does not target a dataset or table, or when validation fails before a BigQuery job is created.
Example Cloud Logging filter:
jsonPayload.event_type="bigquery_mcp_tool_call"
jsonPayload.project_id="ice-mp"To inspect rejected calls by category:
jsonPayload.event_type="bigquery_mcp_tool_call"
jsonPayload.success=false
jsonPayload.rejection_reason="project_not_allowed"Current Phase Coverage
Phase 1: FastAPI app,
/healthz,/health,/mcp, OAuth authorize/callback skeleton, user email lookupPhase 2: six initial BigQuery tools
Phase 3: readonly SQL guard,
maximumBytesBilled,max_results, timeout, basic query error handlingPhase 4: structured JSON audit logs are emitted to stdout for Cloud Logging ingestion
Phase 5: Docker, env example, Secret Manager policy, Cloud Run deployment procedure,
/healthverification, and Firestore-backed session storage are complete forice-shPhase 6: GitHub Actions workflow, Workload Identity Federation, IAM, GitHub Secrets, deploy verification, and Firestore session env pinning are complete
Phase 7:
ice-shOAuth, MCP, BigQuery tools, readonly guard, unauthorized-project rejection, and audit log validation are completePhase 8:
ALLOWED_PROJECT_IDS,ALLOWED_DATASET_IDS,ALLOWED_USER_EMAILS, structured audit rejection categories, allow/reject tests, current pilot defaults, and Firestore-backed session persistence are implemented and validated; project access is currently user-IAM-scoped forsinohara@impress.co.jp; BigQuery audit dataset export and query history UI are evaluated; rollout patterns, allowlist policy, per-project rollout record template, per-project validation, audit requirements, session policy, and follow-up backlog are documented indocs/rollout-policy.md
This server cannot be installed
Maintenance
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/Growth-Management/bigquery-readonly-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server