Claude Code Claude Opus 4.6 commited on
Commit
d783c91
·
1 Parent(s): 749b560

Claude Code: Fix A2A communication - add missing /a2a/jsonrpc endpoint

Browse files

The A2A JSON-RPC endpoint was missing, causing all A2A communication
to fail with 404 errors. Added proper /a2a/jsonrpc POST endpoint that:
- Handles message/send method from other HuggingClaw agents
- Processes messages through the brain
- Returns proper JSON-RPC 2.0 responses
- Updates worker state during processing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +127 -0
app.py CHANGED
@@ -284,6 +284,133 @@ def get_agents():
284
  }]
285
 
286
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  # CRITICAL: Main block to ensure uvicorn.run() executes immediately
288
  # This ensures server binds to 0.0.0.0:7860 regardless of agent initialization
289
  if __name__ == "__main__":
 
284
  }]
285
 
286
 
287
+ class A2AMessage(BaseModel):
288
+ """A2A message format."""
289
+ messageId: str
290
+ role: str
291
+ parts: list
292
+
293
+
294
+ class A2AParts(BaseModel):
295
+ """A2A message parts."""
296
+ type: str
297
+ text: str
298
+
299
+
300
+ class A2AParams(BaseModel):
301
+ """A2A request params."""
302
+ message: A2AMessage
303
+
304
+
305
+ class A2ARequest(BaseModel):
306
+ """A2A JSON-RPC request format."""
307
+ jsonrpc: str = "2.0"
308
+ id: str
309
+ method: str
310
+ params: Optional[A2AParams] = None
311
+
312
+
313
+ @app.post("/a2a/jsonrpc")
314
+ async def a2a_jsonrpc(request: Request):
315
+ """
316
+ A2A JSON-RPC endpoint for agent-to-agent communication.
317
+
318
+ Handles message/send method from other HuggingClaw agents.
319
+ """
320
+ try:
321
+ # Parse JSON-RPC request
322
+ body = await request.json()
323
+ msg_id = body.get("id", "")
324
+ method = body.get("method", "")
325
+
326
+ # Handle message/send method
327
+ if method == "message/send":
328
+ params = body.get("params", {})
329
+ message_data = params.get("message", {})
330
+ parts = message_data.get("parts", [])
331
+
332
+ # Extract text from parts
333
+ user_message = ""
334
+ for part in parts:
335
+ if part.get("type") == "text" or part.get("kind") == "text":
336
+ user_message = part.get("text", "")
337
+ break
338
+
339
+ # Update worker state to processing
340
+ global _worker_state
341
+ _worker_state["current_state"] = "processing"
342
+ _worker_state["last_heartbeat"] = datetime.utcnow().isoformat() + "+00:00"
343
+
344
+ try:
345
+ # Process message through brain
346
+ from error_handlers import handle_brain_response
347
+ response_text = handle_brain_response(user_message)
348
+
349
+ # Reset state to idle after processing
350
+ _worker_state["current_state"] = "idle"
351
+
352
+ # Return A2A JSON-RPC success response
353
+ return JSONResponse({
354
+ "jsonrpc": "2.0",
355
+ "id": msg_id,
356
+ "result": {
357
+ "status": {
358
+ "state": "completed",
359
+ "message": {
360
+ "parts": [{
361
+ "type": "text",
362
+ "kind": "text",
363
+ "text": response_text
364
+ }]
365
+ }
366
+ }
367
+ }
368
+ }, status_code=200)
369
+
370
+ except Exception as e:
371
+ # Reset state on error
372
+ _worker_state["current_state"] = "idle"
373
+ logger.error(f"[A2A] Brain processing failed: {e}")
374
+
375
+ # Return A2A error response
376
+ return JSONResponse({
377
+ "jsonrpc": "2.0",
378
+ "id": msg_id,
379
+ "result": {
380
+ "status": {
381
+ "state": "failed",
382
+ "message": {
383
+ "parts": [{
384
+ "type": "text",
385
+ "text": f"Error: {str(e)}"
386
+ }]
387
+ }
388
+ }
389
+ }
390
+ }, status_code=200)
391
+
392
+ # Unknown method
393
+ return JSONResponse({
394
+ "jsonrpc": "2.0",
395
+ "id": msg_id,
396
+ "error": {
397
+ "code": -32601,
398
+ "message": f"Method not found: {method}"
399
+ }
400
+ }, status_code=200)
401
+
402
+ except Exception as e:
403
+ logger.error(f"[A2A] Request parsing failed: {e}")
404
+ return JSONResponse({
405
+ "jsonrpc": "2.0",
406
+ "id": "",
407
+ "error": {
408
+ "code": -32700,
409
+ "message": "Parse error"
410
+ }
411
+ }, status_code=400)
412
+
413
+
414
  # CRITICAL: Main block to ensure uvicorn.run() executes immediately
415
  # This ensures server binds to 0.0.0.0:7860 regardless of agent initialization
416
  if __name__ == "__main__":