Skip to content

OAuth Update: Adding the Client Credentials & Token Exchange Grant Types #882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 75 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
833a105
Add client credentials OAuth grant
SoldierSacha Jun 3, 2025
66c7e67
Merge pull request #1 from sacha-development-stuff/codex/add-support-…
SoldierSacha Jun 3, 2025
813168a
Allow client credentials in dynamic registration
SoldierSacha Jun 3, 2025
dbbc6ce
Merge pull request #2 from sacha-development-stuff/codex/review-and-i…
SoldierSacha Jun 3, 2025
3f2a351
Refactor OAuth helpers
SoldierSacha Jun 3, 2025
62c729d
Merge pull request #3 from sacha-development-stuff/codex/review-imple…
SoldierSacha Jun 3, 2025
5212ce0
clean up code
SoldierSacha Jun 3, 2025
d9c751f
linting
SoldierSacha Jun 4, 2025
7848e68
Fix tests and pyright errors
SoldierSacha Jun 4, 2025
e325b95
Merge pull request
SoldierSacha Jun 4, 2025
3a45cf8
work
SoldierSacha Jun 4, 2025
2132cde
test
SoldierSacha Jun 4, 2025
5c87fb3
test
SoldierSacha Jun 4, 2025
103e201
test
SoldierSacha Jun 4, 2025
ad59c92
Fix async fixture usage in OAuth tests
SoldierSacha Jun 4, 2025
e18e606
Merge pull request #5 from sacha-development-stuff/codex/fix-attribut…
SoldierSacha Jun 4, 2025
49fa6c2
Fix resumption token updates
SoldierSacha Jun 4, 2025
b46aac4
Merge pull request
SoldierSacha Jun 4, 2025
2daea3f
Add OAuth token exchange support
SoldierSacha Jun 10, 2025
94850e7
implement-rfc-8693-token-exchange-in-mcp-sdk
SoldierSacha Jun 10, 2025
627eebd
work
SoldierSacha Jun 10, 2025
beeb244
Merge branch 'main' into main
SoldierSacha Jun 10, 2025
e92e61d
docs: document token-exchange support
SoldierSacha Jun 10, 2025
5976e77
docs
SoldierSacha Jun 10, 2025
bde2448
test: update expectations for token-exchange
SoldierSacha Jun 10, 2025
a98f33f
Merge pull request #9 from sacha-development-stuff/codex/fix-token-ex…
SoldierSacha Jun 10, 2025
b3b0509
Fix pyright token type errors
SoldierSacha Jun 10, 2025
a3edbeb
fix-argument-type-and-abstract-class-errors
SoldierSacha Jun 10, 2025
9b5ef4d
work
SoldierSacha Jun 10, 2025
a0d24ca
Strip whitespace from SSE resumption token
SoldierSacha Jun 10, 2025
7e02ddd
fix-test_streamablehttp_client_resumption-failure
SoldierSacha Jun 10, 2025
d04d17c
Merge branch 'main' into main
SoldierSacha Jun 13, 2025
2d6c062
merge with recent branch
SoldierSacha Jun 13, 2025
02597a2
feat: support combined client creds and token exchange
SoldierSacha Jun 14, 2025
e717dbe
adding token exchange + client credentials as a valid registration gr…
SoldierSacha Jun 14, 2025
1f23248
merge with recent branch
SoldierSacha Jun 14, 2025
ded6b89
Handle closed stream when sending notifications
SoldierSacha Jun 14, 2025
05c46e6
fix-test_streamablehttp_client_resumption-failure
SoldierSacha Jun 14, 2025
bb480a2
Merge branch 'main' into main
SoldierSacha Jun 16, 2025
22c5ef2
Merge branch 'main' into main
SoldierSacha Jun 18, 2025
8fdc5f9
merge with recent branch
SoldierSacha Jun 18, 2025
9f7ae6c
test: stabilize resumption notifications
SoldierSacha Jun 18, 2025
2ab4ad0
Merge pull request #14 from sacha-development-stuff/codex/fix-notific…
SoldierSacha Jun 18, 2025
9e06753
Merge branch 'main' into main
SoldierSacha Jun 23, 2025
b935a6f
Resolve merge conflicts and integrate client credential features
SoldierSacha Jun 24, 2025
c1d0acc
Merge pull request #15 from sacha-development-stuff/codex/fix-merge-c…
SoldierSacha Jun 24, 2025
94cefe3
test: restore missing fixtures
SoldierSacha Jun 24, 2025
482c05e
Merge pull request #16 from sacha-development-stuff/codex/resolve-mer…
SoldierSacha Jun 24, 2025
a41187e
merge with recent branch
SoldierSacha Jun 24, 2025
b7d1aad
merge with recent branch
SoldierSacha Jun 24, 2025
1329ab7
merge with recent branch
SoldierSacha Jun 24, 2025
6d1305d
merge with recent branch
SoldierSacha Jun 24, 2025
f61e57e
merge with recent branch
SoldierSacha Jun 24, 2025
f402804
merge with recent branch
SoldierSacha Jun 25, 2025
b1b34e5
Merge branch 'main' into main
SoldierSacha Jun 25, 2025
4a8294c
docs: document client credentials and introspection
SoldierSacha Jun 25, 2025
75ca216
Merge pull request #17 from sacha-development-stuff/codex/add-client-…
SoldierSacha Jun 25, 2025
0a95397
merge with recent branch
SoldierSacha Jun 25, 2025
30e3c79
Merge branch 'main' into main
SoldierSacha Jun 26, 2025
44bc5a0
Merge branch 'main' into main
SoldierSacha Jun 27, 2025
ceb1e19
Merge branch 'main' into main
SoldierSacha Jun 29, 2025
3bf695c
merge with recent branch
SoldierSacha Jun 29, 2025
a7a7a43
merge with recent branch
SoldierSacha Jun 29, 2025
5e77e28
merge with recent branch
SoldierSacha Jun 29, 2025
9057f02
Merge branch 'main' into main
SoldierSacha Jul 8, 2025
26627c1
merge with recent branch
SoldierSacha Jul 8, 2025
b8c0ba3
merge with recent branch
SoldierSacha Jul 8, 2025
4360875
merge with recent branch
SoldierSacha Jul 8, 2025
1704192
Merge branch 'main' into main
SoldierSacha Jul 8, 2025
4b5eaf2
merge with recent branch
SoldierSacha Jul 8, 2025
ff9d079
merge with recent branch
SoldierSacha Jul 8, 2025
f87b7b6
merge with recent branch
SoldierSacha Jul 8, 2025
6182ac2
Merge branch 'main' into main
SoldierSacha Jul 9, 2025
3e8365b
Merge branch 'main' into main
SoldierSacha Jul 10, 2025
1099d6a
Merge branch 'main' into main
SoldierSacha Jul 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,11 @@ This ensures your client UI shows the most user-friendly names that servers prov
The SDK includes [authorization support](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for connecting to protected MCP servers:

```python
from mcp.client.auth import OAuthClientProvider, TokenStorage
from mcp.client.auth import (
OAuthClientProvider,
TokenExchangeProvider,
TokenStorage,
)
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
Expand Down Expand Up @@ -1322,6 +1326,24 @@ async def main():
callback_handler=lambda: ("auth_code", None),
)

# For machine-to-machine scenarios, use ClientCredentialsProvider
# instead of OAuthClientProvider.

# If you already have a user token from another provider, you can
# exchange it for an MCP token using the token_exchange grant
# implemented by TokenExchangeProvider.
token_exchange_auth = TokenExchangeProvider(
server_url="https://api.example.com",
client_metadata=OAuthClientMetadata(
client_name="My Client",
redirect_uris=["http://localhost:3000/callback"],
grant_types=["client_credentials", "token_exchange"],
response_types=["code"],
),
storage=CustomTokenStorage(),
subject_token_supplier=lambda: "user_token",
)

# Use with streamable HTTP client
async with streamablehttp_client(
"https://api.example.com/mcp", auth=oauth_auth
Expand Down
4 changes: 4 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
The Python SDK exposes the entire `mcp` package for use in your own projects.
It includes an OAuth server implementation with support for the RFC 8693
`token_exchange` grant type.

::: mcp
4 changes: 4 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
This is the MCP Server implementation in Python.

It only contains the [API Reference](api.md) for the time being.

The built-in OAuth server supports the RFC 8693 `token_exchange` grant type,
allowing clients to exchange user tokens from external providers for MCP
access tokens.
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,52 @@ async def exchange_authorization_code(
scope=" ".join(authorization_code.scopes),
)

async def exchange_client_credentials(self, client: OAuthClientInformationFull, scopes: list[str]) -> OAuthToken:
"""Exchange client credentials for an MCP access token."""
mcp_token = f"mcp_{secrets.token_hex(32)}"
self.tokens[mcp_token] = AccessToken(
token=mcp_token,
client_id=client.client_id,
scopes=scopes,
expires_at=int(time.time()) + 3600,
)
return OAuthToken(
access_token=mcp_token,
token_type="Bearer",
expires_in=3600,
scope=" ".join(scopes),
)

async def exchange_token(
self,
client: OAuthClientInformationFull,
subject_token: str,
subject_token_type: str,
actor_token: str | None,
actor_token_type: str | None,
scope: list[str] | None,
audience: str | None,
resource: str | None,
) -> OAuthToken:
"""Exchange an external token for an MCP access token."""
if not subject_token:
raise ValueError("Invalid subject token")

mcp_token = f"mcp_{secrets.token_hex(32)}"
self.tokens[mcp_token] = AccessToken(
token=mcp_token,
client_id=client.client_id,
scopes=scope or [self.settings.mcp_scope],
expires_at=int(time.time()) + 3600,
resource=resource,
)
return OAuthToken(
access_token=mcp_token,
token_type="Bearer",
expires_in=3600,
scope=" ".join(scope or [self.settings.mcp_scope]),
)

async def load_access_token(self, token: str) -> AccessToken | None:
"""Load and validate an access token."""
access_token = self.tokens.get(token)
Expand Down
Loading
Loading