modelcontextprotocol / modelcontextprotocol/python-sdk

Revocation requires client_secret to be present

Open Beginner friendly
#3,508 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v1 v2
Dominant language
Python
Stars
24.3k
Forks
4k
Avg merge
1d 1h
Merged PRs (30d)
31

Description

Initial Checks
Release line

2.x (current stable)

Description
What happened?

When Claude Code (or any other public client) triggers revocation - revoke handler requires client_secret to be present, as seen here:

https://github.com/modelcontextprotocol/python-sdk/blob/9972c21aa42054fb1450c5fc614761ed11847ec6/src/mcp/server/auth/handlers/revoke.py#L23

Per OAuth 2.0 specifications (RFC 6749 Section 2.3 and RFC 7009 Section 5), public clients do not have a client_secret and identify themselves using only client_id.
This leads to issue when public clients do not sent client secret at all.

In Pydantic v2, defining a field as str | None without a default value still marks the field as required (i.e., nullable value, but the key must exist in the request body).

Steps to reproduce
  1. Authenticate using a public OAuth client (e.g., Claude Code CLI).
  2. Trigger token revocation (e.g., re-authenticating or logging out).
  3. The authorization server returns a 400 Bad Request with:
    {"error":"invalid_request","error_description":"client_secret: Field required"}
Expected behavior

client_secret should be optional so public clients can revoke tokens without sending a client_secret key.

Fix

Adding default value for client_secret:

client_secret: str | None = None
Workaround

As workaround I'm overriding Request type:

from mcp.server.auth.handlers import revoke

class FixedRevocationRequest(revoke.RevocationRequest):
    client_secret: str | None = None

revoke.RevocationRequest = FixedRevocationRequest
Example Code
from mcp.server.auth.handlers.revoke import RevocationRequest
from pydantic import ValidationError

# Simulate a public client request: client_secret parameter omitted entirely
form_no_secret = {"token": "abc", "client_id": "local-scurri-mcp"}

try:
    req = RevocationRequest.model_validate(form_no_secret)
    print("no-secret: OK ->", req)
except ValidationError as e:
    print("no-secret: FAILS ->", e.errors()[0]["msg"])
Python & MCP Python SDK
MCP version:                                                                                                     2.2.0
Python version:                                                                                                 3.13.9

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/mcp/server/auth/handlers/revoke.py at RevocationRequest and review the linked validation behavior. Confirm that a public-client revocation request may omit client_secret, then use the provided Pydantic example with only token and client_id to verify that validation succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authentication
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
91/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.