"""HTTP request tool — make outbound HTTP calls."""
import json
import httpx
from .base import Tool, ToolResult
_TIMEOUT = 30.0
class HttpRequestTool(Tool):
name = "http_request"
description = (
"Make an HTTP request to an external URL. "
"Supports GET, POST, PUT, PATCH, DELETE. Returns status code and response body."
)
parameters = {
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "PATCH", "DELETE"],
"description": "HTTP method",
},
"url": {"type": "string", "description": "Full URL to request"},
"headers": {
"type": "object",
"description": "Optional HTTP headers as key-value pairs",
},
"body": {
"type": "object",
"description": "Optional JSON body for POST/PUT/PATCH requests",
},
"params": {
"type": "object",
"description": "Optional query parameters",
},
},
"required": ["method", "url"],
}
async def execute(self, params: dict) -> ToolResult:
method = params["method"].upper()
url = params["url"]
headers = params.get("headers") or {}
body = params.get("body")
query_params = params.get("params")
try:
async with httpx.AsyncClient(timeout=_TIMEOUT, follow_redirects=True) as client:
response = await client.request(
method=method,
url=url,
headers=headers,
json=body,
params=query_params,
)
# Try to decode as JSON for nicer output
try:
body_repr = json.dumps(response.json(), ensure_ascii=False, indent=2)
except Exception:
body_repr = response.text[:4096] # cap large responses
output = f"Status: {response.status_code}\n\n{body_repr}"
return ToolResult(
success=response.is_success,
output=output,
metadata={"status_code": response.status_code, "headers": dict(response.headers)},
error=None if response.is_success else f"HTTP {response.status_code}",
)
except httpx.TimeoutException:
return ToolResult(success=False, output=f"Request timed out after {_TIMEOUT}s", error="timeout")
except Exception as e:
return ToolResult(success=False, output=f"Request failed: {e}", error=str(e))