Skip to content

AuthInfoMiddleware logs all_auth_paths_failed at WARNING on every streamable-http session when no bearer is expected #971

Description

@vyakunin

What happens

Running --transport streamable-http with no MCP-level auth provider, every session resolution logs at WARNING:

[AuthInfoMiddleware] No authenticated user resolved reason=all_auth_paths_failed token=none token_kind=no_bearer session_id=...

On a shared daemon this is ~1000 lines/day. It became ~90% of an 8 MB log, which is what made me look — every Google tool call was succeeding the whole time.

Why it fires

auth/auth_info_middleware.py (1.22.1, ~lines 228-242): with no Authorization: Bearer header the middleware records token_kind=no_bearer and falls through to its fallbacks — but the session-store / single-user fallback is gated on transport:

transport_mode = get_transport_mode()

if transport_mode == "stdio":
    # In stdio mode, check if there's a session with credentials
    # This is ONLY safe in stdio mode because it's single-user

On streamable-http that branch never runs, so nothing can resolve and the all_auth_paths_failed WARNING at ~line 364 is unconditional for this configuration.

For a deployment that does no per-request OAuth — auth is per-tool-call user_google_email plus credentials in WORKSPACE_MCP_CREDENTIALS_DIR — there is no bearer to send, so token_kind=no_bearer is the designed steady state rather than a failure. The server is already loopback-bound and single-credential; it says so itself at startup:

[WARNING] Legacy streamable-http mode has no MCP-level auth provider; binding to 127.0.0.1 by default.

So the server knows it is in the mode where this warning cannot mean anything.

--single-user / MCP_SINGLE_USER_MODE does not change it — that flag is read in main.py and auth/google_auth.py, never in this gate, which keys only on transport mode.

Suggested fix

Either would work; (2) is the smaller change.

  1. Let the single-user fallback apply when the server is loopback-bound with no auth provider configured, not only in stdio. The stated reason for the gate is "it's single-user", which is equally true here — the constraint is really single-user, and transport is standing in for it.

  2. Downgrade this specific line to DEBUG when the server has no MCP-level auth provider, keeping WARNING for the case that is actionable: a bearer that was presented and did not resolve (token_kind=google_oauth / jwt_or_other).

Workaround, for anyone hitting this

A filter dropping only records carrying both reason=all_auth_paths_failed and token_kind=no_bearer, so a rejected bearer still logs. The repo already has this pattern — core/log_formatter.py:

def install_noisy_log_filters() -> None:
    """Install targeted filters for known noisy third-party log lines."""
    target_logger = logging.getLogger("mcp.server.streamable_http")
    ...

Happy to open a PR for (2) along those lines if you'd prefer it in-tree.

Version: 1.22.1, --transport streamable-http, no MCP_ENABLE_OAUTH21, credentials on disk via WORKSPACE_MCP_CREDENTIALS_DIR.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions