Skip to content

MCP Python SDK Implementation Gap-6: HTTP requests are not enforced to include Authorization header #1434

@younaman

Description

@younaman

Initial Checks

Description

Description

According to the spec, “Authorization MUST be included in every HTTP request from client to server, even within the same logical session.” The current Python SDK only attaches Authorization when the transport is created with an auth provider; if auth is omitted (or a non-auth client is used), requests are sent without Authorization. This makes it easy for client implementations to violate the MUST requirement.

Evidence

src/mcp/client/streamable_http.py uses httpx client with the provided auth parameter; no guard/warning if auth is None.

async with httpx_client_factory(
    headers=transport.request_headers,
    timeout=httpx.Timeout(transport.timeout, read=transport.sse_read_timeout),
    auth=transport.auth,  # If auth is None, there are no guards/rejects.
) as client:
@asynccontextmanager
async def streamablehttp_client(
    url: str,
    headers: dict[str, str] | None = None,
    timeout: float | timedelta = 30,
    sse_read_timeout: float | timedelta = 60 * 5,
    terminate_on_close: bool = True,
    httpx_client_factory: McpHttpClientFactory = create_mcp_http_client,
    auth: httpx.Auth | None = None,  # Caller can give "none"
) -> AsyncGenerator[
    tuple[
        MemoryObjectReceiveStream[SessionMessage | Exception],
        MemoryObjectSendStream[SessionMessage],
        GetSessionIdCallback,
    ],
    None,
]:

src/mcp/client/sse.py same pattern.

async with httpx_client_factory(
    headers=headers, auth=auth, timeout=httpx.Timeout(timeout, read=sse_read_timeout)
) as client:  # If auth is None, there are no guards/rejects.

src/mcp/client/auth.py adds Authorization only when tokens exist; there is no transport-level enforcement that all requests must go through this auth.

def _add_auth_header(self, request: httpx.Request) -> None:
    """Add authorization header to request if we have valid tokens."""
    if self.context.current_tokens and self.context.current_tokens.access_token:
        request.headers["Authorization"] = f"Bearer {self.context.current_tokens.access_token}"

Impact

Client implementations can inadvertently send unauthenticated HTTP requests, violating the spec’s MUST.

Proposed remediation

Enforce auth: if HTTP transport is used without an auth provider, fail fast or emit a strong warning.
Provide a secure default: default to OAuthClientProvider (or a no-op that still injects Authorization where possible) unless explicitly disabled.

Optionally add a runtime guard in post_writer/SSE paths that rejects sending requests when Authorization is absent (unless explicitly allowed for bootstrap endpoints).

I am not very confident with this finding. If one maintainer can make a double check and let me know if it's a real issue or not, I will be very appreciative!

Example Code

Python & MCP Python SDK

latest

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Significant bug affecting many users, highly requested featureauthIssues and PRs related to Authentication / OAuthbugSomething isn't workingready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions