Skip to content

Commit 3f58d5d

Browse files
authored
fix(mcp_proxy_for_aws/utils.py): add override for bedrock-agentcore service name detection (aws#79)
Fixes failures when connecting to an Agentcore service.
1 parent 0eefae5 commit 3f58d5d

File tree

3 files changed

+65
-20
lines changed

3 files changed

+65
-20
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ docker build -t mcp-proxy-for-aws .
7979
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|--- |
8080
| `endpoint` | MCP endpoint URL (e.g., `https://your-service.us-east-1.amazonaws.com/mcp`) | N/A |Yes |
8181
| --- | --- | --- |--- |
82-
| `--service` | AWS service name for SigV4 signing | Inferred from endpoint if not provided |No |
82+
| `--service` | AWS service name for SigV4 signing, if omitted we try to infer this from the url | Inferred from endpoint if not provided |No |
8383
| `--profile` | AWS profile for AWS credentials to use | Uses `AWS_PROFILE` environment variable if not set |No |
8484
| `--region` | AWS region to use | Uses `AWS_REGION` environment variable if not set, defaults to `us-east-1` |No |
8585
| `--metadata` | Metadata to inject into MCP requests as key=value pairs (e.g., `--metadata KEY1=value1 KEY2=value2`) | `AWS_REGION` is automatically injected based on `--region` if not provided |No |
@@ -279,6 +279,15 @@ uv sync
279279

280280
---
281281

282+
## Troubleshooting
283+
284+
### Handling `Authentication error - Invalid credentials`
285+
We try to autodetect the service from the url, sometimes this fails, ensure that `--service` is set correctly to the
286+
service you are attempting to connect to.
287+
Otherwise the SigV4 signing will not be able to be verified by the service you connect to, resulting in this error.
288+
Also ensure that you have valid IAM credentials on your machine before retrying.
289+
290+
282291
## Development & Contributing
283292

284293
For development setup, testing, and contribution guidelines, see:

mcp_proxy_for_aws/utils.py

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@
1818
import httpx
1919
import logging
2020
import os
21-
import re
2221
from fastmcp.client.transports import StreamableHttpTransport
2322
from mcp_proxy_for_aws.sigv4_helper import create_sigv4_client
24-
from typing import Any, Dict, Optional
23+
from typing import Any, Dict, Optional, Tuple
2524
from urllib.parse import urlparse
2625

2726

@@ -72,34 +71,61 @@ def client_factory(
7271
)
7372

7473

74+
def get_service_name_and_region_from_endpoint(endpoint: str) -> Tuple[str, str]:
75+
"""Extract service name and region from an endpoint URL.
76+
77+
Args:
78+
endpoint: The endpoint URL to parse
79+
80+
Returns:
81+
Tuple of (service_name, region). Either value may be empty string if not found.
82+
83+
Notes:
84+
- Matches bedrock-agentcore endpoints (gateway and runtime)
85+
- Matches AWS API Gateway endpoints (service.region.api.aws)
86+
- Falls back to extracting first hostname segment as service name
87+
"""
88+
# Parse AWS service from endpoint URL
89+
parsed = urlparse(endpoint)
90+
hostname = parsed.hostname or ''
91+
match hostname.split('.'):
92+
case [*_, 'bedrock-agentcore', region, 'amazonaws', 'com']:
93+
return 'bedrock-agentcore', region # gateway and runtime
94+
case [service, region, 'api', 'aws']:
95+
return service, region
96+
case [service, *_]:
97+
# Fallback: extract first segment as service name
98+
return service, ''
99+
case _:
100+
logger.warning('Could not parse endpoint, no hostname found')
101+
return '', ''
102+
103+
75104
def determine_service_name(endpoint: str, service: Optional[str] = None) -> str:
76-
"""Validate and determine the service name.
105+
"""Validate and determine the service name and possibly region from an endpoint.
77106
78107
Args:
79108
endpoint: The endpoint URL
80109
service: Optional service name
81110
82111
Returns:
83112
Validated service name
113+
Validated region
84114
85115
Raises:
86116
ValueError: If service cannot be determined
87117
"""
88118
if service:
89119
return service
90120

91-
# Parse AWS service from endpoint URL
92-
parsed = urlparse(endpoint)
93-
hostname = parsed.hostname or ''
94-
95-
# Extract service name (first part before first dot or dash)
96-
service_match = re.match(r'^([^.]+)', hostname)
97-
determined_service = service_match.group(1) if service_match else None
121+
logger.info('Resolving service name')
122+
endpoint_service, _ = get_service_name_and_region_from_endpoint(endpoint)
123+
determined_service = service or endpoint_service
98124

99125
if not determined_service:
100126
raise ValueError(
101-
f"Could not determine AWS service name from endpoint '{endpoint}'. "
102-
'Please provide the service name explicitly using --service argument.'
127+
f"Could not determine AWS service name and region from endpoint '{endpoint}' and they were not provided."
128+
'Please provide the service name explicitly using --service argument and the region via --region argument.'
103129
)
104130
return determined_service
105131

@@ -122,14 +148,10 @@ def determine_aws_region(endpoint: str, region: Optional[str]) -> str:
122148
return region
123149

124150
# Parse AWS region from endpoint URL
125-
parsed = urlparse(endpoint)
126-
hostname = parsed.hostname or ''
127-
128-
# Extract region name (pattern: service.region.api.aws or service-name.region.api.aws)
129-
region_match = re.search(r'\.([a-z0-9-]+)\.api\.aws', hostname)
130-
if region_match:
151+
_, endpoint_region = get_service_name_and_region_from_endpoint(endpoint)
152+
if endpoint_region:
131153
logger.debug('Region determined through endpoint URL')
132-
return region_match.group(1)
154+
return endpoint_region
133155

134156
environment_region = os.getenv('AWS_REGION')
135157
if environment_region:

tests/unit/test_utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,20 @@ def test_validate_service_name_service_parsing_with_dot(self):
136136
result = determine_service_name(endpoint)
137137
assert result == 'service'
138138

139+
def test_validate_service_name_bedrock_agentcore(self):
140+
"""Test parsing service name for bedrock-agentcore endpoints."""
141+
# Test various bedrock-agentcore endpoint formats
142+
test_cases = [
143+
'https://my-agent.gateway.bedrock-agentcore.us-west-2.amazonaws.com', # Clean gateway
144+
'https://bedrock-agentcore.us-east-1.amazonaws.com', # Clean runtime
145+
'https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-east-1%3A216123456714%3Aruntime%2Fhosted_agent_99wdf-hYKYrgAHVr/invocations',
146+
'https://gateway-quick-start-242206-rsdehprct2.gateway.bedrock-agentcore.eu-central-1.amazonaws.com/mcp',
147+
]
148+
149+
for endpoint in test_cases:
150+
result = determine_service_name(endpoint)
151+
assert result == 'bedrock-agentcore', f'Failed for endpoint: {endpoint}'
152+
139153
def test_validate_service_name_service_parsing_simple_hostname(self):
140154
"""Test parsing service from simple hostname."""
141155
endpoint = 'https://myservice'

0 commit comments

Comments
 (0)