Core GCP MCP Tools: Cloud Storage, BigQuery, Cloud Run, and Cloud Logging
Google Cloud doesn't ship a single official first-party MCP server the way some vendors do — what you'll actually deploy is one of a handful of community and Google-maintained implementations (the most active being googleapis/genai-toolbox for BigQuery/AlloyDB/Cloud SQL access, and several gcp-mcp community servers that wrap gcloud and the client libraries directly). In practice, most teams standardize on a server that exposes four surfaces: Cloud Storage (GCS), BigQuery, Cloud Run, and Cloud Logging. That's the working set this module covers, because it maps to the daily loop of a backend or data engineer: store artifacts, query data, deploy services, and read logs when something breaks.
Cloud Storage tools typically expose list_buckets, list_objects, read_object (with a size cap — most servers refuse to stream anything over a few MB inline), get_object_metadata, and sometimes generate_signed_url. This is read-heavy by design. Very few teams grant write/delete scopes to an agent's GCS tools, because a hallucinated gsutil rm -r gs://prod-bucket/** prompt is a real risk, not a theoretical one.
BigQuery is the tool surface with the most depth, and rightly so — it's where cost and correctness both bite. A well-built BigQuery MCP tool set gives you list_datasets, list_tables, get_table_schema, execute_query (usually with a mandatory or default dry_run first pass), and get_query_job_stats. Some servers (Google's own genai-toolbox) let you predefine parameterized queries as named tools — e.g., a get_daily_active_users tool backed by a fixed SQL template with a date parameter — which is a materially safer pattern than letting the model free-write SQL against production datasets.
Cloud Run tools cover the deploy-inspection half of the workflow: list_services, get_service, list_revisions, get_revision_traffic, and get_logs (usually delegating to Cloud Logging's logging.googleapis.com API with a resource filter pinned to run.googleapis.com/Revision). Almost no MCP server exposes deploy or update-traffic as a callable tool — that's deliberate. Inspection is safe to automate; mutating production traffic splits generally isn't, unless you've built and reviewed that guardrail yourself.
Cloud Logging, when it's not bundled into Cloud Run's log tool, exposes query_logs with Cloud Logging's filter syntax (not raw SQL — this trips people up the first time) and list_log_entries with severity and time-range bounds.
Typical GCP MCP tool surface (varies by server implementation):
gcs.list_buckets(project_id)
gcs.list_objects(bucket, prefix?)
gcs.read_object(bucket, path, max_bytes?)
bigquery.list_datasets(project_id)
bigquery.get_table_schema(dataset, table)
bigquery.execute_query(sql, dry_run=true, max_bytes_billed?)
run.list_services(region)
run.list_revisions(service_name)
run.get_logs(service_name, since, severity?)
logging.query_logs(filter, limit, freshness_window)
Tips
- Before adopting any GCP MCP server, read its source to confirm which tools are read-only vs. mutating — READMEs are frequently aspirational and lag the actual tool list.
- Google'sgenai-toolboxproject is the most actively maintained option as of 2026 and supports BigQuery, AlloyDB, Cloud SQL, and Spanner through one config-driven server — start there before rolling a custom one.
- If a server exposes a rawexecute_querywithout adry_rundefault, wrap it yourself before giving it to a coding agent unsupervised.
GCP MCP Authentication: Service Accounts, ADC, and Project Scoping
GCP MCP servers authenticate the same way any other GCP client library does — there's no MCP-specific auth mechanism. That means you have three realistic options: Application Default Credentials (ADC) from a logged-in gcloud session, a service account JSON key file, or Workload Identity Federation if the MCP server itself runs inside GCP (a GKE pod or Cloud Run job). For a developer's laptop running Claude Code or Cursor, you'll use one of the first two.
ADC is the fastest path and the one I'd default to for individual developer use:
gcloud auth application-default login
gcloud config set project my-gcp-project-id
The downside: ADC credentials inherit your personal IAM roles. If you're a Project Owner, so is your AI agent, for every action that server exposes tools for. That's fine for a solo side project, dangerous for anything touching a shared production project.
For team or CI use, a dedicated service account with narrowly scoped roles is the correct pattern:
gcloud iam service-accounts create mcp-agent-readonly \
--display-name="MCP Agent - Read Only Data Access" \
--project=my-gcp-project-id
gcloud iam service-accounts keys create ./mcp-agent-key.json \
--iam-account=mcp-agent-readonly@my-gcp-project-id.iam.gserviceaccount.com
gcloud projects add-iam-policy-binding my-gcp-project-id \
--member="serviceAccount:mcp-agent-readonly@my-gcp-project-id.iam.gserviceaccount.com" \
--role="roles/bigquery.dataViewer"
Point the MCP server at the key with the standard environment variable — every Google client library respects this without any server-specific config:
export GOOGLE_APPLICATION_CREDENTIALS="/Users/you/.gcp/mcp-agent-key.json"
Project scoping matters more than people expect. Most GCP MCP servers take a project_id either as a startup argument or as a per-call parameter. If your organization has 40 projects and the server defaults to whatever gcloud config get-value project currently returns, you can end up querying the wrong project's BigQuery datasets silently — the query succeeds, it's just against staging-project instead of analytics-project, and nothing errors out. Pin the project explicitly in the MCP server config rather than relying on ambient gcloud state.
{
"mcpServers": {
"gcp": {
"command": "npx",
"args": ["-y", "@google-cloud/mcp-toolbox"],
"env": {
"GOOGLE_APPLICATION_CREDENTIALS": "/Users/you/.gcp/mcp-agent-key.json",
"GOOGLE_CLOUD_PROJECT": "analytics-project-prod"
}
}
}
}
Rotate service account keys. A JSON key file with no expiration sitting in ~/.gcp/ is a standing liability — set a 90-day rotation reminder or, better, move to Workload Identity Federation once the workflow stabilizes and you're running the MCP server from a machine GCP can attest.
Tips
- Never commit a service account key to a repo — add*.jsonkey file patterns to.gitignoreexplicitly, and to.git-secretsorgitleaksconfig if you run pre-commit scanning.
- Rungcloud auth application-default print-access-tokento sanity-check which identity a session is actually using before trusting an MCP tool's output.
- If two team members share a machine, ADC credentials are per-OS-user, not per-project — a stalegcloud auth loginunder a different account is a common source of "wrong project" confusion.
What AI Can Automate on GCP: Data Queries, Deploy Inspection, and Log Analysis
The honest scope of what an AI coding agent does well with GCP MCP is narrower than the marketing suggests, and that's fine — narrow-but-reliable beats broad-but-flaky. Three categories consistently work well.
Data queries. Ask an agent "what's the schema of the orders table in analytics.raw_events, and give me a query for daily order counts by region for the last 30 days" — it fetches the schema via bigquery.get_table_schema, writes correct SQL against the real column names (not guessed ones), dry-runs it, reports the bytes scanned, and only then offers to execute. This eliminates the single most common BigQuery mistake: writing SQL against a schema you're misremembering from three months ago.
Deploy inspection. "Why did the last deploy of checkout-api fail health checks?" is a genuinely strong use case. The agent lists revisions, diffs the failing one against the last healthy one, pulls the relevant log window via Cloud Logging, and — this is the part that saves real time — correlates a specific log line (e.g., a missing env var causing a startup panic) with the revision's config diff. That's a 20-minute manual investigation compressed into two or three tool calls.
Log analysis. Structured log querying is where MCP genuinely beats a human clicking through the Cloud Logging console. Give it a time window and a severity filter, and it will summarize error clusters, count occurrences, and flag the log entries worth reading in full — instead of you scrolling through 4,000 INFO lines to find the 12 ERROR ones.
Prompt example:
"Query Cloud Logging for ERROR-severity entries from the
checkout-api Cloud Run service in the last 2 hours. Group by
error message pattern and tell me which one is new (not present
in the prior 24 hours)."
What doesn't work well, and where I'd keep a human in the loop: multi-step data pipeline orchestration decisions (should this backfill run now or during the maintenance window?), IAM policy changes (the blast radius of a wrong grant is too high for autonomous execution), and any BigQuery query touching more than a few hundred GB without a human confirming the cost estimate first. The agent can propose all of these; it shouldn't execute the destructive or expensive ones unattended.
Tips
- Frame Cloud Run debugging prompts around a specific revision ID, not just the service name — agents sometimes grab the wrong revision when multiple are serving split traffic.
- For log analysis, always specify a bounded time window in the prompt; an unboundedquery_logscall against a busy service can return megabytes of entries and blow your context budget.
- Treat AI-generated BigQuery SQL as a draft for review, not an execute-on-sight artifact, until you've built enough trust in a specific agent/prompt combination on your actual schemas.
IAM Role Design and Billing Guardrails for Agent Access
This is the section that actually prevents the bad outcomes, so treat it as load-bearing, not optional. The default temptation — grant the agent's service account roles/editor or roles/owner "to keep things simple" — is the single most common GCP MCP misconfiguration I see, and it's the one that turns a scoped tool integration into an unbounded blast radius.
Build role grants around the four tool surfaces, not around convenience:
PROJECT_ID="my-gcp-project-id"
SA="mcp-agent-readonly@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="roles/bigquery.dataViewer"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="roles/bigquery.jobUser"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="roles/storage.objectViewer"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="roles/run.viewer"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA}" --role="roles/logging.viewer"
Notice what's absent: bigquery.dataEditor, storage.objectAdmin, run.admin, and anything in the roles/iam.* family. If a future workflow genuinely needs write access — say, an agent that provisions a new BigQuery dataset for a project — grant that as a separate, explicitly-scoped service account used only for that workflow, not a blanket upgrade to the general-purpose one.
Billing guardrails are the second half of the story, because bigquery.jobUser still lets someone run a query that scans your entire 40 TB events table. Two mechanisms compound well together:
- Per-user/per-project custom quotas on
bigquery.googleapis.com/quota/query/usage, capped daily. maximumBytesBilledenforced at the query level, which the MCP server should set on everyexecute_querycall, not just recommend.
-- Setting a hard cap on bytes billed for a single query.
-- If the query would scan more than 1 GB, it fails before running
-- instead of silently billing for a full table scan.
SELECT region, COUNT(*) AS orders
FROM `analytics-project.raw_events.orders`
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY region;
bq query --use_legacy_sql=false \
--maximum_bytes_billed=1000000000 \
'SELECT region, COUNT(*) AS orders
FROM `analytics-project.raw_events.orders`
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY region'
Set an org-level budget alert as the last line of defense — this catches the scenario no per-query cap does: dozens of small, individually-cheap queries adding up over a runaway agentic loop.
gcloud billing budgets create \
--billing-account=XXXXXX-XXXXXX-XXXXXX \
--display-name="MCP Agent BigQuery Spend" \
--budget-amount=200USD \
--threshold-rule=percent=0.5 \
--threshold-rule=percent=0.9 \
--threshold-rule=percent=1.0
Tips
- Grantbigquery.jobUserat the project level butbigquery.dataViewerat the dataset level where possible —bq datasets updatesupports per-dataset ACLs that are tighter than project-wide viewer access.
- Setmaximum_bytes_billedas a default in the MCP server's own config, not something you rely on the LLM to remember to pass on every call.
- Review the Cloud Audit Logs for the service account monthly (protoPayload.authenticationInfo.principalEmail="mcp-agent-readonly@...") — this is the ground truth for what the agent actually did, independent of what it reported doing.
Tips
Tips
- Start every new GCP MCP integration against a sandbox project with synthetic data, not production — the failure modes (wrong project, oversized scan, wrong bucket) are cheap to survive there and expensive everywhere else.
- Pin exact tool versions in your MCP config (npx -y @package@1.4.2instead of unpinned@latest) — GCP MCP servers are young enough that breaking changes between minor versions are common.
- Document the service account's granted roles next to the MCP config file itself (a comment or a siblingIAM.md) — six months later nobody remembers whymcp-agent-readonlycan also read Cloud Storage.