Learn how to expose an agent built with Google ADK as an A2A server via the to_a2a utility, use a remote A2A agent as a sub-agent, and the adapter pattern for other frameworks like LangChain, LangGraph, CrewAI, and the OpenAI Agents SDK.

In episode 11 we discussed the gRPC binding: the protobuf definition for A2A services, the advantages of streaming and backpressure, and when to choose gRPC over HTTP/JSON. Now we step up one level of abstraction. Instead of writing an A2A server from scratch with a2a-sdk or a2a-sdk-ts, we'll hook up popular agent frameworks so they "speak" A2A directly.
The big question is simple: you already have an agent running on your favorite framework — how do you make that agent discoverable and callable by agents from a different framework? The answer is framework integration. Google ADK has native A2A support, while other frameworks need the adapter pattern.
This episode's roadmap: we start by getting to know Google ADK, then practice exposing an ADK agent as an A2A server, using a remote A2A agent as a sub-agent, and finally dissect the adapter pattern for LangChain, LangGraph, CrewAI, up to the OpenAI Agents SDK.
Google ADK (Agent Development Kit) is Google's official Python framework for building agents, introduced around the time A2A was first donated to the Linux Foundation. ADK is designed for production: it has agent, sub-agent, tools, session, and event-based execution concepts. Since mid-2025, ADK has had native A2A support, meaning you don't need to write your own JSON-RPC handler.
Info
One thing that makes ADK interesting: agents in ADK are hierarchical. A root agent can have local sub-agents, and those sub-agents can be swapped for remote agents communicating over A2A without changing the root agent's code.
Installing ADK and its supporting SDK is done in one go:
pip install google-adk a2a-sdk uvicornThe installed A2A SDK version is detected automatically by ADK, whether 0.3.x or 1.x.x. That means you don't need to adjust your code when the SDK is upgraded.
The fastest way to expose an ADK agent is the to_a2a utility. This function turns an existing agent into an ASGI app ready to run with uvicorn, while also automatically generating an agent card from the agent's metadata. Just add the to_a2a import and one line, a2a_app = to_a2a(agent):
from google.adk import Agent
from google.adk.tools import google_search
from google.adk.a2a.utils.agent_to_a2a import to_a2a
agent = Agent(
name="fakta_agent",
model="gemini-2.5-flash-lite",
description="Agent yang mencari fakta menarik menggunakan Google Search.",
instruction="Kamu adalah agent yang membantu menemukan fakta menarik dan akurat.",
tools=[google_search],
)
a2a_app = to_a2a(agent)Run the server:
uvicorn agent:a2a_app --host localhost --port 8001The auto-generated agent card can be accessed at the standard endpoint:
curl http://localhost:8001/.well-known/agent-card.jsonWarning
There are two ways to expose an ADK agent. The first is to_a2a as above, ideal for full control via uvicorn. The second is the adk api_server --a2a CLI, which serves agents based on the agent.json file and automatically integrates with adk web for debugging. For production, to_a2a gives you more control over deployment.
Now we reverse direction: how can another ADK agent use that A2A server? ADK provides the RemoteA2aAgent component — a sub-agent that communicates with a remote agent over the A2A protocol. The root agent just lists the remote agent's card URL.
from google.adk import Agent
from google.adk.agents import RemoteA2aAgent
from google.adk.sessions import InMemorySessionService
remote = RemoteA2aAgent(
name="remote_fakta",
agent_card_url="http://localhost:8001/.well-known/agent-card.json",
)
supervisor = Agent(
name="supervisor",
model="gemini-2.5-flash",
instruction="Gunakan sub-agent remote_fakta untuk mencari fakta menarik.",
sub_agents=[remote],
)
session_service = InMemorySessionService()
app = to_a2a(supervisor)The pattern RemoteA2aAgent runs behind the scenes: the supervisor extracts relevant data from its session, then calls the remote agent by sending a prompt and arguments via message/send. The remote agent manages its own session on its server, then returns the result. This is the opaque agent principle: the supervisor doesn't know how the remote agent works internally, it only knows how to communicate. This bridge is also cross-framework — agents from other frameworks can be called as ADK sub-agents as long as they expose a compatible A2A endpoint.
Frameworks like LangChain, LangGraph, and CrewAI don't have native A2A support like ADK. The solution is the adapter: a thin layer that translates a framework's "give text, return text" contract into the A2A task lifecycle. The community provides the a2a-adapter library, which has ready-made adapters for many frameworks.
Install with the extra for your framework:
pip install "a2a-adapter[langchain]" "a2a-adapter[langgraph]" "a2a-adapter[crewai]"Example of exposing a LangChain runnable as an A2A server:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from a2a_adapter import LangChainAdapter, serve_agent
chain = ChatPromptTemplate.from_template("Answer: {input}") | ChatOpenAI(model="gpt-4o-mini")
adapter = LangChainAdapter(runnable=chain, input_key="input")
serve_agent(adapter, port=8002)For LangGraph, just wrap the compiled graph:
from a2a_adapter import LangGraphAdapter, serve_agent
graph = builder.compile()
adapter = LangGraphAdapter(graph=graph)
serve_agent(adapter, port=9002)For CrewAI, the adapter wraps the crew along with its timeout:
from a2a_adapter import CrewAIAdapter, serve_agent
adapter = CrewAIAdapter(crew=your_crew, timeout=600)
serve_agent(adapter, port=8001)All these adapters generate automatic AgentCards, task management, SSE streaming support (LangChain and LangGraph auto-detect streaming), and push notifications, because the protocol tasks are fully handled by the A2A SDK.
For frameworks without a ready-made adapter, you can write your own. The minimal contract is implementing the invoke method of BaseA2AAdapter. The following example wraps an agent from the OpenAI Agents SDK:
from a2a_adapter import BaseA2AAdapter, serve_agent
class OpenAIAgentsAdapter(BaseA2AAdapter):
async def invoke(self, user_input, context_id=None, **kwargs):
result = await your_openai_agent.run(user_input)
return result.final_output
adapter = OpenAIAgentsAdapter()
serve_agent(adapter, port=8003)Notice the clean division of responsibilities. The adapter only answers one question: "given text, return text". Everything else — the task store, SSE, push notifications, and AgentCard serving — is handled by the A2A SDK. This is the key design principle of the adapter pattern: you don't need to understand the entire protocol spec to take part in the A2A ecosystem.
Success
This pattern works in both directions. Any framework — n8n, OpenClaw, Hermes, Ollama, even a plain Python function — can be wrapped with an adapter and become an A2A citizen in a few lines of code. As a result, your team's agent catalog is no longer tied to a single framework.
In this episode we learned that A2A doesn't force you to write a server from scratch. Google ADK has native A2A support via to_a2a, and RemoteA2aAgent for remote consumption, while other frameworks can be integrated through the adapter pattern provided by the a2a-adapter library. These two integration directions — exposing and consuming — let any framework join a multi-agent network.
Here's the core takeaway:
to_a2a turns an ADK agent into an A2A server and generates an agent card automatically.RemoteA2aAgent lets a root agent use a remote agent as a cross-framework sub-agent.invoke method to join the A2A ecosystem.From now on, imagine your network: several agents from different frameworks calling each other over one common protocol. But once many agents are connected, security questions arise — where is the network boundary, who is allowed in, and how do you secure each node.
In the next episode, episode 13, we'll discuss Secure Multi-Agent Deployment: private versus public agent topologies, egress and ingress policies, leveraging a service mesh, and hardening like rate limiting, input sanitization, and sandboxing tool execution on remote agents. See you there!