modelcontextprotocol / modelcontextprotocol/go-sdk

proposal: client DPoP support (SEP-1932 baseline, no nonce)

Open
#1,139 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

P3
Dominant language
Go
Stars
5.1k
Forks
543
Avg merge
1d 17h
Merged PRs (30d)
37

Description

Background

SEP-1932 / RFC 9449 DPoP conformance scenarios landed in the conformance suite. #1138 teaches the harness everything-client to pass auth/dpop via a hand-rolled path. That is not SDK support — apps using AuthorizationCodeHandler + StreamableClientTransport still cannot obtain or present DPoP-bound tokens.

Today setMCPHeaders hardcodes Authorization: Bearer (mcp/streamable.go), and OAuthHandler only exposes TokenSource + Authorize — enough for Bearer, not for a fresh per-request DPoP proof (htm/htu/ath/jti).

Proposal (baseline only; no nonce)

Ship one SDK PR that:

  1. Helpers (e.g. under oauthex) — ES256 keypair, proof builder, ath, HTU normalize, RFC-vector tests. No nonce in the public API yet.
  2. Transport — use token.Type() (default Bearer); attach per-request proofs without breaking existing OAuthHandler implementors.
  3. AuthorizationCodeHandler — explicit opt-in DPoP config; proof on token exchange + refresh; implement the per-request hook.
  4. Conformance — rewrite everything-client auth/dpop to use the SDK path (acceptance test). Keep auth/dpop-nonce in baseline.yml.
Hook shape (prefer non-breaking)

Do not add a method to the OAuthHandler interface (breaking for external implementors). Prefer one of:

  • A (preferred): optional interface type-asserted in setMCPHeaders, e.g. handlers that also implement PrepareRequest(ctx, *http.Request) error get called after Authorization is set.
  • B: StreamableClientTransport.PrepareRequest func field set by the app / handler wiring.

AuthorizationCodeHandler with DPoP enabled would implement A (or set B).

Explicit opt-in only — no auto-enable from dpop_bound_access_tokens_required in v1.

Out of scope (follow later, same pattern)

  • auth/dpop-nonce (AS/RS use_dpop_nonce retry)
  • Server RequireDPoP middleware
  • Legacy SSE client OAuth/DPoP

Related

  • Harness: #1138
  • Spec: SEP-1932 / RFC 9449
  • Conformance scenarios: auth/dpop, auth/dpop-nonce

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 with mcp/streamable.go and the OAuthHandler and AuthorizationCodeHandler entry points to trace header setup, token exchange, refresh, and request preparation. Review the RFC-vector test locations and the everything-client auth/dpop path, then verify the conformance scenario uses the SDK path while auth/dpop-nonce remains in baseline.yml.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, authentication, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
40/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.