Building a BigQuery & Google Maps Agent with Google ADK & MCP
Objectives
Understand how to connect to MCP server and call MCP tools
Uses a real, working BigQuery agent to explore following foundational patterns
MCP Core Pattern
Agent Planner + Tools Pattern
Authentication Patterns for MCP
Tool Composition
What to build
An adk web agent that connects to Google’s fully managed BigQuery and Maps MCP servers, authenticates using Application Default Credentials, and answers natural language questions about data stored in BigQuery
MCP Core Pattern
Note
MCP (Concept):
MCP design separates the AI application from its tool integrations
MCP help create a modular and extensible approach to AI apps and tool development
Core architectural components
MCP Host (AI application)
MCP Server (adapter or a proxy for an external tool, data source, or API)
MCP Client (maintains the active connection with the Server)
Operationalizing MCP
Agent never imports a BigQuery & Google maps Python library directly. Instead, connect to a server that exposes operations as MCP tools
This decouples agent from the data source entirely
Use ADK
McpToolsetto automatically connect, discover, adapt and proxyConnect
Opens a persistent HTTPS connection to the MCP server endpoint using
StreamableHTTPConnectionParams
Discover
Calls
list_toolson the MCP server to get a manifest of all available tools and their input schemasNote: The
list_toolsmethod is an internal function of an MCP server used to advertise its capabilities to an Agent Development Kit (ADK) client. You would not typically call list_tools() directly as an end-user but rather configure your ADK agent to connect to an MCP server, upon which the agent will automatically discover and use the available tools
Adapt
Converts MCP tool schemas into ADK-native BaseTool objects that the
LlmAgentcan use.
Proxy
When the agent calls a tool, McpToolset forwards the call via call_tool and returns the result.
Agent Planner + Tools Pattern
Keep the instruction focused on reasoning strategy, not data
Agent doesn’t hardcode column names or SQL in the instructions
Agent discover schema at runtime via the MCP tools
This makes the agent resilient to schema changes
Authentication Patterns
BigQuery remote MCP (Model Context Protocol) servers use the OAuth 2.0 protocol with Google Cloud Identity and Access Management (IAM) for authentication and authorization
API keys are not supported.
Use Application Default Credentials (ADC) — the Google Cloud standard
gcloud auth application-default login(in terminal)import google.auth.transport.requests(import in agent; used in .refresh())
Tool Composition - Multi-Tool Agents
Composing multiple tool servers enables agent to reason across completely different data domains in a single conversation turn
i.e., Reason using data from BigQuery and Google maps MCP in a single conversation turn
Implementation
Agent
"""
agent.py
--------
Bakery Location Intelligence Agent.
Uses two Google-managed MCP servers:
1. BigQuery MCP — demographic, foot-traffic, pricing, and sales data
2. Maps MCP — place search, routing, and distance calculations
Run with:
cd bakery_agent/ # parent directory of mcp_bakery_app/
adk web .
"""
import os
import dotenv
from mcp_bakery_app import tools
import google.auth
from google.adk.agents import LlmAgent
from google.adk.models import Gemini
from google.genai import types
dotenv.load_dotenv()
# ── Project config ────────────────────────────────────────────────────────────
#PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT", "project_not_set")
DATASET = "mcp_bakery"
_, project_id = google.auth.default()
os.environ["GOOGLE_CLOUD_PROJECT"] = project_id
os.environ["GOOGLE_CLOUD_LOCATION"] = "global"
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "True"
# ── Initialise MCP toolsets ───────────────────────────────────────────────────
# Each call connects to the remote MCP server and discovers its tools.
# Toolsets are created once at module load time and reused across requests.
maps_toolset = tools.get_maps_mcp_toolset()
bigquery_toolset = tools.get_bigquery_mcp_toolset()
# ── Agent definition ──────────────────────────────────────────────────────────
root_agent = LlmAgent(
name="root_agent",
model=Gemini(
model="gemini-3-flash-preview",
retry_options=types.HttpRetryOptions(attempts=3),
),
instruction=f"""
You are an expert Bakery Location Intelligence Agent helping an entrepreneur
decide where to open a new bakery in the Los Angeles area.
You have access to two complementary data sources:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. BIGQUERY TOOLSET — structured data in the `{DATASET}` dataset
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Run all jobs from project: {project_id}
Always fully qualify table names: `{project_id}.{DATASET}.TABLE_NAME`
Available tables and their purpose:
• demographics — zip_code, neighborhood, median_household_income,
total_population, median_age, bachelors_degree_pct,
foot_traffic_index
• foot_traffic — zip_code, time_of_day (morning/afternoon/evening),
foot_traffic_score
• bakery_prices — store_name, product_type, price, region, is_organic
• sales_history_weekly — week_start_date, store_location, product_type,
quantity_sold, total_revenue
Workflow for BigQuery questions:
1. Use get_dataset_info to confirm the dataset exists.
2. Use get_table_info to inspect schema before writing SQL.
3. Use execute_sql to run SQL and retrieve results.
4. If a query errors, read the error message, fix the SQL, and retry.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2. MAPS TOOLSET — real-world location intelligence
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Use for:
• Searching for bakeries, cafés, or competitors in a specific area
Find the region with the highest foot traffic and lowest competitor density.
Always include a hyperlink to an interactive Google Maps view in your
response when showing location-based results.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RESPONSE GUIDELINES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• Combine data insights with real-world location context in every answer.
• Be specific — cite zip codes, neighbourhoods, dollar amounts, and scores.
• Format numbers clearly: currency as $X.XX, percentages as X.X%.
• When you recommend a location, justify it with at least two data points
from BigQuery AND one real-world observation from Maps.
• Keep responses concise but complete. Use bullet points for comparisons.
""",
tools=[maps_toolset, bigquery_toolset],
)
Tool collection
"""
tools.py
--------
MCP toolset factory functions for the Bakery Location Agent.
Two remote Google-managed MCP servers are used:
- BigQuery MCP : https://bigquery.googleapis.com/mcp
- Maps MCP : https://mapstools.googleapis.com/mcp
Authentication
- BigQuery uses Application Default Credentials (ADC).
Run once before starting the agent:
gcloud auth application-default login
- Maps uses a plain API key injected as an X-Goog-Api-Key header.
Set MAPS_API_KEY in your .env file.
"""
import os
import dotenv
import google.auth
import google.auth.transport.requests
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
# ── MCP server endpoints (Google-managed, no infra to run) ──────────────────
BIGQUERY_MCP_URL = "https://bigquery.googleapis.com/mcp"
MAPS_MCP_URL = "https://mapstools.googleapis.com/mcp"
# ── Maps toolset ─────────────────────────────────────────────────────────────
def get_maps_mcp_toolset() -> McpToolset:
"""
Connect to the Google Maps Platform MCP server.
Auth: API key passed as X-Goog-Api-Key header.
The MAPS_API_KEY env var must be set in .env (or the shell environment).
Tools exposed by this server (selection):
"""
dotenv.load_dotenv()
maps_api_key = os.getenv("MAPS_API_KEY", "no_api_key_found")
toolset = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=MAPS_MCP_URL,
headers={
"x-goog-api-key": maps_api_key,
},
)
)
print("✅ Maps MCP toolset configured (Streamable HTTP).")
return toolset
# ── BigQuery toolset ──────────────────────────────────────────────────────────
def get_bigquery_mcp_toolset() -> McpToolset:
"""
Connect to the Google BigQuery MCP server using Application Default Credentials.
Auth: OAuth 2.0 Bearer token generated from ADC, refreshed on every call
so the agent always starts with a valid token.
Also passes x-goog-user-project so BigQuery bills the correct project.
⚠ Token lifetime is ~60 min. If adk web runs longer, restart the agent
after re-authenticating: gcloud auth application-default login
Tools exposed by this server (selection):
- list_dataset_ids
- get_dataset_info
- list_table_ids
- get_table_info
- execute_sql
"""
credentials, project_id = google.auth.default(
scopes=["https://www.googleapis.com/auth/bigquery"]
)
# Force a refresh so the token is valid at agent startup
credentials.refresh(google.auth.transport.requests.Request())
oauth_token = credentials.token
toolset = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=BIGQUERY_MCP_URL,
headers={
"Authorization": f"Bearer {oauth_token}",
"x-goog-user-project": project_id,
},
)
)
print("✅ BigQuery MCP toolset configured (Streamable HTTP).")
return toolset
Deployment
uv run adk web . --no-reload
Terminal output:
INFO: Started server process [9643]
INFO: Waiting for application startup.
+-----------------------------------------------------------------------------+
| ADK Web Server started |
| |
| For local testing, access at http://127.0.0.1:8000. |
+-----------------------------------------------------------------------------+
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Output
Output
