GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/Advanced
Advanced/2026-04-11Advanced

Connecting Gemini Agents with Google's A2A Protocol: Design, Implementation, and Cloud Run Operations

Build cross-framework agent coordination with the Agent2Agent (A2A) protocol and Gemini API. Agent card design, measured comparison of three coordination patterns, and the operational details that matter on Cloud Run.

a2a2agent2agentgemini-api277multi-agent2adk2protocol2production140

Premium Article

One night I split my article pipeline into three agents. Topic selection, drafting, quality checks. Cleaner separation, I thought.

A week later I was staring at my own glue code, stuck. As an indie developer, I own every seam I create — there is no platform team to hand them to. The three agents were written against different libraries, and every seam meant reshaping JSON, picking a timeout, and hand-rolling retry logic. The glue had grown longer than the agents.

Google's Agent2Agent (A2A) protocol is an attempt to standardize that seam. Agents built on different frameworks, from different providers, speak one HTTP-based spec. Neither side needs to know how the other is implemented.

What follows walks through the A2A specification, builds a FastAPI implementation alongside Gemini API and ADK, and takes it all the way to Cloud Run. The later sections cover the operational lessons I only found by running it.

Who this is for:

  • Developers with existing Gemini API agent experience
  • Engineers looking to build multi-agent coordination systems
  • Teams ready to apply A2A protocol in production environments

Reading Gemini API Function Calling: Tool Integration in Practice and Building a Custom Gemini API Agent Loop Without ADK first will make the design decisions here easier to follow.


A2A Protocol Specification and Core Concepts

Protocol Architecture

A2A is a lightweight specification built on HTTP/JSON-RPC 2.0. Each agent operates as an "A2A server," exposing standardized endpoints that any A2A client can interact with.

The four core components of A2A are:

① Agent Card: A JSON metadata document describing an agent's capabilities, endpoint URL, and authentication requirements. Published at /.well-known/agent.json, this is how other agents "discover" your agent.

② Task: The unit of work a client delegates to an agent. Each task has a unique ID and progresses through a lifecycle: submitted → working → completed / failed.

③ Artifact: The output produced by a task. Supports multiple MIME types (text, JSON, files) and can be streamed incrementally during processing.

④ Message: The communication channel during task execution. Enables multi-turn dialogue between user and agent, or between coordinating agents.

Communication Flow

Client                          A2A Server (Agent)
  |                                     |
  |--- GET /.well-known/agent.json ---> |  ① Discover agent capabilities
  |<-- {"name":"...", "skills":[...]} --|
  |                                     |
  |--- POST /tasks/send --------------> |  ② Submit task
  |    {"id":"task-123",                |
  |     "message":{"role":"user",...}}  |
  |<-- {"status":{"state":"submitted"}} |
  |                                     |
  |--- GET /tasks/get?id=task-123 ----> |  ③ Poll for result
  |<-- {"status":{"state":"completed"}, |
  |     "artifacts":[...]}              |
  |                                     |
  |  OR — subscribe to real-time stream |
  |--- POST /tasks/sendSubscribe -----> |
  |<== data: {"type":"artifact",...}  --|  (SSE streaming)
  |<== data: {"type":"status",...}   --|

Designing an Agent Card

The agent card is your agent's calling card—it determines what requests other agents will send your way. A well-designed card improves discoverability and reduces integration friction.

{
  "name": "Data Analysis Agent",
  "description": "Statistical analysis of CSV and JSON data with insights and visualizations",
  "url": "https://data-agent.example.run.app",
  "version": "1.2.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": false
  },
  "authentication": {
    "schemes": ["Bearer"]
  },
  "skills": [
    {
      "id": "analyze-csv",
      "name": "CSV Statistical Analysis",
      "description": "Accepts a CSV file or text, returns statistics (mean, variance, correlation) and key business insights",
      "inputModes": ["text", "file"],
      "outputModes": ["text", "data"]
    },
    {
      "id": "generate-chart",
      "name": "Data Visualization",
      "description": "Automatically selects the best chart type (bar, line, scatter) and generates it from numerical data",
      "inputModes": ["data"],
      "outputModes": ["file"]
    }
  ]
}

Design guidelines:

  • Write description in one sentence that captures the agent's specific domain
  • Make skills[].description concrete: what goes in, what comes out
  • Be precise with inputModes / outputModes—clients use these to decide if your agent is the right choice

Setup and Basic A2A Server Implementation

Installing Dependencies

# Google ADK (includes A2A server utilities)
pip install google-adk
 
# Gemini API client
pip install google-generativeai
 
# Web framework + utilities
pip install fastapi uvicorn httpx pyjwt

Minimal A2A Server

# a2a_server.py
import uuid
import asyncio
from datetime import datetime
from typing import Optional
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
import google.generativeai as genai
import os
 
# Initialize Gemini API
# ⚠️ Always load API keys from environment variables in production
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
model = genai.GenerativeModel("gemini-2.5-pro")
 
app = FastAPI(title="Gemini A2A Agent", version="1.0.0")
 
# Task store (use Redis or Firestore in production)
tasks: dict[str, dict] = {}
 
# =====================
# Agent Card Definition
# =====================
AGENT_CARD = {
    "name": "Gemini General Analysis Agent",
    "description": "Text analysis, summarization, and structured data extraction powered by Gemini 2.5 Pro",
    "url": "https://your-agent.run.app",
    "version": "1.0.0",
    "capabilities": {"streaming": True, "pushNotifications": False},
    "authentication": {"schemes": ["Bearer"]},
    "skills": [
        {
            "id": "analyze-text",
            "name": "Text Analysis",
            "description": "Analyzes input text and returns summaries, insights, or structured JSON",
            "inputModes": ["text"],
            "outputModes": ["text", "data"]
        }
    ]
}
 
# =====================
# Endpoints
# =====================
 
@app.get("/.well-known/agent.json")
async def get_agent_card():
    """Expose the agent card (required A2A endpoint)"""
    return AGENT_CARD
 
class TaskRequest(BaseModel):
    id: str
    message: dict
    sessionId: Optional[str] = None
 
@app.post("/tasks/send")
async def send_task(req: TaskRequest, background_tasks: BackgroundTasks):
    """Accept a task asynchronously and begin processing"""
    task_id = req.id
    tasks[task_id] = {
        "id": task_id,
        "status": {"state": "submitted", "timestamp": datetime.utcnow().isoformat()},
        "artifacts": [],
        "history": [req.message]
    }
    background_tasks.add_task(process_with_gemini, task_id, req.message)
    return tasks[task_id]
 
@app.get("/tasks/get")
async def get_task(id: str):
    """Return the current state of a task"""
    if id not in tasks:
        raise HTTPException(status_code=404, detail="Task not found")
    return tasks[id]
 
async def process_with_gemini(task_id: str, message: dict):
    """Internal function: call Gemini API and store the result"""
    try:
        tasks[task_id]["status"]["state"] = "working"
 
        user_text = " ".join(
            part.get("text", "")
            for part in message.get("parts", [])
            if part.get("type") == "text"
        )
 
        # Run blocking Gemini call in a thread pool
        response = await asyncio.to_thread(model.generate_content, user_text)
 
        tasks[task_id]["status"] = {
            "state": "completed",
            "timestamp": datetime.utcnow().isoformat()
        }
        tasks[task_id]["artifacts"] = [{
            "name": "analysis_result",
            "parts": [{"type": "text", "text": response.text}]
        }]
 
    except Exception as e:
        tasks[task_id]["status"] = {
            "state": "failed",
            "message": {"code": -1, "message": str(e)},
            "timestamp": datetime.utcnow().isoformat()
        }

Verify it works:

# Start the server
uvicorn a2a_server:app --reload
 
# Fetch the agent card
curl http://localhost:8000/.well-known/agent.json
 
# Submit a task
curl -X POST http://localhost:8000/tasks/send \
  -H "Content-Type: application/json" \
  -d '{"id":"task-001","message":{"role":"user","parts":[{"type":"text","text":"Explain AI agents in 3 sentences."}]}}'
 
# Retrieve the result
curl "http://localhost:8000/tasks/get?id=task-001"
# → {"status":{"state":"completed"},"artifacts":[{"parts":[{"type":"text","text":"..."}]}]}

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Build the full A2A skeleton in working FastAPI code — agent cards, task lifecycle, and artifacts
Compare pipeline, fan-out, and dynamic routing on the same task with measured latency and token costs, and learn when each one wins
Get the operational details the spec leaves out — agent card cache drift, task retry idempotency, and how A2A fits alongside Managed Agents
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Advanced2026-04-26
Custom Gemini API Agent Loop Without ADK — A Complete Production Guide to Tool Calling, Memory, and Parallel Execution
Build production-grade AI agents using Gemini API directly without Google ADK. This guide covers custom agent loops, tool calling patterns, sliding window memory, parallel execution, and battle-tested error recovery strategies.
Advanced2026-07-09
Setting a Token Budget Per Free User: Balancing AdMob Revenue Against AI Feature Cost
Rate limits protect requests per minute. They do nothing for the invoice that arrives at the end of the month. Here is how I derive a per-user token budget from ad revenue, keep the ledger inside a single call wrapper, degrade gracefully at a soft cap, and detect abuse with one concentration ratio.
Advanced2026-07-07
Render Structured Output Field by Field as It Streams: Safe Partial JSON Parsing
With responseSchema streaming, the screen stays blank until the JSON closes. This walks through a partial parser that safely completes unclosed JSON, plus anti-flicker fencing that never lets a field move backward, and shows how time-to-first-field dropped from about 2.4s to 0.4s in practice.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →