What happened?
A streamable-http MCP server configured with requiresOAuth: true never starts the OAuth flow — the Google consent screen is never shown. LibreChat reports the server as "reinitialized successfully," oauthRequired: false, and the token store stays empty. The failure only surfaces later, when a tool is actually executed, as an auth error from the upstream API.
Concrete repro server: Google's official hosted Drive MCP at https://drivemcp.googleapis.com/mcp/v1.
Root cause
LibreChat only initiates OAuth when the MCP server returns HTTP 401/403 on connect:
MCPConnection.connectClient() emits oauthRequired only from the catch block, gated by isOAuthError(error) (401/403) — packages/api/src/mcp/connection.ts:1116 (matcher at :1503-1538).
MCPConnectionFactory.getOAuthTokens() finds no stored token, logs "...will trigger OAuth flow", then just returns null — it does not start anything (packages/api/src/mcp/MCPConnectionFactory.ts:283).
createConnection() then connects unauthenticated and, because the server replies 200, succeeds — so oauthRequired is never emitted (MCPConnectionFactory.ts:226-247).
reinitMCPServer therefore returns { success: true, oauthRequired: false, oauthUrl: null } (api/server/services/Tools/mcp.js).
- The frontend only opens the consent popup when
oauthRequired && oauthUrl (client/src/hooks/MCP/useMCPServerManager.ts:339-348), so nothing happens.
Google's hosted Drive MCP does not follow the 401-challenge pattern. Verified directly with curl (no Authorization header):
POST initialize → HTTP 200 (returns serverInfo/capabilities)
POST tools/list → HTTP 200 (returns the full tool list)
POST tools/call → HTTP 200 with an in-body JSON-RPC error: "Request is missing required authentication credential. Expected OAuth 2 access token..."
It does expose RFC 9728 metadata at /.well-known/oauth-protected-resource/mcp/v1 (authorization_servers: ["https://accounts.google.com/"]), so OAuth is clearly required — it's just never enforced at connect time. Net effect: requiresOAuth: true has effectively no effect for such servers.
Steps to reproduce
- Configure the server in
librechat.yaml:
mcpServers:
google-drive:
type: streamable-http
url: https://drivemcp.googleapis.com/mcp/v1
requiresOAuth: true
oauth:
client_id: ${GOOGLE_DRIVE_CLIENT_ID}
client_secret: ${GOOGLE_DRIVE_CLIENT_SECRET}
scope: "https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/drive.file"
token_url: https://oauth2.googleapis.com/token
authorization_url: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent
(Redirect URI https://<domain>/api/mcp/google-drive/oauth/callback registered in Google Cloud.)
- Reinitialize/connect the server in the UI — it reports success, no consent screen.
- Run a tool → fails: "Request is missing required authentication credential."
Expected behavior
When requiresOAuth: true (or oauth/protected-resource metadata is present) and no valid token exists, LibreChat should proactively start the OAuth flow instead of waiting for a 401 — i.e., treat "OAuth-required server + no token" the same as a 401 challenge.
Suggested fix
When requiresOAuth: true (or oauth/protected-resource metadata is present) and no valid token exists, proactively start the OAuth flow instead of waiting for a 401. LibreChat already discovers RFC 9728 metadata; the gap is only that getOAuthTokens() returns null silently (MCPConnectionFactory.ts:283) and oauthRequired is emitted solely from the 401 catch path (connection.ts:1116). Trigger it in the user-connection path (MCPConnectionFactory.createConnection), gated so tool discovery (discoverToolsInternal) and app-level startup connections still list tools unauthenticated.
This mirrors the fix being made in Dify for the identical server/behavior (see Related).
Related
Workaround
Front the server with a sidecar (nginx/Caddy) that returns 401 for unauthenticated requests, forcing LibreChat into the OAuth flow.
Version / Environment
- LibreChat
v0.8.6-rc1 (image registry.librechat.ai/danny-avila/librechat-dev:latest)
librechat.yaml schema version 1.3.11
- Transport:
streamable-http; server: Google hosted Drive MCP (https://drivemcp.googleapis.com/mcp/v1)
- Deployment: Docker Compose
What happened?
A
streamable-httpMCP server configured withrequiresOAuth: truenever starts the OAuth flow — the Google consent screen is never shown. LibreChat reports the server as "reinitialized successfully,"oauthRequired: false, and the token store stays empty. The failure only surfaces later, when a tool is actually executed, as an auth error from the upstream API.Concrete repro server: Google's official hosted Drive MCP at
https://drivemcp.googleapis.com/mcp/v1.Root cause
LibreChat only initiates OAuth when the MCP server returns HTTP 401/403 on connect:
MCPConnection.connectClient()emitsoauthRequiredonly from thecatchblock, gated byisOAuthError(error)(401/403) —packages/api/src/mcp/connection.ts:1116(matcher at:1503-1538).MCPConnectionFactory.getOAuthTokens()finds no stored token, logs"...will trigger OAuth flow", then just returns null — it does not start anything (packages/api/src/mcp/MCPConnectionFactory.ts:283).createConnection()then connects unauthenticated and, because the server replies 200, succeeds — sooauthRequiredis never emitted (MCPConnectionFactory.ts:226-247).reinitMCPServertherefore returns{ success: true, oauthRequired: false, oauthUrl: null }(api/server/services/Tools/mcp.js).oauthRequired && oauthUrl(client/src/hooks/MCP/useMCPServerManager.ts:339-348), so nothing happens.Google's hosted Drive MCP does not follow the 401-challenge pattern. Verified directly with curl (no Authorization header):
POST initialize→ HTTP 200 (returns serverInfo/capabilities)POST tools/list→ HTTP 200 (returns the full tool list)POST tools/call→ HTTP 200 with an in-body JSON-RPC error:"Request is missing required authentication credential. Expected OAuth 2 access token..."It does expose RFC 9728 metadata at
/.well-known/oauth-protected-resource/mcp/v1(authorization_servers: ["https://accounts.google.com/"]), so OAuth is clearly required — it's just never enforced at connect time. Net effect:requiresOAuth: truehas effectively no effect for such servers.Steps to reproduce
librechat.yaml:https://<domain>/api/mcp/google-drive/oauth/callbackregistered in Google Cloud.)Expected behavior
When
requiresOAuth: true(oroauth/protected-resource metadata is present) and no valid token exists, LibreChat should proactively start the OAuth flow instead of waiting for a 401 — i.e., treat "OAuth-required server + no token" the same as a 401 challenge.Suggested fix
When
requiresOAuth: true(oroauth/protected-resource metadata is present) and no valid token exists, proactively start the OAuth flow instead of waiting for a 401. LibreChat already discovers RFC 9728 metadata; the gap is only thatgetOAuthTokens()returns null silently (MCPConnectionFactory.ts:283) andoauthRequiredis emitted solely from the 401catchpath (connection.ts:1116). Trigger it in the user-connection path (MCPConnectionFactory.createConnection), gated so tool discovery (discoverToolsInternal) and app-level startup connections still list tools unauthenticated.This mirrors the fix being made in Dify for the identical server/behavior (see Related).
Related
drivemcp.googleapis.com/mcp/v1): Google returns HTTP 200 oninitializewithout auth, so OAuth never triggers and no tokens are stored. Proposed fix in fix(mcp): start OAuth when initialize succeeds without tokens langgenius/dify#36352 (discover OAuth metadata when tokens are absent, then initiate the flow). Confirms this is upstream Google behavior affecting multiple clients, not LibreChat-specific.Workaround
Front the server with a sidecar (nginx/Caddy) that returns 401 for unauthenticated requests, forcing LibreChat into the OAuth flow.
Version / Environment
v0.8.6-rc1(imageregistry.librechat.ai/danny-avila/librechat-dev:latest)librechat.yamlschema version1.3.11streamable-http; server: Google hosted Drive MCP (https://drivemcp.googleapis.com/mcp/v1)