From 064808d5d86ac338e8381bd80ea99a33e2a4bcc7 Mon Sep 17 00:00:00 2001 From: gauravSsinha Date: Tue, 26 May 2026 01:08:35 -0500 Subject: [PATCH] fix(auth): add Accept: application/json header to OAuth token requests Some OAuth providers (e.g., GitHub) return form-encoded responses by default unless the client explicitly requests JSON via the Accept header. This causes token exchange to fail with a parse error since the SDK expects JSON responses. Add 'Accept: application/json' to all token endpoint requests: - Authorization code token exchange (oauth2.py) - Token refresh (oauth2.py) - Client credentials grant (client_credentials.py) - Private key JWT grant (client_credentials.py) - Signed JWT assertion grant (client_credentials.py) This aligns with RFC 6749 Section 5.1 which specifies that token responses use JSON, and ensures interoperability with providers that require explicit content negotiation. Fixes #1523 Signed-off-by: Gaurav Kumar Sinha --- src/mcp/client/auth/extensions/client_credentials.py | 9 ++++++--- src/mcp/client/auth/oauth2.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/mcp/client/auth/extensions/client_credentials.py b/src/mcp/client/auth/extensions/client_credentials.py index cb6dafb407..a866593c62 100644 --- a/src/mcp/client/auth/extensions/client_credentials.py +++ b/src/mcp/client/auth/extensions/client_credentials.py @@ -92,7 +92,7 @@ async def _exchange_token_client_credentials(self) -> httpx.Request: "grant_type": "client_credentials", } - headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded"} + headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"} # Use standard auth methods (client_secret_basic, client_secret_post, none) token_data, headers = self.context.prepare_token_auth(token_data, headers) @@ -320,7 +320,7 @@ async def _exchange_token_client_credentials(self) -> httpx.Request: "grant_type": "client_credentials", } - headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded"} + headers: dict[str, str] = {"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"} # Add JWT client authentication (RFC 7523 Section 2.2) await self._add_client_authentication_jwt(token_data=token_data) @@ -481,5 +481,8 @@ async def _exchange_token_jwt_bearer(self) -> httpx.Request: token_url = self._get_token_endpoint() return httpx.Request( - "POST", token_url, data=token_data, headers={"Content-Type": "application/x-www-form-urlencoded"} + "POST", + token_url, + data=token_data, + headers={"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"}, ) diff --git a/src/mcp/client/auth/oauth2.py b/src/mcp/client/auth/oauth2.py index 72309f5775..65e29d02c8 100644 --- a/src/mcp/client/auth/oauth2.py +++ b/src/mcp/client/auth/oauth2.py @@ -402,7 +402,7 @@ async def _exchange_token_authorization_code( token_data["resource"] = self.context.get_resource_url() # RFC 8707 # Prepare authentication based on preferred method - headers = {"Content-Type": "application/x-www-form-urlencoded"} + headers = {"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"} token_data, headers = self.context.prepare_token_auth(token_data, headers) return httpx.Request("POST", token_url, data=token_data, headers=headers) @@ -447,7 +447,7 @@ async def _refresh_token(self) -> httpx.Request: refresh_data["resource"] = self.context.get_resource_url() # RFC 8707 # Prepare authentication based on preferred method - headers = {"Content-Type": "application/x-www-form-urlencoded"} + headers = {"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"} refresh_data, headers = self.context.prepare_token_auth(refresh_data, headers) return httpx.Request("POST", token_url, data=refresh_data, headers=headers)