@@ -88,7 +88,8 @@ def load_env_config() -> Dict[str, Optional[str]]:
88
88
'client_id' : None ,
89
89
'client_secret' : None ,
90
90
'region' : None ,
91
- 'user_pool_id' : None
91
+ 'user_pool_id' : None ,
92
+ 'domain' : None
92
93
}
93
94
94
95
if DOTENV_AVAILABLE :
@@ -113,6 +114,7 @@ def load_env_config() -> Dict[str, Optional[str]]:
113
114
env_config ['client_secret' ] = os .getenv ('COGNITO_CLIENT_SECRET' )
114
115
env_config ['region' ] = os .getenv ('AWS_REGION' )
115
116
env_config ['user_pool_id' ] = os .getenv ('COGNITO_USER_POOL_ID' )
117
+ env_config ['domain' ] = os .getenv ('COGNITO_DOMAIN' )
116
118
117
119
return env_config
118
120
@@ -141,6 +143,12 @@ def parse_arguments() -> argparse.Namespace:
141
143
parser .add_argument ('--message' , type = str , default = 'what is the current time in Clarksburg, MD' ,
142
144
help = 'Message to send to the agent' )
143
145
146
+ # Authentication method arguments
147
+ parser .add_argument ('--use-session-cookie' , action = 'store_true' ,
148
+ help = 'Use session cookie authentication instead of M2M' )
149
+ parser .add_argument ('--session-cookie-file' , type = str , default = '~/.mcp/session_cookie' ,
150
+ help = 'Path to session cookie file (default: ~/.mcp/session_cookie)' )
151
+
144
152
# Cognito authentication arguments - now optional if available in environment
145
153
parser .add_argument ('--client-id' , type = str , default = env_config ['client_id' ],
146
154
help = 'Cognito App Client ID (can be set via COGNITO_CLIENT_ID env var)' )
@@ -150,24 +158,34 @@ def parse_arguments() -> argparse.Namespace:
150
158
help = 'Cognito User Pool ID (can be set via COGNITO_USER_POOL_ID env var)' )
151
159
parser .add_argument ('--region' , type = str , default = env_config ['region' ],
152
160
help = 'AWS region for Cognito (can be set via AWS_REGION env var)' )
161
+ parser .add_argument ('--domain' , type = str , default = env_config ['domain' ],
162
+ help = 'Cognito custom domain (can be set via COGNITO_DOMAIN env var)' )
153
163
parser .add_argument ('--scopes' , type = str , nargs = '*' , default = None ,
154
164
help = 'Optional scopes for the token request' )
155
165
156
166
args = parser .parse_args ()
157
167
158
- # Validate that required Cognito parameters are available (either from command line or environment)
159
- missing_params = []
160
- if not args .client_id :
161
- missing_params .append ('--client-id (or COGNITO_CLIENT_ID env var)' )
162
- if not args .client_secret :
163
- missing_params .append ('--client-secret (or COGNITO_CLIENT_SECRET env var)' )
164
- if not args .user_pool_id :
165
- missing_params .append ('--user-pool-id (or COGNITO_USER_POOL_ID env var)' )
166
- if not args .region :
167
- missing_params .append ('--region (or AWS_REGION env var)' )
168
-
169
- if missing_params :
170
- parser .error (f"Missing required parameters: { ', ' .join (missing_params )} " )
168
+ # Validate authentication parameters based on method
169
+ if args .use_session_cookie :
170
+ # For session cookie auth, we just need the cookie file
171
+ cookie_path = os .path .expanduser (args .session_cookie_file )
172
+ if not os .path .exists (cookie_path ):
173
+ parser .error (f"Session cookie file not found: { cookie_path } \n "
174
+ f"Run 'python auth_server/cli_auth.py' to authenticate first" )
175
+ else :
176
+ # For M2M auth, validate Cognito parameters
177
+ missing_params = []
178
+ if not args .client_id :
179
+ missing_params .append ('--client-id (or COGNITO_CLIENT_ID env var)' )
180
+ if not args .client_secret :
181
+ missing_params .append ('--client-secret (or COGNITO_CLIENT_SECRET env var)' )
182
+ if not args .user_pool_id :
183
+ missing_params .append ('--user-pool-id (or COGNITO_USER_POOL_ID env var)' )
184
+ if not args .region :
185
+ missing_params .append ('--region (or AWS_REGION env var)' )
186
+
187
+ if missing_params :
188
+ parser .error (f"Missing required parameters for M2M authentication: { ', ' .join (missing_params )} " )
171
189
172
190
return args
173
191
@@ -210,21 +228,25 @@ def calculator(expression: str) -> str:
210
228
211
229
@tool
212
230
async def invoke_mcp_tool (mcp_registry_url : str , server_name : str , tool_name : str , arguments : Dict [str , Any ],
213
- auth_token : str , user_pool_id : str , client_id : str , region : str ) -> str :
231
+ auth_token : str = None , user_pool_id : str = None , client_id : str = None , region : str = None ,
232
+ auth_method : str = "m2m" , session_cookie : str = None ) -> str :
214
233
"""
215
234
Invoke a tool on an MCP server using the MCP Registry URL and server name with authentication.
216
235
217
236
This tool creates an MCP SSE client and calls the specified tool with the provided arguments.
237
+ Supports both M2M (JWT) and session cookie authentication.
218
238
219
239
Args:
220
240
mcp_registry_url (str): The URL of the MCP Registry
221
241
server_name (str): The name of the MCP server to connect to
222
242
tool_name (str): The name of the tool to invoke
223
243
arguments (Dict[str, Any]): Dictionary containing the arguments for the tool
224
- auth_token (str): Bearer token for authentication
244
+ auth_token (str): Bearer token for authentication (for M2M)
225
245
user_pool_id (str): Cognito User Pool ID for X-User-Pool-Id header
226
246
client_id (str): Cognito Client ID for X-Client-Id header
227
247
region (str): AWS region for X-Region header
248
+ auth_method (str): Authentication method ("m2m" or "session_cookie")
249
+ session_cookie (str): Session cookie value (for session auth)
228
250
229
251
Returns:
230
252
str: The result of the tool invocation as a string
@@ -246,21 +268,29 @@ async def invoke_mcp_tool(mcp_registry_url: str, server_name: str, tool_name: st
246
268
server_url = urljoin (base_url , f"{ server_name } /sse" )
247
269
print (f"Server URL: { server_url } " )
248
270
249
- # Prepare headers for authentication
271
+ # Prepare headers based on authentication method
250
272
headers = {
251
- 'Authorization' : f'Bearer { auth_token } ' ,
252
- 'X-User-Pool-Id' : user_pool_id ,
253
- 'X-Client-Id' : client_id ,
254
- 'X-Region' : region
273
+ 'X-User-Pool-Id' : user_pool_id or '' ,
274
+ 'X-Client-Id' : client_id or '' ,
275
+ 'X-Region' : region or 'us-east-1'
255
276
}
256
277
257
- # Create redacted headers for logging (mask sensitive information)
258
- redacted_headers = {
259
- 'Authorization' : f'Bearer { redact_sensitive_value (auth_token )} ' ,
260
- 'X-User-Pool-Id' : redact_sensitive_value (user_pool_id ),
261
- 'X-Client-Id' : redact_sensitive_value (client_id ),
262
- 'X-Region' : region # Region is not sensitive
263
- }
278
+ if auth_method == "session_cookie" and session_cookie :
279
+ headers ['Cookie' ] = f'mcp_gateway_session={ session_cookie } '
280
+ redacted_headers = {
281
+ 'Cookie' : f'mcp_gateway_session={ redact_sensitive_value (session_cookie )} ' ,
282
+ 'X-User-Pool-Id' : redact_sensitive_value (user_pool_id ) if user_pool_id else '' ,
283
+ 'X-Client-Id' : redact_sensitive_value (client_id ) if client_id else '' ,
284
+ 'X-Region' : region or 'us-east-1'
285
+ }
286
+ else :
287
+ headers ['Authorization' ] = f'Bearer { auth_token } '
288
+ redacted_headers = {
289
+ 'Authorization' : f'Bearer { redact_sensitive_value (auth_token )} ' ,
290
+ 'X-User-Pool-Id' : redact_sensitive_value (user_pool_id ) if user_pool_id else '' ,
291
+ 'X-Client-Id' : redact_sensitive_value (client_id ) if client_id else '' ,
292
+ 'X-Region' : region or 'us-east-1'
293
+ }
264
294
265
295
try :
266
296
# Create an MCP SSE client and call the tool with authentication headers
@@ -400,7 +430,7 @@ async def main():
400
430
"""
401
431
Main function that:
402
432
1. Parses command line arguments
403
- 2. Generates Cognito M2M authentication token
433
+ 2. Generates Cognito M2M authentication token OR loads session cookie
404
434
3. Sets up the LangChain MCP client and Bedrock model with authentication
405
435
4. Creates a LangGraph agent with available tools
406
436
5. Invokes the agent with the provided message
@@ -414,40 +444,75 @@ async def main():
414
444
logger .info (f"Connecting to MCP server: { server_url } " )
415
445
logger .info (f"Using model: { args .model } " )
416
446
logger .info (f"Message: { args .message } " )
417
- logger .info (f"Cognito User Pool ID: { redact_sensitive_value (args .user_pool_id )} " )
418
- logger .info (f"Cognito Client ID: { redact_sensitive_value (args .client_id )} " )
419
- logger .info (f"AWS Region: { args .region } " )
420
-
421
- # Generate Cognito M2M authentication token
422
- try :
423
- logger .info ("Generating Cognito M2M authentication token..." )
424
- token_data = generate_token (
425
- client_id = args .client_id ,
426
- client_secret = args .client_secret ,
427
- user_pool_id = args .user_pool_id ,
428
- region = args .region ,
429
- scopes = args .scopes
430
- )
431
- access_token = token_data .get ('access_token' )
432
- if not access_token :
433
- raise ValueError ("No access token received from Cognito" )
434
- logger .info ("Successfully generated authentication token" )
435
- except Exception as e :
436
- logger .error (f"Failed to generate authentication token: { e } " )
437
- return
447
+ logger .info (f"Authentication method: { 'Session Cookie' if args .use_session_cookie else 'M2M Token' } " )
448
+
449
+ # Initialize authentication variables
450
+ access_token = None
451
+ session_cookie = None
452
+ auth_method = "session_cookie" if args .use_session_cookie else "m2m"
453
+
454
+ if args .use_session_cookie :
455
+ # Load session cookie from file
456
+ try :
457
+ cookie_path = os .path .expanduser (args .session_cookie_file )
458
+ with open (cookie_path , 'r' ) as f :
459
+ session_cookie = f .read ().strip ()
460
+ logger .info (f"Successfully loaded session cookie from { cookie_path } " )
461
+ except Exception as e :
462
+ logger .error (f"Failed to load session cookie: { e } " )
463
+ return
464
+ else :
465
+ # Generate Cognito M2M authentication token
466
+ logger .info (f"Cognito User Pool ID: { redact_sensitive_value (args .user_pool_id )} " )
467
+ logger .info (f"Cognito Client ID: { redact_sensitive_value (args .client_id )} " )
468
+ logger .info (f"AWS Region: { args .region } " )
469
+
470
+ try :
471
+ logger .info ("Generating Cognito M2M authentication token..." )
472
+ token_data = generate_token (
473
+ client_id = args .client_id ,
474
+ client_secret = args .client_secret ,
475
+ user_pool_id = args .user_pool_id ,
476
+ region = args .region ,
477
+ scopes = args .scopes ,
478
+ domain = args .domain
479
+ )
480
+ access_token = token_data .get ('access_token' )
481
+ if not access_token :
482
+ raise ValueError ("No access token received from Cognito" )
483
+ logger .info ("Successfully generated authentication token" )
484
+ except Exception as e :
485
+ logger .error (f"Failed to generate authentication token: { e } " )
486
+ return
438
487
439
488
# Initialize the model
440
489
model = ChatBedrockConverse (model_id = args .model , region_name = 'us-east-1' )
441
490
442
491
try :
443
- # Prepare headers for MCP client authentication
444
- auth_headers = {
445
- 'Authorization' : f'Bearer { access_token } ' ,
446
- 'X-User-Pool-Id' : args .user_pool_id ,
447
- 'X-Client-Id' : args .client_id ,
448
- 'X-Region' : args .region
449
- }
450
- logger .info (f"Using authentication headers: { auth_headers } " )
492
+ # Prepare headers for MCP client authentication based on method
493
+ if args .use_session_cookie :
494
+ auth_headers = {
495
+ 'Cookie' : f'mcp_gateway_session={ session_cookie } ' ,
496
+ 'X-User-Pool-Id' : args .user_pool_id or '' ,
497
+ 'X-Client-Id' : args .client_id or '' ,
498
+ 'X-Region' : args .region or 'us-east-1'
499
+ }
500
+ else :
501
+ auth_headers = {
502
+ 'Authorization' : f'Bearer { access_token } ' ,
503
+ 'X-User-Pool-Id' : args .user_pool_id ,
504
+ 'X-Client-Id' : args .client_id ,
505
+ 'X-Region' : args .region
506
+ }
507
+
508
+ # Log redacted headers
509
+ redacted_headers = {}
510
+ for k , v in auth_headers .items ():
511
+ if k in ['Authorization' , 'Cookie' , 'X-User-Pool-Id' , 'X-Client-Id' ]:
512
+ redacted_headers [k ] = redact_sensitive_value (v ) if v else ''
513
+ else :
514
+ redacted_headers [k ] = v
515
+ logger .info (f"Using authentication headers: { redacted_headers } " )
451
516
452
517
# Initialize MCP client with the server configuration and authentication headers
453
518
client = MultiServerMCPClient (
@@ -478,14 +543,30 @@ async def main():
478
543
479
544
# Load and format the system prompt with the current time and MCP registry URL
480
545
system_prompt_template = load_system_prompt ()
481
- system_prompt = system_prompt_template .format (
482
- current_utc_time = current_utc_time ,
483
- mcp_registry_url = args .mcp_registry_url ,
484
- auth_token = access_token ,
485
- user_pool_id = args .user_pool_id ,
486
- client_id = args .client_id ,
487
- region = args .region
488
- )
546
+
547
+ # Prepare authentication parameters for system prompt
548
+ if args .use_session_cookie :
549
+ system_prompt = system_prompt_template .format (
550
+ current_utc_time = current_utc_time ,
551
+ mcp_registry_url = args .mcp_registry_url ,
552
+ auth_token = '' , # Not used for session cookie auth
553
+ user_pool_id = args .user_pool_id or '' ,
554
+ client_id = args .client_id or '' ,
555
+ region = args .region or 'us-east-1' ,
556
+ auth_method = auth_method ,
557
+ session_cookie = session_cookie
558
+ )
559
+ else :
560
+ system_prompt = system_prompt_template .format (
561
+ current_utc_time = current_utc_time ,
562
+ mcp_registry_url = args .mcp_registry_url ,
563
+ auth_token = access_token ,
564
+ user_pool_id = args .user_pool_id ,
565
+ client_id = args .client_id ,
566
+ region = args .region ,
567
+ auth_method = auth_method ,
568
+ session_cookie = '' # Not used for M2M auth
569
+ )
489
570
490
571
# Format the message with system message first
491
572
formatted_messages = [
0 commit comments