Claude Code Claude Opus 4.6 commited on
Commit
adb357b
·
1 Parent(s): fd7f636

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

Browse files

- Add A2A JSON-RPC endpoint to app.py with message/send, health/check, ping, agent/info methods
- Enable a2a-gateway plugin in openclaw.json
- Fix missing inter-agent communication endpoint that was causing A2A failures

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

Files changed (2) hide show
  1. app.py +144 -0
  2. openclaw/.openclaw/openclaw.json +6 -0
app.py CHANGED
@@ -252,6 +252,150 @@ async def agent_push(request_data: dict):
252
  "recordedStatus": status
253
  }
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  if __name__ == "__main__":
256
  port = int(os.environ.get("PORT", 7860))
257
  uvicorn.run(app, host="0.0.0.0", port=port)
 
252
  "recordedStatus": status
253
  }
254
 
255
+
256
+ # A2A JSON-RPC endpoint for agent-to-agent communication
257
+ @app.post("/a2a/jsonrpc")
258
+ async def a2a_jsonrpc(request_data: dict):
259
+ """
260
+ Agent-to-Agent communication endpoint using JSON-RPC 2.0 protocol.
261
+
262
+ Expected format:
263
+ {
264
+ "jsonrpc": "2.0",
265
+ "id": "msg-123",
266
+ "method": "message/send",
267
+ "params": {...}
268
+ }
269
+ """
270
+ # Validate JSON-RPC format
271
+ jsonrpc_version = request_data.get("jsonrpc")
272
+ if jsonrpc_version != "2.0":
273
+ return {
274
+ "jsonrpc": "2.0",
275
+ "id": request_data.get("id", ""),
276
+ "error": {
277
+ "code": -32600,
278
+ "message": "Invalid Request - jsonrpc version must be 2.0"
279
+ }
280
+ }
281
+
282
+ method = request_data.get("method", "")
283
+ params = request_data.get("params", {})
284
+ req_id = request_data.get("id", "")
285
+
286
+ # Handle different A2A methods
287
+ try:
288
+ if method == "message/send":
289
+ # Handle incoming message from another agent
290
+ message = params.get("message", {})
291
+ message_id = message.get("messageId", req_id)
292
+
293
+ logger.info(f"[A2A] Received message: {message_id}")
294
+
295
+ # Process the message using Cain's brain
296
+ from error_handlers import handle_brain_response
297
+ content = message.get("parts", [{}])[0].get("text", "")
298
+ response_text = handle_brain_response(content)
299
+
300
+ return {
301
+ "jsonrpc": "2.0",
302
+ "id": req_id,
303
+ "result": {
304
+ "messageId": f"response-{message_id}",
305
+ "role": "assistant",
306
+ "parts": [{"type": "text", "text": response_text}],
307
+ "agent": "cain"
308
+ }
309
+ }
310
+
311
+ elif method == "health/check":
312
+ # Health check from another agent
313
+ from error_handlers import handle_status_file_read
314
+ status = handle_status_file_read()
315
+
316
+ return {
317
+ "jsonrpc": "2.0",
318
+ "id": req_id,
319
+ "result": {
320
+ "agent": "cain",
321
+ "status": status.get("health", "HEALTHY"),
322
+ "state": status.get("current_state", "idle"),
323
+ "timestamp": datetime.utcnow().isoformat() + "+00:00"
324
+ }
325
+ }
326
+
327
+ elif method == "ping":
328
+ # Simple ping for connectivity check
329
+ return {
330
+ "jsonrpc": "2.0",
331
+ "id": req_id,
332
+ "result": {
333
+ "agent": "cain",
334
+ "pong": True,
335
+ "timestamp": datetime.utcnow().isoformat() + "+00:00"
336
+ }
337
+ }
338
+
339
+ elif method == "agent/info":
340
+ # Return agent information
341
+ from error_handlers import handle_status_file_read
342
+ status = handle_status_file_read()
343
+
344
+ return {
345
+ "jsonrpc": "2.0",
346
+ "id": req_id,
347
+ "result": {
348
+ "agent_id": "cain",
349
+ "name": "Cain",
350
+ "role": "interaction",
351
+ "status": status.get("health", "HEALTHY"),
352
+ "state": status.get("current_state", "idle"),
353
+ "capabilities": [
354
+ "message_send",
355
+ "conversation_process",
356
+ "memory_read",
357
+ "memory_write",
358
+ "session_archive",
359
+ "context_manage",
360
+ "health_check",
361
+ "get_status",
362
+ "agent_ping"
363
+ ],
364
+ "parents": ["Adam", "Eve"],
365
+ "workspace": "tao-shen/HuggingClaw-Cain"
366
+ }
367
+ }
368
+
369
+ else:
370
+ # Unknown method
371
+ return {
372
+ "jsonrpc": "2.0",
373
+ "id": req_id,
374
+ "error": {
375
+ "code": -32601,
376
+ "message": f"Method not found: {method}",
377
+ "available_methods": [
378
+ "message/send",
379
+ "health/check",
380
+ "ping",
381
+ "agent/info"
382
+ ]
383
+ }
384
+ }
385
+
386
+ except Exception as e:
387
+ logger.error(f"[A2A] Error processing request: {e}")
388
+ return {
389
+ "jsonrpc": "2.0",
390
+ "id": req_id,
391
+ "error": {
392
+ "code": -32603,
393
+ "message": "Internal error",
394
+ "data": str(e)
395
+ }
396
+ }
397
+
398
+
399
  if __name__ == "__main__":
400
  port = int(os.environ.get("PORT", 7860))
401
  uvicorn.run(app, host="0.0.0.0", port=port)
openclaw/.openclaw/openclaw.json CHANGED
@@ -48,6 +48,12 @@
48
  },
49
  "coding-agent": {
50
  "enabled": true
 
 
 
 
 
 
51
  }
52
  }
53
  },
 
48
  },
49
  "coding-agent": {
50
  "enabled": true
51
+ },
52
+ "a2a-gateway": {
53
+ "enabled": true,
54
+ "port": 18800,
55
+ "protocol": "jsonrpc",
56
+ "timeout": 90
57
  }
58
  }
59
  },