You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor: adapt OAuthClientProvider at transport boundary (non-breaking)
Alternative to the breaking 'extends AuthProvider' approach. Instead of
requiring OAuthClientProvider implementations to add token() +
onUnauthorized(), the transport constructor classifies the authProvider
option once and adapts OAuth providers via adaptOAuthProvider().
- OAuthClientProvider interface is unchanged from v1
- Transport option: authProvider?: AuthProvider | OAuthClientProvider
- Constructor: if OAuth, store both original (for finishAuth/403) and
adapted (for _commonHeaders/401) — classification happens once, no
runtime type guards in the hot path
- 4 built-in providers no longer need token()/onUnauthorized()
- migration.md/migration-SKILL.md entries removed — nothing to migrate
- Changeset downgraded to minor
Net -142 lines vs the breaking approach. Same transport simplification,
zero migration burden. Duck-typing via isOAuthClientProvider()
('tokens' + 'clientMetadata' in provider) at construction only.
Unify client auth around a minimal `AuthProvider` interface
6
-
7
-
**Breaking:** Transport `authProvider` option now accepts the new minimal `AuthProvider` interface instead of being typed as `OAuthClientProvider`. `OAuthClientProvider` now extends `AuthProvider`, so most existing code continues to work — but custom implementations must add a `token()` method.
5
+
Add `AuthProvider` for composable bearer-token auth; transports adapt `OAuthClientProvider` automatically
8
6
9
7
- New `AuthProvider` interface: `{ token(): Promise<string | undefined>; onUnauthorized?(ctx): Promise<void> }`. Transports call `token()` before every request and `onUnauthorized()` on 401 (then retry once).
10
-
-`OAuthClientProvider` extends `AuthProvider`. Custom implementations must add `token()` (typically `return (await this.tokens())?.access_token`) and optionally `onUnauthorized()` (typically `return handleOAuthUnauthorized(this, ctx)`).
11
-
- Built-in providers (`ClientCredentialsProvider`, `PrivateKeyJwtProvider`, `StaticPrivateKeyJwtProvider`, `CrossAppAccessProvider`) implement both methods — existing user code is unchanged.
12
-
- New `handleOAuthUnauthorized(provider, ctx)` helper runs the standard OAuth flow from `onUnauthorized`.
13
-
- New `isOAuthClientProvider()` type guard for gating OAuth-specific transport features like `finishAuth()`.
14
-
- Transports no longer inline OAuth orchestration — ~50 lines of `auth()` calls, WWW-Authenticate parsing, and circuit-breaker state moved into `onUnauthorized()` implementations.
8
+
- Transport `authProvider` option now accepts `AuthProvider | OAuthClientProvider`. OAuth providers are adapted internally via `adaptOAuthProvider()` — no changes needed to existing `OAuthClientProvider` implementations.
9
+
- For simple bearer tokens (API keys, gateway-managed tokens, service accounts): `{ authProvider: { token: async () => myKey } }` — one-line object literal, no class.
10
+
- New `adaptOAuthProvider(provider)` export for explicit adaptation.
11
+
- New `handleOAuthUnauthorized(provider, ctx)` helper — the standard OAuth `onUnauthorized` behavior.
12
+
- New `isOAuthClientProvider()` type guard.
13
+
- New `UnauthorizedContext` type.
15
14
- Exported previously-internal auth helpers for building custom flows: `applyBasicAuth`, `applyPostAuth`, `applyPublicAuth`, `executeTokenRequest`.
16
15
17
-
See `docs/migration.md` for before/after examples.
16
+
Transports are simplified internally — ~50 lines of inline OAuth orchestration (auth() calls, WWW-Authenticate parsing, circuit-breaker state) moved into the adapter's `onUnauthorized()` implementation. `OAuthClientProvider` itself is unchanged.
if (errorinstanceofOAuthError&&error.code===OAuthErrorCode.InvalidClient) { ... }
204
204
```
205
205
206
-
### Client `OAuthClientProvider` now extends `AuthProvider`
207
-
208
-
Transport `authProvider` options now accept the minimal `AuthProvider` interface. `OAuthClientProvider` extends it, so built-in providers work unchanged — custom implementations must add `token()`.
Copy file name to clipboardExpand all lines: docs/migration.md
-38Lines changed: 0 additions & 38 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -648,44 +648,6 @@ The new design:
648
648
-`ProtocolError` with `ProtocolErrorCode`: For errors that are serialized and sent as JSON-RPC error responses
649
649
-`SdkError` with `SdkErrorCode`: For local errors that are thrown/rejected locally and never leave the SDK
650
650
651
-
### Client `authProvider` unified around `AuthProvider`
652
-
653
-
Transport `authProvider` options now accept the minimal `AuthProvider` interface rather than being typed as `OAuthClientProvider`. `OAuthClientProvider` extends `AuthProvider`, so built-in providers and most existing code continue to work unchanged — but custom
654
-
`OAuthClientProvider` implementations must add a `token()` method.
655
-
656
-
**What changed:** transports now call `authProvider.token()` before every request (instead of `authProvider.tokens()?.access_token`), and call `authProvider.onUnauthorized()` on 401 (instead of inlining OAuth orchestration). One code path handles both simple bearer tokens and
657
-
full OAuth.
658
-
659
-
**If you implement `OAuthClientProvider` directly** (the interactive browser-redirect pattern), add:
660
-
661
-
```ts
662
-
classMyProviderimplementsOAuthClientProvider {
663
-
// ...existing 8 required members...
664
-
665
-
// Required: return the current access token
666
-
async token():Promise<string|undefined> {
667
-
return (awaitthis.tokens())?.access_token;
668
-
}
669
-
670
-
// Required: runs the OAuth flow on 401 — without this, 401 throws with no recovery
**If you use `ClientCredentialsProvider`, `PrivateKeyJwtProvider`, `StaticPrivateKeyJwtProvider`, or `CrossAppAccessProvider`** — no change. These already implement both methods.
678
-
679
-
**If you have simple bearer tokens** (API keys, gateway tokens, externally-managed tokens), you can now skip `OAuthClientProvider` entirely:
680
-
681
-
```ts
682
-
// Before: had to implement 8 OAuthClientProvider members with no-op stubs
683
-
// After:
684
-
const transport =newStreamableHTTPClientTransport(url, {
@@ -215,11 +221,11 @@ export class SSEClientTransport implements Transport {
215
221
* Call this method after the user has finished authorizing via their user agent and is redirected back to the MCP client application. This will exchange the authorization code for an access token, enabling the next connection attempt to successfully auth.
0 commit comments