Skip to content

Troubleshooting

Chenglei Yuan edited this page Jun 4, 2026 · 1 revision

Troubleshooting

Logs go to stderr. Bump verbosity with LOG_LEVEL=debug (or trace) to see discovery, token, and bridge activity. Secrets are redacted automatically.

Common problems

The browser never opens / I'm on a headless box

The proxy prints the authorize URL to stderr — copy it and open it manually. On a remote host, also forward the callback port. See Remote Hosts (SSH Port Forwarding).

"redirect_uri mismatch" from the IdP

The redirect URI the proxy uses (http://127.0.0.1:53682/callback by default, or your redirectUri / callbackPort override) must be registered exactly with your IdP. Register the loopback form, and keep the port in sync with OAUTH2_CALLBACK_PORT.

Login times out

The interactive flow waits callbackTimeoutSeconds (default 300) for you to finish. Increase it, or check that the callback port is reachable (firewall, or a broken SSH forward).

Startup aborts with a config/validation error (exit code 2)

The merged configuration failed zod validation, or a required field is missing, or token prefetch failed. Read the stderr message — it names the offending field. Common causes: missing clientId/tokenUrl, a non-integer port or timeout, or malformed OAUTH2_EXTRA_PARAMS JSON. See Configuration.

"insecure" / cleartext URL rejected at startup

A non-loopback http:// URL was supplied or discovered. Switch to https://, use a loopback SSH tunnel, or (trusted local testing only) set ALLOW_INSECURE_HTTP=true. See Security.

Discovery can't find the endpoints

Confirm the upstream serves /.well-known/oauth-protected-resource. If not, disable discovery (DISCOVERY_ENABLED=false) and set oauth2.tokenUrl (and oauth2.authorizationUrl for interactive login) explicitly. See Discovery.

Repeated 401s from the upstream

The proxy invalidates the token and retries once on a 401. Persistent 401s usually mean wrong scope/audience, an expired or revoked grant, or a client not authorized for the resource. Check the upstream's auth requirements.

Long tool calls get cut off

Raise upstream.timeoutMs (UPSTREAM_TIMEOUT_MS) above your MCP client's tool-call timeout. The default is 5 minutes.

Server notifications aren't arriving

Server-initiated notifications require the long-lived server stream. Ensure upstream.openServerStream is true and that the upstream supports a GET text/event-stream channel. The proxy disables the stream if the upstream answers 404/405. See Bridge Internals.

Cached login seems stuck / I want to re-authenticate

Delete the encrypted cache file in the token cache directory (see OAuth2 Grants and Tokens for paths) or point OAUTH2_TOKEN_CACHE_DIR elsewhere, then relaunch to trigger a fresh interactive login.

FAQ

Does it support multiple upstreams at once? No — one proxy process targets one upstream. Run multiple proxy instances for multiple upstreams.

Does it do per-user delegation / multi-tenant auth? No. It acts as a single configured OAuth2 client. For end-user identity, use the authorization_code grant so the token represents the logged-in user.

Can it inject my own Authorization header from the client? No. The proxy owns the upstream Authorization header. See Security.

Is the refresh-token cache safe? It's AES-256-GCM encrypted at rest, but the key lives beside it for non-interactive relaunch. It protects against casual exposure, not an attacker with access to your user account. See Security.

Still stuck? Open an issue at github.com/ChengleiYuan/mcp-oauth2-proxy/issues with LOG_LEVEL=debug output (secrets are redacted).

Clone this wiki locally