Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tour of Agent Protocols: MCP, A2A, AG-UI, A2UI ...

Tour of Agent Protocols: MCP, A2A, AG-UI, A2UI with ADK

Avatar for Mete Atamel

Mete Atamel

April 02, 2026

More Decks by Mete Atamel

Other Decks in Technology

Transcript

  1. Tour of Agent Protocols: MCP, A2A, AG-UI, A2UI with ADK

    Mete Atamel Developer Advocate @ Google @meteatamel atamel.dev speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols
  2. modelcontextprotocol.io An open protocol that defines a standardized way to

    provide tools (functions) and context to your LLMs
  3. After MCP MCP Host AI App MCP Client MCP Client

    MCP Client MCP Server MCP Server MCP Server (local) (remote) (remote) Tools
  4. MCP Data and Transport Layers MCP Client MCP Client Stdio

    transport ✉ Streamable HTTP transport ✉ Streamable HTTP transport MCP Server MCP Server (remote) (local) ✉ JSON-RPC 2.0
  5. MCP support in different tools Stdio transport Streamable HTTP transport

    Tools Resources Prompts Claude Desktop ✅ ❌ ✅ ✅ ✅ Gemini CLI ✅ ✅ ✅ ✅* ✅ Agent Development Kit ✅ ✅ ✅ ❌ ❌ *only static MCP resources
  6. MCP in Agent Development Kit root_agent = Agent( ... tools=[

    MCPToolset( connection_params=StdioServerParameters( command='npx', args=[ "-y", "@modelcontextprotocol/server-filesystem", os.path.abspath(TARGET_FOLDER_PATH), ], ),
  7. MCP is a stateless protocol now Every client request send

    the protocol version and the capabilities its _meta field Servers advertise their supported versions and capabilities through the mandatory server/discover request
  8. Tools Tools enable AI models to interact with external systems

    Each tool defines a specific operation with inputs and outputs
  9. Tools mcp = FastMCP(name="Tool Example") @mcp.tool() def sum(a: int, b:

    int) -> int: """Add two numbers together.""" return a + b @mcp.tool() def get_weather(city: str, unit: str = "celsius") -> str: """Get weather for a city.""" # This would normally call a weather API return f"Weather in {city}: 22degrees{unit[0].upper()}" github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/create-local-mcp
  10. Resources Resources provide read-only access to data that the AI

    application can retrieve and provide as context to models
  11. Resources mcp = FastMCP(name="Resource Example") @mcp.resource("file://documents/{name}") def read_document(name: str) ->

    str: """Read a document by name.""" # This would normally read from disk return f"Content of {name}" @mcp.resource("config://settings") def get_settings() -> str: """Get application settings.""" return """{ "theme": "dark", "language": "en", "debug": false }"""
  12. Prompts mcp = FastMCP(name="Prompt Example") @mcp.prompt(title="Code Review") def review_code(code: str)

    -> str: return f"Please review this code:\n\n{code}" @mcp.prompt(title="Debug Assistant") def debug_error(error: str) -> list[base.Message]: return [ base.UserMessage("I'm seeing this error:"), base.UserMessage(error), base.AssistantMessage("I'll help debug that. What have you tried so far?"), ]
  13. Tools, Resources, Prompts Who controls it? Use Cases Tools Model-controlled:

    Model decides when to call these Allow LLM to interact with external systems Resources App-controlled: App decides when to call these Provide read-only access to data that the AI application can retrieve and provide as context to models Prompts User-controlled: The user decides when to use these Provide reusable prompts for a domain, or showcase how to best use the MCP server github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/document-server
  14. Deploy MCP servers to Cloud Run Agent MCP Client ✅

    Deploy your MCP server alongside your AI agents, one product to learn Cloud Run ✅ Scalable: Cloud Run will scale your MCP server automatically based on demand MCP Server ✅ Centralized server: Share access to a centralized MCP server with team members through IAM privileges, allowing them to connect to it from their local machines instead of all running their own servers locally ✅ Security: Cloud Run provides an easy way to force authenticated requests to your MCP server Cloud Run External resources github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/deploy-mcp-to-cloudrun
  15. Elicitation Elicitation enables MCP servers to request approval, ask for

    missing info, clarification requests from users via MCP clients for more dynamic workflows
  16. Elicitation MCP Client User MCP Server 1. tools/call request 2.

    elicitation/create (Request more information) 3. Present elicitation UI 4. Provide requested information 5. Return user response 6. tools/call response
  17. Elicitation – MCP Server @mcp.tool async def approve_action(ctx: Context) ->

    str: """Simple tool that asks for user approval. No response.""" result = await ctx.elicit( "Approve this action?", response_type=None) if result.action == "accept": print(f"Accepted!") return "Action approved!" print("Declined or Cancelled!") return "Action not approved!" github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/elicitation
  18. Elicitation – MCP Client async def elicitation_handler_approve_action(message: str, response_type: type,

    params, context): print(f"Server asks: {message}") try: user_input = input(f"Your response: ") if user_input.lower() in ["no", "n"]: return ElicitResult(action="decline") return ElicitResult(action="accept") client = Client(" http://127.0.0.1:8080/mcp/ ", elicitation_handler= elicitation_handler_approve_action) async with client: result = await client.call_tool("approve_action") print(f"Tool result: {result.content[0].text}") github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/elicitation
  19. Progress Reporting and Monitoring MCP supports optional progress reporting from

    MCP servers and progress tracking from MCP clients for long-running operations
  20. Progress – MCP Server @mcp.tool async def process_items(items: list[str], ctx:

    Context) -> dict: """Process a list of items with progress updates.""" total = len(items) results = [] for i, item in enumerate(items): # Report progress as we process each item await ctx.report_progress(progress=i, total=total) # Simulate processing time await asyncio.sleep(0.1) results.append(item.upper()) # Report completion await ctx.report_progress(progress=total, total=total) return {"Processed": len(results), "results": results} github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/progress
  21. Progress – MCP Client async def progress_handler( progress: float, total:

    float | None, message: str | None ) -> None: if total is not None: if total == 100: percentage = (progress / total) * 100 print(f"Progress: {percentage:.1f}%") else: print(f"Progress: {progress}/{total}") else: print(f"Progress: {progress}") github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/progress
  22. Agent2Agent Protocol (A2A) Mete Atamel Developer Advocate @ Google @meteatamel

    atamel.dev speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/a2a
  23. Agents need to talk …but how? ✈ Flight agent 🏨

    Hotel agent 🎒 Travel agent 📌 Guide agent 🏄 Activity agent
  24. Agent2Agent (A2A) Protocol a2a-protocol.org An open protocol started at Google,

    now part of Agentic AI Foundation (AAIF), that standardizes how agents running on diverse frameworks and platforms communicate
  25. A2A GitHub 🏢 github.com/a2aproject - GitHub Organization 📜 github.com/a2aproject/A2A -

    Protocol and Documentation 🐍 github.com/a2aproject/a2a-python - Python SDK 🕸 github.com/a2aproject/a2a-js - TypeScript/Node.js SDK ☕ github.com/a2aproject/a2a-java - Java SDK 🦫 github.com/a2aproject/a2a-go - Go SDK 🔱 github.com/a2aproject/a2a-dotnet - C#/.NET SDK 📋 github.com/a2aproject/a2a-samples - Code Samples/Demos 🔍 github.com/a2aproject/a2a-inspector - Test A2A Agents
  26. A2A SDK pip install a2a-sdk from a2a.types import AgentCard, AgentSkill

    agent_card = AgentCard( name='Hello World Agent', description='Just a hello world agent', url='http://localhost:9999/', version='1.0.0', default_input_modes=['text'], default_ouput_modes=['text'], capabilities=AgentCapabilities(streaming=True), skills=[skill], ) goo.gle/a2a-python-sdk
  27. Agent Discovery User Client Agent A (Client) Agent B (Remote)

    What can you do? How can I contact you?
  28. Agent Discovery Agent Card - A JSON metadata document describing

    an agent's identity, capabilities, endpoint, skills, and authentication requirements User Client Agent A (Client) Agent B (Remote) Agent Card /.well-known/agent-card.json
  29. AgentSkill skill = AgentSkill( id='get_exchange_rate', name='Currency Exchange Rates Tool', description='Gets

    exchange rates between currencies', input_modes=['text/plain'], output_modes=['text/plain'], tags=['currency conversion', 'currency exchange'], examples=['What is 1 GBP in USD?'], )
  30. AgentCard agent_card = AgentCard( name='Currency Agent', description='Helps with exchange rates

    for currencies', url=f'http://{host}:{port}/', version='0.0.1', defaultInputModes=["text/plain"], defaultOutputModes=["text/plain"], capabilities=AgentCapabilities(streaming=True), supported_interfaces=[AgentInterface( protocol_binding='JSONRPC', url='http://127.0.0.1:9999', protocol_version='1.0')], skills=[skill])
  31. AgentExecutor DefaultRequestHandler Bridge between Agent and A2A AgentExecutor User Client

    Agent A (Client) Agent B (Remote) HTTP(S) Message Role (user, agent) Parts (text, file, or JSON) HTTP(S) Message Role (user, agent) Part (text, file, or JSON)
  32. AgentExecutor class AgentExecutor(ABC): """ Implementations of this interface contain the

    core logic of the agent, executing tasks based on requests and publishing updates to an event queue. """ @abstractmethod async def execute(self, context: RequestContext, event_queue: EventQueue ) -> None: """Execute the agent's logic for a given request context. ) @abstractmethod async def cancel( self, context: RequestContext, event_queue: EventQueue ) -> None:
  33. Agent Executor - Example async def execute(self, context: RequestContext, event_queue:

    EventQueue) -> None: # 1. Collect a task from request context if context.current_task: task = context.current_task else: # 1.1 If there is no task, create one and add it event queue task = new_task_from_user_message(context.message) await event_queue.enqueue_event(task) 53
  34. Agent Executor - Example async def execute(self, context: RequestContext, event_queue:

    EventQueue) -> None: ... # 2. Update task status in EventQueue using TaskUpdater class object task_updater = TaskUpdater( event_queue=event_queue, task_id=task.id, context_id=task.context_id ) await task_updater.update_status( state=TaskState.TASK_STATE_WORKING, message=new_text_message('Processing request...'), ) 54
  35. Agent Executor - Example async def execute(self, context: RequestContext, event_queue:

    EventQueue) -> None: ... # 3. Collect user request from request content and invoke LLM agent to generate content query = get_message_text(context.message) if query: result = await self.agent.invoke(user_request=query) else: result = 'No text input is provided!' 55
  36. Agent Executor - Example async def execute(self, context: RequestContext, event_queue:

    EventQueue) -> None: ... # 4. Add generated response as an artifact to EventQueue await task_updater.add_artifact( parts=[new_text_part(text=result, media_type='text/plain')]) print('Result: ', result) # 5. Update task status to completed await task_updater.update_status( state=TaskState.TASK_STATE_COMPLETED, message=new_text_message('Request is completed!'), ) 56
  37. A2A with Agent Development Kit (ADK) pip install google-adk[a2a] Expose

    an ADK Agent over A2A with automatic agent card generation and AgentExecutor already implemented from google.adk.agents.llm_agent import Agent from google.adk.a2a.utils.agent_to_a2a import to_a2a root_agent = Agent( model='gemini-2.0-flash', name='hello_world_agent', # ... ) # Make your agent A2A-compatible a2a_app = to_a2a(root_agent, port=8001) goo.gle/adk-a2a
  38. Option 1: Messages for trivial interactions Message - A single

    turn of communication between a client and an agent, containing content and a role ("user" or "agent") User Client Agent A (Client) Agent B (Remote) HTTP(S) Message Role (user, agent) Parts (text, file, or JSON) HTTP(S) Message Role (user, agent) Part (text, file, or JSON)
  39. Option 2: Tasks for stateful interactions Task - A stateful

    unit of work initiated by an agent, with a unique ID and defined lifecycle User Task Status: Unspecified Submitted Working Completed Failed Cancelled Input required Rejected Auth Required Client Agent A (Client) Agent B (Remote) Message Role Task ID Status (user, agent) Parts (text, file, or JSON) HTTP(S) ⚙ Processing….
  40. Option 2: Tasks for stateful interactions Artifact - A tangible

    output generated by an agent during a task (for example, a document, image, or structured data) User Task Status: Unspecified Submitted Working Completed Failed Cancelled Input required Rejected Auth Required Client Agent A (Client) Agent B (Remote) Message Role Task (user, agent) Parts (text, file, or JSON) JSON-RPC HTTP(S) ID Status Artifact Parts (text, file, or JSON) JSON-RPC
  41. Agent Interactions Request/Response (Polling) User ❌ Simple but inefficient Client

    Agent A (Client) Agent B (Remote) Message Role Task ID Status (user, agent) Parts (text, file, or JSON) HTTP(S)
  42. Agent Interactions Streaming with SSE User Client Agent A (Client)

    Agent B (Remote) HTTPS Agent Card Push updates Best suited for: Real-time progress monitoring of long-running tasks Receive large results (artifacts) incrementally. Interactive, conversational exchanges where immediate feedback or partial responses are beneficial with low latency Message Role (user, agent) Parts (text, file, or JSON) JSON-RPC Initial task Messages Artifacts streaming: true
  43. Agent Interactions Push Notifications for disconnected clients User Client Agent

    A (Client) Agent B (Remote) HTTPS Agent Card Best suited for: Very long-running tasks that can take minutes, hours, or days to complete. Clients that cannot or prefer not to maintain persistent connections, such as mobile applications. Clients only need to be notified of significant state changes rather than continuous updates. Message Role (user, agent) Parts (text, file, or JSON) JSON-RPC pushNotifications: true
  44. A2A Client – fetch agent card import httpx from a2a.client

    import A2ACardResolver # Initialize the A2ACardResolver instance with an HTTP client, base URL, # and use the default path for the agent card. async with httpx.AsyncClient() as httpx_client: resolver = A2ACardResolver( httpx_client=httpx_client, base_url='http://127.0.0.1:9999', ) public_agent_card = await resolver.get_agent_card() 67
  45. A2A Client – send non-streaming message from a2a.client import ClientConfig,

    create_client from a2a.helpers import new_text_message from a2a.types import Role, SendMessageRequest config = ClientConfig(streaming=False) client = await create_client(agent=public_agent_card, client_config=config) # Create a new text message to be sent to the A2A Server. message = new_text_message('Why is the sky blue?', role=Role.ROLE_USER) request = SendMessageRequest(message=message) print('Response:') async for chunk in client.send_message(request): print(chunk) 68
  46. A2A Client – send streaming message client_config = ClientConfig(streaming=True) client

    = await create_client(agent=public_agent_card, client_config=client_config) print('Response:') async for chunk in client.send_message(request): print(chunk) 69
  47. A2A with Agent Development Kit (ADK) pip install google-adk[a2a] Consume

    a remote agent in ADK from google.adk.agents.remote_a2a_agent import RemoteA2aAgent currency_agent = RemoteA2aAgent( name="currency_agent", description="Agent that can convert from one currency to another.", agent_card=( f"https://google.com/.well-known/agent-card.json" ), ) goo.gle/adk-a2a
  48. Agent-User Interaction Protocol (AG-UI) Mete Atamel Developer Advocate @ Google

    @meteatamel atamel.dev speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/ag-ui
  49. Agent-User Interaction (AG-UI) Protocol docs.ag-ui.com AG-UI is an open, lightweight,

    event-based protocol, created by the CopilotKit, that standardizes how agent backends connect to agent frontends
  50. • CopilotKit is the reference client implementation • Most major

    agent frameworks such as LangGraph, CrewAI, Google ADK, and more are supported
  51. AG-UI vs. MCP, A2A MCP gives agents tools A2A allows

    agents-to-agent communication AG-UI brings agents into user-facing applications
  52. Agent to UI Protocol (A2UI) Mete Atamel Developer Advocate @

    Google @meteatamel atamel.dev speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/a2ui
  53. Agent to UI Protocol (A2UI) a2ui.org A2UI is a generative

    UI protocol, from Google, that enables AI agents to generate rich, interactive user interfaces across web, mobile, desktop
  54. When a user asks a question to an agent (e.g.

    "What are some good restaurants in New York?"), the agent can not only return the list of restaurants, but also return UI descriptions that can be used by renderers to display the restaurants in a rich interactive format
  55. AG-UI vs. A2UI AG-UI connects frontends to agentic backends A2UI

    is a declarative generative UI spec, which agents can use to return UI widgets as part of their responses
  56. Four main message types 1. createSurface: Create a new surface

    and specify its catalog 2. updateComponents: Add or update UI components in a surface 3. updateDataModel: Update application state 4. deleteSurface: Remove an UI surface
  57. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

    11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. { "version": "v0.9", "createSurface": { "surfaceId": "main", "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json" } } { "version": "v0.9", "updateComponents": { "surfaceId": "main", "components": [...] } } { "version": "v0.9", "updateDataModel": { "surfaceId": "main", "path": "/user", "value": { "name": "Alice" } } }
  58. Catalog of UI Components • Layout: Row, Column, List -

    arrange other components • Display: Text, Image, Icon, Video, Divider - show information • Interactive: Button, TextField, CheckBox, DateTimeInput, Slider - user input • Container: Card, Tabs, Modal - group and organize content a2ui.org/reference/components
  59. Transport options • A2UI is transport-agnostic, meaning any mechanism that

    can deliver JSON messages works • Currently, A2A and AG UI are supported with REST API, WebSockets, and SSE as planned or proposed. See Transports on the latest supported transports. a2ui.org/concepts/transports/#available-transports
  60. Renderers • Once the A2UI JSON messages containing UI descriptions

    are generated by the agent, they need to be converted into native UI components by renderers • For web, there's Lit and Angular renderers and Flutter (GenUI SDK) for mobile/desktop/web a2ui.org/reference/renderers/#maintained-renderers
  61. Message Flow Book a table for 2 tomorrow at 7pm

    1. Agent creates surface 1. 2. 3. 4. 5. 6. 7. { "version": "v0.9", "createSurface": { "surfaceId": "booking", "catalogId": "https://a2ui.org/specification/v0_9/basic_catalog.json" } }
  62. Message Flow 2. Agent defines UI structure 1. 2. 3.

    4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. { "version": "v0.9", "updateComponents": { "surfaceId": "booking", "components": [ { "id": "root", "component": "Column", "children": ["header", "guests-field", "submit-btn"] }, { "id": "header", "component": "Text", "text": "Confirm Reservation", "variant": "h1" }, ...
  63. Message Flow 3. Agent populates data 1. 2. 3. 4.

    5. 6. 7. 8. 9. 10. 11. { "version": "v0.9", "updateDataModel": { "surfaceId": "booking", "path": "/reservation", "value": { "datetime": "2025-12-16T19:00:00Z", "guests": "2" } } }
  64. Message Flow 4. User edits guests to "3" → Client

    updates /reservation/guests automatically
  65. Message Flow 5. User clicks "Confirm" → Client sends action

    1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. { "version": "v0.9", "action": { "name": "confirm", "surfaceId": "booking", "context": { "details": { "datetime": "2025-12-16T19:00:00Z", "guests": "3" } } } }
  66. Message Flow 6. Agent responds → Updates UI or sends

    1. 2. 3. 4. { "version": "v0.9", "deleteSurface": { "surfaceId": "booking" } }
  67. Integrate A2UI into your agent 1. Define your catalog (basic

    or bring your own) with optional examples 1. 2. 3. 4. 5. 6. my_catalog = CatalogConfig.from_path( name="<MY_CATALOG_NAME>", catalog_path=("file:///path/to/catalog.json"), # Optional: help LLM with "few-shot" learning examples_path="path/to/examples/folder/*.json" ),
  68. Integrate A2UI into your agent 2. Initialize the Schema Manager

    to manage A2UI Spec versions 1. 2. 3. 4. schema_manager = A2uiSchemaManager( version="0.9", catalogs=[my_catalog], )
  69. Integrate A2UI into your agent 3. Generate a system prompt

    to handle A2UI instructions and create an agent 1. 2. 3. 4. 5. system_instruction = schema_manager.generate_system_prompt( role_description="You are a helpful assistant great at generating UI...", ) my_agent = AnyAgentFrameworkLLMAgent(instruction=system_instruction, ...)
  70. Integrate A2UI into your agent 4. Execute and Stream the

    UI 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. def handle_turn(user_query): llm_response = my_agent.respond(user_query) # The SDK helps parse, fix, and validate the LLM's JSON on the fly selected_catalog = schema_manager.get_selected_catalog() final_parts = parse_response_to_parts(llm_response, selected_catalog.validator) yield { "is_task_complete": True, "parts": final_parts, }
  71. Thank you! Mete Atamel Developer Advocate at Google @meteatamel atamel.dev

    speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols