66CloudFormation stacks and AWS Secrets Manager.
77"""
88
9+ import json
910import logging
1011import time
1112from datetime import timedelta
@@ -38,6 +39,8 @@ def __init__(
3839 server_stack_name : Optional [str ] = None ,
3940 server_stack_url_output_key : str = "McpServerUrl" ,
4041 server_stack_region : str = "us-west-2" ,
42+ server_ssm_parameter_name : Optional [str ] = None ,
43+ server_ssm_region : str = "us-west-2" ,
4144 ** kwargs ,
4245 ):
4346 # Handle camelCase parameter names from JSON config
@@ -46,6 +49,10 @@ def __init__(
4649 "serverStackUrlOutputKey" , server_stack_url_output_key
4750 )
4851 self .server_stack_region = kwargs .get ("serverStackRegion" , server_stack_region )
52+ self .server_ssm_parameter_name = kwargs .get (
53+ "serverSsmParameterName" , server_ssm_parameter_name
54+ )
55+ self .server_ssm_region = kwargs .get ("serverSsmRegion" , server_ssm_region )
4956
5057 # Fixed auth stack configuration
5158 self .auth_stack_name = "LambdaMcpServer-Auth"
@@ -221,6 +228,8 @@ def __init__(self, name: str, config: AutomatedOAuthConfig):
221228 "server_stack_name" : config .server_stack_name ,
222229 "server_stack_url_output_key" : config .server_stack_url_output_key ,
223230 "server_stack_region" : config .server_stack_region ,
231+ "server_ssm_parameter_name" : config .server_ssm_parameter_name ,
232+ "server_ssm_region" : config .server_ssm_region ,
224233 "auth_stack_name" : config .auth_stack_name ,
225234 "auth_stack_region" : config .auth_stack_region ,
226235 }
@@ -234,9 +243,18 @@ def __init__(self, name: str, config: AutomatedOAuthConfig):
234243 async def initialize (self ) -> None :
235244 """Initialize the server connection with automated OAuth authentication."""
236245 try :
237- # Get server URL from CloudFormation
238- logging .debug ("Retrieving server URL from CloudFormation..." )
239- server_url = await self ._get_server_url_from_cloudformation ()
246+ # Get server URL from CloudFormation or SSM
247+ if self .oauth_config .server_stack_name :
248+ logging .debug ("Retrieving server URL from CloudFormation..." )
249+ server_url = await self ._get_server_url_from_cloudformation ()
250+ elif self .oauth_config .server_ssm_parameter_name :
251+ logging .debug ("Retrieving server URL from SSM parameter..." )
252+ server_url = await self ._get_server_url_from_ssm ()
253+ else :
254+ raise ValueError (
255+ "Either server_stack_name or server_ssm_parameter_name must be provided"
256+ )
257+
240258 self .config ["server_url" ] = server_url
241259
242260 logging .debug (f"Connecting to OAuth-protected MCP server: { server_url } " )
@@ -545,6 +563,65 @@ async def _get_client_secret_from_secrets_manager(self) -> str:
545563 logging .error (f"Failed to retrieve client secret:" , error )
546564 raise ValueError (f"Could not retrieve OAuth client secret: { error } " )
547565
566+ async def _get_server_url_from_ssm (self ) -> str :
567+ """Retrieve the server URL from SSM parameter."""
568+ try :
569+ logging .debug (
570+ f"Retrieving server URL from SSM parameter: { self .oauth_config .server_ssm_parameter_name } "
571+ )
572+
573+ # Create SSM client
574+ session = boto3 .Session ()
575+ ssm_client = session .client (
576+ "ssm" , region_name = self .oauth_config .server_ssm_region
577+ )
578+
579+ response = ssm_client .get_parameter (
580+ Name = self .oauth_config .server_ssm_parameter_name
581+ )
582+
583+ parameter_value = response ["Parameter" ]["Value" ]
584+
585+ # Parse JSON and extract URL
586+ try :
587+ parameter_json = json .loads (parameter_value )
588+ server_url = parameter_json .get ("url" )
589+
590+ if not server_url :
591+ raise ValueError (
592+ f"No 'url' key found in SSM parameter JSON: { self .oauth_config .server_ssm_parameter_name } "
593+ )
594+
595+ logging .debug (f"Retrieved server URL from SSM: { server_url } " )
596+ return server_url
597+
598+ except json .JSONDecodeError as e :
599+ raise ValueError (
600+ f"SSM parameter value is not valid JSON: { self .oauth_config .server_ssm_parameter_name } . Error: { e } "
601+ )
602+
603+ except ClientError as error :
604+ error_code = error .response ["Error" ]["Code" ]
605+ if error_code == "ParameterNotFound" :
606+ raise ValueError (
607+ f"SSM parameter '{ self .oauth_config .server_ssm_parameter_name } ' not found"
608+ )
609+ elif error_code in ["AccessDenied" , "UnauthorizedOperation" ]:
610+ raise ValueError (
611+ f"Insufficient permissions to access SSM parameter '{ self .oauth_config .server_ssm_parameter_name } '. "
612+ "Ensure your AWS credentials have ssm:GetParameter permission."
613+ )
614+ else :
615+ raise ValueError (
616+ f"Could not retrieve server URL from SSM parameter { self .oauth_config .server_ssm_parameter_name } : { error } "
617+ )
618+
619+ except Exception as error :
620+ logging .error (f"Failed to retrieve server URL from SSM:" , error )
621+ raise ValueError (
622+ f"Could not retrieve server URL from SSM parameter { self .oauth_config .server_ssm_parameter_name } : { error } "
623+ )
624+
548625 async def _get_server_url_from_cloudformation (self ) -> str :
549626 """Retrieve the server URL from CloudFormation stack outputs."""
550627 try :
0 commit comments