forked from aws/agentcore-cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhandler.py
More file actions
144 lines (110 loc) · 4.18 KB
/
Copy pathhandler.py
File metadata and controls
144 lines (110 loc) · 4.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
Lambda handler for AgentCore Gateway tools.
This template mirrors the FastMCP server tools but runs as a Lambda function
behind an AgentCore Gateway.
Tool routing uses bedrockAgentCoreToolName from client context:
Format: {target_name}___{tool_name}
"""
import json
import logging
import urllib.request
import urllib.error
from typing import Any, Dict, Optional
logger = logging.getLogger()
logger.setLevel(logging.INFO)
HTTP_TIMEOUT = 10
TOOLS = {}
def tool(name: str):
"""Decorator to register a tool handler."""
def decorator(func):
TOOLS[name] = func
return func
return decorator
def lambda_handler(event: Dict[str, Any], context) -> Dict[str, Any]:
"""Route incoming gateway requests to the appropriate tool."""
try:
extended_name = context.client_context.custom.get("bedrockAgentCoreToolName", "")
tool_name = None
if "___" in extended_name:
tool_name = extended_name.split("___", 1)[1]
if not tool_name:
return _response(400, {"error": "Missing tool name in bedrockAgentCoreToolName"})
handler = TOOLS.get(tool_name)
if not handler:
return _response(400, {"error": f"Unknown tool: {tool_name}"})
result = handler(event)
return _response(200, {"result": result})
except Exception as e:
logger.exception("Tool execution failed")
return _response(500, {"error": str(e)})
def _response(status_code: int, body: Dict[str, Any]) -> Dict[str, Any]:
"""Consistent JSON response wrapper."""
return {"statusCode": status_code, "body": json.dumps(body)}
def _fetch_json(url: str) -> Optional[Dict[str, Any]]:
"""Fetch JSON from URL with error handling."""
try:
req = urllib.request.Request(url, headers={"User-Agent": "AgentCore-Tool/1.0"})
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT) as resp:
return json.loads(resp.read().decode())
except (urllib.error.URLError, json.JSONDecodeError) as e:
logger.warning(f"Failed to fetch {url}: {e}")
return None
@tool("{{Name}}_lookup_ip")
def lookup_ip(event: Dict[str, Any]) -> str:
"""Look up geolocation and network info for an IP address.
Args:
ip_address: IPv4 or IPv6 address to look up
"""
ip_address = event.get("ip_address", "")
if not ip_address:
return "Missing required parameter: ip_address"
data = _fetch_json(f"http://ip-api.com/json/{ip_address}")
if not data:
return f"Failed to look up IP: {ip_address}"
if data.get("status") == "fail":
return f"Lookup failed: {data.get('message', 'unknown error')}"
return (
f"IP: {data['query']}\n"
f"Location: {data['city']}, {data['regionName']}, {data['country']}\n"
f"ISP: {data['isp']}\n"
f"Organization: {data['org']}\n"
f"Timezone: {data['timezone']}"
)
@tool("{{Name}}_get_random_user")
def get_random_user(event: Dict[str, Any]) -> str:
"""Generate a random user profile for testing or mock data."""
data = _fetch_json("https://randomuser.me/api/")
if not data or "results" not in data:
return "Failed to generate random user."
user = data["results"][0]
name = user["name"]
location = user["location"]
return (
f"Name: {name['first']} {name['last']}\n"
f"Email: {user['email']}\n"
f"Location: {location['city']}, {location['country']}\n"
f"Phone: {user['phone']}"
)
@tool("{{Name}}_fetch_post")
def fetch_post(event: Dict[str, Any]) -> str:
"""Fetch a post by ID from JSONPlaceholder API.
Args:
post_id: The post ID (1-100)
"""
post_id = event.get("post_id")
if post_id is None:
return "Missing required parameter: post_id"
try:
post_id = int(post_id)
except (TypeError, ValueError):
return "post_id must be an integer"
if not 1 <= post_id <= 100:
return "Post ID must be between 1 and 100."
data = _fetch_json(f"https://jsonplaceholder.typicode.com/posts/{post_id}")
if not data:
return f"Failed to fetch post {post_id}."
return (
f"Post #{data['id']}\n"
f"Title: {data['title']}\n\n"
f"{data['body']}"
)