Mete Atamel Developer Advocate @ Google @meteatamel atamel.dev speakerdeck.com/meteatamel github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols
the protocol version and the capabilities its _meta field Servers advertise their supported versions and capabilities through the mandatory server/discover request
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
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 }"""
-> 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?"), ]
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
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
elicitation/create (Request more information) 3. Present elicitation UI 4. Provide requested information 5. Return user response 6. tools/call response
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
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
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
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)
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:
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
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
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
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)
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….
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
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
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
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
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
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
"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
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
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
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
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
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, ...)