Toolbox version
us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:1.2.0 (also reproduces on main as of 2026-05-15)
What happens
Configuring an authService with type: generic, mcpEnabled: true, and Google as the OIDC provider per the docs at docs/en/documentation/configuration/authentication/generic.md:
kind: authService
name: claude-ai
type: generic
audience: "<client_id>.apps.googleusercontent.com"
authorizationServer: https://accounts.google.com
introspectionEndpoint: https://www.googleapis.com/oauth2/v3/tokeninfo
introspectionMethod: GET
introspectionParamName: access_token
mcpEnabled: true
scopesRequired:
- openid
- email
Every authenticated MCP request from claude.ai (which sends a Google OAuth opaque access token) returns HTTP 500 with this in the toolbox logs:
ERROR "unexpected error during MCP auth validation"
failed to parse introspection response: json: cannot unmarshal string into Go struct field .exp of type int64
Why
Google's https://www.googleapis.com/oauth2/v3/tokeninfo returns numeric fields as JSON strings:
{
"aud": "1234-abcd.apps.googleusercontent.com",
"sub": "...",
"scope": "openid https://www.googleapis.com/auth/userinfo.email",
"exp": "1747345200",
"expires_in": "3599",
"email": "...",
"email_verified": "true"
}
But internal/auth/generic/generic.go:367 declares the parser struct as:
var introspectResp struct {
Active *bool `json:"active"`
Scope string `json:"scope"`
Aud json.RawMessage `json:"aud"`
Audience json.RawMessage `json:"audience"`
Exp int64 `json:"exp"`
Iss string `json:"iss"`
}
Exp int64 cannot accept the quoted string, so unmarshaling fails before the audience/scope checks can even run. The Google example in the official docs therefore can't actually authenticate any real Google access token end-to-end.
Reproduction
- Deploy toolbox to Cloud Run with
mcpEnabled: true + the auth config above.
- Connect claude.ai (or any MCP client that uses MCP OAuth) and complete the Google sign-in.
- claude.ai sends
Authorization: Bearer <google_access_token> to /mcp.
- Toolbox returns HTTP 500 with the unmarshal error in logs.
Suggested fix
Use json.Number for the numeric fields, then convert to int64 with .Int64():
var introspectResp struct {
Active *bool `json:"active"`
Scope string `json:"scope"`
Aud json.RawMessage `json:"aud"`
Audience json.RawMessage `json:"audience"`
Exp json.Number `json:"exp"`
Iss string `json:"iss"`
}
json.Number accepts both raw numbers and number-as-string. Combined with decoder.UseNumber() (or by switching from json.Unmarshal to a json.Decoder with that option), this round-trips both shapes safely. Same fix applies if/when iat, nbf, or expires_in are added to the struct.
Workaround for now
A 30-line Go Cloud Function in front of tokeninfo that coerces the offending fields to numbers before returning. Toolbox's introspectionEndpoint points at the proxy. Happy to share the snippet if useful — would rather see this fixed upstream and drop it.
Why this matters
The Google example is the only worked path in the docs for end-to-end MCP OAuth with Google as the IdP. Anyone following it on a stock toolbox image hits this on the first authenticated request, and the failure mode (HTTP 500 with no client-visible error message) makes it look like a misconfiguration on the client side.
Toolbox version
us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:1.2.0(also reproduces onmainas of 2026-05-15)What happens
Configuring an
authServicewithtype: generic,mcpEnabled: true, and Google as the OIDC provider per the docs at docs/en/documentation/configuration/authentication/generic.md:Every authenticated MCP request from claude.ai (which sends a Google OAuth opaque access token) returns HTTP 500 with this in the toolbox logs:
Why
Google's
https://www.googleapis.com/oauth2/v3/tokeninforeturns numeric fields as JSON strings:{ "aud": "1234-abcd.apps.googleusercontent.com", "sub": "...", "scope": "openid https://www.googleapis.com/auth/userinfo.email", "exp": "1747345200", "expires_in": "3599", "email": "...", "email_verified": "true" }But
internal/auth/generic/generic.go:367declares the parser struct as:Exp int64cannot accept the quoted string, so unmarshaling fails before the audience/scope checks can even run. The Google example in the official docs therefore can't actually authenticate any real Google access token end-to-end.Reproduction
mcpEnabled: true+ the auth config above.Authorization: Bearer <google_access_token>to/mcp.Suggested fix
Use
json.Numberfor the numeric fields, then convert to int64 with.Int64():json.Numberaccepts both raw numbers and number-as-string. Combined withdecoder.UseNumber()(or by switching fromjson.Unmarshalto ajson.Decoderwith that option), this round-trips both shapes safely. Same fix applies if/wheniat,nbf, orexpires_inare added to the struct.Workaround for now
A 30-line Go Cloud Function in front of tokeninfo that coerces the offending fields to numbers before returning. Toolbox's
introspectionEndpointpoints at the proxy. Happy to share the snippet if useful — would rather see this fixed upstream and drop it.Why this matters
The Google example is the only worked path in the docs for end-to-end MCP OAuth with Google as the IdP. Anyone following it on a stock toolbox image hits this on the first authenticated request, and the failure mode (HTTP 500 with no client-visible error message) makes it look like a misconfiguration on the client side.