Skip to content

Commit 574e0cb

Browse files
Songbird99claude
andcommitted
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 <[email protected]>
1 parent f6cdb1a commit 574e0cb

File tree

1 file changed

+34
-11
lines changed

1 file changed

+34
-11
lines changed

ai/src/fuzzforge_ai/remote_agent.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,50 @@ def __init__(self, url: str):
2626
"""Initialize connection to a remote agent"""
2727
self.url = url.rstrip('/')
2828
self.agent_card = None
29-
self.client = httpx.AsyncClient(timeout=120.0)
29+
self.client = httpx.AsyncClient(timeout=120.0, follow_redirects=True)
3030
self.context_id = None
3131

3232
async def get_agent_card(self) -> Optional[Dict[str, Any]]:
3333
"""Get the agent card from the remote agent"""
34-
try:
35-
# Try new path first (A2A 0.3.0+)
36-
response = await self.client.get(f"{self.url}/.well-known/agent-card.json")
37-
response.raise_for_status()
38-
self.agent_card = response.json()
39-
return self.agent_card
40-
except Exception:
41-
# Try old path for compatibility
34+
# If URL already points to a .json file, fetch it directly
35+
if self.url.endswith('.json'):
4236
try:
43-
response = await self.client.get(f"{self.url}/.well-known/agent.json")
37+
response = await self.client.get(self.url)
4438
response.raise_for_status()
4539
self.agent_card = response.json()
40+
41+
# Use canonical URL from agent card if provided
42+
if isinstance(self.agent_card, dict) and "url" in self.agent_card:
43+
self.url = self.agent_card["url"].rstrip('/')
44+
4645
return self.agent_card
4746
except Exception as e:
4847
print(f"Failed to get agent card from {self.url}: {e}")
4948
return None
49+
50+
# Try both agent-card.json (A2A 0.3.0+) and agent.json (legacy)
51+
well_known_paths = [
52+
"/.well-known/agent-card.json",
53+
"/.well-known/agent.json",
54+
]
55+
56+
for path in well_known_paths:
57+
try:
58+
response = await self.client.get(f"{self.url}{path}")
59+
response.raise_for_status()
60+
self.agent_card = response.json()
61+
62+
# Use canonical URL from agent card if provided
63+
if isinstance(self.agent_card, dict) and "url" in self.agent_card:
64+
self.url = self.agent_card["url"].rstrip('/')
65+
66+
return self.agent_card
67+
except Exception:
68+
continue
69+
70+
print(f"Failed to get agent card from {self.url}")
71+
print("Tip: If agent is at /a2a/something, use full URL: /register http://host:port/a2a/something")
72+
return None
5073

5174
async def send_message(self, message: str | Dict[str, Any] | List[Dict[str, Any]]) -> str:
5275
"""Send a message to the remote agent using A2A protocol"""
@@ -93,7 +116,7 @@ async def send_message(self, message: str | Dict[str, Any] | List[Dict[str, Any]
93116
payload["params"]["contextId"] = self.context_id
94117

95118
# Send to root endpoint per A2A protocol
96-
response = await self.client.post(f"{self.url}/", json=payload)
119+
response = await self.client.post(self.url, json=payload)
97120
response.raise_for_status()
98121
result = response.json()
99122

0 commit comments

Comments
 (0)