Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3e5d7aa
- Implemented SEP-990 feature for providing support for Enterprise Ma…
BinoyOza-okta Nov 21, 2025
70937cc
Added test cases for missing lines of code.
BinoyOza-okta Nov 25, 2025
51f8fcc
- Added tests cases for few of the missing lines. src/mcp/client/auth…
BinoyOza-okta Nov 25, 2025
b8d2c97
- Fixed pre-commit errors.
BinoyOza-okta Nov 25, 2025
cd8d111
- Tried to fix the ruff error.
BinoyOza-okta Nov 25, 2025
f2421eb
- Fixed ruff errors.
BinoyOza-okta Nov 26, 2025
ab23ceb
- Removed server side changes for enterprise_managed_auth.py
BinoyOza-okta Nov 26, 2025
204bd86
- Added README.md changes for SEP-990 implementation for enterprise m…
BinoyOza-okta Nov 26, 2025
add0cc2
- Resolved pyright checks error.
BinoyOza-okta Nov 26, 2025
9e4c83f
- Resolved README.md file fixes for removing unused imports.
BinoyOza-okta Nov 27, 2025
9da5f5b
- Resolved pyright errors.
BinoyOza-okta Nov 27, 2025
5663563
- Added new test cases for the missing code lines.
BinoyOza-okta Nov 27, 2025
5ef7740
- Fixed the failing test cases.
BinoyOza-okta Nov 27, 2025
7587731
- Fixed the test cases.
BinoyOza-okta Nov 27, 2025
e57633d
- Added typing for request payload structures TokenExchangeRequestDat…
BinoyOza-okta Dec 12, 2025
36d30b3
- Updated test case to include IDJAGClaims type model to verify payload.
BinoyOza-okta Dec 12, 2025
f86758e
feat: Add conformance tests for enterprise managed authorization (SEP…
BinoyOza-okta Jan 22, 2026
2a67fcd
- Fix uv.lock.
BinoyOza-okta Jan 30, 2026
649d9d8
feat(auth): migrate enterprise auth conformance tests to @modelcontex…
BinoyOza-okta Feb 19, 2026
3565513
Fixed end-of-file-fixer for test_enterprise_managed_auth_client.py.
BinoyOza-okta Feb 19, 2026
14dd313
Fixed pre-commit hooks issues.
BinoyOza-okta Feb 19, 2026
a3a9af3
Fixed Ruff formatting issues.
BinoyOza-okta Feb 19, 2026
6653fb2
Fixed pyright issues.
BinoyOza-okta Feb 19, 2026
57802c8
Moved the changes from README.md to README.v2.md.
BinoyOza-okta Feb 19, 2026
84330a3
- Removed Unicode characters from the example snippet file.
BinoyOza-okta Feb 20, 2026
f01bf16
- Removed unused parameters from the EnterpriseAuthOAuthClientProvide…
BinoyOza-okta Feb 20, 2026
170df60
refactor(auth): address code review feedback for enterprise managed auth
BinoyOza-okta Mar 17, 2026
e2f3136
- Removed the non-existing scenario from docstring.
BinoyOza-okta Mar 17, 2026
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
100 changes: 99 additions & 1 deletion .github/actions/conformance/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Contract:
- MCP_CONFORMANCE_SCENARIO env var -> scenario name
- MCP_CONFORMANCE_CONTEXT env var -> optional JSON (for client-credentials scenarios)
- MCP_CONFORMANCE_CONTEXT env var -> optional JSON (for auth scenarios)
- Server URL as last CLI argument (sys.argv[1])
- Must exit 0 within 30 seconds

Expand All @@ -16,7 +16,16 @@
elicitation-sep1034-client-defaults - Elicitation with default accept callback
auth/client-credentials-jwt - Client credentials with private_key_jwt
auth/client-credentials-basic - Client credentials with client_secret_basic
auth/cross-app-access-complete-flow - Enterprise managed OAuth (SEP-990) - v0.1.14+
auth/* - Authorization code flow (default for auth scenarios)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these scenarios do not exist, only the top one does.


Enterprise Auth (SEP-990):
The conformance package v0.1.14+ (https://github.com/modelcontextprotocol/conformance/pull/110)
provides the scenario 'auth/cross-app-access-complete-flow' which tests the complete
enterprise managed OAuth flow: IDP ID token → ID-JAG → access token.

The client receives test context (idp_id_token, idp_token_endpoint, etc.) via
MCP_CONFORMANCE_CONTEXT environment variable and performs the token exchange flows automatically.
"""

import asyncio
Expand Down Expand Up @@ -314,6 +323,95 @@ async def run_auth_code_client(server_url: str) -> None:
await _run_auth_session(server_url, oauth_auth)


@register("auth/cross-app-access-complete-flow")
async def run_cross_app_access_complete_flow(server_url: str) -> None:
"""Enterprise managed auth: Complete SEP-990 flow (OIDC ID token → ID-JAG → access token).

This scenario is provided by @modelcontextprotocol/conformance@0.1.14+ (PR #110).
It tests the complete enterprise managed OAuth flow using token exchange (RFC 8693)
and JWT bearer grant (RFC 7523).
"""
from mcp.client.auth.extensions.enterprise_managed_auth import (
EnterpriseAuthOAuthClientProvider,
TokenExchangeParameters,
)

context = get_conformance_context()
# The conformance package provides these fields
idp_id_token = context.get("idp_id_token")
idp_token_endpoint = context.get("idp_token_endpoint")
idp_issuer = context.get("idp_issuer")

# For cross-app access, we need to determine the MCP server's resource ID and auth issuer
# The conformance package sets up the auth server, and the MCP server URL is passed to us

if not idp_id_token:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'idp_id_token'")
if not idp_token_endpoint:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'idp_token_endpoint'")
if not idp_issuer:
raise RuntimeError("MCP_CONFORMANCE_CONTEXT missing 'idp_issuer'")

# Extract base URL and construct auth issuer and resource ID
# The conformance test sets up auth server at a known location
base_url = server_url.replace("/mcp", "")
auth_issuer = context.get("auth_issuer", base_url)
resource_id = context.get("resource_id", server_url)

logger.debug("Cross-app access flow:")
logger.debug(f" IDP Issuer: {idp_issuer}")
logger.debug(f" IDP Token Endpoint: {idp_token_endpoint}")
logger.debug(f" Auth Issuer: {auth_issuer}")
logger.debug(f" Resource ID: {resource_id}")

# Create token exchange parameters from IDP ID token
token_exchange_params = TokenExchangeParameters.from_id_token(
id_token=idp_id_token,
mcp_server_auth_issuer=auth_issuer,
mcp_server_resource_id=resource_id,
scope=context.get("scope"),
)

# Get pre-configured client credentials from context (if provided)
client_id = context.get("client_id")
client_secret = context.get("client_secret")

# Create storage and pre-configure client info if credentials are provided
storage = InMemoryTokenStorage()

# Create enterprise auth provider
enterprise_auth = EnterpriseAuthOAuthClientProvider(
server_url=server_url,
client_metadata=OAuthClientMetadata(
client_name="conformance-cross-app-client",
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
grant_types=["urn:ietf:params:oauth:grant-type:jwt-bearer"],
response_types=["token"],
),
storage=storage,
idp_token_endpoint=idp_token_endpoint,
token_exchange_params=token_exchange_params,
)

# If client credentials are provided in context, use them instead of dynamic registration
if client_id and client_secret:
from mcp.shared.auth import OAuthClientInformationFull

logger.debug(f"Using pre-configured client credentials: {client_id}")
client_info = OAuthClientInformationFull(
client_id=client_id,
client_secret=client_secret,
token_endpoint_auth_method="client_secret_basic",
grant_types=["urn:ietf:params:oauth:grant-type:jwt-bearer"],
response_types=["token"],
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
)
enterprise_auth.context.client_info = client_info
await storage.set_client_info(client_info)

await _run_auth_session(server_url, enterprise_auth)


async def _run_auth_session(server_url: str, oauth_auth: OAuthClientProvider) -> None:
"""Common session logic for all OAuth flows."""
client = httpx.AsyncClient(auth=oauth_auth, timeout=30.0)
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
with:
enable-cache: true
version: 0.9.5
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24
- run: uv sync --frozen --all-extras --package mcp
- run: npx @modelcontextprotocol/conformance@0.1.13 client --command 'uv run --frozen python .github/actions/conformance/client.py' --suite all
- run: npx @modelcontextprotocol/conformance@0.1.14 client --command 'uv run --frozen python .github/actions/conformance/client.py' --suite all
Loading
Loading