Add native boto3 AWS tools, remove broken AWS MCP client
This commit is contained in:
@@ -63,25 +63,6 @@ class _SigV4HttpxAuth(httpx.Auth):
|
||||
if self._actor_id:
|
||||
request.headers['x-actor-id'] = self._actor_id
|
||||
yield request
|
||||
class _AwsMcpSigV4Auth(httpx.Auth):
|
||||
"""SigV4 auth for AWS MCP Server (service: aws-mcp)."""
|
||||
def auth_flow(self, request):
|
||||
creds = boto3.Session().get_credentials().get_frozen_credentials()
|
||||
parsed = _urlparse(str(request.url))
|
||||
aws_req = botocore.awsrequest.AWSRequest(
|
||||
method=request.method,
|
||||
url=str(request.url),
|
||||
data=request.content or b'',
|
||||
headers={
|
||||
'Host': parsed.hostname,
|
||||
'Content-Type': request.headers.get('content-type', 'application/json'),
|
||||
}
|
||||
)
|
||||
botocore.auth.SigV4Auth(creds, 'aws-mcp', 'us-east-1').add_auth(aws_req)
|
||||
for k, v in aws_req.headers.items():
|
||||
request.headers[k] = v
|
||||
yield request
|
||||
|
||||
|
||||
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig
|
||||
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
|
||||
@@ -334,6 +315,63 @@ def apply_iam_permission(action: str, resource: str, policy_name: str) -> str:
|
||||
return f"Applied policy '{policy_name}': Allow {action} on {resource}."
|
||||
|
||||
|
||||
@tool
|
||||
def aws_list_lambda_functions(region: str = "us-east-1") -> str:
|
||||
"""List AWS Lambda functions in the specified region. Uses execution role credentials directly via boto3."""
|
||||
import boto3
|
||||
client = boto3.client("lambda", region_name=region)
|
||||
paginator = client.get_paginator("list_functions")
|
||||
functions = []
|
||||
for page in paginator.paginate():
|
||||
for fn in page["Functions"]:
|
||||
functions.append(f"{fn['FunctionName']} ({fn['Runtime']})")
|
||||
return f"{len(functions)} Lambda functions in {region}:\n" + "\n".join(functions)
|
||||
|
||||
|
||||
@tool
|
||||
def aws_get_cost_and_usage(start_date: str, end_date: str, granularity: str = "MONTHLY") -> str:
|
||||
"""Get AWS Cost and Usage report. start_date and end_date in YYYY-MM-DD format. Uses execution role credentials."""
|
||||
import boto3
|
||||
client = boto3.client("ce", region_name="us-east-1")
|
||||
response = client.get_cost_and_usage(
|
||||
TimePeriod={"Start": start_date, "End": end_date},
|
||||
Granularity=granularity,
|
||||
Metrics=["UnblendedCost"]
|
||||
)
|
||||
lines = []
|
||||
for result in response["ResultsByTime"]:
|
||||
period = f"{result['TimePeriod']['Start']} to {result['TimePeriod']['End']}"
|
||||
cost = result["Total"]["UnblendedCost"]["Amount"]
|
||||
unit = result["Total"]["UnblendedCost"]["Unit"]
|
||||
lines.append(f"{period}: {cost} {unit}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@tool
|
||||
def aws_describe_service(service: str, region: str = "us-east-1") -> str:
|
||||
"""Describe an AWS service. service can be: lambda, s3, cloudformation, dynamodb, sqs. Returns summary of key resources."""
|
||||
import boto3
|
||||
session = boto3.Session(region_name=region)
|
||||
if service == "s3":
|
||||
client = session.client("s3")
|
||||
buckets = client.list_buckets()["Buckets"]
|
||||
return f"{len(buckets)} S3 buckets: " + ", ".join(b["Name"] for b in buckets[:20])
|
||||
elif service == "cloudformation":
|
||||
client = session.client("cloudformation")
|
||||
stacks = client.list_stacks(StackStatusFilter=["CREATE_COMPLETE", "UPDATE_COMPLETE", "ROLLBACK_COMPLETE"])["StackSummaries"]
|
||||
return f"{len(stacks)} stacks: " + ", ".join(s["StackName"] for s in stacks[:20])
|
||||
elif service == "dynamodb":
|
||||
client = session.client("dynamodb")
|
||||
tables = client.list_tables()["TableNames"]
|
||||
return f"{len(tables)} DynamoDB tables: " + ", ".join(tables[:20])
|
||||
elif service == "sqs":
|
||||
client = session.client("sqs")
|
||||
queues = client.list_queues().get("QueueUrls", [])
|
||||
return f"{len(queues)} SQS queues: " + ", ".join(q.split("/")[-1] for q in queues[:20])
|
||||
else:
|
||||
return f"Service {service} not yet implemented. Try: lambda, s3, cloudformation, dynamodb, sqs"
|
||||
|
||||
|
||||
# ── Entrypoint ────────────────────────────────────────────────────────────
|
||||
|
||||
# Module-level actor_id for tool closures (set per-invocation)
|
||||
@@ -443,6 +481,7 @@ async def main(payload: dict, context):
|
||||
_now = datetime.now(_tz)
|
||||
_time_str = _now.strftime('%A, %B %d, %Y %I:%M %p %Z')
|
||||
system_prompt = system_prompt + f'\n\nCurrent date/time: {_time_str}'
|
||||
system_prompt = system_prompt + '\n\nNative AWS tools available: aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service. Use these directly for AWS API calls — NEVER use run_code for AWS queries.'
|
||||
print(f'[main] System prompt time injection: {_time_str}')
|
||||
|
||||
# Model: claude-sonnet-4-6 via cross-region inference
|
||||
@@ -458,29 +497,14 @@ async def main(payload: dict, context):
|
||||
home_assistant, connect_google_account, list_google_accounts, remove_google_account,
|
||||
manage_service, manage_mcp_connection, schedule_reminder, list_reminders, cancel_reminder,
|
||||
list_calendars, get_calendar_events, list_gmail_messages, get_gmail_message,
|
||||
run_code, send_file, request_iam_permission, apply_iam_permission]
|
||||
run_code, send_file, request_iam_permission, apply_iam_permission,
|
||||
aws_list_lambda_functions, aws_get_cost_and_usage, aws_describe_service]
|
||||
|
||||
# Load user's dynamic MCP connections
|
||||
mcp_connections = services.get('mcp_connections', [])
|
||||
mcp_clients, _mcp_to_close = mcp_loader.load_mcp_tools(mcp_connections, actor_id)
|
||||
|
||||
# AWS MCP Server connection (system-level, SigV4 auth)
|
||||
_aws_mcp_client = None
|
||||
_aws_mcp_tools = []
|
||||
try:
|
||||
from strands.tools.mcp import MCPClient
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
_aws_mcp_client = MCPClient(
|
||||
lambda: streamablehttp_client(config.AWS_MCP_URL, auth=_AwsMcpSigV4Auth())
|
||||
)
|
||||
_aws_mcp_client.start()
|
||||
_aws_mcp_tools = [_aws_mcp_client]
|
||||
except Exception as _e:
|
||||
import traceback
|
||||
print(f"[main] AWS MCP client failed to start: {type(_e).__name__}: {_e}")
|
||||
print(traceback.format_exc())
|
||||
|
||||
all_tools = base_tools + mcp_clients + _aws_mcp_tools
|
||||
all_tools = base_tools + mcp_clients
|
||||
|
||||
agent = Agent(
|
||||
model=model,
|
||||
@@ -505,11 +529,6 @@ async def main(payload: dict, context):
|
||||
_typing_active = False
|
||||
session_manager.close()
|
||||
mcp_loader.close_mcp_clients(_mcp_to_close)
|
||||
if _aws_mcp_client:
|
||||
try:
|
||||
_aws_mcp_client.stop()
|
||||
except Exception:
|
||||
pass
|
||||
# Check if session exceeds window — flag for compaction on next invocation
|
||||
memory_manager.check_window_and_flag(actor_id, session_id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user