Skip to content

Commit 0b8d517

Browse files
committed
feat: Support for SSM parameters in integ tests
1 parent e67bcdf commit 0b8d517

File tree

4 files changed

+883
-182
lines changed

4 files changed

+883
-182
lines changed

e2e_tests/python/server_clients/automated_oauth.py

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
CloudFormation stacks and AWS Secrets Manager.
77
"""
88

9+
import json
910
import logging
1011
import time
1112
from 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

Comments
 (0)