London? Recap: Traditional Tool Calling Weather Service {“forecast”:”rainy”} getWeather(“London”) It’s rainy in London Currency Service {“GBP:USD”:”1.36”} getCurrency(“GBP:USD) 1 British Pounds in US Dollars? 1 British Pounds is 1.36 US Dollars How’s the weather in London? Tools: getWeather, getCurrency getWeather(“London”) {“forecast”:”rainy”} It’s rainy in London 1 British Pounds in US Dollars? Tools: getWeather, getCurrency getCurrency(“GBP:USD”) {“GBP:USD”:”1.36”} 1 British Pounds is 1.36 US Dollars
(ADK) supports MCP tools out of the box Claude Desktop is good in testing stdio based MCP servers MCP support in different tools github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/use-existing-mcp
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
Run MCP Server Cloud Run External resources ✅ Deploy your MCP server alongside your AI agents, one product to learn ✅ Scalable: Cloud Run will scale your MCP server automatically based on demand ✅ 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 github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/deploy-mcp-to-cloudrun
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
on its behalf This is useful for: • Server uses LLM without directly integrating with it • Clients integrate and pay for their LLM usage • Human review in the middle before and after LLM call
Context) -> str: """Generate a Python code example for a given concept.""" response = await ctx.sample( messages=f"Write a Python code example demonstrating '{concept}'.", system_prompt="You are an expert Python programmer.", model_preferences=["gemini-2.5-pro", "gemini-2.5-flash"], temperature=0.2, max_tokens=800 ) return response.text github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/sampling
context: RequestContext) -> str: # Extract message content contents = [] for message in messages: content = message.content.text if hasattr(message.content, 'text') else str(message.content) contents.append(content) # Use the provided preferences system_prompt = params.systemPrompt or "You are a helpful assistant." temperature = params.temperature or 0.7 max_tokens = params.maxTokens or 500 model = params.modelPreferences.hints[0].name if params.modelPreferences else "gemini-2.5-flash" # Integrate with the LLM response_text = await generate_content(model, system_prompt, temperature, max_tokens, contents) return response_text github.com/meteatamel/genai-beyond-basics/tree/main/samples/protocols/mcp/sampling
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
Role (user, agent) Parts (text, file, or JSON) Message Role (user, agent) Part (text, file, or JSON) HTTP(S) 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")
(Client) Agent B (Remote) HTTP(S) Message Role (user, agent) Parts (text, file, or JSON) Message Role (user, agent) Part (text, file, or JSON) HTTP(S) AgentExecutor A2AStarletteApplication
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:
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) A2A with Agent Development Kit (ADK) Expose an ADK Agent over A2A with AgentExecutor already implemented goo.gle/adk-a2a
unit of work initiated by an agent, with a unique ID and defined lifecycle Client User Agent A (Client) Message Role (user, agent) Parts (text, file, or JSON) Task ID Status HTTP(S) Agent B (Remote) ⚙ Processing…. Task Status: - Unspecified - Submitted - Working - Completed - Failed - Cancelled - Input required - Rejected - Auth Required
(user, agent) Parts (text, file, or JSON) JSON-RPC Task ID Status HTTP(S) Artifact Parts (text, file, or JSON) JSON-RPC 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) Task Status: - Unspecified - Submitted - Working - Completed - Failed - Cancelled - Input required - Rejected - Auth Required
async for item in self.agent.stream(query, task.contextId): is_task_complete = item['is_task_complete'] require_user_input = item['require_user_input'] if not is_task_complete and not require_user_input: updater.update_status(...) elif require_user_input: updater.update_status(..., final=True) break else: updater.add_artifact(...) updater.complete() break 80 Agent Executor Example
async for item in self.agent.stream(query, task.contextId): is_task_complete = item['is_task_complete'] require_user_input = item['require_user_input'] if not is_task_complete and not require_user_input: updater.update_status(...) elif require_user_input: updater.update_status(..., final=True) break else: updater.add_artifact(...) updater.complete() break 81 Agent Executor Example
async for item in self.agent.stream(query, task.contextId): is_task_complete = item['is_task_complete'] require_user_input = item['require_user_input'] if not is_task_complete and not require_user_input: updater.update_status(...) elif require_user_input: updater.update_status(..., final=True) break else: updater.add_artifact(...) updater.complete() break 82 Agent Executor Example
async for item in self.agent.stream(query, task.contextId): is_task_complete = item['is_task_complete'] require_user_input = item['require_user_input'] if not is_task_complete and not require_user_input: updater.update_status(...) elif require_user_input: updater.update_status(..., final=True) break else: updater.add_artifact(...) updater.complete() break 83 Agent Executor Example
async for item in self.agent.stream(query, task.contextId): is_task_complete = item['is_task_complete'] require_user_input = item['require_user_input'] if not is_task_complete and not require_user_input: updater.update_status(...) elif require_user_input: updater.update_status(..., final=True) break else: updater.add_artifact(...) updater.complete() break 84 Agent Executor Example
Agent B (Remote) HTTPS Message Role (user, agent) Parts (text, file, or JSON) JSON-RPC Agent Card streaming: true Push updates Initial task Messages Artifacts 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
A (Client) Agent B (Remote) HTTPS Message Role (user, agent) Parts (text, file, or JSON) JSON-RPC Agent Card pushNotifications: true 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.
name="currency_agent", description="Agent that can convert from one currency to another.", agent_card=( f"https://google.com/.well-known/agent-card.json" ), ) A2A with Agent Development Kit (ADK) Consume a remote agent in ADK goo.gle/adk-a2a
enable communication & collaboration between AI agents. Vertex AI Agent Engine Managed platform to deploy, manage, and scale AI agents in production. Model Context Protocol Open protocol that standardizes how applications provide context to LLMs. Agent Development Kit Open-source, code-first toolkit for building, evaluating, and deploying AI agents.
execution • Bidirectional Interaction: Agents accept input from users, enabling collaborative workflows with humans • Transport Agnostic: AG-UI doesn’t mandate how events are delivered, supporting various transport mechanisms including Server-Sent Events (SSE), webhooks, WebSockets, and more Main Benefits
"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
2. updateComponents: Add or update UI components in a surface 3. updateDataModel: Update application state 4. deleteSurface: Remove an UI surface Four main message types
Display: Text, Image, Icon, Video, Divider - show information • Interactive: Button, TextField, CheckBox, DateTimeInput, Slider - user input • Container: Card, Tabs, Modal - group and organize content Catalog of UI Components a2ui.org/reference/components
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. Transport options a2ui.org/concepts/transports/#available-transports
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 Renderers a2ui.org/reference/renderers/#maintained-renderers