Google Cloud BigQuery connector: redirect_uri_mismatch, and the pasted OAuth Client ID appears unused (reopening #43959 / #48957 / #62271 with a full repro)

Status Open
Maintainer reply None cached
Activity 3 comments · opened Aug 8, 2026

Summary

The Google Cloud BigQuery directory connector fails OAuth with Error 400: redirect_uri_mismatch, and the Client ID / Client Secret the install dialog asks for do not appear to change the outcome. Other Google connectors (Gmail, Google Calendar, Google Drive) connect normally on the same account and machine.

This has been reported at least three times — #43959, #48957, #62271 — all closed as not planned. I am reopening it with a full reproduction because those reports were closed without one, and the failure is total: there is no way to use this connector.

Environment

  • Claude Desktop (Windows 11 x64), installed fresh from the official download during this test
  • Plan: Max
  • Google Cloud project with BigQuery enabled; user has roles/bigquery.jobUser and dataset-level dataViewer
  • Same account and machine: Gmail / Calendar / Drive connectors all connect successfully

Steps to reproduce

  1. Settings → Connectors → Add → Browse connectors → search bigquery → Google Cloud BigQuery
  2. The install dialog asks for four things: get an OAuth Client ID from Google, paste it, get a Client Secret, paste it
  3. Paste a Client ID and Secret from an OAuth client in your own Google Cloud project
  4. Continue → sign in with Google

Expected: the Google consent screen for the pasted OAuth client.

Actual: Access blocked: This app's request is invalid — Error 400: redirect_uri_mismatch. Error details give only flowName=GeneralOAuthFlow.

What I tried, and what it ruled out

I assumed this was my misconfiguration and worked through it systematically:

| Attempt | Result |
|---|---|
| OAuth client of type Desktop app | redirect_uri_mismatch |
| OAuth client of type Web application, with https://claude.ai/api/mcp/auth_callback in Authorized redirect URIs (the URI Anthropic's own docs specify) | redirect_uri_mismatch, identical |
| Re-run via the connector's retry / Connect button after registering the new client | identical error — the flow appears to reuse credentials cached from the first attempt rather than re-reading the dialog values |

The second row is the important one: a Web-application client with the documented redirect URI registered is exactly the configuration the docs describe, and it fails the same way.

I could not find any UI to clear or re-enter the stored credentials for an already-installed connector, so I could not force a clean retry with the new client. If that path exists, pointing me at it would let me narrow this further.

The likely cause, from the earlier reports

I did not capture the client id from the failing request myself, so I am citing rather than asserting: reporters on #43959 and #62271 state the failing request carries Anthropic's own OAuth client id (699272716442-c8vv4tojssh38erhltaj70k0lmi7p449.apps.googleusercontent.com) rather than the pasted one. That would explain why the pasted credentials make no difference, and why the redirect URI on our client is irrelevant.

If that is right, the dialog is asking users for credentials it does not use, and the fix is on the server side.

Impact

  • The connector is unusable — not degraded, unusable.
  • The dialog actively misleads: it asks for a Client ID and Secret, so every user reasonably concludes the failure is their Google Cloud configuration and goes looking there. I spent about an hour creating and re-creating OAuth clients before checking the issue tracker.
  • Three separate people filed this and were closed as not planned, so the next person will also start by assuming it is their own setup.

Suggestions, in order of cost

  1. If the pasted credentials genuinely are not used, remove or disable those fields for this connector, or label them clearly. That alone would save every future user the hour.
  2. Surface the actual client_id and redirect_uri in the error surface so a user can tell in one glance whose client failed.
  3. Register the correct redirect URI on the connector's OAuth client, if that is all this is.
  4. If the connector is not currently supported, marking it as such in the directory would be kinder than an install flow that cannot complete.

Workaround for anyone who lands here

Skip the directory connector. A ~250-line stdio MCP server calling bq with your own gcloud credentials works today, on any plan, with no OAuth involved:

claude mcp add bigquery \
  -e BQ_PROJECT=your-project \
  -e BQ_DATA_PROJECT=project-that-owns-the-data \
  -e BQ_LOCATION=your-location \
  -- /path/to/bigquery_mcp.py

Points worth building in if you write your own: enforce read-only in code rather than in the prompt (asked to delete rows, a model will write DELETE FROM), --dry_run every query first and refuse above a byte cap, and grant the identity bigquery.jobUser + dataViewer and not dataEditor so the warehouse refuses writes even if the code is wrong.

View original on GitHub ↗

3 Comments

skunpoj · 2 months ago

Follow-up from the same investigation — I filed this about the built-in connector's redirect_uri_mismatch, then hit a second, separate problem while running the stdio-MCP workaround. Posting it here rather than as a new issue because it's one thread start to finish.

To be clear about scope: this is not a claim that the built-in BigQuery connector is fixed, or that the workaround is verified end-to-end. The OAuth problem in the original post stands. This is one additional, reproducible finding about the MCP client's behaviour.

What happened. Running a custom stdio MCP server (BigQuery over the user's own gcloud credentials), Claude Desktop loaded it, discovered its tools, and called them — but queries appeared to "crash the connector."

The numbers that make it actionable:

  • A single bq query on that machine measured 30–90 seconds (a slow/nested VM; each bq invocation cold-starts Python + gcloud).
  • The MCP server's own log (%APPDATA%\Claude\logs\mcp-server-*.log) showed zero errors, zero exceptions, zero disconnects across the whole session — the server was still servicing the call.
  • Claude Desktop nonetheless reported the connector had crashed, at roughly the 60-second mark.

So a slow-but-correct tool is indistinguishable from a crashed one from the user's seat, and the surfaced error points at the wrong layer.

Requests, most useful first:

  1. Make the MCP tool timeout configurable (per-server in the config), or raise the default — some tools are legitimately slow on first call.
  2. When a call times out, report it as a timeout, distinct from a crash — the current wording sends people to debug a server that never failed.
  3. Surface the server's log path / stderr in the UI on failure, so users read the server's own account instead of inferring from the client.

For anyone hitting this meanwhile: the server-side fix is to stop paying a per-call subprocess — call the BigQuery REST API directly and cache the access token (and prime it at server start), which took a 30–90s call down to well under a second. That's a workaround for slow backends, not a substitute for a configurable client timeout.

rjyptv8tjw-beep · 1 month ago

Second repro, different symptom — Authorization with the MCP server failed, not redirect_uri_mismatch

Adding a data point from claude.ai web (Max plan, consumer Google account, macOS). Same connector, but the failure surfaces later in the flow than in the original report: Google consent succeeds, then the connector fails with Authorization with the MCP server failed. Three attempts, three references: ofid_956d55c61d0815d2, ofid_94db99e472885a96, ofid_8ce1af5a60ecfe7d.

Posting because I spent roughly two hours assuming it was my Google Cloud config, exactly as the original reporter predicted. It wasn't. Everything on the Google side verified correct:

| Checked | Result |
|---|---|
| MCP endpoint auth | tools/call on bigquery.googleapis.com/mcp → HTTP 200 with my own gcloud token; 401 without one (control) |
| Scope | /.well-known/oauth-protected-resource/mcp advertises auth/bigquery; the consent screen granted exactly that |
| IAM | roles/mcp.toolUser + roles/bigquery.jobUser + roles/bigquery.dataViewer, all verified by read-back |
| OAuth client | Web application, https://claude.ai/api/mcp/auth_callback registered |
| Audience | In production — no test-user gate |
| Discovery | /.well-known/oauth-protected-resource/{mcp,list_dataset_ids} → 200, valid metadata |

So the MCP server accepts my identity and advertises the scope I hold, while the connector cannot authorise. That is consistent with this issue's hypothesis that the pasted client isn't used — I did not capture the outgoing client_id myself, so I'm corroborating rather than confirming it.

One caution on the existing workaround, re: the timeout comment above — shelling out to bq per call is what makes it slow enough to look like a crash. Calling the REST API (POST /bigquery/v2/projects/{p}/queries) with a token cached at server start keeps queries well under a second and avoids the issue entirely. Worth building in from the start rather than as a fix.

Echoing the original suggestion: if the pasted Client ID and Secret genuinely are unused, removing or labelling those fields would save every future user the detour.

rjyptv8tjw-beep · 1 month ago

**Workaround that actually works: skip the directory connector, add the same URL as a custom connector**

Following up on my earlier comment — I got this working. The directory connector still fails, but the custom-connector path succeeds, and the difference is visible in the OAuth request.

The recipe

  1. Remove the Google Cloud BigQuery directory connector (Connectors → the connector → ⋮ → Remove). This is also the "clean retry" the original post couldn't find — removing is the only way to clear stored credentials.
  2. Add → Add custom connector, with:
  • Name: anything
  • MCP server URL: https://bigquery.googleapis.com/mcp
  • (It will refuse with "A connector with this URL already exists" if you skip step 1.)
  1. On the next screen it auto-detects Authentication: Sign in now and OAuth client: Use your own OAuth client. Enter:
  • OAuth client ID — from a Web-application client in your project, with https://claude.ai/api/mcp/auth_callback in Authorized redirect URIs
  • OAuth client secret — ⚠️ this is the bit that matters. The field is labelled "(optional)" and "Leave blank unless your authorization server requires one". Google does require it: a Web-application client cannot complete the token exchange with PKCE alone. Leaving it blank produced Authorization with Google Cloud BigQuery failed for me every time.
  1. Add → Connect → consent. Done.

Why this works where the directory connector doesn't

With the custom connector, the authorization request carries my client id:

client_id=2174680823XX-...apps.googleusercontent.com   <- mine
code_challenge_method=S256
redirect_uri=https://claude.ai/api/mcp/auth_callback
resource=https://bigquery.googleapis.com/mcp
scope=https://www.googleapis.com/auth/bigquery

That is direct evidence for this issue's hypothesis: the custom path uses the client you supply, and the directory path apparently does not.

Verified end to end

A query through the connector returned rows matching what bq returns independently on the same data (top 3 USA names 1910-2013: James 4,942,431 / John 4,834,422 / Robert 4,718,787).

Two things worth doing afterwards

  • Block the write tools. The connector exposes 8 tools; Execute sql (write-capable) sits under Write/delete tools. Set that group to Blocked, and grant the identity bigquery.jobUser + bigquery.dataViewer and not dataEditor, so writes fail at IAM too.
  • Set a query quota. The hosted server has no byte cap and BigQuery's default ceiling is 200 TiB/project/day. I set bigquery.googleapis.com/quota/query/usage (unit 1/d/{project}, value in MiB) to 30720 = 30 GiB/day via the Service Usage API, so a month at the daily maximum still fits inside the 1 TiB/month free tier.

The suggestion in the original post stands, with one addition: if the directory connector's credential fields are genuinely unused, remove them — and in the custom connector, the secret field being labelled "(optional)" is what cost me four failed attempts.