Skip to content

BigQuery MCP sample stops working after the access token expires#7217

Description

@vishal-bulbule

馃敶 Required Information

Describe the Bug:

The bigquery_mcp sample (contributing/samples/integrations/bigquery_mcp/agent.py) fetches an
Application Default Credentials access token once, when the module is imported, and passes it as a
fixed Authorization header on the McpToolset. Access tokens expire after about an hour, and
nothing refreshes this one, so in a long-running adk web or adk api_server process every
BigQuery MCP call fails after the first hour until the process is restarted.

The session manager's own ADC refresh does not help here: it is only used on the mTLS path, and it
skips requests that already have an Authorization header.

Steps to Reproduce:

  1. gcloud auth application-default login
  2. Load the sample and call the list_dataset_ids tool: it works.
  3. Keep the same process running for more than an hour and call the tool again.

Expected Behavior:

The toolset keeps working as long as the process runs, refreshing the token when it expires.

Observed Behavior:

The first call works. After the token expires, about an hour later in the same process, the
tool call is rejected, while the same call with a refreshed token still works:

09:12:57 before expiry, current sample: OK, list_dataset_ids returned 52 datasets
09:13:00 before expiry, fixed sample  : OK, list_dataset_ids returned 52 datasets
10:18:17 current sample credentials valid: False
10:18:18 after expiry, current sample : FAILED (tool call rejected)
10:18:20 after expiry, fixed sample   : OK, list_dataset_ids returned 52 datasets

Note that tools/list on this server does not require a valid token, so listing tools still
succeeds with an expired one. The failure only shows on a tool call.

Environment Details:

  • ADK Library Version (pip show google-adk): main (d57c84f)
  • Desktop OS: macOS
  • Python Version (python -V): 3.11

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: N/A (the failure is in the MCP call, before the model)

馃煛 Optional Information

Regression:

No. The sample has always fetched the token once at import.

Additional Context:

A header_provider that refreshes the credentials when they are no longer valid fixes it, and
works on both the mTLS and the regular endpoint. I have a small PR ready.

Minimal Reproduction Code:

# Calls list_dataset_ids with the headers the sample's toolset holds, waits for
# the token to expire, then calls it again with the same headers.
import asyncio
import datetime

import httpx2
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client

import agent  # contributing/samples/integrations/bigquery_mcp/agent.py

URL = "https://bigquery.googleapis.com/mcp"


async def call_tool(label):
  headers = dict(agent.bigquery_mcp_toolset.connection_params.headers or {})
  client = httpx2.AsyncClient(headers=headers, timeout=60)
  try:
    async with streamable_http_client(URL, http_client=client) as (r, w):
      async with ClientSession(r, w) as session:
        await session.initialize()
        await session.call_tool("list_dataset_ids", {"projectId": "PROJECT_ID"})
    print(label, "OK")
  except Exception as e:
    print(label, "FAILED", type(e).__name__)


async def main():
  await call_tool("before expiry:")
  expiry = agent.credentials.expiry
  while datetime.datetime.utcnow() < expiry + datetime.timedelta(seconds=90):
    await asyncio.sleep(60)
  await call_tool("after expiry: ")


asyncio.run(main())

How often has this issue occurred?:

  • Always (100%)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions