mirror of
https://github.com/FuzzingLabs/fuzzforge_ai.git
synced 2026-02-12 19:12:49 +00:00
Support flexible A2A agent registration and fix redirects
- Accept direct .json URLs (e.g., http://host/.well-known/agent-card.json) - Accept base agent URLs (e.g., http://host/a2a/sentinel) - Extract canonical URL from agent card response - Try both agent-card.json and agent.json for compatibility - Follow HTTP redirects for POST requests (fixes 307 redirects) - Remove trailing slash from POST endpoint to avoid redirect loops 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -26,27 +26,50 @@ class RemoteAgentConnection:
|
|||||||
"""Initialize connection to a remote agent"""
|
"""Initialize connection to a remote agent"""
|
||||||
self.url = url.rstrip('/')
|
self.url = url.rstrip('/')
|
||||||
self.agent_card = None
|
self.agent_card = None
|
||||||
self.client = httpx.AsyncClient(timeout=120.0)
|
self.client = httpx.AsyncClient(timeout=120.0, follow_redirects=True)
|
||||||
self.context_id = None
|
self.context_id = None
|
||||||
|
|
||||||
async def get_agent_card(self) -> Optional[Dict[str, Any]]:
|
async def get_agent_card(self) -> Optional[Dict[str, Any]]:
|
||||||
"""Get the agent card from the remote agent"""
|
"""Get the agent card from the remote agent"""
|
||||||
try:
|
# If URL already points to a .json file, fetch it directly
|
||||||
# Try new path first (A2A 0.3.0+)
|
if self.url.endswith('.json'):
|
||||||
response = await self.client.get(f"{self.url}/.well-known/agent-card.json")
|
|
||||||
response.raise_for_status()
|
|
||||||
self.agent_card = response.json()
|
|
||||||
return self.agent_card
|
|
||||||
except Exception:
|
|
||||||
# Try old path for compatibility
|
|
||||||
try:
|
try:
|
||||||
response = await self.client.get(f"{self.url}/.well-known/agent.json")
|
response = await self.client.get(self.url)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
self.agent_card = response.json()
|
self.agent_card = response.json()
|
||||||
|
|
||||||
|
# Use canonical URL from agent card if provided
|
||||||
|
if isinstance(self.agent_card, dict) and "url" in self.agent_card:
|
||||||
|
self.url = self.agent_card["url"].rstrip('/')
|
||||||
|
|
||||||
return self.agent_card
|
return self.agent_card
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to get agent card from {self.url}: {e}")
|
print(f"Failed to get agent card from {self.url}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Try both agent-card.json (A2A 0.3.0+) and agent.json (legacy)
|
||||||
|
well_known_paths = [
|
||||||
|
"/.well-known/agent-card.json",
|
||||||
|
"/.well-known/agent.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in well_known_paths:
|
||||||
|
try:
|
||||||
|
response = await self.client.get(f"{self.url}{path}")
|
||||||
|
response.raise_for_status()
|
||||||
|
self.agent_card = response.json()
|
||||||
|
|
||||||
|
# Use canonical URL from agent card if provided
|
||||||
|
if isinstance(self.agent_card, dict) and "url" in self.agent_card:
|
||||||
|
self.url = self.agent_card["url"].rstrip('/')
|
||||||
|
|
||||||
|
return self.agent_card
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"Failed to get agent card from {self.url}")
|
||||||
|
print("Tip: If agent is at /a2a/something, use full URL: /register http://host:port/a2a/something")
|
||||||
|
return None
|
||||||
|
|
||||||
async def send_message(self, message: str | Dict[str, Any] | List[Dict[str, Any]]) -> str:
|
async def send_message(self, message: str | Dict[str, Any] | List[Dict[str, Any]]) -> str:
|
||||||
"""Send a message to the remote agent using A2A protocol"""
|
"""Send a message to the remote agent using A2A protocol"""
|
||||||
@@ -93,7 +116,7 @@ class RemoteAgentConnection:
|
|||||||
payload["params"]["contextId"] = self.context_id
|
payload["params"]["contextId"] = self.context_id
|
||||||
|
|
||||||
# Send to root endpoint per A2A protocol
|
# Send to root endpoint per A2A protocol
|
||||||
response = await self.client.post(f"{self.url}/", json=payload)
|
response = await self.client.post(self.url, json=payload)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user