Skip to content

Commit 2935c7c

Browse files
authored
Merge branch 'main' into dependabot/uv/uv-version-updates-9f9aec68ce
2 parents 38d8fef + 36dd241 commit 2935c7c

6 files changed

Lines changed: 288 additions & 145 deletions

File tree

mcp_proxy_for_aws/cli.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Command-line interface argument parsing for MCP Proxy for AWS."""
16+
17+
import argparse
18+
import os
19+
from mcp_proxy_for_aws import __version__
20+
from mcp_proxy_for_aws.utils import within_range
21+
22+
23+
def parse_args():
24+
"""Parse command line arguments.
25+
26+
Returns:
27+
argparse.Namespace: Parsed command line arguments
28+
"""
29+
parser = argparse.ArgumentParser(
30+
description=f'MCP Proxy for AWS v{__version__}',
31+
formatter_class=argparse.RawDescriptionHelpFormatter,
32+
epilog="""
33+
Examples:
34+
# Run with your endpoint
35+
mcp-proxy-for-aws <SigV4 MCP endpoint URL>
36+
37+
# Run with custom service and profile
38+
mcp-proxy-for-aws <SigV4 MCP endpoint URL> --service <aws-service> --profile default
39+
40+
# Run with write permissions enabled
41+
mcp-proxy-for-aws <SigV4 MCP endpoint URL> --read-only
42+
""",
43+
)
44+
45+
parser.add_argument(
46+
'endpoint',
47+
help='SigV4 MCP endpoint URL',
48+
)
49+
50+
parser.add_argument(
51+
'--service',
52+
help='AWS service name for SigV4 signing (inferred from endpoint if not provided)',
53+
)
54+
55+
parser.add_argument(
56+
'--profile',
57+
help='AWS profile to use (uses AWS_PROFILE environment variable if not provided)',
58+
default=os.getenv('AWS_PROFILE'),
59+
)
60+
61+
parser.add_argument(
62+
'--region',
63+
help='AWS region to use (uses AWS_REGION environment variable if not provided, with final fallback to us-east-1)',
64+
default=None,
65+
)
66+
67+
parser.add_argument(
68+
'--read-only',
69+
action='store_true',
70+
help='Disable tools which may require write permissions (readOnlyHint True or unknown)',
71+
)
72+
73+
parser.add_argument(
74+
'--log-level',
75+
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
76+
default='ERROR',
77+
help='Set the logging level (default: ERROR)',
78+
)
79+
80+
parser.add_argument(
81+
'--retries',
82+
type=int,
83+
default=0,
84+
choices=range(0, 11),
85+
metavar='[0-10]',
86+
help='Number of retries when calling endpoint mcp (default: 0) - setting this to 0 disables retries.',
87+
)
88+
89+
parser.add_argument(
90+
'--timeout',
91+
type=within_range(0),
92+
default=180.0,
93+
help='Timeout (seconds) when connecting to endpoint (default: 180)',
94+
)
95+
96+
parser.add_argument(
97+
'--connect-timeout',
98+
type=within_range(0),
99+
default=60.0,
100+
help='Connection timeout (seconds) when connecting to endpoint (default: 60)',
101+
)
102+
103+
parser.add_argument(
104+
'--read-timeout',
105+
type=within_range(0),
106+
default=120.0,
107+
help='Read timeout (seconds) when connecting to endpoint (default: 120)',
108+
)
109+
110+
parser.add_argument(
111+
'--write-timeout',
112+
type=within_range(0),
113+
default=180.0,
114+
help='Write timeout (seconds) when connecting to endpoint (default: 180)',
115+
)
116+
117+
return parser.parse_args()

mcp_proxy_for_aws/server.py

Lines changed: 2 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,19 @@
2222
5. Supporting tool refresh
2323
"""
2424

25-
import argparse
2625
import asyncio
2726
import httpx
2827
import logging
29-
import os
3028
from fastmcp.server.middleware.error_handling import RetryMiddleware
3129
from fastmcp.server.middleware.logging import LoggingMiddleware
3230
from fastmcp.server.server import FastMCP
33-
from mcp_proxy_for_aws import __version__
31+
from mcp_proxy_for_aws.cli import parse_args
3432
from mcp_proxy_for_aws.logging_config import configure_logging
3533
from mcp_proxy_for_aws.middleware.tool_filter import ToolFilteringMiddleware
3634
from mcp_proxy_for_aws.utils import (
3735
create_transport_with_sigv4,
3836
determine_aws_region,
3937
determine_service_name,
40-
within_range,
4138
)
4239
from typing import Any
4340

@@ -111,7 +108,7 @@ def add_retry_middleware(mcp: FastMCP, retries: int) -> None:
111108
mcp.add_middleware(RetryMiddleware(retries))
112109

113110

114-
def add_logging_middleware(mcp: FastMCP, log_level: int) -> None:
111+
def add_logging_middleware(mcp: FastMCP, log_level: str) -> None:
115112
"""Add logging middleware."""
116113
if log_level != 'DEBUG':
117114
return
@@ -127,99 +124,6 @@ def add_logging_middleware(mcp: FastMCP, log_level: int) -> None:
127124
)
128125

129126

130-
def parse_args():
131-
"""Parse command line arguments."""
132-
parser = argparse.ArgumentParser(
133-
description=f'MCP Proxy for AWS v{__version__}',
134-
formatter_class=argparse.RawDescriptionHelpFormatter,
135-
epilog="""
136-
Examples:
137-
# Run with your endpoint
138-
mcp-proxy-for-aws <SigV4 MCP endpoint URL>
139-
140-
# Run with custom service and profile
141-
mcp-proxy-for-aws <SigV4 MCP endpoint URL> --service <aws-service> --profile default
142-
143-
# Run with write permissions enabled
144-
mcp-proxy-for-aws <SigV4 MCP endpoint URL> --read-only
145-
""",
146-
)
147-
148-
parser.add_argument(
149-
'endpoint',
150-
help='SigV4 MCP endpoint URL',
151-
)
152-
153-
parser.add_argument(
154-
'--service',
155-
help='AWS service name for SigV4 signing (inferred from endpoint if not provided)',
156-
)
157-
158-
parser.add_argument(
159-
'--profile',
160-
help='AWS profile to use (uses AWS_PROFILE environment variable if not provided)',
161-
default=os.getenv('AWS_PROFILE'),
162-
)
163-
164-
parser.add_argument(
165-
'--region',
166-
help='AWS region to use (uses AWS_REGION environment variable if not provided, with final fallback to us-east-1)',
167-
default=None,
168-
)
169-
170-
parser.add_argument(
171-
'--read-only',
172-
action='store_true',
173-
help='Disable tools which may require write permissions (readOnlyHint True or unknown)',
174-
)
175-
176-
parser.add_argument(
177-
'--log-level',
178-
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
179-
default='INFO',
180-
help='Set the logging level (default: INFO)',
181-
)
182-
183-
parser.add_argument(
184-
'--retries',
185-
type=int,
186-
default=0,
187-
choices=range(0, 11),
188-
metavar='[0-10]',
189-
help='Number of retries when calling endpoint mcp (default: 0) - setting this to 0 disables retries.',
190-
)
191-
192-
parser.add_argument(
193-
'--timeout',
194-
type=within_range(0),
195-
default=180.0,
196-
help='Timeout (seconds) when connecting to endpoint (default: 180)',
197-
)
198-
199-
parser.add_argument(
200-
'--connect-timeout',
201-
type=within_range(0),
202-
default=60.0,
203-
help='Connection timeout (seconds) when connecting to endpoint (default: 60)',
204-
)
205-
206-
parser.add_argument(
207-
'--read-timeout',
208-
type=within_range(0),
209-
default=120.0,
210-
help='Read timeout (seconds) when connecting to endpoint (default: 120)',
211-
)
212-
213-
parser.add_argument(
214-
'--write-timeout',
215-
type=within_range(0),
216-
default=180.0,
217-
help='Write timeout (seconds) when connecting to endpoint (default: 180)',
218-
)
219-
220-
return parser.parse_args()
221-
222-
223127
def main():
224128
"""Run the MCP server."""
225129
args = parse_args()

mcp_proxy_for_aws/sigv4_helper.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -81,43 +81,44 @@ async def _handle_error_response(response: httpx.Response) -> None:
8181
response: The HTTP response object
8282
8383
Raises:
84-
httpx.HTTPStatusError: With enhanced error message containing response details
84+
No raises. let the mcp http client handle the errors.
8585
"""
8686
if response.is_error:
87+
# warning only because the SDK logs error
88+
log_level = logging.WARNING
89+
if (
90+
# The server MAY respond 405 to GET (SSE) and DELETE (session).
91+
response.status_code == 405 and response.request.method in ('GET', 'DELETE')
92+
) or (
93+
# The server MAY terminate the session at any time, after which it MUST
94+
# respond to requests containing that session ID with HTTP 404 Not Found.
95+
response.status_code == 404 and response.request.method == 'POST'
96+
):
97+
log_level = logging.DEBUG
98+
8799
try:
88-
# Read response content to extract error details
100+
# read the content and settle the response content. required to get body (.json(), .text)
89101
await response.aread()
90102
except Exception as e:
91-
logger.error('Failed to read response: %s', e)
103+
logger.debug('Failed to read response: %s', e)
104+
# do nothing and let the client and SDK handle the error
105+
return
92106

93107
# Try to extract error details with fallbacks
94-
error_msg = ''
95108
try:
96109
# Try to parse JSON error details
97110
error_details = response.json()
98-
logger.error('HTTP %d Error Details: %s', response.status_code, error_details)
99-
error_msg = f'HTTP {response.status_code}: {error_details} for url {response.url}'
111+
logger.log(log_level, 'HTTP %d Error Details: %s', response.status_code, error_details)
100112
except Exception:
101113
# If JSON parsing fails, use response text or status code
102114
try:
103115
response_text = response.text
104-
logger.error('HTTP %d Error: %s', response.status_code, response_text)
105-
error_msg = f'HTTP {response.status_code}: {response_text} for url {response.url}'
116+
logger.log(log_level, 'HTTP %d Error: %s', response.status_code, response_text)
106117
except Exception:
107118
# Fallback to just status code and URL
108-
logger.error('HTTP %d Error for url %s', response.status_code, response.url)
109-
error_msg = f'HTTP {response.status_code} Error for url {response.url}'
110-
111-
# Raise the status error with enhanced message
112-
try:
113-
response.raise_for_status()
114-
except httpx.HTTPStatusError as e:
115-
# Replace the error message and throw HTTP error
116-
if error_msg:
117-
raise httpx.HTTPStatusError(
118-
message=error_msg, request=e.request, response=e.response
119+
logger.log(
120+
log_level, 'HTTP %d Error for url %s', response.status_code, response.url
119121
)
120-
raise e
121122

122123

123124
def create_aws_session(profile: Optional[str] = None) -> boto3.Session:

0 commit comments

Comments
 (0)