1010 python -m mcp_conformance_auth_client <scenario> <server-url>
1111
1212Scenarios:
13- auth/* - Authorization code flow scenarios (default behavior)
14- auth/client-credentials-jwt - Client credentials with JWT authentication (SEP-1046)
13+ auth/* - Authorization code flow scenarios (default behavior)
14+ auth/client-credentials-jwt - Client credentials with JWT authentication (SEP-1046)
15+ auth/client-credentials-basic - Client credentials with client_secret_basic
1516"""
1617
1718import asyncio
4041# The client_id expected by the conformance test
4142CONFORMANCE_TEST_CLIENT_ID = "conformance-test-client"
4243
44+ # Static credentials for client_secret_basic flow
45+ CONFORMANCE_CLIENT_SECRET = "conformance-test-secret"
46+
4347# Set up logging to stderr (stdout is for conformance test output)
4448logging .basicConfig (
4549 level = logging .DEBUG ,
@@ -160,9 +164,9 @@ async def run_authorization_code_client(server_url: str) -> None:
160164 await _run_session (server_url , oauth_auth )
161165
162166
163- async def run_client_credentials_client (server_url : str ) -> None :
167+ async def run_client_credentials_jwt_client (server_url : str ) -> None :
164168 """
165- Run the conformance test client with client credentials flow (SEP-1046).
169+ Run the conformance test client with client credentials flow using private_key_jwt (SEP-1046).
166170
167171 This function:
168172 1. Connects to the MCP server with OAuth client_credentials grant
@@ -171,7 +175,7 @@ async def run_client_credentials_client(server_url: str) -> None:
171175 4. Lists available tools
172176 5. Calls a test tool
173177 """
174- logger .debug (f"Starting conformance auth client (client_credentials ) for { server_url } " )
178+ logger .debug (f"Starting conformance auth client (client_credentials_jwt ) for { server_url } " )
175179
176180 # Create JWT parameters for private_key_jwt authentication
177181 jwt_params = JWTParameters (
@@ -199,6 +203,46 @@ async def run_client_credentials_client(server_url: str) -> None:
199203 await _run_session (server_url , oauth_auth )
200204
201205
206+ async def run_client_credentials_basic_client (server_url : str ) -> None :
207+ """
208+ Run the conformance test client with client credentials flow using client_secret_basic.
209+
210+ This function:
211+ 1. Connects to the MCP server with OAuth client_credentials grant
212+ 2. Uses client_secret_basic authentication with static credentials
213+ 3. Initializes the session
214+ 4. Lists available tools
215+ 5. Calls a test tool
216+ """
217+ logger .debug (f"Starting conformance auth client (client_credentials_basic) for { server_url } " )
218+
219+ # Create storage pre-populated with static client credentials
220+ storage = InMemoryTokenStorage ()
221+ await storage .set_client_info (
222+ OAuthClientInformationFull (
223+ client_id = CONFORMANCE_TEST_CLIENT_ID ,
224+ client_secret = CONFORMANCE_CLIENT_SECRET ,
225+ redirect_uris = [AnyUrl ("http://localhost:0/unused" )],
226+ token_endpoint_auth_method = "client_secret_basic" ,
227+ )
228+ )
229+
230+ # Create OAuth authentication handler for client_credentials flow with basic auth
231+ oauth_auth = RFC7523OAuthClientProvider (
232+ server_url = server_url ,
233+ client_metadata = OAuthClientMetadata (
234+ client_name = CONFORMANCE_TEST_CLIENT_ID ,
235+ redirect_uris = [AnyUrl ("http://localhost:0/unused" )], # Required but unused
236+ grant_types = ["client_credentials" ],
237+ response_types = [],
238+ token_endpoint_auth_method = "client_secret_basic" ,
239+ ),
240+ storage = storage ,
241+ )
242+
243+ await _run_session (server_url , oauth_auth )
244+
245+
202246async def _run_session (server_url : str , oauth_auth : OAuthClientProvider ) -> None :
203247 """Common session logic for all OAuth flows."""
204248 # Connect using streamable HTTP transport with OAuth
@@ -233,16 +277,19 @@ def main() -> None:
233277 print (f"Usage: { sys .argv [0 ]} <scenario> <server-url>" , file = sys .stderr )
234278 print ("" , file = sys .stderr )
235279 print ("Scenarios:" , file = sys .stderr )
236- print (" auth/* - Authorization code flow (default)" , file = sys .stderr )
237- print (" auth/client-credentials-jwt - Client credentials with JWT auth (SEP-1046)" , file = sys .stderr )
280+ print (" auth/* - Authorization code flow (default)" , file = sys .stderr )
281+ print (" auth/client-credentials-jwt - Client credentials with JWT auth (SEP-1046)" , file = sys .stderr )
282+ print (" auth/client-credentials-basic - Client credentials with client_secret_basic" , file = sys .stderr )
238283 sys .exit (1 )
239284
240285 scenario = sys .argv [1 ]
241286 server_url = sys .argv [2 ]
242287
243288 try :
244289 if scenario == "auth/client-credentials-jwt" :
245- asyncio .run (run_client_credentials_client (server_url ))
290+ asyncio .run (run_client_credentials_jwt_client (server_url ))
291+ elif scenario == "auth/client-credentials-basic" :
292+ asyncio .run (run_client_credentials_basic_client (server_url ))
246293 else :
247294 # Default to authorization code flow for all other auth/* scenarios
248295 asyncio .run (run_authorization_code_client (server_url ))
0 commit comments