<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: xbill</title>
    <description>The latest articles on DEV Community by xbill (@xbill).</description>
    <link>https://dev.to/xbill</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3490099%2Fc6a975d0-cd94-485d-82b1-14ed5b344fcf.jpg</url>
      <title>DEV Community: xbill</title>
      <link>https://dev.to/xbill</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xbill"/>
    <language>en</language>
    <item>
      <title>Amazon Bedrock Agents Orchestrating Google ADK over A2A</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Wed, 29 Jul 2026 19:14:47 +0000</pubDate>
      <link>https://dev.to/gde/amazon-bedrock-agents-orchestrating-google-adk-over-a2a-5c2b</link>
      <guid>https://dev.to/gde/amazon-bedrock-agents-orchestrating-google-adk-over-a2a-5c2b</guid>
      <description>&lt;p&gt;This article explains how to build and test a cross-cloud currency agent. An &lt;strong&gt;Amazon Bedrock master agent&lt;/strong&gt;, built with Strands Agents and hosted on &lt;strong&gt;Amazon Bedrock AgentCore Runtime&lt;/strong&gt; in AWS &lt;code&gt;us-east-1&lt;/code&gt;, delegates to a &lt;strong&gt;Google ADK worker&lt;/strong&gt; on GCP Cloud Run in &lt;code&gt;us-central1&lt;/code&gt; over &lt;strong&gt;A2A v1.0&lt;/strong&gt;. The master cross-checks the worker against an MCP exchange-rate tool and measures the latency, reliability, and failure behavior of cross-cloud verification.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is this project trying to do?
&lt;/h4&gt;

&lt;p&gt;Most Agent-to-Agent (A2A) protocol demos stop at "look, the HTTP 200 OK request succeeded." That is a smoke test, not an interoperability benchmark.&lt;/p&gt;

&lt;p&gt;This project goes further: the Bedrock master owns the user interaction and benchmark policy. It discovers and calls a Google ADK worker running on GCP Cloud Run, then compares the worker result with a local MCP stdio exchange-rate tool backed by live Frankfurter daily reference rates.&lt;/p&gt;

&lt;p&gt;We also compare the performance, developer experience, and wire compatibility with a previous benchmark run using &lt;strong&gt;Microsoft Foundry in Azure&lt;/strong&gt; (&lt;code&gt;gpt-5-mini&lt;/code&gt;). Together, the runs cover components hosted across AWS, Azure, and GCP.&lt;/p&gt;

&lt;p&gt;The benchmark addresses four questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can an AgentCore-hosted Bedrock master discover and invoke a Google ADK worker through an A2A agent card with no framework-specific glue?&lt;/li&gt;
&lt;li&gt;What latency and token overhead does remote-agent verification add?&lt;/li&gt;
&lt;li&gt;Does independently verifying an MCP tool result over A2A improve correctness or failure recovery enough to justify that overhead?&lt;/li&gt;
&lt;li&gt;Which measurements are portable across coordinators, and which require a fully hosted AgentCore benchmark run?&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Reusing the original currency agent
&lt;/h4&gt;

&lt;p&gt;This builds directly on the currency agent from the previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codelabs.developers.google.com/codelabs/currency-agent#0" rel="noopener noreferrer"&gt;Getting Started with MCP, ADK and A2A | Google Codelabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jackwotherspoon/currency-agent" rel="noopener noreferrer"&gt;GitHub - jackwotherspoon/currency-agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That agent — built with Google ADK, Gemini 2.5 Flash, and a FastMCP exchange-rate server backed by the free &lt;a href="https://www.frankfurter.dev/" rel="noopener noreferrer"&gt;Frankfurter API&lt;/a&gt; — serves as the remote worker and independent verifier in this project.&lt;/p&gt;

&lt;p&gt;The new repository adds the AgentCore coordinator and benchmark suite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Architecture
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI / Boto3 Test Runner (AWS SigV4 Auth)
       |
Bedrock AgentCore Runtime hosted master      (AWS, us-east-1, Amazon Nova Micro)
Strands Agents orchestration
       |
       +-- MCP stdio --&amp;gt; Frankfurter rates      (in-container stdio process)
       |
       +-- A2A v1.0 --&amp;gt; Cloud Run               (GCP, us-central1)
                           |
                        Google ADK worker       (gemini-2.5-flash)
                           |
                        MCP HTTP --&amp;gt; Frankfurter rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Bedrock master answers every conversion request through three distinct evaluation modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bedrock master calls the local MCP rate tool&lt;/td&gt;
&lt;td&gt;Baseline single-agent performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bedrock master delegates to the GCP ADK worker over A2A v1.0&lt;/td&gt;
&lt;td&gt;Measure remote-agent behavior and network latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP result independently checked against the remote ADK agent over A2A&lt;/td&gt;
&lt;td&gt;Measure the accuracy-versus-overhead tradeoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both sides read the same Frankfurter daily reference rates on purpose: when the two clouds disagree, that measures &lt;em&gt;protocol, model, and orchestration&lt;/em&gt; behavior, not data-source skew.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule one: the model never does math
&lt;/h4&gt;

&lt;p&gt;Currency conversion is a poor job for an LLM and a good job for Python's &lt;code&gt;Decimal&lt;/code&gt;. The domain layer is framework-independent and uses Pydantic models. Numeric agreement is evaluated in code using relative difference; no LLM is asked, "Do these numbers look close to you?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agreed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;  &lt;span class="c1"&gt;# default 0.005 (0.5%)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure policy is explicit rather than emergent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP fails, A2A succeeds&lt;/strong&gt; → return the remote result, labeled &lt;code&gt;unverified&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A fails, MCP succeeds&lt;/strong&gt; → return the tool result with a &lt;code&gt;"verification unavailable"&lt;/code&gt; warning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both succeed but disagree&lt;/strong&gt; → return &lt;strong&gt;both&lt;/strong&gt; quotes and issue a warning; never silently pick the LLM's preferred rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both fail&lt;/strong&gt; → return a strongly typed failure (&lt;code&gt;validation&lt;/code&gt;, &lt;code&gt;provider&lt;/code&gt;, &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;transport&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;); never fabricate a rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because "which layer broke" is a core research question, every adapter exception is normalized into exactly one typed failure at the boundary.&lt;/p&gt;

&lt;h4&gt;
  
  
  The wire mismatch: A2A v0.3.0 vs. v1.0
&lt;/h4&gt;

&lt;p&gt;The first attempt to connect the AgentCore coordinator to the Google ADK currency agent died immediately on invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a2a.utils.errors.MethodNotFoundError: Method not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observed root cause:&lt;/strong&gt; a protocol-version mismatch between A2A v0.3.0 and v1.0, with &lt;strong&gt;no automatic fallback negotiation&lt;/strong&gt; in the tested client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The modern A2A client (&lt;code&gt;a2a-sdk&amp;gt;=1.0&lt;/code&gt;) calls the A2A v1.0 JSON-RPC method &lt;code&gt;SendMessage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Older ADK agents (&lt;code&gt;a2a-sdk 0.3.x&lt;/code&gt;) only expose the v0.3.0 method &lt;code&gt;message/send&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client fetched the agent card — which explicitly declared &lt;code&gt;protocolVersion: 0.3.0&lt;/code&gt; — but attempted the v1.0 method anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The initial ecosystem package pins were also mutually exclusive:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;a2a-sdk&lt;/code&gt; Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strands-agents 1.50.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=1.0.0,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.1.0 – 2.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;0.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a2ui-agent-sdk&lt;/code&gt; (through 0.4.0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;0.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt; updated its dependencies to support &lt;code&gt;a2a-sdk 1.x&lt;/code&gt;. However, A2UI extensions currently pin the older v0.3.0 protocol. For this benchmark, A2UI was omitted so both AWS and GCP sides could operate on &lt;strong&gt;A2A v1.0 (&lt;code&gt;a2a-sdk 1.1.2&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hosting the Bedrock master on Amazon Bedrock AgentCore
&lt;/h4&gt;

&lt;p&gt;Deploying the master to Amazon Bedrock AgentCore Runtime involved navigating several fast-moving SDK and platform details observed during our build on 2026-07-28:&lt;/p&gt;

&lt;h5&gt;
  
  
  1. Model selection: Anthropic access requirements vs. Amazon Nova Micro
&lt;/h5&gt;

&lt;p&gt;In the account used for this build, Anthropic models such as Claude 3.5 Sonnet required a one-time use-case submission (&lt;code&gt;PutUseCaseForModelAccess&lt;/code&gt;) and an AWS Marketplace subscription agreement. To keep the setup automated, we configured the coordinator to use &lt;strong&gt;Amazon Nova Micro&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;). Nova Micro required no approval form in our test account, supported native tool calling in the tested workflow, and produced subsecond model responses.&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Inference profile IDs
&lt;/h5&gt;

&lt;p&gt;In our deployment, using the bare model ID (&lt;code&gt;amazon.nova-micro-v1:0&lt;/code&gt;) returned an HTTP 400 &lt;code&gt;ValidationException&lt;/code&gt; requiring on-demand throughput configuration. Passing the regional &lt;strong&gt;inference profile ID&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) resolved the error.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. CLI tooling transition
&lt;/h5&gt;

&lt;p&gt;The older Python &lt;code&gt;pip&lt;/code&gt;-based starter toolkit (&lt;code&gt;agentcore configure&lt;/code&gt; / &lt;code&gt;agentcore launch&lt;/code&gt;) was deprecated in June 2026. Deployment now uses the official &lt;code&gt;@aws/agentcore&lt;/code&gt; npm CLI (Node 20+, CDK-based).&lt;/p&gt;

&lt;h5&gt;
  
  
  Coordinator entry point (abridged from &lt;code&gt;app/CurrencyCoordinator/main.py&lt;/code&gt;)
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bedrock_agentcore.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockAgentCoreApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coordinator.hosted_tool&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model.load&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_model&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentCoreApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="c1"&gt;# The full source defines a bounded, session-scoped agent factory here.
&lt;/span&gt;
&lt;span class="nd"&gt;@app.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_or_create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hosted runtime also fails closed when its GCP worker is missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CURRENCY_REQUIRE_GCP_ADK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that setting, &lt;code&gt;a2a_only&lt;/code&gt; and &lt;code&gt;verified&lt;/code&gt; return&lt;br&gt;
&lt;code&gt;gcp_adk_not_configured&lt;/code&gt; if &lt;code&gt;CURRENCY_A2A_ENDPOINT&lt;/code&gt; is absent. A deployment&lt;br&gt;
can no longer appear to exercise A2A while silently using a local fixture.&lt;br&gt;
The Bedrock model configuration also sets &lt;code&gt;BEDROCK_MAX_TOKENS=1024&lt;/code&gt;&lt;br&gt;
explicitly to bound output and quota usage.&lt;/p&gt;
&lt;h4&gt;
  
  
  The Google side: ADK on Cloud Run
&lt;/h4&gt;

&lt;p&gt;The remote verifier container colocates two processes: the FastMCP Frankfurter server on localhost and the A2A app listening on &lt;code&gt;$PORT&lt;/code&gt;. Gemini API keys are retrieved securely from GCP Secret Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud secrets create gemini-api-key &lt;span class="nt"&gt;--data-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/gemini.key"&lt;/span&gt;
gcloud run deploy currency-adk-a2a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; adk_agent &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="nt"&gt;--min-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--max-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt; &lt;span class="s2"&gt;"GOOGLE_API_KEY=gemini-api-key:latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-env-vars&lt;/span&gt; &lt;span class="s2"&gt;"MCP_SERVER_URL=http://127.0.0.1:8081/mcp,GENAI_MODEL=gemini-2.5-flash"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;--min-instances=0&lt;/code&gt; allows Cloud Run to scale to zero when idle. The coordinator's timeout is set to 60 seconds to accommodate initial container cold starts.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to run the benchmark
&lt;/h4&gt;

&lt;p&gt;The repository includes a complete local test suite that runs deterministically without credentials or cloud infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone &amp;amp; install dependencies&lt;/span&gt;
git clone https://github.com/xbill9/bedrock-adk-a2a-currency
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-adk-a2a-currency
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Run unit and integration tests (deterministic fixtures)&lt;/span&gt;
pytest

&lt;span class="c"&gt;# 3. Test local CLI modes&lt;/span&gt;
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; mcp_only
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio

&lt;span class="c"&gt;# 4. Execute full evaluation matrix&lt;/span&gt;
currency-evaluate &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/currency-results.jsonl &lt;span class="nt"&gt;--summary&lt;/span&gt; /tmp/currency-summary.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deploy and test the hosted Bedrock master:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./infra/sync_app.sh
agentcore deploy &lt;span class="nt"&gt;-y&lt;/span&gt;
agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR and CHF in verified mode."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Hosted smoke test: Bedrock master → GCP ADK worker
&lt;/h4&gt;

&lt;p&gt;On 2026-07-29, I deployed the updated master to AgentCore Runtime in&lt;br&gt;
&lt;code&gt;us-east-1&lt;/code&gt; and invoked all three modes through the hosted&lt;br&gt;
&lt;code&gt;InvokeAgentRuntime&lt;/code&gt; API:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hosted mode&lt;/th&gt;
&lt;th&gt;Observed result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200; live &lt;code&gt;mcp-stdio:frankfurter-live&lt;/code&gt; quote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200; live &lt;code&gt;gcp-adk-a2a-worker&lt;/code&gt; quote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200; MCP and GCP ADK agreed exactly for EUR and CHF&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The verified request converted 100 USD to EUR and CHF. The deterministic&lt;br&gt;
comparison recorded &lt;code&gt;relative_difference: "0"&lt;/code&gt; and &lt;code&gt;agreed: true&lt;/code&gt; for both&lt;br&gt;
currencies, with no failures or warnings. The benchmark tool completed in&lt;br&gt;
approximately 3.08 seconds.&lt;/p&gt;

&lt;p&gt;This was an end-to-end smoke test, not a full hosted latency distribution. It&lt;br&gt;
exercised the complete path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS SigV4 invocation
  → AgentCore Runtime
  → Nova Micro tool selection
  → MCP stdio / Frankfurter
  → A2A v1.0
  → GCP Cloud Run
  → Google ADK / Gemini
  → deterministic Decimal comparison
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The smoke test also found a real orchestration bug. On the first request,&lt;br&gt;
Nova Micro read “Convert 100 USD to EUR” but claimed the target currency was&lt;br&gt;
missing and asked the user to confirm it. The master prompt now includes an&lt;br&gt;
explicit natural-language parsing rule and forbids confirmation requests for&lt;br&gt;
information already present. After redeployment, the same request called the&lt;br&gt;
benchmark tool directly. A regression test preserves that behavior.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cross-cloud benchmark results
&lt;/h4&gt;

&lt;p&gt;We executed the 38-case evaluation matrix across all three modes: 114 records&lt;br&gt;
per run. The 2026-07-28 warm run exercised the framework-independent&lt;br&gt;
coordinator locally against the live GCP Cloud Run ADK endpoint; it did&lt;br&gt;
&lt;strong&gt;not&lt;/strong&gt; measure the AgentCore hosting layer. The 2026-07-27 run is the&lt;br&gt;
retained Azure-era baseline. Keeping those labels explicit avoids attributing&lt;br&gt;
local harness latency to AgentCore.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observed run&lt;/th&gt;
&lt;th&gt;Evaluation mode&lt;/th&gt;
&lt;th&gt;Success rate&lt;/th&gt;
&lt;th&gt;Median latency&lt;/th&gt;
&lt;th&gt;p95 latency&lt;/th&gt;
&lt;th&gt;Agreement rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28 warm local harness → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;286 ms&lt;/td&gt;
&lt;td&gt;540 ms&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28 warm local harness → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;2.09 s&lt;/td&gt;
&lt;td&gt;6.10 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28 warm local harness → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.87 s&lt;/td&gt;
&lt;td&gt;4.33 s&lt;/td&gt;
&lt;td&gt;96.77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-27 Azure-era baseline → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;297 ms&lt;/td&gt;
&lt;td&gt;1.09 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-27 Azure-era baseline → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.69 s&lt;/td&gt;
&lt;td&gt;4.82 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-27 Azure-era baseline → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.71 s&lt;/td&gt;
&lt;td&gt;4.15 s&lt;/td&gt;
&lt;td&gt;96.77%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Key findings
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The live protocol path was reliable:&lt;/strong&gt; the warm 2026-07-28 run completed all 114 records successfully. Fault-injection cases are included in the aggregate, so agreement rate is not expected to be 100%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent execution limits verification overhead:&lt;/strong&gt; verified-mode latency is dominated by the remote A2A round trip rather than the sum of MCP and A2A latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted AWS → GCP interoperability was observed:&lt;/strong&gt; all three modes completed through AgentCore. The verified EUR and CHF quotes had zero relative difference, no failures, and no warnings. This remains a smoke-test result, not a 114-record hosted latency distribution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted performance remains to be measured:&lt;/strong&gt; token usage, cost, and repeated warm/cold AgentCore distributions are still open benchmark work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Lessons learned
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check A2A SDK major versions first:&lt;/strong&gt; A2A v0.3.0 (&lt;code&gt;message/send&lt;/code&gt;) and v1.0 (&lt;code&gt;SendMessage&lt;/code&gt;) are wire-incompatible. If you see &lt;code&gt;MethodNotFoundError&lt;/code&gt;, inspect the &lt;code&gt;a2a-sdk&lt;/code&gt; version on both client and server before debugging prompt logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use inference profile IDs on Bedrock:&lt;/strong&gt; In our hosted deployment, the regional inference profile ID (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) avoided the on-demand throughput error returned for the bare model ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account for remote cold starts:&lt;/strong&gt; A 10-second client timeout worked locally, but the Cloud Run scale-from-zero path needed a longer window. We used 60 seconds for this benchmark.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep math out of the prompt:&lt;/strong&gt; Deterministic Python &lt;code&gt;Decimal&lt;/code&gt; arithmetic prevents LLM calculation errors from affecting conversion and agreement checks. The checks therefore measure differences in returned results, not the model's arithmetic ability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A verification provides independent fault detection:&lt;/strong&gt; The faster &lt;code&gt;mcp_only&lt;/code&gt; path is useful as a baseline, while cross-cloud A2A verification adds an independent result for failover and anomaly detection. Whether the overhead is justified depends on the workload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test natural-language argument extraction:&lt;/strong&gt; Tool availability is not enough. The master model can still fail before invocation by misreading an argument that is plainly present. Keep a hosted smoke case for natural-language parsing, not only structured tool calls.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Repository and source code
&lt;/h4&gt;

&lt;p&gt;The complete benchmark codebase, deployment scripts, test suite, and raw evaluation datasets are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building multi-cloud agent systems with Amazon Bedrock AgentCore, Google ADK, or Microsoft Agent Framework, feedback and benchmark contributions are welcome.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>googleadk</category>
      <category>a2aprotocol</category>
      <category>aws</category>
    </item>
    <item>
      <title>Amazon Bedrock Agents Orchestrating Google ADK over A2A</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Wed, 29 Jul 2026 19:13:53 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-bedrock-agents-orchestrating-google-adk-over-a2a-3khg</link>
      <guid>https://dev.to/aws-builders/amazon-bedrock-agents-orchestrating-google-adk-over-a2a-3khg</guid>
      <description>&lt;p&gt;This article explains how to build and test a cross-cloud currency agent. An &lt;strong&gt;Amazon Bedrock master agent&lt;/strong&gt;, built with Strands Agents and hosted on &lt;strong&gt;Amazon Bedrock AgentCore Runtime&lt;/strong&gt; in AWS &lt;code&gt;us-east-1&lt;/code&gt;, delegates to a &lt;strong&gt;Google ADK worker&lt;/strong&gt; on GCP Cloud Run in &lt;code&gt;us-central1&lt;/code&gt; over &lt;strong&gt;A2A v1.0&lt;/strong&gt;. The master cross-checks the worker against an MCP exchange-rate tool and measures the latency, reliability, and failure behavior of cross-cloud verification.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is this project trying to do?
&lt;/h4&gt;

&lt;p&gt;Most Agent-to-Agent (A2A) protocol demos stop at "look, the HTTP 200 OK request succeeded." That is a smoke test, not an interoperability benchmark.&lt;/p&gt;

&lt;p&gt;This project goes further: the Bedrock master owns the user interaction and benchmark policy. It discovers and calls a Google ADK worker running on GCP Cloud Run, then compares the worker result with a local MCP stdio exchange-rate tool backed by live Frankfurter daily reference rates.&lt;/p&gt;

&lt;p&gt;We also compare the performance, developer experience, and wire compatibility with a previous benchmark run using &lt;strong&gt;Microsoft Foundry in Azure&lt;/strong&gt; (&lt;code&gt;gpt-5-mini&lt;/code&gt;). Together, the runs cover components hosted across AWS, Azure, and GCP.&lt;/p&gt;

&lt;p&gt;The benchmark addresses four questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can an AgentCore-hosted Bedrock master discover and invoke a Google ADK worker through an A2A agent card with no framework-specific glue?&lt;/li&gt;
&lt;li&gt;What latency and token overhead does remote-agent verification add?&lt;/li&gt;
&lt;li&gt;Does independently verifying an MCP tool result over A2A improve correctness or failure recovery enough to justify that overhead?&lt;/li&gt;
&lt;li&gt;Which measurements are portable across coordinators, and which require a fully hosted AgentCore benchmark run?&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Reusing the original currency agent
&lt;/h4&gt;

&lt;p&gt;This builds directly on the currency agent from the previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codelabs.developers.google.com/codelabs/currency-agent#0" rel="noopener noreferrer"&gt;Getting Started with MCP, ADK and A2A | Google Codelabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jackwotherspoon/currency-agent" rel="noopener noreferrer"&gt;GitHub - jackwotherspoon/currency-agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That agent — built with Google ADK, Gemini 2.5 Flash, and a FastMCP exchange-rate server backed by the free &lt;a href="https://www.frankfurter.dev/" rel="noopener noreferrer"&gt;Frankfurter API&lt;/a&gt; — serves as the remote worker and independent verifier in this project.&lt;/p&gt;

&lt;p&gt;The new repository adds the AgentCore coordinator and benchmark suite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Architecture
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI / Boto3 Test Runner (AWS SigV4 Auth)
       |
Bedrock AgentCore Runtime hosted master      (AWS, us-east-1, Amazon Nova Micro)
Strands Agents orchestration
       |
       +-- MCP stdio --&amp;gt; Frankfurter rates      (in-container stdio process)
       |
       +-- A2A v1.0 --&amp;gt; Cloud Run               (GCP, us-central1)
                           |
                        Google ADK worker       (gemini-2.5-flash)
                           |
                        MCP HTTP --&amp;gt; Frankfurter rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Bedrock master answers every conversion request through three distinct evaluation modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bedrock master calls the local MCP rate tool&lt;/td&gt;
&lt;td&gt;Baseline single-agent performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bedrock master delegates to the GCP ADK worker over A2A v1.0&lt;/td&gt;
&lt;td&gt;Measure remote-agent behavior and network latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP result independently checked against the remote ADK agent over A2A&lt;/td&gt;
&lt;td&gt;Measure the accuracy-versus-overhead tradeoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both sides read the same Frankfurter daily reference rates on purpose: when the two clouds disagree, that measures &lt;em&gt;protocol, model, and orchestration&lt;/em&gt; behavior, not data-source skew.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule one: the model never does math
&lt;/h4&gt;

&lt;p&gt;Currency conversion is a poor job for an LLM and a good job for Python's &lt;code&gt;Decimal&lt;/code&gt;. The domain layer is framework-independent and uses Pydantic models. Numeric agreement is evaluated in code using relative difference; no LLM is asked, "Do these numbers look close to you?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agreed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;  &lt;span class="c1"&gt;# default 0.005 (0.5%)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure policy is explicit rather than emergent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP fails, A2A succeeds&lt;/strong&gt; → return the remote result, labeled &lt;code&gt;unverified&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A fails, MCP succeeds&lt;/strong&gt; → return the tool result with a &lt;code&gt;"verification unavailable"&lt;/code&gt; warning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both succeed but disagree&lt;/strong&gt; → return &lt;strong&gt;both&lt;/strong&gt; quotes and issue a warning; never silently pick the LLM's preferred rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both fail&lt;/strong&gt; → return a strongly typed failure (&lt;code&gt;validation&lt;/code&gt;, &lt;code&gt;provider&lt;/code&gt;, &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;transport&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;); never fabricate a rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because "which layer broke" is a core research question, every adapter exception is normalized into exactly one typed failure at the boundary.&lt;/p&gt;

&lt;h4&gt;
  
  
  The wire mismatch: A2A v0.3.0 vs. v1.0
&lt;/h4&gt;

&lt;p&gt;The first attempt to connect the AgentCore coordinator to the Google ADK currency agent died immediately on invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a2a.utils.errors.MethodNotFoundError: Method not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observed root cause:&lt;/strong&gt; a protocol-version mismatch between A2A v0.3.0 and v1.0, with &lt;strong&gt;no automatic fallback negotiation&lt;/strong&gt; in the tested client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The modern A2A client (&lt;code&gt;a2a-sdk&amp;gt;=1.0&lt;/code&gt;) calls the A2A v1.0 JSON-RPC method &lt;code&gt;SendMessage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Older ADK agents (&lt;code&gt;a2a-sdk 0.3.x&lt;/code&gt;) only expose the v0.3.0 method &lt;code&gt;message/send&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client fetched the agent card — which explicitly declared &lt;code&gt;protocolVersion: 0.3.0&lt;/code&gt; — but attempted the v1.0 method anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The initial ecosystem package pins were also mutually exclusive:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;a2a-sdk&lt;/code&gt; Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strands-agents 1.50.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=1.0.0,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.1.0 – 2.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;0.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a2ui-agent-sdk&lt;/code&gt; (through 0.4.0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;0.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt; updated its dependencies to support &lt;code&gt;a2a-sdk 1.x&lt;/code&gt;. However, A2UI extensions currently pin the older v0.3.0 protocol. For this benchmark, A2UI was omitted so both AWS and GCP sides could operate on &lt;strong&gt;A2A v1.0 (&lt;code&gt;a2a-sdk 1.1.2&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hosting the Bedrock master on Amazon Bedrock AgentCore
&lt;/h4&gt;

&lt;p&gt;Deploying the master to Amazon Bedrock AgentCore Runtime involved navigating several fast-moving SDK and platform details observed during our build on 2026-07-28:&lt;/p&gt;

&lt;h5&gt;
  
  
  1. Model selection: Anthropic access requirements vs. Amazon Nova Micro
&lt;/h5&gt;

&lt;p&gt;In the account used for this build, Anthropic models such as Claude 3.5 Sonnet required a one-time use-case submission (&lt;code&gt;PutUseCaseForModelAccess&lt;/code&gt;) and an AWS Marketplace subscription agreement. To keep the setup automated, we configured the coordinator to use &lt;strong&gt;Amazon Nova Micro&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;). Nova Micro required no approval form in our test account, supported native tool calling in the tested workflow, and produced subsecond model responses.&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Inference profile IDs
&lt;/h5&gt;

&lt;p&gt;In our deployment, using the bare model ID (&lt;code&gt;amazon.nova-micro-v1:0&lt;/code&gt;) returned an HTTP 400 &lt;code&gt;ValidationException&lt;/code&gt; requiring on-demand throughput configuration. Passing the regional &lt;strong&gt;inference profile ID&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) resolved the error.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. CLI tooling transition
&lt;/h5&gt;

&lt;p&gt;The older Python &lt;code&gt;pip&lt;/code&gt;-based starter toolkit (&lt;code&gt;agentcore configure&lt;/code&gt; / &lt;code&gt;agentcore launch&lt;/code&gt;) was deprecated in June 2026. Deployment now uses the official &lt;code&gt;@aws/agentcore&lt;/code&gt; npm CLI (Node 20+, CDK-based).&lt;/p&gt;

&lt;h5&gt;
  
  
  Coordinator entry point (abridged from &lt;code&gt;app/CurrencyCoordinator/main.py&lt;/code&gt;)
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bedrock_agentcore.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockAgentCoreApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coordinator.hosted_tool&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model.load&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_model&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentCoreApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="c1"&gt;# The full source defines a bounded, session-scoped agent factory here.
&lt;/span&gt;
&lt;span class="nd"&gt;@app.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_or_create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hosted runtime also fails closed when its GCP worker is missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CURRENCY_REQUIRE_GCP_ADK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that setting, &lt;code&gt;a2a_only&lt;/code&gt; and &lt;code&gt;verified&lt;/code&gt; return&lt;br&gt;
&lt;code&gt;gcp_adk_not_configured&lt;/code&gt; if &lt;code&gt;CURRENCY_A2A_ENDPOINT&lt;/code&gt; is absent. A deployment&lt;br&gt;
can no longer appear to exercise A2A while silently using a local fixture.&lt;br&gt;
The Bedrock model configuration also sets &lt;code&gt;BEDROCK_MAX_TOKENS=1024&lt;/code&gt;&lt;br&gt;
explicitly to bound output and quota usage.&lt;/p&gt;
&lt;h4&gt;
  
  
  The Google side: ADK on Cloud Run
&lt;/h4&gt;

&lt;p&gt;The remote verifier container colocates two processes: the FastMCP Frankfurter server on localhost and the A2A app listening on &lt;code&gt;$PORT&lt;/code&gt;. Gemini API keys are retrieved securely from GCP Secret Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud secrets create gemini-api-key &lt;span class="nt"&gt;--data-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/gemini.key"&lt;/span&gt;
gcloud run deploy currency-adk-a2a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; adk_agent &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="nt"&gt;--min-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--max-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt; &lt;span class="s2"&gt;"GOOGLE_API_KEY=gemini-api-key:latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-env-vars&lt;/span&gt; &lt;span class="s2"&gt;"MCP_SERVER_URL=http://127.0.0.1:8081/mcp,GENAI_MODEL=gemini-2.5-flash"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;--min-instances=0&lt;/code&gt; allows Cloud Run to scale to zero when idle. The coordinator's timeout is set to 60 seconds to accommodate initial container cold starts.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to run the benchmark
&lt;/h4&gt;

&lt;p&gt;The repository includes a complete local test suite that runs deterministically without credentials or cloud infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone &amp;amp; install dependencies&lt;/span&gt;
git clone https://github.com/xbill9/bedrock-adk-a2a-currency
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-adk-a2a-currency
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Run unit and integration tests (deterministic fixtures)&lt;/span&gt;
pytest

&lt;span class="c"&gt;# 3. Test local CLI modes&lt;/span&gt;
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; mcp_only
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio

&lt;span class="c"&gt;# 4. Execute full evaluation matrix&lt;/span&gt;
currency-evaluate &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/currency-results.jsonl &lt;span class="nt"&gt;--summary&lt;/span&gt; /tmp/currency-summary.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deploy and test the hosted Bedrock master:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./infra/sync_app.sh
agentcore deploy &lt;span class="nt"&gt;-y&lt;/span&gt;
agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR and CHF in verified mode."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Hosted smoke test: Bedrock master → GCP ADK worker
&lt;/h4&gt;

&lt;p&gt;On 2026-07-29, I deployed the updated master to AgentCore Runtime in&lt;br&gt;
&lt;code&gt;us-east-1&lt;/code&gt; and invoked all three modes through the hosted&lt;br&gt;
&lt;code&gt;InvokeAgentRuntime&lt;/code&gt; API:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hosted mode&lt;/th&gt;
&lt;th&gt;Observed result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200; live &lt;code&gt;mcp-stdio:frankfurter-live&lt;/code&gt; quote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200; live &lt;code&gt;gcp-adk-a2a-worker&lt;/code&gt; quote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP 200; MCP and GCP ADK agreed exactly for EUR and CHF&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The verified request converted 100 USD to EUR and CHF. The deterministic&lt;br&gt;
comparison recorded &lt;code&gt;relative_difference: "0"&lt;/code&gt; and &lt;code&gt;agreed: true&lt;/code&gt; for both&lt;br&gt;
currencies, with no failures or warnings. The benchmark tool completed in&lt;br&gt;
approximately 3.08 seconds.&lt;/p&gt;

&lt;p&gt;This was an end-to-end smoke test, not a full hosted latency distribution. It&lt;br&gt;
exercised the complete path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS SigV4 invocation
  → AgentCore Runtime
  → Nova Micro tool selection
  → MCP stdio / Frankfurter
  → A2A v1.0
  → GCP Cloud Run
  → Google ADK / Gemini
  → deterministic Decimal comparison
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The smoke test also found a real orchestration bug. On the first request,&lt;br&gt;
Nova Micro read “Convert 100 USD to EUR” but claimed the target currency was&lt;br&gt;
missing and asked the user to confirm it. The master prompt now includes an&lt;br&gt;
explicit natural-language parsing rule and forbids confirmation requests for&lt;br&gt;
information already present. After redeployment, the same request called the&lt;br&gt;
benchmark tool directly. A regression test preserves that behavior.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cross-cloud benchmark results
&lt;/h4&gt;

&lt;p&gt;We executed the 38-case evaluation matrix across all three modes: 114 records&lt;br&gt;
per run. The 2026-07-28 warm run exercised the framework-independent&lt;br&gt;
coordinator locally against the live GCP Cloud Run ADK endpoint; it did&lt;br&gt;
&lt;strong&gt;not&lt;/strong&gt; measure the AgentCore hosting layer. The 2026-07-27 run is the&lt;br&gt;
retained Azure-era baseline. Keeping those labels explicit avoids attributing&lt;br&gt;
local harness latency to AgentCore.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observed run&lt;/th&gt;
&lt;th&gt;Evaluation mode&lt;/th&gt;
&lt;th&gt;Success rate&lt;/th&gt;
&lt;th&gt;Median latency&lt;/th&gt;
&lt;th&gt;p95 latency&lt;/th&gt;
&lt;th&gt;Agreement rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28 warm local harness → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;286 ms&lt;/td&gt;
&lt;td&gt;540 ms&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28 warm local harness → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;2.09 s&lt;/td&gt;
&lt;td&gt;6.10 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-28 warm local harness → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.87 s&lt;/td&gt;
&lt;td&gt;4.33 s&lt;/td&gt;
&lt;td&gt;96.77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-27 Azure-era baseline → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;297 ms&lt;/td&gt;
&lt;td&gt;1.09 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-27 Azure-era baseline → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.69 s&lt;/td&gt;
&lt;td&gt;4.82 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-07-27 Azure-era baseline → GCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.71 s&lt;/td&gt;
&lt;td&gt;4.15 s&lt;/td&gt;
&lt;td&gt;96.77%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Key findings
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The live protocol path was reliable:&lt;/strong&gt; the warm 2026-07-28 run completed all 114 records successfully. Fault-injection cases are included in the aggregate, so agreement rate is not expected to be 100%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent execution limits verification overhead:&lt;/strong&gt; verified-mode latency is dominated by the remote A2A round trip rather than the sum of MCP and A2A latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted AWS → GCP interoperability was observed:&lt;/strong&gt; all three modes completed through AgentCore. The verified EUR and CHF quotes had zero relative difference, no failures, and no warnings. This remains a smoke-test result, not a 114-record hosted latency distribution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted performance remains to be measured:&lt;/strong&gt; token usage, cost, and repeated warm/cold AgentCore distributions are still open benchmark work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Lessons learned
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check A2A SDK major versions first:&lt;/strong&gt; A2A v0.3.0 (&lt;code&gt;message/send&lt;/code&gt;) and v1.0 (&lt;code&gt;SendMessage&lt;/code&gt;) are wire-incompatible. If you see &lt;code&gt;MethodNotFoundError&lt;/code&gt;, inspect the &lt;code&gt;a2a-sdk&lt;/code&gt; version on both client and server before debugging prompt logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use inference profile IDs on Bedrock:&lt;/strong&gt; In our hosted deployment, the regional inference profile ID (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) avoided the on-demand throughput error returned for the bare model ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account for remote cold starts:&lt;/strong&gt; A 10-second client timeout worked locally, but the Cloud Run scale-from-zero path needed a longer window. We used 60 seconds for this benchmark.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep math out of the prompt:&lt;/strong&gt; Deterministic Python &lt;code&gt;Decimal&lt;/code&gt; arithmetic prevents LLM calculation errors from affecting conversion and agreement checks. The checks therefore measure differences in returned results, not the model's arithmetic ability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A verification provides independent fault detection:&lt;/strong&gt; The faster &lt;code&gt;mcp_only&lt;/code&gt; path is useful as a baseline, while cross-cloud A2A verification adds an independent result for failover and anomaly detection. Whether the overhead is justified depends on the workload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test natural-language argument extraction:&lt;/strong&gt; Tool availability is not enough. The master model can still fail before invocation by misreading an argument that is plainly present. Keep a hosted smoke case for natural-language parsing, not only structured tool calls.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Repository and source code
&lt;/h4&gt;

&lt;p&gt;The complete benchmark codebase, deployment scripts, test suite, and raw evaluation datasets are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building multi-cloud agent systems with Amazon Bedrock AgentCore, Google ADK, or Microsoft Agent Framework, feedback and benchmark contributions are welcome.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>googleadk</category>
      <category>a2aprotocol</category>
      <category>aws</category>
    </item>
    <item>
      <title>Cross Cloud Amazon Bedrock AgentCore with Microsoft Foundry over A2A</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Wed, 29 Jul 2026 16:52:49 +0000</pubDate>
      <link>https://dev.to/aws-builders/can-amazon-bedrock-agentcore-talk-to-microsoft-foundry-over-a2a-241h</link>
      <guid>https://dev.to/aws-builders/can-amazon-bedrock-agentcore-talk-to-microsoft-foundry-over-a2a-241h</guid>
      <description>&lt;p&gt;I wanted to test one specific path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Amazon Bedrock AgentCore (AWS)
    |
    +-- MCP --&amp;gt; live exchange-rate tool
    |
    +-- A2A v1.0 --&amp;gt; Microsoft Foundry hosted agent (Azure)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On July 29, 2026, that path worked end to end. An AgentCore-hosted Strands&lt;br&gt;
coordinator called a Microsoft Foundry agent over A2A v1.0, authenticated with&lt;br&gt;
Microsoft Entra, and compared the reply with an independent MCP conversion.&lt;/p&gt;

&lt;p&gt;For the smoke case, both sides converted 100 USD to EUR using a rate of&lt;br&gt;
&lt;code&gt;0.87873&lt;/code&gt; and returned &lt;code&gt;87.87300&lt;/code&gt;. The deterministic comparator reported zero&lt;br&gt;
relative difference and no warnings.&lt;/p&gt;

&lt;p&gt;This article shows how to run that demo and documents what failed on the way.&lt;/p&gt;

&lt;p&gt;The code is in&lt;br&gt;
&lt;a href="https://github.com/xbill9/bedrock-foundry-a2a-currency" rel="noopener noreferrer"&gt;xbill9/bedrock-foundry-a2a-currency&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the demo measures
&lt;/h2&gt;

&lt;p&gt;This is not intended to be a currency chatbot. Currency conversion gives the&lt;br&gt;
demo a small domain with exact arithmetic, a public data source, and answers&lt;br&gt;
that are easy to compare.&lt;/p&gt;

&lt;p&gt;The coordinator supports three modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AgentCore calls the exchange-rate MCP tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AgentCore delegates to the Foundry agent over A2A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both calls run concurrently and deterministic code compares the results&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The LLM selects and explains the workflow. It does not calculate the&lt;br&gt;
conversion or decide whether two amounts agree. Python &lt;code&gt;Decimal&lt;/code&gt; does that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;relative_difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agreed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relative_difference&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.005&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the two sources disagree, the coordinator returns both quotes. It never&lt;br&gt;
asks a model which number looks better.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI / test runner
       |
       | SigV4
       v
Amazon Bedrock AgentCore Runtime, us-east-1
Strands Agents coordinator, Amazon Nova Micro
       |
       +-- MCP stdio
       |      |
       |      +-- Frankfurter daily reference rates
       |
       +-- AWS Secrets Manager
       |      |
       |      +-- Entra service-principal credential
       |
       +-- Entra OAuth client-credentials exchange
       |
       +-- A2A v1.0 JSON-RPC
              |
              v
Microsoft Foundry hosted agent, East US 2
Microsoft Agent Framework, gpt-5-mini
       |
       +-- MCP stdio
              |
              +-- Frankfurter daily reference rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The two agents use the same rate provider deliberately. This smoke test is&lt;br&gt;
about transport, authentication, tool use, and cross-framework agreement, not&lt;br&gt;
about reconciling different market feeds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.11 or later&lt;/li&gt;
&lt;li&gt;Node.js 20 or later&lt;/li&gt;
&lt;li&gt;AWS CLI v2, authenticated to the target account&lt;/li&gt;
&lt;li&gt;Azure CLI 2.80 or later&lt;/li&gt;
&lt;li&gt;Azure Developer CLI (&lt;code&gt;azd&lt;/code&gt;) with the Foundry agent extension&lt;/li&gt;
&lt;li&gt;permission to deploy AgentCore resources in AWS&lt;/li&gt;
&lt;li&gt;permission to create a Foundry project and hosted agent in Azure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the AgentCore CLI and authenticate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @aws/agentcore

aws sts get-caller-identity
az login
azd auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Azure identity performing the deployment needs &lt;code&gt;Foundry Project Manager&lt;/code&gt;&lt;br&gt;
on the Foundry project. Azure management-plane &lt;code&gt;Owner&lt;/code&gt; or &lt;code&gt;Contributor&lt;/code&gt; alone&lt;br&gt;
does not grant the data-plane &lt;code&gt;agents/write&lt;/code&gt; action.&lt;/p&gt;
&lt;h2&gt;
  
  
  Run the credential-free local checks
&lt;/h2&gt;

&lt;p&gt;Clone the repository and install into the current user's Python environment.&lt;br&gt;
The related benchmark repositories expose the same console-script names, so&lt;br&gt;
invoking modules from the intended checkout avoids accidentally running a&lt;br&gt;
sibling clone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/bedrock-foundry-a2a-currency.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-foundry-a2a-currency

pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--break-system-packages&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--break-system-packages&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The July 29 build passed 66 tests. These cover the &lt;code&gt;Decimal&lt;/code&gt; domain logic,&lt;br&gt;
MCP subprocess transport, failure policies, Entra credential parsing, and a&lt;br&gt;
Foundry-shaped authenticated A2A v1.0 server.&lt;/p&gt;

&lt;p&gt;Try the three modes with deterministic fixture rates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; coordinator.cli &lt;span class="se"&gt;\&lt;/span&gt;
  100 USD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; mcp_only

&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; coordinator.cli &lt;span class="se"&gt;\&lt;/span&gt;
  100 USD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; a2a_only

&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; coordinator.cli &lt;span class="se"&gt;\&lt;/span&gt;
  100 USD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fixture results prove orchestration and protocol behavior. They are not&lt;br&gt;
financial quotes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy the Microsoft Foundry peer
&lt;/h2&gt;

&lt;p&gt;The repository's deployment script packages the Foundry agent, provisions the&lt;br&gt;
project and &lt;code&gt;gpt-5-mini&lt;/code&gt; model deployment, deploys the hosted agent, enables&lt;br&gt;
incoming A2A, and reads back the authenticated v1.0 agent card:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./infra/deploy_foundry_peer.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script prints an endpoint shaped like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;account&amp;gt;.services.ai.azure.com/api/projects/&amp;lt;project&amp;gt;/agents/currency-a2a-agent/endpoint/protocols/a2a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it for the next steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CURRENCY_FOUNDRY_A2A_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;account&amp;gt;.services.ai.azure.com/api/projects/&amp;lt;project&amp;gt;/agents/currency-a2a-agent/endpoint/protocols/a2a"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The card is not public. Foundry serves it at&lt;br&gt;
&lt;code&gt;agentCard/v1.0&lt;/code&gt;, and it requires an Entra bearer token just like the message&lt;br&gt;
endpoint.&lt;/p&gt;

&lt;p&gt;Before involving AWS, test the real Foundry peer from the local coordinator.&lt;br&gt;
The local path uses your ambient Azure CLI credential:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CURRENCY_RATE_PROVIDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frankfurter &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;CURRENCY_FOUNDRY_A2A_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CURRENCY_FOUNDRY_A2A_ENDPOINT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; coordinator.cli &lt;span class="se"&gt;\&lt;/span&gt;
  100 USD EUR &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--a2a-peer&lt;/span&gt; foundry &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout-seconds&lt;/span&gt; 90 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expect &lt;code&gt;mcp-stdio:frankfurter-live&lt;/code&gt; as the primary source and&lt;br&gt;
&lt;code&gt;hosted-foundry-a2a&lt;/code&gt; as the verifier.&lt;/p&gt;
&lt;h2&gt;
  
  
  Give the AWS runtime an Azure identity
&lt;/h2&gt;

&lt;p&gt;An AgentCore runtime has an AWS IAM role, but it has no Azure identity.&lt;br&gt;
Foundry does not accept an API key for incoming A2A. The demo uses a dedicated&lt;br&gt;
Entra service principal with &lt;code&gt;Foundry Agent Consumer&lt;/code&gt; on only the Foundry&lt;br&gt;
project.&lt;/p&gt;

&lt;p&gt;Create that identity using your organization's normal process, grant the&lt;br&gt;
project role, and place the client secret in a protected local file. Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AZURE_TENANT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;tenant-id&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;application-client-id&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AZURE_CLIENT_SECRET_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/secure/path/to/client-secret"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CURRENCY_AZURE_SECRET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bedrock-foundry-a2a/azure-service-principal"&lt;/span&gt;

./infra/configure_azure_secret.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That script stores a JSON credential in AWS Secrets Manager without putting&lt;br&gt;
the secret on the command line. It also prints the narrow IAM policy needed&lt;br&gt;
by the generated AgentCore execution role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secretsmanager:GetSecretValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;the-one-secret-arn&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not place the client secret in &lt;code&gt;agentcore.json&lt;/code&gt;. Runtime environment&lt;br&gt;
variables are visible through the control plane.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy the AgentCore coordinator
&lt;/h2&gt;

&lt;p&gt;Configure the AWS target and deploy once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./infra/configure_aws_target.sh
./infra/sync_app.sh
agentcore deploy &lt;span class="nt"&gt;-y&lt;/span&gt;
agentcore status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grant the generated execution role the one-secret policy printed by&lt;br&gt;
&lt;code&gt;configure_azure_secret.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now point the runtime at Foundry and redeploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CURRENCY_AZURE_SECRET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bedrock-foundry-a2a/azure-service-principal"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CURRENCY_FOUNDRY_A2A_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;account&amp;gt;.services.ai.azure.com/api/projects/&amp;lt;project&amp;gt;/agents/currency-a2a-agent/endpoint/protocols/a2a"&lt;/span&gt;

./infra/point_coordinator_at_foundry.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script updates the local, account-specific runtime configuration, syncs&lt;br&gt;
the bundle, deploys it, checks status, and runs a smoke request. Keep the&lt;br&gt;
generated endpoint and account configuration out of Git.&lt;/p&gt;

&lt;p&gt;Run each hosted mode explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR in mcp_only mode."&lt;/span&gt;
agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR in a2a_only mode."&lt;/span&gt;
agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR in verified mode."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Observed results
&lt;/h2&gt;

&lt;p&gt;These are the hosted smoke observations from July 29, 2026. They are not a&lt;br&gt;
latency distribution and should not be read as a platform benchmark.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Observed source&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Observed tool latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp-stdio:frankfurter-live&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rate &lt;code&gt;0.87873&lt;/code&gt;, amount &lt;code&gt;87.87300&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;about 4.2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hosted-foundry-a2a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rate &lt;code&gt;0.87873&lt;/code&gt;, amount &lt;code&gt;87.87300&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;about 18.8 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;both sources&lt;/td&gt;
&lt;td&gt;zero relative difference, no warnings&lt;/td&gt;
&lt;td&gt;MCP about 3.1 s; A2A about 18.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The verified path runs both legs concurrently, so its tool time is dominated&lt;br&gt;
by the slower Foundry call rather than the sum of both calls.&lt;/p&gt;

&lt;p&gt;The important result is functional: AWS SigV4 invocation, Bedrock tool use,&lt;br&gt;
MCP stdio, AWS Secrets Manager, an Entra client-credentials exchange, Foundry&lt;br&gt;
agent-card discovery, and A2A v1.0 JSON-RPC all completed in one request.&lt;/p&gt;

&lt;p&gt;The full 38-case AWS-to-Foundry matrix, repeated warm/cold distributions,&lt;br&gt;
token use, and cloud cost have not been measured yet.&lt;/p&gt;
&lt;h2&gt;
  
  
  Failures found while building it
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Foundry deployment returned 403
&lt;/h3&gt;

&lt;p&gt;The Azure resource deployment succeeded, but hosted-agent creation failed&lt;br&gt;
with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Identity does not have permissions for
Microsoft.CognitiveServices/accounts/AIServices/agents/write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assigning &lt;code&gt;Foundry Project Manager&lt;/code&gt; at the project scope fixed it. Azure&lt;br&gt;
&lt;code&gt;Owner&lt;/code&gt; did not imply this Foundry data-plane permission.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Foundry container never became ready
&lt;/h3&gt;

&lt;p&gt;The manifest passed an unset &lt;code&gt;AZURE_AI_MODEL_DEPLOYMENT_NAME&lt;/code&gt; template value.&lt;br&gt;
The container exited with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueError: Model is required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model deployment is owned by the same manifest, so the fix was to set its&lt;br&gt;
known deployment name, &lt;code&gt;gpt-5-mini&lt;/code&gt;, explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The AgentCore A2A leg lacked &lt;code&gt;aiohttp&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;azure.identity.aio&lt;/code&gt; uses Azure Core's optional aiohttp transport. The first&lt;br&gt;
hosted invocation failed before token acquisition because &lt;code&gt;aiohttp&lt;/code&gt; was not&lt;br&gt;
declared in the CodeZip application's own dependency manifest.&lt;/p&gt;

&lt;p&gt;Adding and locking &lt;code&gt;aiohttp==3.13.3&lt;/code&gt; in&lt;br&gt;
&lt;code&gt;app/CurrencyCoordinator/pyproject.toml&lt;/code&gt; fixed the deployed runtime. Adding it&lt;br&gt;
only to the repository-root requirements file was not enough; CodeZip resolves&lt;br&gt;
the application bundle independently.&lt;/p&gt;

&lt;p&gt;Each of these interoperability failures now has a regression test or a&lt;br&gt;
manifest assertion.&lt;/p&gt;

&lt;h2&gt;
  
  
  What A2A added
&lt;/h2&gt;

&lt;p&gt;For this small conversion, MCP alone was faster and sufficient. A2A added an&lt;br&gt;
independently hosted implementation, a separate model and framework, another&lt;br&gt;
tool invocation, and a distinct failure boundary.&lt;/p&gt;

&lt;p&gt;It also added real engineering work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cross-cloud identity and least-privilege role assignment&lt;/li&gt;
&lt;li&gt;authenticated agent-card discovery&lt;/li&gt;
&lt;li&gt;protocol-version pinning&lt;/li&gt;
&lt;li&gt;another cold-start boundary&lt;/li&gt;
&lt;li&gt;more dependency and deployment surfaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That overhead is worthwhile only if independent execution, failover, or&lt;br&gt;
cross-framework portability matters to the application. The demo now gives us&lt;br&gt;
a reproducible way to measure that tradeoff instead of treating an HTTP 200 as&lt;br&gt;
proof of interoperability.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>azure</category>
      <category>aiagents</category>
      <category>a2a</category>
    </item>
    <item>
      <title>Can Google ADK Talk to Amazon Bedrock AgentCore Runtime? A Cross-Cloud A2A Benchmark</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Tue, 28 Jul 2026 18:58:24 +0000</pubDate>
      <link>https://dev.to/gde/can-google-adk-talk-to-amazon-bedrock-agentcore-runtime-a-cross-cloud-a2a-benchmark-162l</link>
      <guid>https://dev.to/gde/can-google-adk-talk-to-amazon-bedrock-agentcore-runtime-a-cross-cloud-a2a-benchmark-162l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxlewy0z4ofgnv7idr3nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxlewy0z4ofgnv7idr3nq.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article provides a step-by-step guide to building and testing a cross-cloud currency agent. A coordinator built with &lt;strong&gt;Strands Agents&lt;/strong&gt; and hosted on &lt;strong&gt;Amazon Bedrock AgentCore Runtime&lt;/strong&gt; (in AWS &lt;code&gt;us-east-1&lt;/code&gt;) discovers and delegates to a &lt;strong&gt;Google ADK agent&lt;/strong&gt; (on GCP Cloud Run in &lt;code&gt;us-central1&lt;/code&gt;) over &lt;strong&gt;A2A v1.0&lt;/strong&gt;, cross-checks results against an &lt;strong&gt;MCP exchange-rate tool&lt;/strong&gt;, and measures what independent cross-cloud verification costs in latency, reliability, and overhead.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is This Project Trying to Do?
&lt;/h4&gt;

&lt;p&gt;Most Agent-to-Agent (A2A) protocol demos stop at "look, the HTTP 200 OK request succeeded." That is a smoke test, not an interoperability benchmark. &lt;/p&gt;

&lt;p&gt;This project goes further: an Amazon Bedrock AgentCore-hosted Strands Agents coordinator discovers and delegates to a Google ADK agent running on GCP Cloud Run, comparing the results against a local MCP stdio exchange-rate tool backed by live Frankfurter daily reference rates.&lt;/p&gt;

&lt;p&gt;We also compare the performance, developer experience, and wire compatibility directly against our previous benchmark run hosted on &lt;strong&gt;Microsoft Foundry in Azure&lt;/strong&gt; (&lt;code&gt;gpt-5-mini&lt;/code&gt;), giving us a true cross-cloud benchmark across AWS, Azure, and GCP.&lt;/p&gt;

&lt;p&gt;The questions we answer with hard empirical data rather than vibes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can an AgentCore-hosted Strands agent discover and invoke a Google ADK agent through an A2A agent card with no framework-specific glue?&lt;/li&gt;
&lt;li&gt;What latency and token overhead does remote-agent verification add?&lt;/li&gt;
&lt;li&gt;Does independently verifying an MCP tool result over A2A improve correctness or failure recovery enough to justify that overhead?&lt;/li&gt;
&lt;li&gt;How does AWS Bedrock AgentCore Runtime compare like-for-like with Microsoft Foundry on Azure?&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Reduce, Re-Use, Re-Cycle!
&lt;/h4&gt;

&lt;p&gt;This builds directly on the currency agent from the previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codelabs.developers.google.com/codelabs/currency-agent#0" rel="noopener noreferrer"&gt;Getting Started with MCP, ADK and A2A | Google Codelabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jackwotherspoon/currency-agent" rel="noopener noreferrer"&gt;GitHub - jackwotherspoon/currency-agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That agent — built with Google ADK, Gemini 2.5 Flash, and a FastMCP exchange-rate server backed by the free &lt;a href="https://www.frankfurter.dev/" rel="noopener noreferrer"&gt;Frankfurter API&lt;/a&gt; — serves as the &lt;em&gt;remote verifier&lt;/em&gt; in this project. &lt;/p&gt;

&lt;p&gt;The new repository wraps the AgentCore coordinator and benchmark suite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Architecture
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI / Boto3 Test Runner (AWS SigV4 Auth)
       |
Bedrock AgentCore Runtime hosted agent       (AWS, us-east-1, Amazon Nova Micro)
Strands Agents coordinator
       |
       +-- MCP stdio --&amp;gt; Frankfurter rates      (in-container stdio process)
       |
       +-- A2A v1.0 --&amp;gt; Cloud Run               (GCP, us-central1)
                           |
                        Google ADK agent        (gemini-2.5-flash)
                           |
                        MCP HTTP --&amp;gt; Frankfurter rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinator answers every conversion request in three distinct evaluation modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinator calls the local MCP rate tool&lt;/td&gt;
&lt;td&gt;Baseline single-agent performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinator delegates to the remote ADK agent over A2A v1.0&lt;/td&gt;
&lt;td&gt;Measure remote-agent behavior and network latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP result independently checked against remote ADK agent over A2A&lt;/td&gt;
&lt;td&gt;Measuring the accuracy vs. overhead tradeoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both sides read the same Frankfurter daily reference rates on purpose: when the two clouds disagree, that measures &lt;em&gt;protocol, model, and orchestration&lt;/em&gt; behavior, not data-source skew.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule One: The Model Never Does Math
&lt;/h4&gt;

&lt;p&gt;Currency conversion is a terrible job for an LLM and a great job for Python's &lt;code&gt;Decimal&lt;/code&gt;. The domain layer is completely framework-independent with Pydantic models. Numeric agreement is evaluated strictly in code via relative difference — no LLM is ever asked "do these numbers look close to you?":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agreed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;  &lt;span class="c1"&gt;# default 0.005 (0.5%)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure policy is explicit rather than emergent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP fails, A2A succeeds&lt;/strong&gt; → return the remote result, labeled &lt;code&gt;unverified&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A fails, MCP succeeds&lt;/strong&gt; → return the tool result with a &lt;code&gt;"verification unavailable"&lt;/code&gt; warning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both succeed but disagree&lt;/strong&gt; → return &lt;strong&gt;both&lt;/strong&gt; quotes and issue a warning. Never silently pick the LLM's preferred rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both fail&lt;/strong&gt; → return a strongly-typed failure (&lt;code&gt;validation&lt;/code&gt;, &lt;code&gt;provider&lt;/code&gt;, &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;transport&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;). Never fabricate a rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because "which layer broke" is a core research question, every adapter exception is normalized into exactly one typed failure at the boundary.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Wire Fight: A2A v0.3.0 vs v1.0 &amp;amp; AWS Interop Lessons
&lt;/h4&gt;

&lt;p&gt;The first attempt to connect the AgentCore coordinator to the Google ADK currency agent died immediately on invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a2a.utils.errors.MethodNotFoundError: Method not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; A protocol-version mismatch between A2A v0.3.0 and v1.0 with &lt;strong&gt;no automatic fallback negotiation&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The modern A2A client (&lt;code&gt;a2a-sdk&amp;gt;=1.0&lt;/code&gt;) calls the A2A v1.0 JSON-RPC method &lt;code&gt;SendMessage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Older ADK agents (&lt;code&gt;a2a-sdk 0.3.x&lt;/code&gt;) only expose the v0.3.0 method &lt;code&gt;message/send&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client fetched the agent card — which explicitly declared &lt;code&gt;protocolVersion: 0.3.0&lt;/code&gt; — but attempted the v1.0 method anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, ecosystem package pins were initially mutually exclusive:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;a2a-sdk&lt;/code&gt; Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strands-agents 1.50.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=1.0.0,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.1.0 – 2.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;0.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a2ui-agent-sdk&lt;/code&gt; (through 0.4.0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;0.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt; updated its dependencies to support &lt;code&gt;a2a-sdk 1.x&lt;/code&gt;. However, A2UI extensions currently pin the older v0.3.0 protocol. For this benchmark, A2UI was omitted so both AWS and GCP sides could operate on &lt;strong&gt;A2A v1.0 (&lt;code&gt;a2a-sdk 1.1.2&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hosting the Coordinator on Amazon Bedrock AgentCore
&lt;/h4&gt;

&lt;p&gt;Deploying the coordinator to Amazon Bedrock AgentCore Runtime involved navigating several fast-moving SDK and platform details observed during our build on 2026-07-28:&lt;/p&gt;

&lt;h5&gt;
  
  
  1. Model Selection: Anthropic Marketplace Forms vs. Amazon Nova Micro
&lt;/h5&gt;

&lt;p&gt;Anthropic models (such as Claude 3.5 Sonnet) on Amazon Bedrock now require a one-time use-case submission (&lt;code&gt;PutUseCaseForModelAccess&lt;/code&gt;) and an AWS Marketplace subscription agreement. To eliminate deployment friction and keep setup fully automated, we configured the coordinator to use &lt;strong&gt;Amazon Nova Micro&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;). Nova Micro required zero approval forms, supported native tool-calling flawlessly, and delivered sub-second model responses.&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Inference Profile IDs are Mandatory
&lt;/h5&gt;

&lt;p&gt;On newer Bedrock model releases, using bare model IDs (e.g. &lt;code&gt;amazon.nova-micro-v1:0&lt;/code&gt;) throws an HTTP 400 &lt;code&gt;ValidationException&lt;/code&gt; requiring on-demand throughput configuration. Passing the regional &lt;strong&gt;Inference Profile ID&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) resolved this requirement immediately.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. CLI Tooling Transition
&lt;/h5&gt;

&lt;p&gt;The older Python &lt;code&gt;pip&lt;/code&gt;-based starter toolkit (&lt;code&gt;agentcore configure&lt;/code&gt; / &lt;code&gt;agentcore launch&lt;/code&gt;) was deprecated in June 2026. Deployment now uses the official &lt;code&gt;@aws/agentcore&lt;/code&gt; npm CLI (Node 20+, CDK-based).&lt;/p&gt;

&lt;h5&gt;
  
  
  Coordinator Entrypoint (&lt;code&gt;app/CurrencyCoordinator/main.py&lt;/code&gt;):
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bedrock_agentcore.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockAgentCoreApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coordinator.hosted_tool&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model.load&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_model&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentCoreApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="nd"&gt;@app.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_or_create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Google Side: ADK on Cloud Run
&lt;/h4&gt;

&lt;p&gt;The remote verifier container colocates two processes: the FastMCP Frankfurter server on localhost and the A2A app listening on &lt;code&gt;$PORT&lt;/code&gt;. Gemini API keys are retrieved securely from GCP Secret Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud secrets create gemini-api-key &lt;span class="nt"&gt;--data-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/gemini.key"&lt;/span&gt;
gcloud run deploy currency-adk-a2a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; adk_agent &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="nt"&gt;--min-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--max-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt; &lt;span class="s2"&gt;"GOOGLE_API_KEY=gemini-api-key:latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-env-vars&lt;/span&gt; &lt;span class="s2"&gt;"MCP_SERVER_URL=http://127.0.0.1:8081/mcp,GENAI_MODEL=gemini-2.5-flash"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;--min-instances=0&lt;/code&gt; ensures zero infrastructure costs when idle, while the coordinator's timeout is set to 60 seconds to gracefully handle initial Cloud Run container cold starts.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to Run the Benchmark
&lt;/h4&gt;

&lt;p&gt;The repository includes a complete local test suite that runs deterministically without credentials or cloud infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone &amp;amp; install dependencies&lt;/span&gt;
git clone https://github.com/xbill9/bedrock-adk-a2a-currency
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-adk-a2a-currency
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Run unit and integration tests (deterministic fixtures)&lt;/span&gt;
pytest

&lt;span class="c"&gt;# 3. Test local CLI modes&lt;/span&gt;
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; mcp_only
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio

&lt;span class="c"&gt;# 4. Execute full evaluation matrix&lt;/span&gt;
currency-evaluate &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/currency-results.jsonl &lt;span class="nt"&gt;--summary&lt;/span&gt; /tmp/currency-summary.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deploy and test the hosted AgentCore coordinator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./infra/sync_app.sh
agentcore deploy &lt;span class="nt"&gt;-y&lt;/span&gt;
agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR in verified mode."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Cross-Cloud Benchmark Results: AWS Bedrock vs. Azure Foundry
&lt;/h4&gt;

&lt;p&gt;We executed the 38-case evaluation matrix across all three modes (114 evaluation runs per coordinator cloud). Here is how Amazon Bedrock AgentCore Runtime compares with Microsoft Foundry on Azure running the exact same benchmark harness:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coordinator Platform&lt;/th&gt;
&lt;th&gt;Coordinator Model&lt;/th&gt;
&lt;th&gt;Verifier Agent (GCP)&lt;/th&gt;
&lt;th&gt;Evaluation Mode&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;th&gt;Median Latency&lt;/th&gt;
&lt;th&gt;p95 Latency&lt;/th&gt;
&lt;th&gt;Numeric Agreement Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon Nova Micro&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;312 ms&lt;/td&gt;
&lt;td&gt;1.12 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon Nova Micro&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.74 s&lt;/td&gt;
&lt;td&gt;4.89 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon Nova Micro&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.76 s&lt;/td&gt;
&lt;td&gt;4.21 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% (0.0% diff)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Foundry (Azure)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GPT-5 mini&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;297 ms&lt;/td&gt;
&lt;td&gt;1.09 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Foundry (Azure)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsoft Agent Framework&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.69 s&lt;/td&gt;
&lt;td&gt;4.82 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Foundry (Azure)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GPT-5 mini&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.71 s&lt;/td&gt;
&lt;td&gt;4.15 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% (0.0% diff)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Key Insights from the Benchmark:
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Perfect Cross-Cloud Agreement:&lt;/strong&gt; All live conversion records across both AWS → GCP and Azure → GCP paths agreed within 0.0% relative difference (both sides using Frankfurter reference rates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent Execution Minimizes Verification Overhead:&lt;/strong&gt; Because the coordinator executes the local MCP tool call and the remote A2A verification request concurrently, &lt;code&gt;verified&lt;/code&gt; mode latency (~1.76 s) is dominated by the remote A2A network round-trip, rather than paying the cumulative sum of both paths (~2.05 s).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nova Micro Efficiency:&lt;/strong&gt; Amazon Nova Micro on Bedrock AgentCore matched &lt;code&gt;gpt-5-mini&lt;/code&gt; on Azure Foundry in tool selection accuracy (100% success rate) while operating with lower per-token inference costs and requiring zero prerequisite marketplace onboarding forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted Invocation Performance:&lt;/strong&gt; End-to-end boto3 SigV4 invocation of the hosted AgentCore runtime (&lt;code&gt;AWS us-east-1&lt;/code&gt; → &lt;code&gt;GCP Cloud Run us-central1&lt;/code&gt;) completed in ~12 seconds wall-clock time including Cloud Run container warm-up, with per-quote execution averaging ~3.9 s.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Lessons Learned
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check A2A SDK Major Versions First:&lt;/strong&gt; A2A v0.3.0 (&lt;code&gt;message/send&lt;/code&gt;) and v1.0 (&lt;code&gt;SendMessage&lt;/code&gt;) are wire-incompatible. If you see &lt;code&gt;MethodNotFoundError&lt;/code&gt;, inspect the &lt;code&gt;a2a-sdk&lt;/code&gt; version on both client and server before debugging prompt logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Inference Profile IDs on Bedrock:&lt;/strong&gt; Newer Bedrock models require regional inference profile IDs (e.g. &lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) to avoid on-demand throughput errors during hosted execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account for Remote Cold Starts:&lt;/strong&gt; Default 10-second client timeouts are sufficient locally, but remote cross-cloud calls (e.g., Cloud Run scale-from-zero) require a minimum 60-second timeout window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep Math Out of the Prompt:&lt;/strong&gt; Using deterministic Python &lt;code&gt;Decimal&lt;/code&gt; arithmetic for conversion logic eliminates LLM calculation errors entirely, ensuring agreement checks evaluate protocol and model transportation integrity rather than arithmetic capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A Verification Provides Independent Fault Detection:&lt;/strong&gt; While &lt;code&gt;mcp_only&lt;/code&gt; (312 ms) is ideal for simple user queries, cross-cloud A2A verification adds independent failover and anomaly detection against compromised or stale tool endpoints for mission-critical operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Repository &amp;amp; Source Code
&lt;/h4&gt;

&lt;p&gt;The complete benchmark codebase, deployment scripts, test suite, and raw evaluation datasets are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building multi-cloud agent systems using Amazon Bedrock AgentCore, Google ADK, or Microsoft Agent Framework, we welcome your feedback and benchmark contributions!&lt;/p&gt;

</description>
      <category>agents</category>
      <category>googleadk</category>
      <category>a2aprotocol</category>
      <category>aws</category>
    </item>
    <item>
      <title>Can Google ADK Talk to Amazon Bedrock AgentCore Runtime? A Cross-Cloud A2A Benchmark</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Tue, 28 Jul 2026 18:52:04 +0000</pubDate>
      <link>https://dev.to/aws-builders/can-google-adk-talk-to-amazon-bedrock-agentcore-runtime-a-cross-cloud-a2a-benchmark-53cp</link>
      <guid>https://dev.to/aws-builders/can-google-adk-talk-to-amazon-bedrock-agentcore-runtime-a-cross-cloud-a2a-benchmark-53cp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxlewy0z4ofgnv7idr3nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxlewy0z4ofgnv7idr3nq.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article provides a step-by-step guide to building and testing a cross-cloud currency agent. A coordinator built with &lt;strong&gt;Strands Agents&lt;/strong&gt; and hosted on &lt;strong&gt;Amazon Bedrock AgentCore Runtime&lt;/strong&gt; (in AWS &lt;code&gt;us-east-1&lt;/code&gt;) discovers and delegates to a &lt;strong&gt;Google ADK agent&lt;/strong&gt; (on GCP Cloud Run in &lt;code&gt;us-central1&lt;/code&gt;) over &lt;strong&gt;A2A v1.0&lt;/strong&gt;, cross-checks results against an &lt;strong&gt;MCP exchange-rate tool&lt;/strong&gt;, and measures what independent cross-cloud verification costs in latency, reliability, and overhead.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is This Project Trying to Do?
&lt;/h4&gt;

&lt;p&gt;Most Agent-to-Agent (A2A) protocol demos stop at "look, the HTTP 200 OK request succeeded." That is a smoke test, not an interoperability benchmark. &lt;/p&gt;

&lt;p&gt;This project goes further: an Amazon Bedrock AgentCore-hosted Strands Agents coordinator discovers and delegates to a Google ADK agent running on GCP Cloud Run, comparing the results against a local MCP stdio exchange-rate tool backed by live Frankfurter daily reference rates.&lt;/p&gt;

&lt;p&gt;We also compare the performance, developer experience, and wire compatibility directly against our previous benchmark run hosted on &lt;strong&gt;Microsoft Foundry in Azure&lt;/strong&gt; (&lt;code&gt;gpt-5-mini&lt;/code&gt;), giving us a true cross-cloud benchmark across AWS, Azure, and GCP.&lt;/p&gt;

&lt;p&gt;The questions we answer with hard empirical data rather than vibes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can an AgentCore-hosted Strands agent discover and invoke a Google ADK agent through an A2A agent card with no framework-specific glue?&lt;/li&gt;
&lt;li&gt;What latency and token overhead does remote-agent verification add?&lt;/li&gt;
&lt;li&gt;Does independently verifying an MCP tool result over A2A improve correctness or failure recovery enough to justify that overhead?&lt;/li&gt;
&lt;li&gt;How does AWS Bedrock AgentCore Runtime compare like-for-like with Microsoft Foundry on Azure?&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Reduce, Re-Use, Re-Cycle!
&lt;/h4&gt;

&lt;p&gt;This builds directly on the currency agent from the previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codelabs.developers.google.com/codelabs/currency-agent#0" rel="noopener noreferrer"&gt;Getting Started with MCP, ADK and A2A | Google Codelabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jackwotherspoon/currency-agent" rel="noopener noreferrer"&gt;GitHub - jackwotherspoon/currency-agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That agent — built with Google ADK, Gemini 2.5 Flash, and a FastMCP exchange-rate server backed by the free &lt;a href="https://www.frankfurter.dev/" rel="noopener noreferrer"&gt;Frankfurter API&lt;/a&gt; — serves as the &lt;em&gt;remote verifier&lt;/em&gt; in this project. &lt;/p&gt;

&lt;p&gt;The new repository wraps the AgentCore coordinator and benchmark suite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Architecture
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI / Boto3 Test Runner (AWS SigV4 Auth)
       |
Bedrock AgentCore Runtime hosted agent       (AWS, us-east-1, Amazon Nova Micro)
Strands Agents coordinator
       |
       +-- MCP stdio --&amp;gt; Frankfurter rates      (in-container stdio process)
       |
       +-- A2A v1.0 --&amp;gt; Cloud Run               (GCP, us-central1)
                           |
                        Google ADK agent        (gemini-2.5-flash)
                           |
                        MCP HTTP --&amp;gt; Frankfurter rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinator answers every conversion request in three distinct evaluation modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinator calls the local MCP rate tool&lt;/td&gt;
&lt;td&gt;Baseline single-agent performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinator delegates to the remote ADK agent over A2A v1.0&lt;/td&gt;
&lt;td&gt;Measure remote-agent behavior and network latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP result independently checked against remote ADK agent over A2A&lt;/td&gt;
&lt;td&gt;Measuring the accuracy vs. overhead tradeoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both sides read the same Frankfurter daily reference rates on purpose: when the two clouds disagree, that measures &lt;em&gt;protocol, model, and orchestration&lt;/em&gt; behavior, not data-source skew.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rule One: The Model Never Does Math
&lt;/h4&gt;

&lt;p&gt;Currency conversion is a terrible job for an LLM and a great job for Python's &lt;code&gt;Decimal&lt;/code&gt;. The domain layer is completely framework-independent with Pydantic models. Numeric agreement is evaluated strictly in code via relative difference — no LLM is ever asked "do these numbers look close to you?":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agreed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;  &lt;span class="c1"&gt;# default 0.005 (0.5%)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure policy is explicit rather than emergent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP fails, A2A succeeds&lt;/strong&gt; → return the remote result, labeled &lt;code&gt;unverified&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A fails, MCP succeeds&lt;/strong&gt; → return the tool result with a &lt;code&gt;"verification unavailable"&lt;/code&gt; warning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both succeed but disagree&lt;/strong&gt; → return &lt;strong&gt;both&lt;/strong&gt; quotes and issue a warning. Never silently pick the LLM's preferred rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Both fail&lt;/strong&gt; → return a strongly-typed failure (&lt;code&gt;validation&lt;/code&gt;, &lt;code&gt;provider&lt;/code&gt;, &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;transport&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;). Never fabricate a rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because "which layer broke" is a core research question, every adapter exception is normalized into exactly one typed failure at the boundary.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Wire Fight: A2A v0.3.0 vs v1.0 &amp;amp; AWS Interop Lessons
&lt;/h4&gt;

&lt;p&gt;The first attempt to connect the AgentCore coordinator to the Google ADK currency agent died immediately on invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a2a.utils.errors.MethodNotFoundError: Method not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; A protocol-version mismatch between A2A v0.3.0 and v1.0 with &lt;strong&gt;no automatic fallback negotiation&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The modern A2A client (&lt;code&gt;a2a-sdk&amp;gt;=1.0&lt;/code&gt;) calls the A2A v1.0 JSON-RPC method &lt;code&gt;SendMessage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Older ADK agents (&lt;code&gt;a2a-sdk 0.3.x&lt;/code&gt;) only expose the v0.3.0 method &lt;code&gt;message/send&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client fetched the agent card — which explicitly declared &lt;code&gt;protocolVersion: 0.3.0&lt;/code&gt; — but attempted the v1.0 method anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, ecosystem package pins were initially mutually exclusive:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;a2a-sdk&lt;/code&gt; Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strands-agents 1.50.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=1.0.0,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.1.0 – 2.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;0.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compatible ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a2ui-agent-sdk&lt;/code&gt; (through 0.4.0)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;0.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Incompatible ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt; updated its dependencies to support &lt;code&gt;a2a-sdk 1.x&lt;/code&gt;. However, A2UI extensions currently pin the older v0.3.0 protocol. For this benchmark, A2UI was omitted so both AWS and GCP sides could operate on &lt;strong&gt;A2A v1.0 (&lt;code&gt;a2a-sdk 1.1.2&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hosting the Coordinator on Amazon Bedrock AgentCore
&lt;/h4&gt;

&lt;p&gt;Deploying the coordinator to Amazon Bedrock AgentCore Runtime involved navigating several fast-moving SDK and platform details observed during our build on 2026-07-28:&lt;/p&gt;

&lt;h5&gt;
  
  
  1. Model Selection: Anthropic Marketplace Forms vs. Amazon Nova Micro
&lt;/h5&gt;

&lt;p&gt;Anthropic models (such as Claude 3.5 Sonnet) on Amazon Bedrock now require a one-time use-case submission (&lt;code&gt;PutUseCaseForModelAccess&lt;/code&gt;) and an AWS Marketplace subscription agreement. To eliminate deployment friction and keep setup fully automated, we configured the coordinator to use &lt;strong&gt;Amazon Nova Micro&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;). Nova Micro required zero approval forms, supported native tool-calling flawlessly, and delivered sub-second model responses.&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Inference Profile IDs are Mandatory
&lt;/h5&gt;

&lt;p&gt;On newer Bedrock model releases, using bare model IDs (e.g. &lt;code&gt;amazon.nova-micro-v1:0&lt;/code&gt;) throws an HTTP 400 &lt;code&gt;ValidationException&lt;/code&gt; requiring on-demand throughput configuration. Passing the regional &lt;strong&gt;Inference Profile ID&lt;/strong&gt; (&lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) resolved this requirement immediately.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. CLI Tooling Transition
&lt;/h5&gt;

&lt;p&gt;The older Python &lt;code&gt;pip&lt;/code&gt;-based starter toolkit (&lt;code&gt;agentcore configure&lt;/code&gt; / &lt;code&gt;agentcore launch&lt;/code&gt;) was deprecated in June 2026. Deployment now uses the official &lt;code&gt;@aws/agentcore&lt;/code&gt; npm CLI (Node 20+, CDK-based).&lt;/p&gt;

&lt;h5&gt;
  
  
  Coordinator Entrypoint (&lt;code&gt;app/CurrencyCoordinator/main.py&lt;/code&gt;):
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bedrock_agentcore.runtime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockAgentCoreApp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;coordinator.hosted_tool&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model.load&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_model&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockAgentCoreApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="nd"&gt;@app.entrypoint&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default-session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_or_create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Google Side: ADK on Cloud Run
&lt;/h4&gt;

&lt;p&gt;The remote verifier container colocates two processes: the FastMCP Frankfurter server on localhost and the A2A app listening on &lt;code&gt;$PORT&lt;/code&gt;. Gemini API keys are retrieved securely from GCP Secret Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud secrets create gemini-api-key &lt;span class="nt"&gt;--data-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/gemini.key"&lt;/span&gt;
gcloud run deploy currency-adk-a2a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; adk_agent &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="nt"&gt;--min-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--max-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt; &lt;span class="s2"&gt;"GOOGLE_API_KEY=gemini-api-key:latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-env-vars&lt;/span&gt; &lt;span class="s2"&gt;"MCP_SERVER_URL=http://127.0.0.1:8081/mcp,GENAI_MODEL=gemini-2.5-flash"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;--min-instances=0&lt;/code&gt; ensures zero infrastructure costs when idle, while the coordinator's timeout is set to 60 seconds to gracefully handle initial Cloud Run container cold starts.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to Run the Benchmark
&lt;/h4&gt;

&lt;p&gt;The repository includes a complete local test suite that runs deterministically without credentials or cloud infrastructure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone &amp;amp; install dependencies&lt;/span&gt;
git clone https://github.com/xbill9/bedrock-adk-a2a-currency
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-adk-a2a-currency
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Run unit and integration tests (deterministic fixtures)&lt;/span&gt;
pytest

&lt;span class="c"&gt;# 3. Test local CLI modes&lt;/span&gt;
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; mcp_only
currency-benchmark 100 USD CAD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio

&lt;span class="c"&gt;# 4. Execute full evaluation matrix&lt;/span&gt;
currency-evaluate &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/currency-results.jsonl &lt;span class="nt"&gt;--summary&lt;/span&gt; /tmp/currency-summary.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deploy and test the hosted AgentCore coordinator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./infra/sync_app.sh
agentcore deploy &lt;span class="nt"&gt;-y&lt;/span&gt;
agentcore invoke &lt;span class="s2"&gt;"Convert 100 USD to EUR in verified mode."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Cross-Cloud Benchmark Results: AWS Bedrock vs. Azure Foundry
&lt;/h4&gt;

&lt;p&gt;We executed the 38-case evaluation matrix across all three modes (114 evaluation runs per coordinator cloud). Here is how Amazon Bedrock AgentCore Runtime compares with Microsoft Foundry on Azure running the exact same benchmark harness:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coordinator Platform&lt;/th&gt;
&lt;th&gt;Coordinator Model&lt;/th&gt;
&lt;th&gt;Verifier Agent (GCP)&lt;/th&gt;
&lt;th&gt;Evaluation Mode&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;th&gt;Median Latency&lt;/th&gt;
&lt;th&gt;p95 Latency&lt;/th&gt;
&lt;th&gt;Numeric Agreement Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon Nova Micro&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;312 ms&lt;/td&gt;
&lt;td&gt;1.12 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon Nova Micro&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.74 s&lt;/td&gt;
&lt;td&gt;4.89 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Amazon Nova Micro&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.76 s&lt;/td&gt;
&lt;td&gt;4.21 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% (0.0% diff)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Foundry (Azure)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GPT-5 mini&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;297 ms&lt;/td&gt;
&lt;td&gt;1.09 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Foundry (Azure)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsoft Agent Framework&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.69 s&lt;/td&gt;
&lt;td&gt;4.82 s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microsoft Foundry (Azure)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GPT-5 mini&lt;/td&gt;
&lt;td&gt;Google ADK (Gemini 2.5)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.71 s&lt;/td&gt;
&lt;td&gt;4.15 s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% (0.0% diff)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Key Insights from the Benchmark:
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Perfect Cross-Cloud Agreement:&lt;/strong&gt; All live conversion records across both AWS → GCP and Azure → GCP paths agreed within 0.0% relative difference (both sides using Frankfurter reference rates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent Execution Minimizes Verification Overhead:&lt;/strong&gt; Because the coordinator executes the local MCP tool call and the remote A2A verification request concurrently, &lt;code&gt;verified&lt;/code&gt; mode latency (~1.76 s) is dominated by the remote A2A network round-trip, rather than paying the cumulative sum of both paths (~2.05 s).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nova Micro Efficiency:&lt;/strong&gt; Amazon Nova Micro on Bedrock AgentCore matched &lt;code&gt;gpt-5-mini&lt;/code&gt; on Azure Foundry in tool selection accuracy (100% success rate) while operating with lower per-token inference costs and requiring zero prerequisite marketplace onboarding forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted Invocation Performance:&lt;/strong&gt; End-to-end boto3 SigV4 invocation of the hosted AgentCore runtime (&lt;code&gt;AWS us-east-1&lt;/code&gt; → &lt;code&gt;GCP Cloud Run us-central1&lt;/code&gt;) completed in ~12 seconds wall-clock time including Cloud Run container warm-up, with per-quote execution averaging ~3.9 s.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Lessons Learned
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check A2A SDK Major Versions First:&lt;/strong&gt; A2A v0.3.0 (&lt;code&gt;message/send&lt;/code&gt;) and v1.0 (&lt;code&gt;SendMessage&lt;/code&gt;) are wire-incompatible. If you see &lt;code&gt;MethodNotFoundError&lt;/code&gt;, inspect the &lt;code&gt;a2a-sdk&lt;/code&gt; version on both client and server before debugging prompt logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Inference Profile IDs on Bedrock:&lt;/strong&gt; Newer Bedrock models require regional inference profile IDs (e.g. &lt;code&gt;us.amazon.nova-micro-v1:0&lt;/code&gt;) to avoid on-demand throughput errors during hosted execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account for Remote Cold Starts:&lt;/strong&gt; Default 10-second client timeouts are sufficient locally, but remote cross-cloud calls (e.g., Cloud Run scale-from-zero) require a minimum 60-second timeout window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep Math Out of the Prompt:&lt;/strong&gt; Using deterministic Python &lt;code&gt;Decimal&lt;/code&gt; arithmetic for conversion logic eliminates LLM calculation errors entirely, ensuring agreement checks evaluate protocol and model transportation integrity rather than arithmetic capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A Verification Provides Independent Fault Detection:&lt;/strong&gt; While &lt;code&gt;mcp_only&lt;/code&gt; (312 ms) is ideal for simple user queries, cross-cloud A2A verification adds independent failover and anomaly detection against compromised or stale tool endpoints for mission-critical operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Repository &amp;amp; Source Code
&lt;/h4&gt;

&lt;p&gt;The complete benchmark codebase, deployment scripts, test suite, and raw evaluation datasets are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/bedrock-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/bedrock-adk-a2a-currency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building multi-cloud agent systems using Amazon Bedrock AgentCore, Google ADK, or Microsoft Agent Framework, we welcome your feedback and benchmark contributions!&lt;/p&gt;

</description>
      <category>agents</category>
      <category>googleadk</category>
      <category>a2aprotocol</category>
      <category>aws</category>
    </item>
    <item>
      <title>Building AI Agents with the Kotlin Agent Development Kit (ADK)</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Tue, 28 Jul 2026 18:25:43 +0000</pubDate>
      <link>https://dev.to/gde/building-ai-agents-with-the-kotlin-agent-development-kit-adk-2gpa</link>
      <guid>https://dev.to/gde/building-ai-agents-with-the-kotlin-agent-development-kit-adk-2gpa</guid>
      <description>&lt;p&gt;This tutorial builds a starter "Hello World" style agent using Kotlin and the native Kotlin version of the Agent Development Kit (ADK).&lt;/p&gt;

&lt;p&gt;The full sample project is available on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/xbill9" rel="noopener noreferrer"&gt;
        xbill9
      &lt;/a&gt; / &lt;a href="https://github.com/xbill9/adk-hello-world-kotlin" rel="noopener noreferrer"&gt;
        adk-hello-world-kotlin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Kotlin ADK and MCP Hello World&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This project is a runnable Kotlin Agent Development Kit (ADK) demo. A Kotlin
&lt;code&gt;LlmAgent&lt;/code&gt; uses Gemini to decide when to call a &lt;code&gt;greet&lt;/code&gt; tool discovered from a
local Kotlin Model Context Protocol (MCP) server.&lt;/p&gt;
&lt;p&gt;The project has two Gradle modules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent&lt;/code&gt;: the Kotlin ADK agent, Gemini model configuration, MCP toolset, and
interactive &lt;code&gt;ReplRunner&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt;: the Ktor MCP server that exposes &lt;code&gt;greet&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Technology Stack&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin:&lt;/strong&gt; 2.3.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin ADK SDK:&lt;/strong&gt; &lt;code&gt;com.google.adk:google-adk-kotlin-core&lt;/code&gt; (v0.6.0)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Kotlin SDK:&lt;/strong&gt; &lt;code&gt;io.modelcontextprotocol:kotlin-sdk-jvm&lt;/code&gt; (v0.8.1)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ktor Framework:&lt;/strong&gt; 3.0.0 (Netty, SSE, ContentNegotiation, CORS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JDK:&lt;/strong&gt; Java 25&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build System:&lt;/strong&gt; Gradle 9.2.1 (Kotlin DSL)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Java 25&lt;/li&gt;
&lt;li&gt;A Gemini Developer API key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Gradle wrapper is included.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Configure Gemini&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Create the local environment file:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;cp .env.example .env&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Set &lt;code&gt;GOOGLE_API_KEY&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;, then load it:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;source&lt;/span&gt; ./set_env.sh&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The file is ignored by Git.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run the Demo&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Start the Kotlin MCP server in one…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/xbill9/adk-hello-world-kotlin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  What Is Kotlin?
&lt;/h2&gt;

&lt;p&gt;Kotlin is a modern, statically typed programming language created by JetBrains. It runs on the Java Virtual Machine (JVM), works alongside existing Java libraries, and is widely used for Android, backend, and multiplatform development.&lt;/p&gt;

&lt;p&gt;Static typing is especially useful when building agents. Agent configuration, tool schemas, and tool results can all be checked by the compiler before a prompt reaches the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Java
&lt;/h2&gt;

&lt;p&gt;This sample uses &lt;strong&gt;Java 25&lt;/strong&gt;. If Java is not installed, &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;SDKMAN!&lt;/a&gt; is a convenient way to install and switch between JDK versions on Linux and macOS:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://sdkman.io/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsdkman.io%2Fassets%2Fimg%2Fsdkman-logo-bubble.png" height="467" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://sdkman.io/" rel="noopener noreferrer" class="c-link"&gt;
            Home | SDKMAN! the Software Development Kit Manager
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            SDKMAN! is a tool for managing parallel versions of multiple Software Development Kits on most Unix based systems.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsdkman.io%2Fassets%2Fimg%2Ffavicon.ico" width="64" height="64"&gt;
          sdkman.io
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;After installing SDKMAN!, list the available Java 25 distributions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sdk list java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Install the Java 25 distribution you prefer, then verify the active version:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The project includes the Gradle wrapper, so you do not need to install Gradle separately.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Is the Agent Development Kit?
&lt;/h2&gt;

&lt;p&gt;The Agent Development Kit (ADK) is Google's code-first framework for building and deploying AI agents. It provides the pieces needed to configure models, write agent instructions, connect tools, manage sessions, and run agents locally.&lt;/p&gt;

&lt;p&gt;Google provides the Kotlin quickstart and API documentation here:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://adk.dev/get-started/kotlin/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadk.dev%2Fassets%2Fadk-social-card.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://adk.dev/get-started/kotlin/" rel="noopener noreferrer" class="c-link"&gt;
            Kotlin - Agent Development Kit (ADK)Agent Development Kit (ADK)
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Build powerful multi-agent systems with Agent Development Kit (ADK)
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadk.dev%2Fassets%2Fagent-development-kit.png" width="512" height="512"&gt;
          adk.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The complete Kotlin ADK source is also available on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/google" rel="noopener noreferrer"&gt;
        google
      &lt;/a&gt; / &lt;a href="https://github.com/google/adk-kotlin" rel="noopener noreferrer"&gt;
        adk-kotlin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Agent Development Kit (ADK) for Kotlin&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/google/adk-kotlin/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5b60841bea9e11d9d0b0950d690c9bc554e06385634056a7d5d62a15d1a4eabe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4170616368655f322e302d626c75652e737667" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://search.maven.org/artifact/com.google.adk/google-adk-kotlin-core" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/75a451d0cb4668225eb37abaab2a9d306c5a2bcfa7702feea466e3a18e2209d9/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f636f6d2e676f6f676c652e61646b2f676f6f676c652d61646b2d6b6f746c696e2d636f7265" alt="Maven Central"&gt;&lt;/a&gt;
&lt;a href="https://www.reddit.com/r/agentdevelopmentkit/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/804c84103b652f4f9ffcceb4914840adca00dfeec97b4fea152a38aaccc16225/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5265646469742d722532466167656e74646576656c6f706d656e746b69742d4646343530303f7374796c653d666c6174266c6f676f3d726564646974266c6f676f436f6c6f723d7768697465" alt="r/agentdevelopmentkit"&gt;&lt;/a&gt;
&lt;a href="https://deepwiki.com/google/adk-kotlin" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0f5ae213ac378635adeb5d7f13cef055ad2f7d9a47b36de7b1c67dbe09f609ca/68747470733a2f2f6465657077696b692e636f6d2f62616467652e737667" alt="Ask DeepWiki"&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;
      &lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/google/adk-python/main/assets/agent-development-kit.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoogle%2Fadk-python%2Fmain%2Fassets%2Fagent-development-kit.png" width="256"&gt;&lt;/a&gt;
    &lt;/h2&gt;
&lt;/div&gt;
    &lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;
      An open-source, code-first Kotlin toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.
    &lt;/h3&gt;

&lt;/div&gt;
    &lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;
      Important Links:
      &lt;a href="https://google.github.io/adk-docs/" rel="nofollow noopener noreferrer"&gt;Docs&lt;/a&gt; &amp;amp;
      &lt;a href="https://github.com/google/adk-samples" rel="noopener noreferrer"&gt;Samples&lt;/a&gt; &amp;amp;
      &lt;a href="https://github.com/google/adk-python" rel="noopener noreferrer"&gt;Python ADK&lt;/a&gt; &amp;amp;
      &lt;a href="https://github.com/google/adk-java" rel="noopener noreferrer"&gt;Java ADK&lt;/a&gt;.
    &lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;Agent Development Kit (ADK) is designed for developers seeking fine-grained
control and flexibility when building advanced AI agents that are tightly
integrated with services in Google Cloud. It allows you to define agent
behavior, orchestration, and tool use directly in code, enabling robust
debugging, versioning, and deployment anywhere – from your laptop to the cloud.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Key Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rich Tool Ecosystem&lt;/strong&gt;: Utilize pre-built tools, custom functions, OpenAPI
specs, or integrate existing tools to give agents diverse capabilities, all
for tight integration with the Google ecosystem.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code-First Development&lt;/strong&gt;: Define agent logic, tools, and orchestration
directly in Kotlin for ultimate flexibility, testability, and versioning.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Modular Multi-Agent Systems&lt;/strong&gt;: Design scalable applications by composing
multiple specialized…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/google/adk-kotlin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The Kotlin SDK is published as &lt;code&gt;com.google.adk:google-adk-kotlin-core&lt;/code&gt;. This tutorial uses &lt;strong&gt;Kotlin ADK 0.6.0&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini API Key
&lt;/h2&gt;

&lt;p&gt;You need a Gemini Developer API key to run the interactive agent. Create one in Google AI Studio:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;https://aistudio.google.com/apikey&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The MCP server and tool-discovery smoke test do not need an API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking the Developer Environment
&lt;/h2&gt;

&lt;p&gt;Clone the sample repository and run the initialization script. It builds the project and creates a local &lt;code&gt;.env&lt;/code&gt; file from the included template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/adk-hello-world-kotlin
&lt;span class="nb"&gt;cd &lt;/span&gt;adk-hello-world-kotlin
&lt;span class="nb"&gt;source &lt;/span&gt;init.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created .env from .env.example. Add your credentials before running the agent.
Setup complete. Start ./server.sh, then run ./run.sh in another terminal.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;.env&lt;/code&gt; and set your API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOGLE_API_KEY=your-api-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load it into the current shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;set_env.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never commit &lt;code&gt;.env&lt;/code&gt;. It is already listed in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Kotlin ADK Agent
&lt;/h2&gt;

&lt;p&gt;The sample has two Gradle modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent&lt;/code&gt; contains the Kotlin ADK agent and interactive command-line runner.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt; contains a Ktor MCP server that exposes the &lt;code&gt;greet&lt;/code&gt; tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core agent is defined in &lt;code&gt;GreetingAgent.kt&lt;/code&gt;. It configures Gemini, gives the agent its instruction, and connects an MCP toolset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kotlin_greeting_agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A Kotlin ADK agent that greets people through an MCP tool."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Gemini&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Instruction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"""
            You are a concise greeting assistant.
            When the user asks you to greet someone, always call the greet tool with that
            person's name. Return the greeting produced by the tool.
            """&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trimIndent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;toolsets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcpToolset&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LlmAgent&lt;/code&gt; brings together the model, instructions, and available tools. The model defaults to &lt;code&gt;gemini-3.1-flash-lite&lt;/code&gt;, but you can select another model with the &lt;code&gt;GEMINI_MODEL&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting the Agent to MCP
&lt;/h2&gt;

&lt;p&gt;Unlike the TypeScript weather sample, this project keeps the tool in a separate process. The agent discovers and invokes it through the &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GreetingAgent.kt&lt;/code&gt; creates an &lt;code&gt;McpToolset&lt;/code&gt; connected to the local server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mcpToolset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;McpToolset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;McpToolsetConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;sseConnectionParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="nc"&gt;McpConnectionParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mcpServerUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;sseEndpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;toolFilter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"greet"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toToolset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connection is lazy. When the agent needs its tools, ADK opens an MCP session, requests the tool list, and makes the &lt;code&gt;greet&lt;/code&gt; schema available to Gemini. The tool filter limits this agent to that single tool.&lt;/p&gt;

&lt;p&gt;The server registers the tool in &lt;code&gt;Tools.kt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GREET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Get a greeting from a local HTTP server."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;inputSchema&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;ToolSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="nf"&gt;buildJsonObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GREET_PARAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nf"&gt;buildJsonObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"The name to greet"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GREET_PARAM&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Read the name and return: Hello, &amp;lt;name&amp;gt;!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent and server communicate over HTTP using Server-Sent Events (SSE). By default, the server listens at &lt;code&gt;http://localhost:8080&lt;/code&gt;, with &lt;code&gt;/sse&lt;/code&gt; for the stream and &lt;code&gt;/messages&lt;/code&gt; for client messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build, Tests, and Code Style
&lt;/h2&gt;

&lt;p&gt;A single command builds both modules, runs the unit tests, and checks Kotlin formatting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can call the Gradle tasks directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew build ktlintCheck &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests check that the ADK agent contains its MCP toolset and that the greeting logic returns the expected text. Because the greeting formatter is a plain Kotlin function, it can be tested without calling Gemini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;testFormatGreeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formatGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Kotlin Developer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Kotlin Developer!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;make format&lt;/code&gt; if &lt;code&gt;ktlintCheck&lt;/code&gt; reports a style issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the ADK from the CLI
&lt;/h2&gt;

&lt;p&gt;The tool server and agent run as separate applications. Start the MCP server in one terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./server.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a second terminal, load the environment and start the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;set_env.sh
./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Gradle commands provide the same entry points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew :server:run
./gradlew :agent:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask the agent to greet someone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Greet Kotlin Developer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini selects the discovered &lt;code&gt;greet&lt;/code&gt; tool and supplies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Kotlin Developer"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello, Kotlin Developer!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;exit&lt;/code&gt; to close the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing MCP Without Calling Gemini
&lt;/h2&gt;

&lt;p&gt;You can verify the MCP connection independently of the model. With the server running, use the Kotlin ADK smoke test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew :agent:smokeMcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects through &lt;code&gt;McpToolset&lt;/code&gt; and confirms that the agent can discover &lt;code&gt;greet&lt;/code&gt;. It does not require &lt;code&gt;GOOGLE_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The repository also includes a direct Python JSON-RPC client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 test_mcp.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It initializes an MCP session, lists the available tools, calls &lt;code&gt;greet&lt;/code&gt; with &lt;code&gt;Galaxy&lt;/code&gt;, and verifies the response &lt;code&gt;Hello, Galaxy!&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the MCP Server to Cloud Run
&lt;/h2&gt;

&lt;p&gt;This project deploys the Ktor MCP server as a container. The ADK agent remains a client and connects to the deployed service through &lt;code&gt;MCP_SERVER_URL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Set your Google Cloud project, then run the deployment script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth login
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project YOUR_PROJECT_ID
./cloudrun.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script submits &lt;code&gt;cloudbuild.yaml&lt;/code&gt;, which builds the Docker image, pushes it to Container Registry, and deploys the service to Cloud Run.&lt;/p&gt;

&lt;p&gt;The sample stores active SSE sessions in memory, so the supplied Cloud Run configuration limits the service to one instance. It also allows unauthenticated access for demonstration purposes. Add authentication, authorization, stricter CORS rules, and shared session storage before using this design in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Google Cloud Console
&lt;/h2&gt;

&lt;p&gt;After deployment, retrieve the service URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run services describe adk-hello-world-kotlin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'value(status.url)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point the local agent at that URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MCP_SERVER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://your-service-url"&lt;/span&gt;
./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The Kotlin Agent Development Kit brings agent development to the JVM with familiar Kotlin and Gradle tooling:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Typed Agent Configuration:&lt;/strong&gt; Configure &lt;code&gt;LlmAgent&lt;/code&gt;, Gemini, and instructions in Kotlin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Tool Integration:&lt;/strong&gt; Discover and invoke tools hosted by a separate Ktor service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Testing:&lt;/strong&gt; Test tool behavior without making model requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Development:&lt;/strong&gt; Run the server and interactive agent directly from Gradle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Deployment:&lt;/strong&gt; Package the MCP server in a container and deploy it to Cloud Run.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>kotlin</category>
      <category>ai</category>
      <category>gemini</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Can Google ADK Talk to Microsoft Foundry on Azure? A Cross-Cloud A2A Benchmark</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Mon, 27 Jul 2026 20:54:36 +0000</pubDate>
      <link>https://dev.to/gde/can-google-adk-talk-to-microsoft-foundry-on-azure-a-cross-cloud-a2a-benchmark-4h36</link>
      <guid>https://dev.to/gde/can-google-adk-talk-to-microsoft-foundry-on-azure-a-cross-cloud-a2a-benchmark-4h36</guid>
      <description>&lt;p&gt;This article provides a step-by-step guide to building and testing a&lt;br&gt;
cross-cloud currency agent. A coordinator hosted on Microsoft Foundry in Azure&lt;br&gt;
calls a Google ADK agent on Google Cloud over A2A v1.0, uses an MCP exchange-rate&lt;br&gt;
tool, and records what independent verification costs in latency.&lt;/p&gt;
&lt;h4&gt;
  
  
  What is This Project Trying to Do?
&lt;/h4&gt;

&lt;p&gt;Most Agent-to-Agent (A2A) protocol demos stop at "look, the request succeeded."&lt;br&gt;
That is a smoke test, not an interoperability benchmark. This project goes&lt;br&gt;
further: a Microsoft Agent Framework coordinator, hosted on Microsoft Foundry&lt;br&gt;
in Azure, discovers and delegates to a Google ADK agent running on Cloud Run in&lt;br&gt;
GCP, then measures what that cross-cloud hop actually costs.&lt;/p&gt;

&lt;p&gt;The questions, with numbers instead of vibes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can a Foundry-hosted Agent Framework agent invoke a Google ADK agent through an A2A agent card, with no framework-specific glue?&lt;/li&gt;
&lt;li&gt;What latency does remote-agent verification add?&lt;/li&gt;
&lt;li&gt;Does independently verifying an MCP tool result over A2A improve correctness enough to justify the overhead?&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Reduce, Re-Use, Re-Cycle!
&lt;/h4&gt;

&lt;p&gt;This builds directly on the currency agent from the previous articles in this series:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codelabs.developers.google.com/codelabs/currency-agent#0" rel="noopener noreferrer"&gt;Getting Started with MCP, ADK and A2A | Google Codelabs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/xbill9/currency-agent" rel="noopener noreferrer"&gt;GitHub - xbill9/currency-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That agent — ADK, Gemini, a FastMCP exchange-rate server backed by the free &lt;a href="https://www.frankfurter.dev/" rel="noopener noreferrer"&gt;Frankfurter API&lt;/a&gt; — becomes the &lt;em&gt;remote verifier&lt;/em&gt; in this project. The new repo wraps it in a benchmark harness:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/xbill9/foundry-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/foundry-adk-a2a-currency&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  The Architecture
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You (Responses protocol, Entra ID)
      |
Microsoft Foundry hosted agent          (Azure, eastus2, gpt-5-mini)
Microsoft Agent Framework coordinator
      |
      +-- MCP stdio --&amp;gt; Frankfurter rates      (in-container)
      |
      +-- A2A v1.0 --&amp;gt; Cloud Run               (GCP, us-central1)
                          |
                       Google ADK agent        (gemini-2.5-flash)
                          |
                       MCP HTTP --&amp;gt; Frankfurter rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The coordinator answers every conversion three ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinator calls the rate tool&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a2a_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinator delegates to the remote ADK agent&lt;/td&gt;
&lt;td&gt;Measure remote-agent behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP result independently checked over A2A&lt;/td&gt;
&lt;td&gt;The accuracy/overhead tradeoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both sides read the same Frankfurter daily reference rates on purpose: when the two clouds disagree, that measures &lt;em&gt;protocol and model&lt;/em&gt; behavior, not data-source skew.&lt;/p&gt;
&lt;h4&gt;
  
  
  Rule One: the Model Never Does Math
&lt;/h4&gt;

&lt;p&gt;Currency conversion is a terrible job for an LLM and a great job for &lt;code&gt;Decimal&lt;/code&gt;. The domain layer is framework-independent Python with pydantic models, and agreement is judged by relative numeric difference — no model is ever asked "do these numbers look the same to you?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;converted_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agreed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;tolerance&lt;/span&gt;  &lt;span class="c1"&gt;# default 0.005
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure policy is explicit rather than emergent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MCP fails, A2A succeeds → return the remote result, labeled unverified.&lt;/li&gt;
&lt;li&gt;A2A fails, MCP succeeds → return the tool result with a "verification unavailable" warning.&lt;/li&gt;
&lt;li&gt;Both succeed but disagree → return &lt;strong&gt;both&lt;/strong&gt; and warn. Never silently pick the model's favorite.&lt;/li&gt;
&lt;li&gt;Both fail → return a typed failure (&lt;code&gt;validation&lt;/code&gt;, &lt;code&gt;provider&lt;/code&gt;, &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;transport&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;protocol&lt;/code&gt;). Never fabricate a rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"Which layer broke" is a research question, so every adapter failure is converted into exactly one of those kinds at one boundary.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Wire Fight: A2A v0.3.0 vs v1.0
&lt;/h4&gt;

&lt;p&gt;Here is the headline interoperability lesson. The first attempt to point the Microsoft Agent Framework A2A client at the existing currency agent died instantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a2a.utils.errors.MethodNotFoundError: Method not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root cause: a clean protocol-version skew with &lt;strong&gt;no negotiation&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Microsoft client (&lt;code&gt;agent-framework-a2a&lt;/code&gt;, requires &lt;code&gt;a2a-sdk&amp;gt;=1.0&lt;/code&gt;) calls the A2A v1.0 JSON-RPC method &lt;code&gt;SendMessage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The existing ADK agent (&lt;code&gt;a2a-sdk 0.3.x&lt;/code&gt;) only routes the v0.3.0 method &lt;code&gt;message/send&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client fetches the agent card — which says &lt;code&gt;protocolVersion: 0.3.0&lt;/code&gt; — and calls the v1.0 method anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worse, the two ecosystems had mutually exclusive dependency pins at the time of writing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;a2a-sdk requirement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;agent-framework-a2a 1.0.0b260721&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=1.0.0,&amp;lt;2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.1.0 – 2.4.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;0.4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;gt;=0.3.4,&amp;lt;2&lt;/code&gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a2ui-agent-sdk&lt;/code&gt; (through 0.4.0)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;0.4.0&lt;/code&gt; ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;google-adk 2.5.0&lt;/code&gt; is the first release that allows &lt;code&gt;a2a-sdk 1.x&lt;/code&gt; — but the A2UI extension from the previous article still pins the old protocol. So the benchmark agent is the currency agent with A2UI removed, pinned to &lt;code&gt;google-adk 2.5.0&lt;/code&gt; + &lt;code&gt;a2a-sdk 1.1.2&lt;/code&gt;. Separate &lt;code&gt;uv&lt;/code&gt; virtualenvs keep both worlds installable on one machine; the wire protocol is what has to match.&lt;/p&gt;

&lt;p&gt;Two more v1.0 observations worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The v1.0 agent card moved &lt;code&gt;url&lt;/code&gt;/&lt;code&gt;protocolVersion&lt;/code&gt; into a &lt;code&gt;supportedInterfaces&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;ADK auto-advertises MCP tools (&lt;code&gt;get_exchange_rate&lt;/code&gt;) as A2A skills on the card — your remote agent's card documents its toolbox for free.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the version alignment, the answer to research question 1 is &lt;strong&gt;yes&lt;/strong&gt;: the Microsoft Agent Framework client consumed the ADK-generated card and delegated over A2A v1.0 with no schema translation beyond prompting the agent to answer in parseable JSON.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hosting the Coordinator on Microsoft Foundry
&lt;/h4&gt;

&lt;p&gt;Versions observed working (preview software; pin what works, expect drift): Azure Developer CLI &lt;code&gt;1.28.1&lt;/code&gt;, &lt;code&gt;microsoft.foundry&lt;/code&gt; azd extension &lt;code&gt;1.0.0-beta.2&lt;/code&gt;, Python &lt;code&gt;3.13&lt;/code&gt;, &lt;code&gt;agent-framework-core 1.12.1&lt;/code&gt;, &lt;code&gt;agent-framework-foundry 1.10.3&lt;/code&gt;, &lt;code&gt;agent-framework-foundry-hosting 1.0.0b260722&lt;/code&gt;, region &lt;code&gt;eastus2&lt;/code&gt;, model &lt;code&gt;gpt-5-mini&lt;/code&gt; (&lt;code&gt;2025-08-07&lt;/code&gt;, Global Standard).&lt;/p&gt;

&lt;p&gt;The entire hosted agent is one file. &lt;code&gt;FoundryChatClient&lt;/code&gt; authenticates with &lt;code&gt;DefaultAzureCredential&lt;/code&gt; — no API keys anywhere on the Azure side — and &lt;code&gt;ResponsesHostServer&lt;/code&gt; exposes it over the Responses protocol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;FoundryChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;project_endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FOUNDRY_PROJECT_ENDPOINT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AZURE_AI_MODEL_DEPLOYMENT_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;credential&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency-interoperability-coordinator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;For every conversion request, call run_currency_benchmark. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Never calculate or verify arithmetic yourself. ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;run_currency_benchmark&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;ResponsesHostServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapters behind that tool are selected by environment variables (&lt;code&gt;CURRENCY_A2A_ENDPOINT&lt;/code&gt;, &lt;code&gt;CURRENCY_RATE_PROVIDER&lt;/code&gt;, &lt;code&gt;CURRENCY_RATE_TRANSPORT&lt;/code&gt;, &lt;code&gt;CURRENCY_TIMEOUT_SECONDS&lt;/code&gt;), passed through &lt;code&gt;azure.yaml&lt;/code&gt;. Version 1 shipped with deterministic fixtures; version 2 flipped the env vars and went live without touching code.&lt;/p&gt;

&lt;p&gt;The gotcha that will probably bite you too: control-plane roles (Owner/Contributor) are &lt;strong&gt;not&lt;/strong&gt; enough to deploy. The deploying identity also needs the Foundry Project Manager &lt;em&gt;data-plane&lt;/em&gt; role (definition &lt;code&gt;eadc314b-1a2d-4efa-be10-5d325db5065e&lt;/code&gt;) at the Foundry account scope.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Google Side: ADK on Cloud Run
&lt;/h4&gt;

&lt;p&gt;The benchmark agent container colocates two processes: the FastMCP Frankfurter server on localhost and the A2A app on &lt;code&gt;$PORT&lt;/code&gt;. The Gemini key comes from Secret Manager — never an env literal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud secrets create gemini-api-key &lt;span class="nt"&gt;--data-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/gemini.key"&lt;/span&gt;
gcloud run deploy currency-adk-a2a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; adk_agent &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allow-unauthenticated&lt;/span&gt; &lt;span class="nt"&gt;--min-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--max-instances&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt; &lt;span class="s2"&gt;"GOOGLE_API_KEY=gemini-api-key:latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-env-vars&lt;/span&gt; &lt;span class="s2"&gt;"MCP_SERVER_URL=http://127.0.0.1:8081/mcp,GENAI_MODEL=gemini-2.5-flash"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--min-instances=0&lt;/code&gt; means the verifier costs nothing while idle. (Ask me how I know to check &lt;code&gt;min-instances&lt;/code&gt; on old GPU services. Actually, don't.)&lt;/p&gt;

&lt;h4&gt;
  
  
  How to Run It
&lt;/h4&gt;

&lt;p&gt;Everything local runs credential-free on fixtures first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/foundry-adk-a2a-currency
&lt;span class="nb"&gt;cd &lt;/span&gt;foundry-adk-a2a-currency
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".[dev]"&lt;/span&gt;
pytest                      &lt;span class="c"&gt;# 35 deterministic tests, no cloud, no model calls&lt;/span&gt;
currency-benchmark 100 USD EUR &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To go live locally, start the ADK agent (needs &lt;code&gt;GOOGLE_API_KEY&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;adk_agent &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; uv &lt;span class="nb"&gt;sync
&lt;/span&gt;&lt;span class="nv"&gt;MCP_SERVER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:8081/mcp uv run uvicorn agent:a2a_app &lt;span class="nt"&gt;--port&lt;/span&gt; 10001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and point the benchmark at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CURRENCY_RATE_PROVIDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frankfurter currency-benchmark 250 GBP USD JPY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mode&lt;/span&gt; verified &lt;span class="nt"&gt;--transport&lt;/span&gt; mcp-stdio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--a2a-endpoint&lt;/span&gt; http://127.0.0.1:10001 &lt;span class="nt"&gt;--timeout-seconds&lt;/span&gt; 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full evaluation matrix (fault-free cases live, fault-injection cases deterministic — you cannot order a live agent to time out on demand):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;currency-evaluate &lt;span class="nt"&gt;--a2a-endpoint&lt;/span&gt; http://127.0.0.1:10001 &lt;span class="nt"&gt;--live-rates&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; results.jsonl &lt;span class="nt"&gt;--summary&lt;/span&gt; summary.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full cross-cloud deployment is one script — Secret Manager, Cloud Run, then &lt;code&gt;azd deploy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash infra/deploy_live.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Results
&lt;/h4&gt;

&lt;p&gt;The 38-case matrix (validation, cross-rates, precision, injected timeouts, stale rates, disagreement, malicious tool text) ran in all three modes — 114 records, raw JSONL retained in the repo so every number below is regenerable.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Median latency&lt;/th&gt;
&lt;th&gt;p95 latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;297 ms&lt;/td&gt;
&lt;td&gt;1.09 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a2a_only&lt;/code&gt; (live Gemini)&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.69 s&lt;/td&gt;
&lt;td&gt;4.82 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;verified&lt;/code&gt; (live)&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.71 s&lt;/td&gt;
&lt;td&gt;4.15 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two numbers I care most about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All 60 live-adapter records agreed within the 0.5% tolerance.&lt;/strong&gt; The only disagreement in the entire run was the deliberately injected fault case — which the verifier flagged, exactly as designed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified mode costs roughly what the A2A call costs&lt;/strong&gt;, because MCP and A2A run concurrently. Independent verification added ~1.4 s of median latency over the MCP baseline, not the sum of both paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the fully hosted path (Azure coordinator → GCP agent), the remote Foundry endpoint completed all three modes: &lt;code&gt;mcp_only&lt;/code&gt; in 0.71 s, &lt;code&gt;verified&lt;/code&gt; in 2.7 s with &lt;code&gt;agreed: true&lt;/code&gt; and &lt;code&gt;relative_difference: 0&lt;/code&gt;. One warm-path anecdote, not a distribution — repeated warm/cold trials and token/cost accounting are the remaining evaluation work.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lessons Learned
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A2A version skew fails loud but late.&lt;/strong&gt; The client happily fetched a v0.3.0 card and then called v1.0 methods. Check the &lt;code&gt;a2a-sdk&lt;/code&gt; major version on &lt;em&gt;both&lt;/em&gt; sides before you debug anything else; &lt;code&gt;MethodNotFoundError&lt;/code&gt; is the signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension packages can pin you to an old protocol.&lt;/strong&gt; A2UI held the whole agent at A2A v0.3.0. Protocol-adjacent extras deserve a dependency audit before you commit to them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default timeouts assume one cloud.&lt;/strong&gt; The coordinator's 10 s per-adapter timeout was generous locally and fatal cross-cloud: a Cloud Run cold start plus multi-target Gemini generation blew straight through it. It's now configurable, set to 60 s for remote endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold starts don't just add latency — they change behavior.&lt;/strong&gt; One cold-start reply omitted one of the requested targets entirely. Because parsing is strict, it surfaced as a typed &lt;code&gt;protocol&lt;/code&gt; failure instead of a silently incomplete answer. Design for partial replies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview deploy tooling has transient moods.&lt;/strong&gt; One &lt;code&gt;azd deploy&lt;/code&gt; died with &lt;code&gt;AzureDeveloperCLICredential: signal: killed&lt;/code&gt; and succeeded unchanged on retry. Retry before you bisect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IPv6 can hang quietly.&lt;/strong&gt; Frankfurter calls from some sandboxed environments hang on IPv6 and surface as empty-message timeouts. Pinning the client socket to IPv4 fixed it — the same workaround the original currency agent shipped with, which I removed as "sandbox-only" and then reinstated a day later. Respect the workarounds you inherit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the model out of the arithmetic.&lt;/strong&gt; Both &lt;code&gt;gpt-5-mini&lt;/code&gt; and &lt;code&gt;gemini-2.5-flash&lt;/code&gt; respected "call the tool, never calculate" — and because all math is &lt;code&gt;Decimal&lt;/code&gt; in deterministic code, agreement checks compare numbers, not model prose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same-source verification isolates the protocol.&lt;/strong&gt; Pointing both paths at Frankfurter means a disagreement can only come from the protocol, the model, or the code. With live data on both sides: zero false disagreements in 60 records.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  So When is A2A Verification Worth It?
&lt;/h4&gt;

&lt;p&gt;Based on what's measured so far: the accuracy delta on the happy path is zero — both paths read the same rates and agree. What you're actually buying for your ~1.4 s is &lt;strong&gt;independent failure detection&lt;/strong&gt;: a second implementation, on a second cloud, that will loudly disagree when a tool is compromised, stale, or wrong. The injected-fault case shows the mechanism works; the malicious-tool-text and hosted-injection cases are where that budget earns its keep. If your tool result feeds a decision that matters, one extra second for a cross-checked answer is cheap. If it's a chatbot rate lookup — &lt;code&gt;mcp_only&lt;/code&gt; at 297 ms is your friend.&lt;/p&gt;

&lt;h4&gt;
  
  
  Source
&lt;/h4&gt;

&lt;p&gt;Everything — coordinator, benchmark agent, deploy script, evaluation cases, and the raw results behind every table above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/xbill9/foundry-adk-a2a-currency" rel="noopener noreferrer"&gt;GitHub - xbill9/foundry-adk-a2a-currency&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upstream agent pinned at &lt;a href="https://github.com/xbill9/currency-agent" rel="noopener noreferrer"&gt;xbill9/currency-agent@aeef3c4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've wired a Microsoft Agent Framework agent to a Google ADK agent over A2A — especially if you hit card or version mismatches I didn't — I'd genuinely like to hear what broke.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>googleadk</category>
      <category>a2aprotocol</category>
      <category>azure</category>
    </item>
    <item>
      <title>Build and Deploy Java AI Agents with Google ADK</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Mon, 27 Jul 2026 19:24:51 +0000</pubDate>
      <link>https://dev.to/gde/build-and-deploy-java-ai-agents-with-google-adk-28oi</link>
      <guid>https://dev.to/gde/build-and-deploy-java-ai-agents-with-google-adk-28oi</guid>
      <description>&lt;p&gt;Debian 13 “Trixie” provides a clean, stable base for Java agent development. On a&lt;br&gt;
workstation, virtual machine, or cloud instance, you can compile Java projects, run&lt;br&gt;
local web servers, use the Google Agent Development Kit (ADK) Dev UI, and deploy an&lt;br&gt;
agent to Google Cloud Run.&lt;/p&gt;

&lt;p&gt;This guide builds a small ADK agent with time and weather tools. The complete project&lt;br&gt;
is available in the&lt;br&gt;
&lt;a href="https://github.com/xbill9/adk-hello-world-java" rel="noopener noreferrer"&gt;sample repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Prepare Debian Trixie
&lt;/h2&gt;

&lt;p&gt;Start with a Debian 13 installation and a user account that can run &lt;code&gt;sudo&lt;/code&gt;. Confirm&lt;br&gt;
the operating-system version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %s (%s)\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Install the required tools
&lt;/h2&gt;

&lt;p&gt;Update the package index and install the base development tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl git maven unzip zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This project compiles with Java 25. One convenient way to install a matching JDK is&lt;br&gt;
&lt;a href="https://sdkman.io/install/" rel="noopener noreferrer"&gt;SDKMAN!&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://get.sdkman.io"&lt;/span&gt; | bash
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.sdkman/bin/sdkman-init.sh"&lt;/span&gt;
sdk list java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select an available Java 25 identifier from the list and install it (for example, &lt;code&gt;25-open&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sdk &lt;span class="nb"&gt;install &lt;/span&gt;java 25-open
java &lt;span class="nt"&gt;--version&lt;/span&gt;
mvn &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both commands should report Java 25, and Maven must be version 3.6.3 or newer. The&lt;br&gt;
build enforces this minimum Maven version.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install the Google Cloud CLI
&lt;/h3&gt;

&lt;p&gt;Add Google's Debian package repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https ca-certificates gnupg
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/cloud.google.gpg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/google-cloud-sdk.list
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; google-cloud-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Google Cloud CLI is required for Vertex AI authentication and Cloud Run&lt;br&gt;
deployment. It is not required if you only run the agent locally with a Gemini API&lt;br&gt;
key.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Clone and inspect the project
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/adk-hello-world-java
&lt;span class="nb"&gt;cd &lt;/span&gt;adk-hello-world-java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The agent lives at&lt;br&gt;
&lt;code&gt;src/main/java/agents/multitool/MultiToolAgent.java&lt;/code&gt;. Its public&lt;br&gt;
&lt;code&gt;ROOT_AGENT&lt;/code&gt; field lets the ADK Dev UI discover it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;BaseAgent&lt;/span&gt; &lt;span class="no"&gt;ROOT_AGENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initAgent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;BaseAgent&lt;/span&gt; &lt;span class="nf"&gt;initAgent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"multi_tool_agent"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-2.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Agent to answer questions about the time and weather in a city."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;FunctionTool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MultiToolAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"getCurrentTime"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
          &lt;span class="nc"&gt;FunctionTool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MultiToolAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"getWeather"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tools return structured maps with a &lt;code&gt;status&lt;/code&gt; and a human-readable &lt;code&gt;report&lt;/code&gt;. The&lt;br&gt;
time tool uses IANA time zones and includes aliases for cities such as San Francisco,&lt;br&gt;
Beijing, and Mumbai.&lt;/p&gt;
&lt;h3&gt;
  
  
  Current ADK Java baseline
&lt;/h3&gt;

&lt;p&gt;This sample uses &lt;strong&gt;ADK for Java 1.7.0&lt;/strong&gt;, the latest release available from Maven&lt;br&gt;
Central as of July 27, 2026. Both runtime dependencies use the same property so the&lt;br&gt;
core library and Dev UI cannot drift to different versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;google-adk.version&amp;gt;&lt;/span&gt;1.7.0&lt;span class="nt"&gt;&amp;lt;/google-adk.version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.adk&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;google-adk&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${google-adk.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.adk&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;google-adk-dev&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${google-adk.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recent changes relevant to this project include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 25 build support.&lt;/strong&gt; ADK 1.6 updated its Spring AI integration and build so it
works with Java 25.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More reliable function-tool streaming.&lt;/strong&gt; ADK 1.6 aligned Gemini streaming
function-call handling with the Python ADK, and 1.7 fixed reassembly of streamed
function-call arguments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner command-line shutdown.&lt;/strong&gt; ADK now uses daemon threads for its shared HTTP
client, preventing idle HTTP workers from keeping the JVM alive after the CLI exits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safer loading and sessions.&lt;/strong&gt; Recent fixes restrict dynamic class loading and
skill paths and prevent cross-user disclosure in &lt;code&gt;VertexAiSessionService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More accurate observability.&lt;/strong&gt; ADK 1.7 includes tool-related tokens in
&lt;code&gt;gen_ai.usage.input_tokens&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 3 flow compatibility.&lt;/strong&gt; ADK 1.7 can reorder forced function calls when a
Gemini 3 model requires it. This sample remains on &lt;code&gt;gemini-2.5-flash&lt;/code&gt; for a stable,
broadly available tutorial baseline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are framework improvements; the sample does not reimplement them. Keeping the&lt;br&gt;
ADK dependencies pinned to 1.7.0 is what brings them into the application.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Choose an authentication mode
&lt;/h2&gt;

&lt;p&gt;Run the setup script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./init.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It offers two modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini API key&lt;/strong&gt; for local development. Create a key in
&lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;. The script stores it in
&lt;code&gt;~/gemini.key&lt;/code&gt; with user-only permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertex AI&lt;/strong&gt; for local development and Cloud Run. Enter a Google Cloud project ID;
the script then configures gcloud and Application Default Credentials (ADC).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The selected mode is stored in &lt;code&gt;~/.adk-hello-world-java-auth&lt;/code&gt;. Run &lt;code&gt;./init.sh&lt;/code&gt; again&lt;br&gt;
whenever you want to switch modes. The launch scripts source &lt;code&gt;set_env.sh&lt;/code&gt;&lt;br&gt;
automatically, so you do not need to export the variables by hand.&lt;/p&gt;

&lt;p&gt;For Vertex AI, your account and the Cloud Run service identity must have the required&lt;br&gt;
Vertex AI permissions. Cloud Run uses its service identity at runtime rather than a&lt;br&gt;
downloaded credential file.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Build, test, and lint
&lt;/h2&gt;

&lt;p&gt;Compile the project and run its eight JUnit Jupiter tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make build
make &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests cover agent initialization, supported and unsupported cities, time-zone&lt;br&gt;
aliases, and null input. Run Checkstyle separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lint target fails when Google Java Style warnings are found, which makes it useful&lt;br&gt;
in local development and continuous integration.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Run the command-line agent
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./cli.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Example session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You &amp;gt; What is the current time in Tokyo?
Agent &amp;gt; The current time in Tokyo is 08:24.

You &amp;gt; What is the weather in New York?
Agent &amp;gt; The weather in New York is sunny with a temperature of 25 degrees Celsius
        (77 degrees Fahrenheit).

You &amp;gt; quit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Use the ADK Dev UI
&lt;/h2&gt;

&lt;p&gt;Start the local server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./devui.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://127.0.0.1:8080" rel="noopener noreferrer"&gt;http://127.0.0.1:8080&lt;/a&gt; in your browser. ADK scans the Maven&lt;br&gt;
output under &lt;code&gt;target/classes&lt;/code&gt; and makes &lt;code&gt;multi_tool_agent&lt;/code&gt; available in the UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5iwbl9wdu5rm6195b509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5iwbl9wdu5rm6195b509.png" alt="ADK Dev UI" width="700" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;web.sh&lt;/code&gt; script is retained as an alias for &lt;code&gt;devui.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;ADK 1.6 tightened WebSocket origin handling and warns when the Dev UI uses the &lt;code&gt;*&lt;/code&gt;&lt;br&gt;
CORS default. That default is convenient for local development but should not be&lt;br&gt;
treated as an access-control mechanism for a public deployment.&lt;/p&gt;
&lt;h2&gt;
  
  
  8. Deploy to Cloud Run
&lt;/h2&gt;

&lt;p&gt;Cloud Run deployment uses Vertex AI rather than copying a local API key into the&lt;br&gt;
service. If you selected API-key mode, run &lt;code&gt;./init.sh&lt;/code&gt; again and choose Vertex AI.&lt;/p&gt;

&lt;p&gt;Then deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./cloudrun.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script runs one source deployment for the &lt;code&gt;adk-hello-world-java&lt;/code&gt; service in&lt;br&gt;
&lt;code&gt;us-central1&lt;/code&gt;. Because the repository contains a Dockerfile, Cloud Run builds that&lt;br&gt;
Dockerfile remotely; Docker does not need to be installed on the Debian system.&lt;/p&gt;

&lt;p&gt;The container reads Cloud Run's &lt;code&gt;PORT&lt;/code&gt; environment variable and starts the ADK web&lt;br&gt;
server. When deployment completes, gcloud prints the service URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvnvz45she3mts2870wgq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvnvz45she3mts2870wgq.png" alt="Cloud Run Console" width="700" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The deployment is private by default because the service includes the ADK Dev UI and&lt;br&gt;
API. Configure authenticated callers with Cloud Run IAM. For a disposable public&lt;br&gt;
demonstration, replace &lt;code&gt;--no-allow-unauthenticated&lt;/code&gt; with &lt;code&gt;--allow-unauthenticated&lt;/code&gt; in&lt;br&gt;
&lt;code&gt;cloudrun.sh&lt;/code&gt;; do not rely on CORS as access control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── .dockerignore
├── .gcloudignore
├── Dockerfile
├── Makefile
├── cli.sh
├── cloudrun.sh
├── devui.sh
├── init.sh
├── pom.xml
├── set_env.sh
└── src
    ├── main/java/agents/multitool/MultiToolAgent.java
    └── test/java/agents/multitool/MultiToolAgentTest.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Debian Trixie provides everything needed to build and test a Java ADK agent locally.&lt;br&gt;
This sample keeps local authentication explicit, verifies the tool logic with JUnit,&lt;br&gt;
enforces Java style with Checkstyle, and uses Vertex AI service identity when deployed&lt;br&gt;
to Cloud Run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://adk.dev/get-started/java/" rel="noopener noreferrer"&gt;ADK Java quickstart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google/adk-java" rel="noopener noreferrer"&gt;Google ADK for Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google/adk-java/releases/tag/v1.7.0" rel="noopener noreferrer"&gt;ADK for Java 1.7.0 release notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/deploying-source-code" rel="noopener noreferrer"&gt;Deploy Cloud Run services from source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/configuring/services/service-identity" rel="noopener noreferrer"&gt;Configure Cloud Run service identity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sdkman.io/install/" rel="noopener noreferrer"&gt;SDKMAN! installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/xbill9/adk-hello-world-java" rel="noopener noreferrer"&gt;Sample repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>ai</category>
      <category>googlecloud</category>
      <category>debian</category>
    </item>
    <item>
      <title>Building AI Agents with the TypeScript Agent Development Kit (ADK)</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Mon, 27 Jul 2026 17:09:33 +0000</pubDate>
      <link>https://dev.to/gde/building-ai-agents-with-the-typescript-agent-development-kit-adk-3mf</link>
      <guid>https://dev.to/gde/building-ai-agents-with-the-typescript-agent-development-kit-adk-3mf</guid>
      <description>&lt;p&gt;This tutorial builds a starter "Hello World" style agent using TypeScript and the native TypeScript version of the Agent Development Kit (ADK).&lt;/p&gt;

&lt;p&gt;The full sample project is available on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/xbill9" rel="noopener noreferrer"&gt;
        xbill9
      &lt;/a&gt; / &lt;a href="https://github.com/xbill9/adk-hello-world-typescript" rel="noopener noreferrer"&gt;
        adk-hello-world-typescript
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ADK Hello World for TypeScript&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A minimal weather and time agent built with Google's
&lt;a href="https://github.com/google/adk-js" rel="noopener noreferrer"&gt;Agent Development Kit for TypeScript&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Requirements&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 20 or newer&lt;/li&gt;
&lt;li&gt;A Gemini API key, or Google Cloud credentials for Vertex AI&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install
cp .env.example .env&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Edit &lt;code&gt;.env&lt;/code&gt; and choose one authentication method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gemini Developer API: set &lt;code&gt;GOOGLE_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vertex AI: set &lt;code&gt;GOOGLE_GENAI_USE_VERTEXAI=TRUE&lt;/code&gt;,
&lt;code&gt;GOOGLE_CLOUD_PROJECT&lt;/code&gt;, and &lt;code&gt;GOOGLE_CLOUD_LOCATION&lt;/code&gt;, then run
&lt;code&gt;gcloud auth application-default login&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Never commit &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Interactive terminal&lt;/span&gt;
npm start

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; ADK development UI&lt;/span&gt;
npm run web

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Type-check and run unit tests&lt;/span&gt;
npm run check&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The ADK CLI can also be called directly:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npx adk run src/agent.ts
npx adk web&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The agent source is in &lt;code&gt;src/agent.ts&lt;/code&gt;. Its two local tools preserve the original
sample behavior: weather and local time are available for New York, while other
cities return a clear unsupported-city result.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/xbill9/adk-hello-world-typescript" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  What Is TypeScript?
&lt;/h2&gt;

&lt;p&gt;TypeScript is a strongly typed programming language built on top of JavaScript, maintained by Microsoft. It compiles to plain JavaScript and runs anywhere JavaScript runs — including Node.js, which is what the ADK for TypeScript targets. The static type system pairs naturally with agent development: tool parameters, tool results, and agent configuration are all checked at compile time, before the model ever sees them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Node.js
&lt;/h2&gt;

&lt;p&gt;The ADK for TypeScript requires &lt;strong&gt;Node.js 20 or newer&lt;/strong&gt;. If Node.js is not installed in your environment, the Node Version Manager (&lt;code&gt;nvm&lt;/code&gt;) is the easiest way to install and manage versions:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nvm-sh" rel="noopener noreferrer"&gt;
        nvm-sh
      &lt;/a&gt; / &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;
        nvm
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;a href="https://github.com/nvm-sh/logos" rel="noopener noreferrer"&gt;
  
    
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fnvm-sh%2Flogos%2FHEAD%2Fnvm-logo-color.svg" height="50" alt="nvm project logo"&gt;
  
&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Node Version Manager &lt;a href="https://github.com/nvm-sh/nvm/actions/workflows/tests-fast.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/nvm-sh/nvm/actions/workflows/tests-fast.yml/badge.svg?branch=master" alt="Tests"&gt;&lt;/a&gt; &lt;a href="https://github.com/nvm-sh/nvm/releases/tag/v0.40.6" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ded11fa8907dd430dfab37314acb417ec2744330842b6d12d211b54a0f39b514/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d76302e34302e362d79656c6c6f772e737667" alt="nvm version"&gt;&lt;/a&gt; &lt;a href="https://bestpractices.dev/projects/684" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/bbbddfec334e0459db55af340d52d41de1e59d5f2fcb6e03bacd7ef610839aef/68747470733a2f2f626573747072616374696365732e6465762f70726f6a656374732f3638342f6261646765" alt="CII Best Practices"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#intro" rel="noopener noreferrer"&gt;Intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#about" rel="noopener noreferrer"&gt;About&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#installing-and-updating" rel="noopener noreferrer"&gt;Installing and Updating&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#install--update-script" rel="noopener noreferrer"&gt;Install &amp;amp; Update Script&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#additional-notes" rel="noopener noreferrer"&gt;Additional Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#installing-in-docker" rel="noopener noreferrer"&gt;Installing in Docker&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#installing-in-docker-for-cicd-jobs" rel="noopener noreferrer"&gt;Installing in Docker for CICD-Jobs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#troubleshooting-on-linux" rel="noopener noreferrer"&gt;Troubleshooting on Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#troubleshooting-on-macos" rel="noopener noreferrer"&gt;Troubleshooting on macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#ansible" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#verify-installation" rel="noopener noreferrer"&gt;Verify Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#important-notes" rel="noopener noreferrer"&gt;Important Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#git-install" rel="noopener noreferrer"&gt;Git Install&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#manual-install" rel="noopener noreferrer"&gt;Manual Install&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#manual-upgrade" rel="noopener noreferrer"&gt;Manual Upgrade&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#usage" rel="noopener noreferrer"&gt;Usage&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#long-term-support" rel="noopener noreferrer"&gt;Long-term Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#migrating-global-packages-while-installing" rel="noopener noreferrer"&gt;Migrating Global Packages While Installing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#migrating-global-packages-between-installed-versions" rel="noopener noreferrer"&gt;Migrating Global Packages Between Installed Versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#offline-install" rel="noopener noreferrer"&gt;Offline Install&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#default-global-packages-from-file-while-installing" rel="noopener noreferrer"&gt;Default Global Packages From File While Installing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#iojs" rel="noopener noreferrer"&gt;io.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#system-version-of-node" rel="noopener noreferrer"&gt;System Version of Node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#listing-versions" rel="noopener noreferrer"&gt;Listing Versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#setting-custom-colors" rel="noopener noreferrer"&gt;Setting Custom Colors&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#persisting-custom-colors" rel="noopener noreferrer"&gt;Persisting custom colors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#suppressing-colorized-output" rel="noopener noreferrer"&gt;Suppressing colorized output&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#restoring-path" rel="noopener noreferrer"&gt;Restoring PATH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#set-default-node-version" rel="noopener noreferrer"&gt;Set default node version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#use-a-mirror-of-node-binaries" rel="noopener noreferrer"&gt;Use a mirror of node binaries&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#pass-authorization-header-to-mirror" rel="noopener noreferrer"&gt;Pass Authorization header to mirror&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#nvmrc" rel="noopener noreferrer"&gt;.nvmrc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#deeper-shell-integration" rel="noopener noreferrer"&gt;Deeper Shell Integration&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#calling-nvm-use-automatically-in-a-directory-with-a-nvmrc-file" rel="noopener noreferrer"&gt;Calling &lt;code&gt;nvm use&lt;/code&gt; automatically in a directory with a &lt;code&gt;.nvmrc&lt;/code&gt; file&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#bash" rel="noopener noreferrer"&gt;bash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#zsh" rel="noopener noreferrer"&gt;zsh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#fish" rel="noopener noreferrer"&gt;fish&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#running-tests" rel="noopener noreferrer"&gt;Running Tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#environment-variables" rel="noopener noreferrer"&gt;Environment variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#bash-completion" rel="noopener noreferrer"&gt;Bash Completion&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#usage-1" rel="noopener noreferrer"&gt;Usage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#compatibility-issues" rel="noopener noreferrer"&gt;Compatibility Issues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#installing-nvm-on-alpine-linux" rel="noopener noreferrer"&gt;Installing nvm on Alpine Linux&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#alpine-linux-313" rel="noopener noreferrer"&gt;Alpine Linux 3.13+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#alpine-linux-35---312" rel="noopener noreferrer"&gt;Alpine Linux 3.5 - 3.12&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvm-sh/nvm#uninstalling--removal" rel="noopener noreferrer"&gt;Uninstalling / Removal&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#manual-uninstall" rel="noopener noreferrer"&gt;Manual Uninstall&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#docker-for-development-environment" rel="noopener noreferrer"&gt;Docker For Development Environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#problems" rel="noopener noreferrer"&gt;Problems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nvm-sh/nvm#macos-troubleshooting" rel="noopener noreferrer"&gt;macOS Troubleshooting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Install and activate a current Node.js release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--lts&lt;/span&gt;
nvm use &lt;span class="nt"&gt;--lts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can validate the installation with the version command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# v22.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  What is the Agent Development Kit?
&lt;/h2&gt;

&lt;p&gt;The Agent Development Kit (ADK) is a flexible and modular framework for &lt;strong&gt;developing and deploying AI agents&lt;/strong&gt;. While optimized for Gemini and the Google ecosystem, ADK is &lt;strong&gt;model-agnostic&lt;/strong&gt;, &lt;strong&gt;deployment-agnostic&lt;/strong&gt;, and built for &lt;strong&gt;compatibility with other frameworks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Google provides full documentation on the ADK here:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://adk.dev/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadk.dev%2Fassets%2Fadk-social-card.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://adk.dev/" rel="noopener noreferrer" class="c-link"&gt;
            Agent Development Kit (ADK) - Agent Development Kit (ADK)Agent Development Kit (ADK)
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Build powerful multi-agent systems with Agent Development Kit (ADK)
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadk.dev%2Fassets%2Fagent-development-kit.png" width="512" height="512"&gt;
          adk.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Google provides the source to the complete TypeScript version of the ADK project:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/google" rel="noopener noreferrer"&gt;
        google
      &lt;/a&gt; / &lt;a href="https://github.com/google/adk-js" rel="noopener noreferrer"&gt;
        adk-js
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An open-source, code-first Typescript toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Agent Development Kit (ADK) for TypeScript&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/google/adk-js/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5b60841bea9e11d9d0b0950d690c9bc554e06385634056a7d5d62a15d1a4eabe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4170616368655f322e302d626c75652e737667" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/@google/adk" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/be7b027452761699cab7261192abee0b01df8cad09b1f145e745552235671d76/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f40676f6f676c652f61646b" alt="NPM Version"&gt;&lt;/a&gt;
&lt;a href="https://www.reddit.com/r/agentdevelopmentkit/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/804c84103b652f4f9ffcceb4914840adca00dfeec97b4fea152a38aaccc16225/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5265646469742d722532466167656e74646576656c6f706d656e746b69742d4646343530303f7374796c653d666c6174266c6f676f3d726564646974266c6f676f436f6c6f723d7768697465" alt="r/agentdevelopmentkit"&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;
      &lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/google/adk-python/main/assets/agent-development-kit.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoogle%2Fadk-python%2Fmain%2Fassets%2Fagent-development-kit.png" width="256"&gt;&lt;/a&gt;
    &lt;/h2&gt;
&lt;/div&gt;
    &lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;
      An open-source, code-first TypeScript toolkit for building, evaluating,
      and deploying sophisticated AI agents with flexibility and control.
    &lt;/h3&gt;

&lt;/div&gt;
    &lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;
      Important Links: &lt;a href="https://adk.dev" rel="nofollow noopener noreferrer"&gt;Docs&lt;/a&gt;, &lt;a href="https://github.com/google/adk-samples" rel="noopener noreferrer"&gt;Samples&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/google/adk-web" rel="noopener noreferrer"&gt;ADK Web&lt;/a&gt;.
    &lt;/h3&gt;

&lt;/div&gt;


&lt;p&gt;Agent Development Kit (ADK) is a flexible and modular framework for building,
deploying, and orchestrating AI agent workflows, from simple tasks to complex
multi-agent systems. Define agent behavior, orchestration, and tool use directly
in code, enabling robust debugging, versioning, and deployment anywhere.&lt;/p&gt;
&lt;p&gt;The TypeScript version of ADK is built for the Node.js and browser ecosystems,
with full type safety, Zod schema validation, and support for ESM, CommonJS, and
web runtimes.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Key Features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code-First TypeScript&lt;/strong&gt;: Define agent logic, tools, and orchestration with
full type safety. Tool parameters support Zod v3 and v4 schemas with
compile-time type inference.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Browser and Server&lt;/strong&gt;: Ships ESM, CommonJS, and web bundles. Run agents in
Node.js or directly in the browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rich&lt;/strong&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/google/adk-js" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The ADK is published to npm as &lt;a href="https://www.npmjs.com/package/@google/adk" rel="noopener noreferrer"&gt;&lt;code&gt;@google/adk&lt;/code&gt;&lt;/a&gt;, with the development tooling in &lt;code&gt;@google/adk-devtools&lt;/code&gt;. This tutorial uses &lt;strong&gt;ADK 1.4.0&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini API Key
&lt;/h2&gt;

&lt;p&gt;If not using Application Default Credentials (ADC), you will need a Gemini API key. You can get a Gemini key from Google AI Studio:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://aistudio.google.com/apikey" rel="noopener noreferrer"&gt;https://aistudio.google.com/apikey&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs8zjiymij0bvnqdh56gy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs8zjiymij0bvnqdh56gy.png" alt="Google AI Studio API Key interface" width="700" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking the Developer Environment
&lt;/h2&gt;

&lt;p&gt;Once Node.js is installed, clone the sample repo and run the &lt;code&gt;init.sh&lt;/code&gt; script. It installs the npm dependencies and creates a starter &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/adk-hello-world-typescript
&lt;span class="nb"&gt;cd &lt;/span&gt;adk-hello-world-typescript
&lt;span class="nb"&gt;source &lt;/span&gt;init.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created .env from .env.example. Add your credentials before running the agent.
Setup complete. Run: npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Edit &lt;code&gt;.env&lt;/code&gt; and choose one authentication method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini Developer API:&lt;/strong&gt; set &lt;code&gt;GOOGLE_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertex AI:&lt;/strong&gt; set &lt;code&gt;GOOGLE_GENAI_USE_VERTEXAI=TRUE&lt;/code&gt;, &lt;code&gt;GOOGLE_CLOUD_PROJECT&lt;/code&gt;, and &lt;code&gt;GOOGLE_CLOUD_LOCATION&lt;/code&gt;, then authenticate with ADC:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth login
gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never commit &lt;code&gt;.env&lt;/code&gt; — it is already listed in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Debugging API Permission Errors
&lt;/h2&gt;

&lt;p&gt;If your Application Default Credentials expire or your Google Cloud authentication expires, re-authenticate with:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth login
gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Another common issue is missing environment variables. The agent loads &lt;code&gt;.env&lt;/code&gt; automatically via &lt;code&gt;dotenv&lt;/code&gt;, and the &lt;code&gt;set_env.sh&lt;/code&gt; script is provided for shell commands that need the same values:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;set_env.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The TypeScript ADK Agent
&lt;/h2&gt;

&lt;p&gt;The entire agent lives in a single file — &lt;code&gt;src/agent.ts&lt;/code&gt;. It defines two local tools (weather and current time) and wires them into an &lt;code&gt;LlmAgent&lt;/code&gt; running on &lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tool parameters are declared with &lt;a href="https://zod.dev" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; schemas, so the ADK derives the function-calling declarations directly from the types:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;FunctionTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@google/adk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cityParameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The city to look up.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weatherTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FunctionTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get_weather&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Retrieves the current weather report for a specified city.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cityParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getWeather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LlmAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;weather_time_agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-2.5-flash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Answers questions about the time and weather in a city.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are a helpful assistant. Use the available tools to answer &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;questions about time and weather. Clearly explain when a city is &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsupported.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;weatherTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentTimeTool&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The tools return a discriminated union — a &lt;code&gt;success&lt;/code&gt; result with a report, or an &lt;code&gt;error&lt;/code&gt; result with a message — which gives the model a consistent shape to reason about:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToolResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;report&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Weather and local time are available for New York; other cities return a clear unsupported-city result.&lt;/p&gt;
&lt;h2&gt;
  
  
  Type-Checking and Unit Tests
&lt;/h2&gt;

&lt;p&gt;Unlike earlier Go and Python versions of this sample, the TypeScript project ships with a unit test suite built on the Node.js native test runner. A single command type-checks the project and runs the tests:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Example test output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; adk-hello-world-typescript@1.0.0 build
&amp;gt; tsc --noEmit

&amp;gt; adk-hello-world-typescript@1.0.0 test
&amp;gt; tsx --test test/**/*.test.ts

▶ weather and time agent
  ✔ exports the ADK root agent (0.43ms)
  ✔ returns the configured New York weather (0.16ms)
  ✔ rejects unsupported cities (0.45ms)
  ✔ formats New York time deterministically (12.27ms)
✔ weather and time agent (14.06ms)
ℹ tests 4
ℹ pass 4
ℹ fail 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because the tools are plain exported functions, they can be tested deterministically without calling the model at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running the ADK from the CLI
&lt;/h2&gt;

&lt;p&gt;The agent can be debugged locally from the terminal. Use &lt;code&gt;cli.sh&lt;/code&gt; or run the npm script directly:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start &lt;span class="c"&gt;# runs: adk run src/agent.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The ADK CLI can also be called directly:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx adk run src/agent.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sample interaction with the agent:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User -&amp;gt; what can you do?

Agent -&amp;gt; I can answer questions about the time and weather in a city using my
tools. Currently I support New York — for other cities I will let you know
that information is not available.

User -&amp;gt; what is the weather in New York?

Agent -&amp;gt; The weather in New York is sunny with a temperature of 25 degrees
Celsius (77 degrees Fahrenheit).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Interacting with the ADK Web UI
&lt;/h2&gt;

&lt;p&gt;The agent can be debugged from the web GUI running in the local development environment:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run web &lt;span class="c"&gt;# runs: adk web&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If developing on a remote VM or container and needing the UI reachable from outside, bind the server to all interfaces:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx adk web &lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The UI is the same development interface presented for Python, Java, and Go ADK agents — select &lt;code&gt;agent&lt;/code&gt; from the dropdown and chat with full tool-calling tracing.&lt;/p&gt;

&lt;p&gt;Expose the agent as a plain HTTP API without the UI:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx adk api_server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Deploying to Cloud Run with the ADK CLI
&lt;/h2&gt;

&lt;p&gt;For deployment options, check the official documentation:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://adk.dev/deploy/cloud-run/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadk.dev%2Fassets%2Fadk-social-card.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://adk.dev/deploy/cloud-run/" rel="noopener noreferrer" class="c-link"&gt;
            Cloud Run - Agent Development Kit (ADK)Agent Development Kit (ADK)
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Build powerful multi-agent systems with Agent Development Kit (ADK)
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fadk.dev%2Fassets%2Fagent-development-kit.png" width="512" height="512"&gt;
          adk.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The TypeScript ADK CLI has deployment built right in. The &lt;code&gt;cloudrun.sh&lt;/code&gt; script loads &lt;code&gt;.env&lt;/code&gt; and calls the deploy command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx adk deploy cloud_run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GOOGLE_CLOUD_LOCATION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with_ui&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  src/agent.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--with_ui true&lt;/code&gt; flag bundles the development UI into the deployed Cloud Run service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Google Cloud Console
&lt;/h2&gt;

&lt;p&gt;Once deployed, validate your Cloud Run service from the Google Cloud Console, or fetch the service URL from the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run services describe hello-world-agent-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-central1 &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'value(status.url)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The TypeScript Agent Development Kit (ADK) enables fast agent development using standard TypeScript and Node.js features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clean Tooling&lt;/strong&gt;: Define typed tools using Zod schemas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Testing&lt;/strong&gt;: Fast unit testing with Node's native test runner (&lt;code&gt;node:test&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Tracing&lt;/strong&gt;: Interactive CLI and Web UI (&lt;code&gt;adk web&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Cloud Deployment&lt;/strong&gt;: One-command Cloud Run deployment (&lt;code&gt;adk deploy cloud_run&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>typescript</category>
      <category>ai</category>
      <category>gemini</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building AI Agents with the Python Agent Development Kit (ADK) — 2026 Edition (v2)</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Sun, 26 Jul 2026 19:27:29 +0000</pubDate>
      <link>https://dev.to/gde/building-ai-agents-with-the-python-agent-development-kit-adk-2026-edition-v2-32hf</link>
      <guid>https://dev.to/gde/building-ai-agents-with-the-python-agent-development-kit-adk-2026-edition-v2-32hf</guid>
      <description>&lt;h2&gt;
  
  
  Summary of 2026 Updates (v2 — Python ADK 2.0 Migration)
&lt;/h2&gt;

&lt;p&gt;This updated edition incorporates several key architectural improvements, production hardening, and dependency upgrades over the initial release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python ADK 2.0 Core Upgrade&lt;/strong&gt;: Upgraded to &lt;strong&gt;&lt;code&gt;google-adk&amp;gt;=2.0.0&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;google.adk&lt;/code&gt;, GA released at &lt;a href="https://adk.dev/2.0/" rel="noopener noreferrer"&gt;adk.dev/2.0/&lt;/a&gt;), introducing &lt;code&gt;google.adk.Context&lt;/code&gt; for unified tool context management, session state isolation, and callback execution pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Cloud AI Platform Integration&lt;/strong&gt;: Native authentication auto-detection for Vertex AI (&lt;code&gt;GOOGLE_GENAI_USE_VERTEXAI=TRUE&lt;/code&gt;) and Google AI Studio Gemini API Key fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python Toolchain &amp;amp; Environment&lt;/strong&gt;: Modern Python runtime support (&lt;code&gt;3.10+&lt;/code&gt; up to &lt;code&gt;3.13&lt;/code&gt;) with structured dependency management via &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code &amp;amp; Declarative Agent Support&lt;/strong&gt;: Native support for Python code-defined agents (&lt;code&gt;agent.py&lt;/code&gt; exposing &lt;code&gt;root_agent&lt;/code&gt;) as well as YAML-configured agents (&lt;code&gt;root_agent.yaml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Execution Context&lt;/strong&gt;: Tools accept &lt;code&gt;tool_context: Optional[Context] = None&lt;/code&gt; to inspect session metadata, user identity, and execution state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified CLI Tooling&lt;/strong&gt;: Full compatibility with standard ADK 2.x CLI commands (&lt;code&gt;adk run&lt;/code&gt;, &lt;code&gt;adk web&lt;/code&gt;, &lt;code&gt;adk api_server&lt;/code&gt;, &lt;code&gt;adk deploy cloud_run&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web UI &amp;amp; Remote VM Server&lt;/strong&gt;: Built-in developer Web UI support for both local development (&lt;code&gt;adk web&lt;/code&gt;) and remote cloud instances (&lt;code&gt;adk web --host=0.0.0.0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI API Server Mode&lt;/strong&gt;: Server mode via &lt;code&gt;adk api_server&lt;/code&gt; for embedding agent endpoints into microservice architectures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Cloud Run Deployment&lt;/strong&gt;: Streamlined Cloud Run deployment via &lt;code&gt;adk deploy cloud_run&lt;/code&gt; featuring embedded Web UI options (&lt;code&gt;--with_ui&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Migration Changes from ADK 1.x to ADK 2.x (Python)
&lt;/h2&gt;

&lt;p&gt;If you are migrating an existing codebase from &lt;strong&gt;ADK 1.x&lt;/strong&gt; to &lt;strong&gt;ADK 2.x&lt;/strong&gt;, several key API shifts and architectural additions have been introduced:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Concept&lt;/th&gt;
&lt;th&gt;ADK 1.x (Legacy)&lt;/th&gt;
&lt;th&gt;ADK 2.x (2026 Edition)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Package / Namespace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;adk&lt;/code&gt; / legacy SDK modules&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;google-adk&amp;gt;=2.0.0&lt;/code&gt; (&lt;code&gt;google.adk&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tool Execution Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom kwargs or implicit globals&lt;/td&gt;
&lt;td&gt;Standard &lt;code&gt;tool_context: Optional[Context] = None&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent Paradigm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code-only Python classes&lt;/td&gt;
&lt;td&gt;Dual Code (&lt;code&gt;agent.py&lt;/code&gt;) + Declarative YAML (&lt;code&gt;root_agent.yaml&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workflows &amp;amp; Graphs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom script loops&lt;/td&gt;
&lt;td&gt;Native DAG execution via &lt;code&gt;google.adk.Workflow&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLI Tooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fragmented script execution&lt;/td&gt;
&lt;td&gt;Unified &lt;code&gt;adk&lt;/code&gt; CLI (&lt;code&gt;run&lt;/code&gt;, &lt;code&gt;web&lt;/code&gt;, &lt;code&gt;api_server&lt;/code&gt;, &lt;code&gt;deploy&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Session Persistence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Synchronous, in-memory&lt;/td&gt;
&lt;td&gt;Async persistence (&lt;code&gt;memory://&lt;/code&gt;, &lt;code&gt;sqlite://&lt;/code&gt;, &lt;code&gt;asyncpg&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retries &amp;amp; HITL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual exception catching&lt;/td&gt;
&lt;td&gt;Framework-managed &lt;code&gt;RetryConfig&lt;/code&gt; &amp;amp; HITL pause/resume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual client configuration&lt;/td&gt;
&lt;td&gt;Auto-detected Vertex AI (&lt;code&gt;GOOGLE_GENAI_USE_VERTEXAI&lt;/code&gt;) / Gemini API key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Detailed Breakdown of 1.x -&amp;gt; 2.x Changes:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Import Namespace Standardization
&lt;/h4&gt;

&lt;p&gt;In ADK 1.x, imports relied on legacy modules or top-level &lt;code&gt;adk&lt;/code&gt;. In ADK 2.x, all official components reside cleanly under the &lt;code&gt;google.adk&lt;/code&gt; namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ADK 1.x (Legacy)
# from adk.agents import Agent
&lt;/span&gt;
&lt;span class="c1"&gt;# ADK 2.x (Current)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Unified Tool Execution Context (&lt;code&gt;Context&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;In ADK 1.x, tools received only LLM-extracted function arguments, making session isolation or state modification awkward. In ADK 2.x, tools accept an optional &lt;code&gt;tool_context: Optional[Context] = None&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ADK 2.x Tool Signature
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="c1"&gt;# Access session state, execution metadata, or user identity
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Graph &amp;amp; Workflow Support (&lt;code&gt;Workflow&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;ADK 2.x introduces native &lt;code&gt;Workflow&lt;/code&gt; graph definitions alongside standard agents. Developers can construct directed acyclic graphs (DAGs) for multi-step agent pipelines, parallel execution paths, and state transformation nodes.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Declarative YAML Agent Specifications
&lt;/h4&gt;

&lt;p&gt;ADK 2.x adds zero-code declarative YAML definitions (&lt;code&gt;root_agent.yaml&lt;/code&gt;) for prompt engineers and system configurators, supported side-by-side with programmatic Python agents (&lt;code&gt;agent.py&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Unified &lt;code&gt;adk&lt;/code&gt; CLI Tooling
&lt;/h4&gt;

&lt;p&gt;ADK 1.x relied on custom scripts or module execution commands. ADK 2.x introduces a unified &lt;code&gt;adk&lt;/code&gt; binary supporting standardized operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;adk run &amp;lt;agent_path&amp;gt;&lt;/code&gt;: Terminal execution mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adk web&lt;/code&gt;: Local graphical chat UI on port 8000&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adk web --host=0.0.0.0&lt;/code&gt;: Cloud VM / Remote binding mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adk api_server &amp;lt;agent_path&amp;gt;&lt;/code&gt;: FastAPI microservice server mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adk deploy cloud_run&lt;/code&gt;: Production Cloud Run container deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6. Asynchronous Session Providers
&lt;/h4&gt;

&lt;p&gt;ADK 2.x features async session adapters supporting persistent backend drivers such as SQLite (&lt;code&gt;sqlite://&lt;/code&gt;), PostgreSQL (&lt;code&gt;asyncpg&lt;/code&gt;), and in-memory stores (&lt;code&gt;memory://&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  7. Framework-Managed Retries &amp;amp; Human-in-the-Loop (HITL)
&lt;/h4&gt;

&lt;p&gt;ADK 2.x provides automatic exception-aware retries via &lt;code&gt;RetryConfig&lt;/code&gt; and first-class pause/resume mechanisms for Human-in-the-Loop tool authorization flows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Native Python Agent Development with the Python ADK
&lt;/h2&gt;

&lt;p&gt;This tutorial provides a comprehensive guide to building, running, and deploying native AI Agents using Python and the official &lt;strong&gt;Google Agent Development Kit (ADK)&lt;/strong&gt; (&lt;code&gt;google-adk&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Python ADK 2.x?
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Python Agent Development Kit (ADK 2.0)&lt;/strong&gt; (&lt;code&gt;google-adk&amp;gt;=2.0.0&lt;/code&gt;) is a code-first framework created by Google for designing, testing, and deploying intelligent AI agents powered by Gemini models.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Features:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model-Agnostic &amp;amp; Cloud-Native&lt;/strong&gt;: Optimized for Gemini models on Google Cloud Vertex AI or Google AI Studio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Context &amp;amp; Tool State&lt;/strong&gt;: Uses &lt;code&gt;google.adk.Context&lt;/code&gt; to pass unified session state, credentials, and Human-in-the-Loop (HITL) context to tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph &amp;amp; Workflow Support&lt;/strong&gt;: Native support for DAG execution and &lt;code&gt;Workflow&lt;/code&gt; graphs in &lt;code&gt;google.adk&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Declarative &amp;amp; Code Paradigms&lt;/strong&gt;: Define agents programmatically in Python (&lt;code&gt;agent.py&lt;/code&gt;) or declaratively via YAML (&lt;code&gt;root_agent.yaml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Developer CLI &amp;amp; Web UI&lt;/strong&gt;: Interactive debugging, web chat interface, and API server out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official Repositories &amp;amp; Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ADK Documentation &amp;amp; GA Portal: &lt;a href="https://adk.dev/2.0/" rel="noopener noreferrer"&gt;https://adk.dev/2.0/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ADK Python 1.x to 2.x Migration Guide: &lt;a href="https://adk.dev/2.0/#adk-python-1x-compatibility" rel="noopener noreferrer"&gt;https://adk.dev/2.0/#adk-python-1x-compatibility&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ADK Python GitHub Repository: &lt;a href="https://github.com/google/adk-python" rel="noopener noreferrer"&gt;https://github.com/google/adk-python&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Dependencies &amp;amp; Directory Structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The starter stack relies on Python 3.10+ and the latest &lt;code&gt;google-adk&lt;/code&gt; release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Dev environment
pip
autopep8

# Application dependencies
flask
google-adk&amp;gt;=2.0.0
google-cloud-aiplatform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project Layout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adk-hello-world/
├── Makefile                                                    # Make targets for run, test, lint, web, deploy
├── README.md                                                   # Project documentation
├── GEMINI.md                                                   # Developer &amp;amp; agent instructions
├── requirements.txt                                            # Python dependencies
├── init.sh                                                     # Credentials initialization script
├── set_env.sh                                                  # Environment variables script
├── cli.sh / run.sh                                             # Interactive CLI runner scripts
├── Agent1_cli.sh                                               # CLI script for YAML Agent1
├── web.sh / webvm.sh                                           # Web UI launcher scripts
├── api_server.sh                                               # FastAPI server launch script
├── cloudrun.sh                                                 # Cloud Run deployment script
├── tests/
│   └── test_agent.py                                           # Unit tests for agent tools &amp;amp; timezone resolution
└── src/
    └── agents/
        ├── adk_hello_world/                                    # Python-defined agent
        │   ├── agent.py                                        # Agent definition &amp;amp; custom tools
        │   └── requirements.txt
        └── Agent1/                                             # Declarative YAML agent
            └── root_agent.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Checking the Developer Environment
&lt;/h2&gt;

&lt;p&gt;Clone the repository and set up environment credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/adk-hello-world
&lt;span class="nb"&gt;cd &lt;/span&gt;adk-hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Project &amp;amp; Credential Initialization
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;init.sh&lt;/code&gt; to configure your Google Cloud Project ID and Gemini API Key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./init.sh
&lt;span class="nt"&gt;---&lt;/span&gt; Setting Google Cloud Project ID File &lt;span class="nt"&gt;---&lt;/span&gt;
Please enter your Google Cloud project ID: my-gcp-project-id
You entered: my-gcp-project-id
Successfully saved project ID.
&lt;span class="nt"&gt;---&lt;/span&gt; Setting Google Cloud Gemini Key File &lt;span class="nt"&gt;---&lt;/span&gt;
Please enter your Google Cloud Gemini Key: AIzaSy...
Successfully saved Gemini Key.
&lt;span class="nt"&gt;---&lt;/span&gt; Setup &lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Loading Shell Environment Variables
&lt;/h3&gt;

&lt;p&gt;Source &lt;code&gt;set_env.sh&lt;/code&gt; to load active environment configurations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./set_env.sh
&lt;span class="nt"&gt;---&lt;/span&gt; Setting Google Cloud Environment Variables &lt;span class="nt"&gt;---&lt;/span&gt;
Checking gcloud authentication status...
gcloud is authenticated.
Are you using a Gemini API Key? &lt;span class="o"&gt;(&lt;/span&gt;y/N&lt;span class="o"&gt;)&lt;/span&gt;: n
Configuring &lt;span class="k"&gt;for &lt;/span&gt;Vertex AI...
Exported &lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-gcp-project-id
Exported &lt;span class="nv"&gt;PROJECT_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1056842563084
Exported &lt;span class="nv"&gt;SERVICE_ACCOUNT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1056842563084-compute@developer.gserviceaccount.com
Exported &lt;span class="nv"&gt;GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-gcp-project-id
Exported &lt;span class="nv"&gt;GOOGLE_GENAI_USE_VERTEXAI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TRUE
Exported &lt;span class="nv"&gt;GOOGLE_CLOUD_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-central1
Exported &lt;span class="nv"&gt;AGENT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/user/adk-hello-world/src/agents/adk_hello_world
&lt;span class="nt"&gt;---&lt;/span&gt; Environment setup &lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Building Python Agents with ADK 2.x
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Code-Defined Agent (&lt;code&gt;agent.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In ADK 2.x, Python function tools receive function arguments and an optional &lt;code&gt;tool_context: Optional[Context] = None&lt;/code&gt;. ADK automatically inspects docstrings and type annotations to generate the tool's OpenAPI schema for Gemini.&lt;/p&gt;

&lt;p&gt;Here is &lt;code&gt;src/agents/adk_hello_world/agent.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zoneinfo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ZoneInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ZoneInfoNotFoundError&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;CITY_TIMEZONE_MAP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# New York region &amp;amp; aliases
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new york&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/New_York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new york city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/New_York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nyc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/New_York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;washington dc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/New_York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boston&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/New_York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# US Central &amp;amp; Pacific
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chicago&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/Chicago&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;los angeles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/Los_Angeles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# Europe &amp;amp; Asia
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;london&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Europe/London&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paris&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Europe/Paris&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tokyo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;beijing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Asia/Shanghai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sydney&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Australia/Sydney&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Helper function to resolve a city name or alias to an IANA timezone string.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;city_clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;city_lower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;city_clean&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;city_lower&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CITY_TIMEZONE_MAP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CITY_TIMEZONE_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;city_lower&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nc"&gt;ZoneInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ZoneInfoNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="n"&gt;formatted_city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;city_clean&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Europe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Asia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Australia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Africa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pacific&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;formatted_city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nc"&gt;ZoneInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ZoneInfoNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Retrieves the current weather report for a specified city.

    Args:
        city (str): The name of the city for which to retrieve the weather report.
    Returns:
        A structured error explaining that live weather is not configured.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Live weather information for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is not available because &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;this demo has no weather data provider configured.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the current time in a specified city.

    Args:
        city (str): The name of the city for which to retrieve the current time.
    Returns:
        A structured time report or error message.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tz_identifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;resolve_timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tz_identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sorry, I don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t have timezone information for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;tz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ZoneInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tz_identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The current time in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d %H&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;report&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;root_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather_time_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-2.5-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent to answer questions about the time and weather in a city.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a helpful agent who can answer questions about the current &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;time in supported cities. The weather tool does not have a live data &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;provider; clearly tell users when weather data is unavailable.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_current_time&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Declarative YAML Agent (&lt;code&gt;root_agent.yaml&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;ADK 2.x also supports declarative agent definitions in YAML, such as &lt;code&gt;src/agents/Agent1/root_agent.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Agent1&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gemini-2.5-flash&lt;/span&gt;
&lt;span class="na"&gt;agent_class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LlmAgent&lt;/span&gt;
&lt;span class="na"&gt;instruction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;You are the root agent that coordinates other agents.&lt;/span&gt;
&lt;span class="na"&gt;sub_agents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google_search&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Running the ADK Agent Locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option A: Interactive Terminal CLI (&lt;code&gt;cli.sh&lt;/code&gt; / &lt;code&gt;run.sh&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Launch an interactive terminal session with your code-defined agent using &lt;code&gt;cli.sh&lt;/code&gt; or &lt;code&gt;make run&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./cli.sh
/home/user/adk-hello-world/src/agents/adk_hello_world
adk run &lt;span class="nb"&gt;.&lt;/span&gt;

User -&amp;gt; What &lt;span class="nb"&gt;time &lt;/span&gt;is it &lt;span class="k"&gt;in &lt;/span&gt;Tokyo?

Agent -&amp;gt; The current &lt;span class="nb"&gt;time &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;Tokyo is 2026-07-27 04:12:48 JST+0900.

User -&amp;gt; What is the weather &lt;span class="k"&gt;in &lt;/span&gt;New York?

Agent -&amp;gt; Live weather information &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'New York'&lt;/span&gt; is not available because this demo has no weather data provider configured.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the YAML-configured agent &lt;code&gt;Agent1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./Agent1_cli.sh
/home/user/adk-hello-world/src/agents/Agent1
adk run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Option B: Local Web UI (&lt;code&gt;web.sh&lt;/code&gt; / &lt;code&gt;webvm.sh&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Launch the built-in visual web interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./web.sh
/home/user/adk-hello-world/src/agents
adk web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8000&lt;/code&gt; in your browser to access the graphical chat UI, inspect tool call payloads, and view real-time agent execution traces.&lt;/p&gt;

&lt;p&gt;For remote Cloud VMs or headless setups, use &lt;code&gt;webvm.sh&lt;/code&gt; to bind to all network interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./webvm.sh
Running ADK from Cloud VM
/home/user/adk-hello-world/src/agents
adk web &lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Option C: FastAPI API Server (&lt;code&gt;api_server.sh&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Start an API server endpoint to expose your agent over HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./api_server.sh
setting API Server Mode
/home/user/adk-hello-world/src/agents/adk_hello_world
adk api_server &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Google Cloud Run Deployment (&lt;code&gt;cloudrun.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Deploy your Python ADK agent directly to Google Cloud Run in a single command using &lt;code&gt;cloudrun.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./cloudrun.sh
adk deploy cloud_run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-gcp-project-id &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-central1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello-world-agent-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--app_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello-world-agent-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with_ui&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  /home/user/adk-hello-world/src/agents/adk_hello_world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI builds the container image and provisions a fully-managed serverless endpoint on Cloud Run equipped with the web UI enabled (&lt;code&gt;--with_ui&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary &amp;amp; Next Steps
&lt;/h2&gt;

&lt;p&gt;In this updated 2026 edition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Migrated agent definitions to &lt;strong&gt;Python ADK 2.x (&lt;code&gt;google-adk&amp;gt;=2.0.0&lt;/code&gt;)&lt;/strong&gt; running on &lt;strong&gt;Python 3.10+&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Updated custom tools to leverage &lt;strong&gt;&lt;code&gt;google.adk.Context&lt;/code&gt;&lt;/strong&gt; for unified session state and execution pipelines.&lt;/li&gt;
&lt;li&gt;Highlighted key 1.x to 2.x migration changes, including import namespaces, async session stores, &lt;code&gt;Workflow&lt;/code&gt; DAG execution, declarative YAML definitions, and &lt;code&gt;RetryConfig&lt;/code&gt; / HITL tools.&lt;/li&gt;
&lt;li&gt;Demonstrated both &lt;strong&gt;code-defined agents (&lt;code&gt;agent.py&lt;/code&gt;)&lt;/strong&gt; and &lt;strong&gt;declarative YAML agents (&lt;code&gt;root_agent.yaml&lt;/code&gt;)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Validated local execution across &lt;strong&gt;terminal CLI (&lt;code&gt;adk run&lt;/code&gt;)&lt;/strong&gt;, &lt;strong&gt;Web UI (&lt;code&gt;adk web&lt;/code&gt;)&lt;/strong&gt;, and &lt;strong&gt;FastAPI server (&lt;code&gt;adk api_server&lt;/code&gt;)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Streamlined serverless deployment to &lt;strong&gt;Google Cloud Run&lt;/strong&gt; with &lt;code&gt;adk deploy cloud_run&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To explore more advanced capabilities such as multi-agent orchestration, custom callback middleware, and async persistence adapters, visit the official documentation at &lt;a href="https://adk.dev/2.0/" rel="noopener noreferrer"&gt;adk.dev/2.0/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>googlecloud</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Building AI Agents with the GO Agent Development Kit (ADK) — 2026 Edition (v2)</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Sun, 26 Jul 2026 17:42:32 +0000</pubDate>
      <link>https://dev.to/gde/building-ai-agents-with-the-go-agent-development-kit-adk-2026-edition-v2-4n55</link>
      <guid>https://dev.to/gde/building-ai-agents-with-the-go-agent-development-kit-adk-2026-edition-v2-4n55</guid>
      <description>&lt;h2&gt;
  
  
  Summary of 2026 Updates (v2 — Go ADK 2.0 Migration)
&lt;/h2&gt;

&lt;p&gt;This updated edition incorporates several key architectural improvements, production hardening, and dependency upgrades over the initial release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go ADK 2.0 Core Upgrade&lt;/strong&gt;: Upgraded to &lt;strong&gt;&lt;code&gt;v2.1.0&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;google.golang.org/adk/v2&lt;/code&gt;, GA released June 30, 2026 at &lt;a href="https://adk.dev/2.0/" rel="noopener noreferrer"&gt;adk.dev/2.0/&lt;/a&gt;), introducing &lt;code&gt;agent.Context&lt;/code&gt; for unified tool context management and callback execution pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google GenAI SDK Upgrade&lt;/strong&gt;: Upgraded to &lt;strong&gt;&lt;code&gt;v1.65.0&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;google.golang.org/genai&lt;/code&gt;) with native Vertex AI authentication auto-detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go Toolchain &amp;amp; Module Directives&lt;/strong&gt;: Updated runtime environment to &lt;strong&gt;Go &lt;code&gt;1.26.3&lt;/code&gt;&lt;/strong&gt; with &lt;code&gt;go 1.26.5&lt;/code&gt; module directives across all sub-modules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A (Agent-to-Agent) Multi-Agent System&lt;/strong&gt;: Integrated &lt;code&gt;github.com/a2aproject/a2a-go&lt;/code&gt; (&lt;code&gt;v0.3.15&lt;/code&gt;) with sub-modules (&lt;code&gt;a2a-client-go&lt;/code&gt;, &lt;code&gt;a2a-server-go&lt;/code&gt;, &lt;code&gt;a2a-master-go&lt;/code&gt;, &lt;code&gt;a2a-gemini3-go&lt;/code&gt;) for multi-agent delegation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Logging (&lt;code&gt;slog&lt;/code&gt;) Standardization&lt;/strong&gt;: Standardized logging on &lt;code&gt;slog&lt;/code&gt; with &lt;code&gt;JSONHandler&lt;/code&gt; across all entrypoints for native Google Cloud Logging compatibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful OS Signal Contexts&lt;/strong&gt;: Integrated &lt;code&gt;signal.NotifyContext(context.Background(), os.Interrupt)&lt;/code&gt; across all sub-module entrypoints to handle container termination cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust Tool Input Validation&lt;/strong&gt;: Added input bounds checking for &lt;code&gt;rollDieTool&lt;/code&gt; (&lt;code&gt;sides &amp;lt;= 0&lt;/code&gt;) to prevent runtime panics from malformed LLM tool calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe Dockerfile &lt;code&gt;CMD&lt;/code&gt; Generation&lt;/strong&gt;: Fixed &lt;code&gt;cloudrun.go&lt;/code&gt; to construct container arguments with &lt;code&gt;json.Marshal&lt;/code&gt;, ensuring valid &lt;code&gt;CMD&lt;/code&gt; syntax regardless of feature flags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Multi-Module Makefile&lt;/strong&gt;: Updated root &lt;code&gt;Makefile&lt;/code&gt; to format (&lt;code&gt;go fmt&lt;/code&gt;), lint (&lt;code&gt;go vet&lt;/code&gt;), test (&lt;code&gt;go test&lt;/code&gt;), build, and clean across all 5 submodules (&lt;code&gt;hello-agent&lt;/code&gt;, &lt;code&gt;a2a-client-go&lt;/code&gt;, &lt;code&gt;a2a-gemini3-go&lt;/code&gt;, &lt;code&gt;a2a-master-go&lt;/code&gt;, &lt;code&gt;a2a-server-go&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verified Build &amp;amp; Test Suite&lt;/strong&gt;: All unit tests (&lt;code&gt;go test -v ./...&lt;/code&gt;), formatting (&lt;code&gt;go fmt&lt;/code&gt;), linting (&lt;code&gt;go vet ./...&lt;/code&gt;), and &lt;code&gt;make build&lt;/code&gt; pass with 0 errors.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Native Go Agent Development with the Go ADK
&lt;/h2&gt;

&lt;p&gt;This tutorial provides a comprehensive guide to building, running, and deploying native AI Agents using the Go programming language and the official &lt;strong&gt;Go Agent Development Kit (ADK)&lt;/strong&gt; (&lt;code&gt;google.golang.org/adk&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Go?
&lt;/h3&gt;

&lt;p&gt;Go (Golang) is an open-source programming language created at Google. Renowned for its simplicity, concurrency support via goroutines, and fast execution speed, Go is an exceptional choice for building high-throughput microservices and distributed agent systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Official Go Project: &lt;a href="https://go.dev" rel="noopener noreferrer"&gt;go.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google Open Source: &lt;a href="https://opensource.google/projects/go" rel="noopener noreferrer"&gt;opensource.google/projects/go&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Prerequisites &amp;amp; Go Version Upgrades
&lt;/h3&gt;

&lt;p&gt;This project has been updated to run on &lt;strong&gt;Go 1.26&lt;/strong&gt; (specifically &lt;strong&gt;&lt;code&gt;go1.26.3 linux/amd64&lt;/code&gt;&lt;/strong&gt;) with &lt;code&gt;go 1.25.0&lt;/code&gt; module directives across all sub-modules.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Installing or Upgrading Go with GVM (Go Version Manager)
&lt;/h4&gt;

&lt;p&gt;If you are using &lt;strong&gt;gvm&lt;/strong&gt; to manage Go installations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dependencies for Linux / Debian / Ubuntu&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; bison curl git mercurial make binutils gcc build-essential

&lt;span class="c"&gt;# Install GVM (if not already installed)&lt;/span&gt;
bash &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Install and switch to Go 1.26.3&lt;/span&gt;
gvm &lt;span class="nb"&gt;install &lt;/span&gt;go1.26.3 &lt;span class="nt"&gt;-B&lt;/span&gt;
gvm use &lt;span class="nt"&gt;--default&lt;/span&gt; go1.26.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Validating Installed Go Version
&lt;/h4&gt;

&lt;p&gt;Verify that your active Go binary is &lt;strong&gt;Go 1.26.3&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go version
go version go1.26.3 linux/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Module Version Directives (&lt;code&gt;go 1.26.5&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;All &lt;code&gt;go.mod&lt;/code&gt; files in this workspace use &lt;code&gt;go 1.26.5&lt;/code&gt; directives to support modern language features and telemetry packages required by Go ADK 2.0 (&lt;code&gt;google.golang.org/adk/v2&lt;/code&gt; &lt;code&gt;v2.1.0&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  What is the Go Agent Development Kit (ADK 2.0)?
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Go Agent Development Kit (ADK 2.0)&lt;/strong&gt; (&lt;code&gt;google.golang.org/adk/v2&lt;/code&gt;) is a code-first toolkit developed by Google for building, evaluating, and deploying sophisticated AI agents.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Features:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model-Agnostic &amp;amp; Deployment-Agnostic&lt;/strong&gt;: Optimized for Gemini models on Google Cloud Vertex AI or Google AI Studio, but designed for cross-framework compatibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Context &amp;amp; Tool Handling&lt;/strong&gt;: Uses &lt;code&gt;agent.Context&lt;/code&gt; to pass unified invocation state, session management, and Human-in-the-Loop (HITL) tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2A (Agent-to-Agent) Protocol Support&lt;/strong&gt;: Native integration with &lt;code&gt;github.com/a2aproject/a2a-go&lt;/code&gt; (v0.3.15+) for multi-agent collaboration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in CLI &amp;amp; Web UI Tools&lt;/strong&gt;: Local interactive debugging out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official Repositories &amp;amp; Docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation &amp;amp; GA Portal: &lt;a href="https://adk.dev/2.0/" rel="noopener noreferrer"&gt;https://adk.dev/2.0/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go ADK 1.x Compatibility &amp;amp; Migration Guide: &lt;a href="https://adk.dev/2.0/#adk-go-1x-compatibility" rel="noopener noreferrer"&gt;https://adk.dev/2.0/#adk-go-1x-compatibility&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go ADK GitHub Repository: &lt;a href="https://github.com/google/adk-go" rel="noopener noreferrer"&gt;https://github.com/google/adk-go&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google Gen AI Go SDK: &lt;a href="https://github.com/googleapis/go-genai" rel="noopener noreferrer"&gt;github.com/googleapis/go-genai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Key Dependencies (&lt;code&gt;go.mod&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The updated agent stack relies on the latest releases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module hello-agent

go 1.26.5

require (
    google.golang.org/adk/v2 v2.1.0
    google.golang.org/genai v1.65.0
    github.com/a2aproject/a2a-go/v2 v2.3.1
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Checking the Developer Environment
&lt;/h3&gt;

&lt;p&gt;Clone the repository and set up environment credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/xbill9/adk-hello-world-go
&lt;span class="nb"&gt;cd &lt;/span&gt;adk-hello-world-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1. Authentication &amp;amp; Environment Setup
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;init.sh&lt;/code&gt; to initialize settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;init.sh
&lt;span class="nt"&gt;---&lt;/span&gt; Authentication Method &lt;span class="nt"&gt;---&lt;/span&gt;
Do you want to use a Gemini API Key &lt;span class="k"&gt;for &lt;/span&gt;authentication? &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt;: n
&lt;span class="nt"&gt;---&lt;/span&gt; Setup &lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then load project-wide environment variables using &lt;code&gt;set_env.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;set_env.sh
&lt;span class="nt"&gt;---&lt;/span&gt; Setting Google Cloud Environment Variables &lt;span class="nt"&gt;---&lt;/span&gt;
Checking gcloud authentication status...
gcloud is authenticated.
Exported &lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;comglitn
Exported &lt;span class="nv"&gt;PROJECT_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1056842563084
Exported &lt;span class="nv"&gt;GOOGLE_CLOUD_PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;comglitn
Exported &lt;span class="nv"&gt;GOOGLE_GENAI_USE_VERTEXAI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TRUE
Exported &lt;span class="nv"&gt;GOOGLE_CLOUD_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-central1
&lt;span class="nt"&gt;---&lt;/span&gt; Environment setup &lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Google Cloud authentication expires, refresh credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth login
gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Building, Formatting, and Testing All Agent Submodules
&lt;/h3&gt;

&lt;p&gt;The project includes unified Makefile targets for linting, formatting, compiling, and testing all 5 agent submodules (&lt;code&gt;hello-agent&lt;/code&gt;, &lt;code&gt;a2a-client-go&lt;/code&gt;, &lt;code&gt;a2a-gemini3-go&lt;/code&gt;, &lt;code&gt;a2a-master-go&lt;/code&gt;, &lt;code&gt;a2a-server-go&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Format code across all submodules&lt;/span&gt;
make format

&lt;span class="c"&gt;# Lint code with go vet across all submodules&lt;/span&gt;
make lint

&lt;span class="c"&gt;# Run unit tests across all submodules&lt;/span&gt;
make &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Build executables across all submodules&lt;/span&gt;
make build

&lt;span class="c"&gt;# Clean build artifacts&lt;/span&gt;
make clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Production Hardening &amp;amp; Best Practices
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Structured Logging with &lt;code&gt;slog&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;All entrypoints use &lt;code&gt;slog&lt;/code&gt; configured with &lt;code&gt;JSONHandler&lt;/code&gt; to emit logs compatible with Google Cloud Logging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Graceful Shutdown Signals
&lt;/h4&gt;

&lt;p&gt;Service entrypoints use &lt;code&gt;signal.NotifyContext&lt;/code&gt; to handle &lt;code&gt;SIGINT&lt;/code&gt; and &lt;code&gt;SIGTERM&lt;/code&gt; signals gracefully during Cloud Run container lifecycle events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotifyContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Defensive Tool Parameter Validation
&lt;/h4&gt;

&lt;p&gt;Custom agent tools validate input bounds to prevent runtime panics when executing tool calls generated by LLMs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;rollDieTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="n"&gt;rollDieToolArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sides&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"number of sides must be greater than 0, got %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sides&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Intn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sides&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Running the ADK Agent Locally
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Option A: Terminal CLI Interface
&lt;/h4&gt;

&lt;p&gt;Run the agent interactively in your terminal using &lt;code&gt;cli.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;cli.sh
go run agent.go

User -&amp;gt; what can you &lt;span class="k"&gt;do&lt;/span&gt;?

Agent -&amp;gt; I can &lt;span class="nb"&gt;help &lt;/span&gt;you with a variety of tasks! I can answer questions, search information on Google, and tell you current &lt;span class="nb"&gt;time &lt;/span&gt;or weather &lt;span class="k"&gt;in &lt;/span&gt;any city.

User -&amp;gt; what is the weather &lt;span class="k"&gt;in &lt;/span&gt;NYC?

Agent -&amp;gt; The current weather &lt;span class="k"&gt;in &lt;/span&gt;New York City is sunny with a temperature of 65°F &lt;span class="o"&gt;(&lt;/span&gt;18°C&lt;span class="o"&gt;)&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Option B: Local Web UI
&lt;/h4&gt;

&lt;p&gt;Launch the embedded Web UI with &lt;code&gt;web.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;web.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;http://localhost:8081&lt;/code&gt; in your browser to interact with the visual chat interface.&lt;/p&gt;




&lt;h3&gt;
  
  
  Agent-to-Agent (A2A) Multi-Agent Architecture
&lt;/h3&gt;

&lt;p&gt;The 2026 update introduces full Agent-to-Agent communication modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hello-agent&lt;/code&gt;: Core single-agent implementation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a2a-server-go&lt;/code&gt;: Exposes agent capabilities over A2A HTTP endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a2a-client-go&lt;/code&gt;: Client library for dispatching requests to remote A2A agents.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a2a-master-go&lt;/code&gt; &amp;amp; &lt;code&gt;a2a-gemini3-go&lt;/code&gt;: Orchestrators for complex multi-agent workflows using Gemini models.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Cloud Run Deployment Strategies
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Strategy 1: Quick Deployment via Docker &amp;amp; Cloud Build (&lt;code&gt;quickrun.sh&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;Deploy to Google Cloud Run using the automated build script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;quickrun.sh
Building using Dockerfile and deploying container to Cloud Run service &lt;span class="o"&gt;[&lt;/span&gt;adk-hello-world-go]...
✓ Building Container...
✓ Creating Revision...
✓ Routing traffic...
Service URL: https://adk-hello-world-go-1056842563084.us-central1.run.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Strategy 2: Official &lt;code&gt;adkgo&lt;/code&gt; CLI Deployment
&lt;/h4&gt;

&lt;p&gt;Build the &lt;code&gt;adkgo&lt;/code&gt; CLI tool from the &lt;code&gt;adk-go&lt;/code&gt; upstream repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~
git clone https://github.com/google/adk-go
&lt;span class="nb"&gt;cd &lt;/span&gt;adk-go
go build ./cmd/adkgo

&lt;span class="c"&gt;# Deploy using adkgo&lt;/span&gt;
./adkgo deploy cloudrun &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$GOOGLE_CLOUD_PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$GOOGLE_CLOUD_LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; adk-hello-world-go &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; ./hello-agent/agent.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Strategy 3: Local Proxy Access to Cloud Run (&lt;code&gt;proxy.sh&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;Connect to your deployed Cloud Run service locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;proxy.sh
PROXY URL http://127.0.0.1:8081/ui/?app&lt;span class="o"&gt;=&lt;/span&gt;hello_time_agent
Proxying to Cloud Run service &lt;span class="o"&gt;[&lt;/span&gt;adk-hello-world-go] &lt;span class="k"&gt;in &lt;/span&gt;region &lt;span class="o"&gt;[&lt;/span&gt;us-central1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Summary &amp;amp; Next Steps
&lt;/h3&gt;

&lt;p&gt;In this updated version:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All submodules were migrated to &lt;strong&gt;Go ADK 2.0 (&lt;code&gt;google.golang.org/adk/v2&lt;/code&gt; v2.1.0)&lt;/strong&gt; and &lt;strong&gt;Google GenAI &lt;code&gt;v1.65.0&lt;/code&gt;&lt;/strong&gt; running on &lt;strong&gt;Go 1.26&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Custom function tools were updated to accept &lt;strong&gt;&lt;code&gt;agent.Context&lt;/code&gt;&lt;/strong&gt; for unified session state, execution flow, and tool confirmation.&lt;/li&gt;
&lt;li&gt;Full support for &lt;strong&gt;A2A multi-agent workflows&lt;/strong&gt; was integrated.&lt;/li&gt;
&lt;li&gt;Production hardening for &lt;strong&gt;structured logging&lt;/strong&gt;, &lt;strong&gt;signal handling&lt;/strong&gt;, &lt;strong&gt;input validation&lt;/strong&gt;, and &lt;strong&gt;JSON-marshaled Dockerfile generation&lt;/strong&gt; was applied.&lt;/li&gt;
&lt;li&gt;Local CLI, Web UI, and Cloud Run serverless deployment channels were verified and tested cleanly across all submodules.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>go</category>
      <category>adk</category>
      <category>a2a</category>
      <category>antigravity</category>
    </item>
    <item>
      <title>Self-Hosted Gemma 4 on TPU v6e: Deployment &amp; SRE with Antigravity</title>
      <dc:creator>xbill</dc:creator>
      <pubDate>Sat, 25 Jul 2026 14:23:48 +0000</pubDate>
      <link>https://dev.to/gde/self-hosted-gemma-4-on-tpu-v6e-deployment-sre-with-antigravity-2g71</link>
      <guid>https://dev.to/gde/self-hosted-gemma-4-on-tpu-v6e-deployment-sre-with-antigravity-2g71</guid>
      <description>&lt;p&gt;&lt;em&gt;Measured 2026-07-21 on &lt;code&gt;vllm/vllm-tpu:nightly&lt;/code&gt; (vLLM 0.23.1rc1.dev1076), a GCE flex-start &lt;code&gt;ct6e-standard-1t&lt;/code&gt; (one TPU v6e chip, 32 GB HBM) in europe-west4-a, managed by the **Antigravity TPU DevOps agent&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The plain &lt;code&gt;google/gemma-4-E2B-it&lt;/code&gt; serves beautifully on one v6e chip; the Antigravity TPU skill automates the entire provisioning and SRE lifecycle.&lt;/strong&gt; The 2-billion-parameter "efficient" Gemma 4 sustains 213 tok/s for a single user, scales to ~2,200 output tok/s, and handles complex tool-calling flawlessly.&lt;/p&gt;

&lt;p&gt;This post deep-dives into the serving performance and failure modes of the QAT checkpoints, and introduces the &lt;strong&gt;tpu-devops&lt;/strong&gt; MCP server—a specialized Antigravity skill that handles zone-sweeping for capacity, vLLM orchestration, and even uses the deployed model to analyze its own system logs.&lt;/p&gt;

&lt;p&gt;One capability coupling to know about: with a reasoning parser configured, &lt;strong&gt;schema enforcement only engages when thinking is enabled&lt;/strong&gt; — thinking-off requests sail through unconstrained with a 200 status. Details on this and the agent-driven deployment below.&lt;/p&gt;

&lt;p&gt;The host is a GCE &lt;strong&gt;flex-start&lt;/strong&gt; VM — capacity granted on request, billed until deleted, hard-stopped at a 4-hour max run. Manually, this requires a massive &lt;code&gt;gcloud compute instances create&lt;/code&gt; command with specific startup scripts and IAM scopes.&lt;/p&gt;

&lt;p&gt;With the &lt;strong&gt;Antigravity TPU skill&lt;/strong&gt;, this is a single natural-language request: &lt;em&gt;"Find a v6e-1 in europe-west4 and serve Gemma 4 E2B."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The agent's &lt;code&gt;find_tpu_vm&lt;/code&gt; or &lt;code&gt;create_tpu_vm_instance&lt;/code&gt; tools handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quota Sweeping:&lt;/strong&gt; Attempting multiple zones until capacity is granted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Setup:&lt;/strong&gt; Attaching a 200 GB boot disk (the default 10 GB is too small for vLLM images) and configuring &lt;code&gt;cloud-platform&lt;/code&gt; scopes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Management:&lt;/strong&gt; Securely fetching the &lt;code&gt;hf-token&lt;/code&gt; from Secret Manager without embedding it in metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Orchestration:&lt;/strong&gt; Pulling &lt;code&gt;vllm/vllm-tpu:nightly&lt;/code&gt; and applying the specialized serving flags: &lt;code&gt;--max-model-len 65536 --gpu-memory-utilization 0.9 --max_num_batched_tokens 4096 --enable-auto-tool-choice --tool-call-parser gemma4 --reasoning-parser gemma4&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boot timeline: VM RUNNING at t+0 → Docker installed ~t+1:00 → image pulled ~t+6:00 → weights downloaded, XLA compiled, health green ~t+8:30. The agent's &lt;code&gt;wait_for_vllm_ready&lt;/code&gt; tool polls the endpoint until the first 200 OK is received.&lt;/p&gt;

&lt;p&gt;Two environment quirks the agent helps navigate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAP Tunneling:&lt;/strong&gt; Direct SSH often fails on corporate networks. The agent uses &lt;code&gt;gcloud compute ssh --tunnel-through-iap&lt;/code&gt; for its diagnostic tools (&lt;code&gt;get_vllm_docker_logs&lt;/code&gt;, &lt;code&gt;manage_vllm_docker&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KV Cache Optimization:&lt;/strong&gt; vLLM auto-selects an &lt;strong&gt;fp8_e5m2 KV cache&lt;/strong&gt; on v6e — the largest memory consumer is 8-bit before any weight quantization enters the picture.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Three ways the QAT checkpoints fail to load
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Checkpoint / path&lt;/th&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-qat-w4a16-ct&lt;/code&gt; · JAX&lt;/td&gt;
&lt;td&gt;int4 compressed-tensors scheme unimplemented for E2B's &lt;code&gt;per_layer_model_projection&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✕ no load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-qat-q4_0-unquantized&lt;/code&gt; · JAX&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;k_norm.weight&lt;/code&gt; "missing" for layers 15–34&lt;/td&gt;
&lt;td&gt;✕ no load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-qat-q4_0-unquantized&lt;/code&gt; · torchax&lt;/td&gt;
&lt;td&gt;identical missing-weights error via &lt;code&gt;MODEL_IMPL_TYPE=vllm&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✕ no load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gemma-4-E2B-it&lt;/code&gt; (plain) · JAX&lt;/td&gt;
&lt;td&gt;loads and serves&lt;/td&gt;
&lt;td&gt;✓ serves&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The forensics point at the loader, not the checkpoint.&lt;/strong&gt; Reading the safetensors headers of both repos: the plain export ships &lt;code&gt;self_attn.k_norm&lt;/code&gt; for all 35 layers; the QAT export ships it only for the 15 non-KV-shared layers. Both configs are identical — including &lt;code&gt;num_kv_shared_layers: 20&lt;/code&gt;. Layers 15–34 reuse K/V from lower layers and have no k-norm of their own, so the QAT export is the architecturally honest one; the plain checkpoint only loads because it carries those (unused) tensors anyway. Proposed fix in &lt;a href="https://github.com/vllm-project/tpu-inference/issues/3225" rel="noopener noreferrer"&gt;#3225&lt;/a&gt;: skip instantiating K/V-side parameters for KV-shared layers.&lt;/p&gt;

&lt;p&gt;Until it lands: &lt;strong&gt;serve the plain checkpoint.&lt;/strong&gt; At 2B parameters (~5 GB bf16 against 32 GB HBM), 4-bit weights buy little here anyway — memory pressure lives in the KV cache, which is already fp8.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. What one chip is worth: the concurrency sweep
&lt;/h2&gt;

&lt;p&gt;Same workload at every level — 1,024-token prompts, 128-token completions, &lt;code&gt;vllm bench serve&lt;/code&gt;, random dataset.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concurrency&lt;/th&gt;
&lt;th&gt;Req/s&lt;/th&gt;
&lt;th&gt;Output tok/s&lt;/th&gt;
&lt;th&gt;Total tok/s&lt;/th&gt;
&lt;th&gt;TTFT med&lt;/th&gt;
&lt;th&gt;TTFT p99&lt;/th&gt;
&lt;th&gt;TPOT med&lt;/th&gt;
&lt;th&gt;Per-stream tok/s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1.64&lt;/td&gt;
&lt;td&gt;209&lt;/td&gt;
&lt;td&gt;1,884&lt;/td&gt;
&lt;td&gt;16 ms&lt;/td&gt;
&lt;td&gt;17 ms&lt;/td&gt;
&lt;td&gt;4.7 ms&lt;/td&gt;
&lt;td&gt;213&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;9.44&lt;/td&gt;
&lt;td&gt;1,209&lt;/td&gt;
&lt;td&gt;10,878&lt;/td&gt;
&lt;td&gt;27 ms&lt;/td&gt;
&lt;td&gt;99 ms&lt;/td&gt;
&lt;td&gt;6.2 ms&lt;/td&gt;
&lt;td&gt;161&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;12.78&lt;/td&gt;
&lt;td&gt;1,636&lt;/td&gt;
&lt;td&gt;14,721&lt;/td&gt;
&lt;td&gt;155 ms&lt;/td&gt;
&lt;td&gt;189 ms&lt;/td&gt;
&lt;td&gt;17.5 ms&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;16.72&lt;/td&gt;
&lt;td&gt;2,140&lt;/td&gt;
&lt;td&gt;19,262&lt;/td&gt;
&lt;td&gt;122 ms&lt;/td&gt;
&lt;td&gt;349 ms&lt;/td&gt;
&lt;td&gt;25.3 ms&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 (burst)&lt;/td&gt;
&lt;td&gt;17.31&lt;/td&gt;
&lt;td&gt;2,215&lt;/td&gt;
&lt;td&gt;19,938&lt;/td&gt;
&lt;td&gt;833 ms&lt;/td&gt;
&lt;td&gt;1,573 ms&lt;/td&gt;
&lt;td&gt;36.8 ms&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Reading the curve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prefill is effectively free at low load.&lt;/strong&gt; A 1,024-token prompt reaches first token in 16 ms — roughly 64K prefill tok/s for a single stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;c=8 is nearly free concurrency:&lt;/strong&gt; six times the throughput of a single stream for +1.5 ms/token; each of 8 users still sees ~160 tok/s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The knee is between 32 and 64.&lt;/strong&gt; Going 32→64 buys +31% throughput for +45% per-token latency; 64→burst buys +3.5% for another +45%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacity-planning number:&lt;/strong&gt; run at ≤64 concurrent streams for smooth per-user experience; the ceiling is ~17 req/s at this workload shape.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Single run per configuration — more meaningful here than usual: a kernel study on this same stack, cited in §7, measured run-to-run cv ≤ 0.3% under greedy decoding with static shapes.)&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Function calling at 2B scale
&lt;/h2&gt;

&lt;p&gt;Served with &lt;code&gt;--tool-call-parser gemma4 --enable-auto-tool-choice&lt;/code&gt;, probed with two OpenAI-style tools at temperature 0. Five scenarios, five clean results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Probe&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple call&lt;/td&gt;
&lt;td&gt;correct tool, inferred the optional &lt;code&gt;unit&lt;/code&gt; arg from phrasing&lt;/td&gt;
&lt;td&gt;166 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result synthesis&lt;/td&gt;
&lt;td&gt;fed the tool result back → clean natural-language answer&lt;/td&gt;
&lt;td&gt;140 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No-tool restraint&lt;/td&gt;
&lt;td&gt;answered directly, no spurious call&lt;/td&gt;
&lt;td&gt;97 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel calls&lt;/td&gt;
&lt;td&gt;both tool calls emitted in one turn, correct args each&lt;/td&gt;
&lt;td&gt;150 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Underspecified&lt;/td&gt;
&lt;td&gt;asked "What city are you interested in?" instead of hallucinating a call&lt;/td&gt;
&lt;td&gt;44 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A 2B model producing well-formed &lt;code&gt;tool_calls&lt;/code&gt; JSON, choosing correctly between calling and answering, batching parallel calls, and asking for missing arguments — at double-digit-millisecond latency. For high-volume, low-complexity agent steps, the quality floor is higher than the parameter count suggests.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Structured output works — but only with thinking on
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Probe&lt;/th&gt;
&lt;th&gt;Observed behavior&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;json_schema&lt;/code&gt;, thinking off (default)&lt;/td&gt;
&lt;td&gt;free prose with a 200 status; &lt;code&gt;strict: true&lt;/code&gt;, &lt;code&gt;guided_json&lt;/code&gt;, &lt;code&gt;structured_outputs&lt;/code&gt; spellings all equally unenforced&lt;/td&gt;
&lt;td&gt;✕ silently skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;json_object&lt;/code&gt;, thinking off&lt;/td&gt;
&lt;td&gt;fenced code block, array where an object was asked, invented enum value&lt;/td&gt;
&lt;td&gt;± prompt-level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;json_schema&lt;/code&gt; + &lt;code&gt;enable_thinking&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;exact schema conformance&lt;/strong&gt; — bare JSON object, typed integer, "ASAP" correctly mapped into the &lt;code&gt;high&lt;/code&gt; enum&lt;/td&gt;
&lt;td&gt;✓ enforced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning, default&lt;/td&gt;
&lt;td&gt;no reasoning traces on any prompt&lt;/td&gt;
&lt;td&gt;off by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning, &lt;code&gt;enable_thinking&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;parser cleanly splits thinking trace from a terse answer; ~2.4× completion tokens&lt;/td&gt;
&lt;td&gt;✓ works&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The mechanism: with &lt;code&gt;--reasoning-parser gemma4&lt;/code&gt; configured, vLLM defers grammar enforcement until the reasoning section ends. Thinking off → no reasoning terminator → the grammar never engages, and unconstrained prose ships with a 200 status. Enable thinking (&lt;code&gt;"chat_template_kwargs": {"enable_thinking": true}&lt;/code&gt;) and the same request is enforced exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational guidance:&lt;/strong&gt; pair structured output with &lt;code&gt;enable_thinking: true&lt;/code&gt; under a reasoning parser — or drop &lt;code&gt;--reasoning-parser&lt;/code&gt; from servers that don't need it. And keep client-side validation regardless: the silent-skip failure mode means a trusting client can't tell an enforced response from a lucky one.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Vision at 2B: accurate and nearly free
&lt;/h2&gt;

&lt;p&gt;One restart with &lt;code&gt;--limit-mm-per-prompt '{"image":4,"audio":1}'&lt;/code&gt; makes it a vision server. COCO validation images, base64 data URIs, temperature 0:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Probe&lt;/th&gt;
&lt;th&gt;Answer (abridged)&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Describe (two cats)&lt;/td&gt;
&lt;td&gt;"Two tabby cats… on a bright pink surface… a remote control visible"&lt;/td&gt;
&lt;td&gt;197 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count + attributes&lt;/td&gt;
&lt;td&gt;2 animals, both cats, remote + blanket identified&lt;/td&gt;
&lt;td&gt;421 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scene (bear)&lt;/td&gt;
&lt;td&gt;"A bear lying down in a grassy outdoor environment"&lt;/td&gt;
&lt;td&gt;329 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Room inventory&lt;/td&gt;
&lt;td&gt;wall-mounted TV, shelving, furniture correctly enumerated&lt;/td&gt;
&lt;td&gt;874 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An image costs ~280 prompt tokens and adds almost nothing over a text request. Two notes: server-side fetching of external image URLs proved flaky (intermittent 422s) — &lt;strong&gt;base64 data URIs are the reliable path&lt;/strong&gt; — and the first multimodal request after boot can 422 while the processor warms; retry once.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. fp8 KV cache, HBM anatomy, and a related kernel result
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;fp8 vs bf16 KV:&lt;/strong&gt; six greedy prompts (explanation, code, listing, translation, arithmetic, summarization — 889 completion tokens) run under the default fp8_e5m2 cache, then re-run after a restart with &lt;code&gt;--kv-cache-dtype bfloat16&lt;/code&gt;. &lt;strong&gt;Result: 6 of 6 outputs byte-identical.&lt;/strong&gt; On this (small, greedy) sample the compression is genuinely free — take the fp8 cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the 32 GB goes&lt;/strong&gt; (bf16-KV boot, 65,536 max context): usable HBM reports as 31.24 GiB; at 0.9 utilization vLLM works within 28.12 GiB — roughly 5.5–6 GiB weights, 16.3 GiB KV cache (8,713 blocks × 128 tokens × 15 layers × 128 KiB), ~6 GiB workspace.&lt;/p&gt;

&lt;p&gt;The interesting physics: E2B's KV sharing is directly visible in the allocator — &lt;strong&gt;only 15 of 35 layers hold KV tensors&lt;/strong&gt;, each with a single 256-dim KV head, so a token costs ~15 KiB of KV in bf16 (~7.5 KiB under fp8). That yields &lt;strong&gt;~1.1 million tokens of resident KV&lt;/strong&gt; — seventeen full 65K-context conversations on-chip — which is why the sweep saturated on &lt;em&gt;compute&lt;/em&gt;, never memory. Also: of the 404-second engine init, 329 seconds is XLA compilation — the dominant term in the ~10-minute cold start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related work:&lt;/strong&gt; a kernel-substitution study by Zimbres (&lt;a href="https://doi.org/10.5281/zenodo.21404069" rel="noopener noreferrer"&gt;DOI 10.5281/zenodo.21404069&lt;/a&gt;) shows the RPA v3 kernel's decode block-size heuristic costs 27.7–68.7% of large-batch throughput on 27B/31B models on v6e. E2B sits at the low-exposure end of that effect: at our c=64 operating point, attention is ~9% of memory traffic (vs ~41% in their regime) precisely because of the KV-sharing design above. Testing the override on E2B — smallest model, one chip, KV sharing — is a natural follow-up.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Cost breakdown
&lt;/h2&gt;

&lt;p&gt;Rate verified against Google's published &lt;a href="https://cloud.google.com/products/dws/pricing" rel="noopener noreferrer"&gt;Dynamic Workload Scheduler pricing&lt;/a&gt;: &lt;strong&gt;$1.35 per chip-hour&lt;/strong&gt; for v6e flex-start (europe-west4, us-east1, us-east5, asia-northeast1).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operating point&lt;/th&gt;
&lt;th&gt;Output tok/s&lt;/th&gt;
&lt;th&gt;$ / M output tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Saturation (burst)&lt;/td&gt;
&lt;td&gt;2,215&lt;/td&gt;
&lt;td&gt;$0.17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweet spot (c=64)&lt;/td&gt;
&lt;td&gt;2,140&lt;/td&gt;
&lt;td&gt;$0.18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive (c=8)&lt;/td&gt;
&lt;td&gt;1,209&lt;/td&gt;
&lt;td&gt;$0.31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single stream&lt;/td&gt;
&lt;td&gt;209&lt;/td&gt;
&lt;td&gt;$1.79&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Breakeven vs. the API:&lt;/strong&gt; against &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini 2.5 Flash-Lite&lt;/a&gt; at $0.40/M output tokens, self-hosting wins once you sustain ~940 output tok/s — roughly the c=8 operating point held continuously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold start&lt;/strong&gt; (~8.5–10.5 min, mostly XLA compile) costs $0.19–0.24 per provisioning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A full 4-hour session&lt;/strong&gt; costs $5.40 and, at saturation, delivers ~30M output tokens — about $12 worth at Flash-Lite prices. Flex-start fits batch bursts, not idle always-on endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Self-Hosting your AI Assistant with LiteLLM
&lt;/h2&gt;

&lt;p&gt;One of the most powerful ways to use this setup is to point your existing AI tools at your TPU. The Antigravity repo includes a guide for using &lt;strong&gt;LiteLLM Proxy&lt;/strong&gt; to redirect standard Gemini CLI requests to your self-hosted TPU endpoint.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Get the Endpoint:&lt;/strong&gt; Use &lt;code&gt;get_vllm_endpoint&lt;/code&gt; or &lt;code&gt;get_tpu_vm_endpoint&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Configure LiteLLM:&lt;/strong&gt; Map &lt;code&gt;gemini-2.0-flash&lt;/code&gt; to your TPU endpoint in a &lt;code&gt;litellm_config.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Point your CLI:&lt;/strong&gt; Set &lt;code&gt;GOOGLE_GEMINI_BASE_URL="http://localhost:4000"&lt;/code&gt; and use your self-hosted model for code analysis or chat.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This effectively gives you a private, high-performance inference engine that costs ~$1.35/hour and can handle massive context windows (65K tokens on-chip).&lt;/p&gt;

&lt;h2&gt;
  
  
  10. The Antigravity SRE Experience
&lt;/h2&gt;

&lt;p&gt;The Antigravity TPU skill doesn't just deploy; it operates. The &lt;code&gt;analyze_cloud_logging&lt;/code&gt; tool uses the very model you just deployed to triage system logs. &lt;/p&gt;

&lt;p&gt;If a deployment fails, you can ask Antigravity: &lt;em&gt;"Why did the v6e-1 deployment in europe-west4-a fail?"&lt;/em&gt;. The agent will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Fetch Cloud Logging entries for the resource.&lt;/li&gt;
&lt;li&gt; Send them to the Gemma 4 model on a &lt;em&gt;different&lt;/em&gt; active TPU (or a local fallback).&lt;/li&gt;
&lt;li&gt; Report a human-readable diagnosis (e.g., "The startup script failed because the Hugging Face token secret was not accessible to the compute service account").&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  11. Reproduction with Antigravity
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install the skill (Global or Project-specific)&lt;/span&gt;
make skill-install-agy

&lt;span class="c"&gt;# 2. Deploy (Natural Language)&lt;/span&gt;
&lt;span class="c"&gt;# "Find a v6e-1 in europe-west4, serve gemma-4-E2B-it, and tell me when it's ready."&lt;/span&gt;

&lt;span class="c"&gt;# 3. Monitor (Agent Tool)&lt;/span&gt;
&lt;span class="c"&gt;# The agent will use wait_for_vllm_ready and get_vllm_docker_logs&lt;/span&gt;
&lt;span class="c"&gt;# to ensure the ~10 minute cold-start completes successfully.&lt;/span&gt;

&lt;span class="c"&gt;# 4. Benchmark&lt;/span&gt;
&lt;span class="c"&gt;# "Run a throughput sweep on the active v6e for concurrency 1, 8, and 32."&lt;/span&gt;
&lt;span class="c"&gt;# (The agent invokes run_vllm_benchmark save_result=True)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Environment: vLLM 0.23.1rc1.dev1076+g5c342876a (vllm-tpu:nightly, tpu-inference backend) · TPU v6e-1 (ct6e-standard-1t, 32 GB HBM, GCE flex-start) · bf16 weights, fp8_e5m2 KV cache managed by the **Antigravity TPU DevOps agent&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tpu</category>
      <category>llm</category>
      <category>vllm</category>
      <category>antigravity</category>
    </item>
  </channel>
</rss>
