# Can Google ADK Talk to Microsoft Foundry on Azure? A Cross-Cloud A2A Benchmark

> Source: <https://dev.to/gde/can-google-adk-talk-to-microsoft-foundry-on-azure-a-cross-cloud-a2a-benchmark-4h36>
> Published: 2026-07-27 20:54:36+00:00

This article provides a step-by-step guide to building and testing a

cross-cloud currency agent. A coordinator hosted on Microsoft Foundry in Azure

calls a Google ADK agent on Google Cloud over A2A v1.0, uses an MCP exchange-rate

tool, and records what independent verification costs in latency.

Most Agent-to-Agent (A2A) protocol demos stop at "look, the request succeeded."

That is a smoke test, not an interoperability benchmark. This project goes

further: a Microsoft Agent Framework coordinator, hosted on Microsoft Foundry

in Azure, discovers and delegates to a Google ADK agent running on Cloud Run in

GCP, then measures what that cross-cloud hop actually costs.

The questions, with numbers instead of vibes:

This builds directly on the currency agent from the previous articles in this series:

[Getting Started with MCP, ADK and A2A | Google Codelabs](https://codelabs.developers.google.com/codelabs/currency-agent#0)

[GitHub - xbill9/currency-agent](https://github.com/xbill9/currency-agent)

That agent — ADK, Gemini, a FastMCP exchange-rate server backed by the free [Frankfurter API](https://www.frankfurter.dev/) — becomes the *remote verifier* in this project. The new repo wraps it in a benchmark harness:

[GitHub - xbill9/foundry-adk-a2a-currency](https://github.com/xbill9/foundry-adk-a2a-currency)

```
You (Responses protocol, Entra ID)
      |
Microsoft Foundry hosted agent          (Azure, eastus2, gpt-5-mini)
Microsoft Agent Framework coordinator
      |
      +-- MCP stdio --> Frankfurter rates      (in-container)
      |
      +-- A2A v1.0 --> Cloud Run               (GCP, us-central1)
                          |
                       Google ADK agent        (gemini-2.5-flash)
                          |
                       MCP HTTP --> Frankfurter rates
```

The coordinator answers every conversion three ways:

| Mode | What happens | Why it exists |
|---|---|---|
`mcp_only` |
Coordinator calls the rate tool | Baseline |
`a2a_only` |
Coordinator delegates to the remote ADK agent | Measure remote-agent behavior |
`verified` |
MCP result independently checked over A2A | The accuracy/overhead tradeoff |

Both sides read the same Frankfurter daily reference rates on purpose: when the two clouds disagree, that measures *protocol and model* behavior, not data-source skew.

Currency conversion is a terrible job for an LLM and a great job for `Decimal`

. 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?"

```
difference = abs(primary.converted_amount - verifier.converted_amount)
relative = difference / abs(primary.converted_amount)
agreed = relative <= tolerance  # default 0.005
```

The failure policy is explicit rather than emergent:

`validation`

, `provider`

, `authentication`

, `transport`

, `timeout`

, `protocol`

). Never fabricate a rate."Which layer broke" is a research question, so every adapter failure is converted into exactly one of those kinds at one boundary.

Here is the headline interoperability lesson. The first attempt to point the Microsoft Agent Framework A2A client at the existing currency agent died instantly:

```
a2a.utils.errors.MethodNotFoundError: Method not found
```

Root cause: a clean protocol-version skew with **no negotiation**.

`agent-framework-a2a`

, requires `a2a-sdk>=1.0`

) calls the A2A v1.0 JSON-RPC method `SendMessage`

.`a2a-sdk 0.3.x`

) only routes the v0.3.0 method `message/send`

.`protocolVersion: 0.3.0`

— and calls the v1.0 method anyway.Worse, the two ecosystems had mutually exclusive dependency pins at the time of writing:

| Package | a2a-sdk requirement |
|---|---|
`agent-framework-a2a 1.0.0b260721` |
`>=1.0.0,<2` |
`google-adk 2.1.0 – 2.4.0` |
`>=0.3.4,<0.4` |
`google-adk 2.5.0` |
`>=0.3.4,<2` ✅ |
`a2ui-agent-sdk` (through 0.4.0) |
`<0.4.0` ❌ |

`google-adk 2.5.0`

is the first release that allows `a2a-sdk 1.x`

— 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 `google-adk 2.5.0`

+ `a2a-sdk 1.1.2`

. Separate `uv`

virtualenvs keep both worlds installable on one machine; the wire protocol is what has to match.

Two more v1.0 observations worth knowing:

`url`

/`protocolVersion`

into a `supportedInterfaces`

array.`get_exchange_rate`

) as A2A skills on the card — your remote agent's card documents its toolbox for free.After the version alignment, the answer to research question 1 is **yes**: 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.

Versions observed working (preview software; pin what works, expect drift): Azure Developer CLI `1.28.1`

, `microsoft.foundry`

azd extension `1.0.0-beta.2`

, Python `3.13`

, `agent-framework-core 1.12.1`

, `agent-framework-foundry 1.10.3`

, `agent-framework-foundry-hosting 1.0.0b260722`

, region `eastus2`

, model `gpt-5-mini`

(`2025-08-07`

, Global Standard).

The entire hosted agent is one file. `FoundryChatClient`

authenticates with `DefaultAzureCredential`

— no API keys anywhere on the Azure side — and `ResponsesHostServer`

exposes it over the Responses protocol:

```
agent = Agent(
    client=FoundryChatClient(
        project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
        model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
        credential=DefaultAzureCredential(),
    ),
    name="currency-interoperability-coordinator",
    instructions="For every conversion request, call run_currency_benchmark. "
                 "Never calculate or verify arithmetic yourself. ...",
    tools=[run_currency_benchmark],
)
ResponsesHostServer(agent).run()
```

The adapters behind that tool are selected by environment variables (`CURRENCY_A2A_ENDPOINT`

, `CURRENCY_RATE_PROVIDER`

, `CURRENCY_RATE_TRANSPORT`

, `CURRENCY_TIMEOUT_SECONDS`

), passed through `azure.yaml`

. Version 1 shipped with deterministic fixtures; version 2 flipped the env vars and went live without touching code.

The gotcha that will probably bite you too: control-plane roles (Owner/Contributor) are **not** enough to deploy. The deploying identity also needs the Foundry Project Manager *data-plane* role (definition `eadc314b-1a2d-4efa-be10-5d325db5065e`

) at the Foundry account scope.

The benchmark agent container colocates two processes: the FastMCP Frankfurter server on localhost and the A2A app on `$PORT`

. The Gemini key comes from Secret Manager — never an env literal:

```
gcloud secrets create gemini-api-key --data-file="$HOME/gemini.key"
gcloud run deploy currency-adk-a2a \
  --source adk_agent --region us-central1 \
  --allow-unauthenticated --min-instances=0 --max-instances=2 \
  --set-secrets "GOOGLE_API_KEY=gemini-api-key:latest" \
  --set-env-vars "MCP_SERVER_URL=http://127.0.0.1:8081/mcp,GENAI_MODEL=gemini-2.5-flash"
```

`--min-instances=0`

means the verifier costs nothing while idle. (Ask me how I know to check `min-instances`

on old GPU services. Actually, don't.)

Everything local runs credential-free on fixtures first:

```
git clone https://github.com/xbill9/foundry-adk-a2a-currency
cd foundry-adk-a2a-currency
pip3 install --user -e ".[dev]"
pytest                      # 35 deterministic tests, no cloud, no model calls
currency-benchmark 100 USD EUR --mode verified --transport mcp-stdio
```

To go live locally, start the ADK agent (needs `GOOGLE_API_KEY`

):

```
cd adk_agent && uv sync
MCP_SERVER_URL=http://127.0.0.1:8081/mcp uv run uvicorn agent:a2a_app --port 10001
```

and point the benchmark at it:

```
CURRENCY_RATE_PROVIDER=frankfurter currency-benchmark 250 GBP USD JPY \
  --mode verified --transport mcp-stdio \
  --a2a-endpoint http://127.0.0.1:10001 --timeout-seconds 60
```

The full evaluation matrix (fault-free cases live, fault-injection cases deterministic — you cannot order a live agent to time out on demand):

```
currency-evaluate --a2a-endpoint http://127.0.0.1:10001 --live-rates \
  --output results.jsonl --summary summary.json
```

The full cross-cloud deployment is one script — Secret Manager, Cloud Run, then `azd deploy`

:

```
bash infra/deploy_live.sh
```

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.

| Mode | Success | Median latency | p95 latency |
|---|---|---|---|
`mcp_only` |
100% | 297 ms | 1.09 s |
`a2a_only` (live Gemini) |
100% | 1.69 s | 4.82 s |
`verified` (live) |
100% | 1.71 s | 4.15 s |

The two numbers I care most about:

On the fully hosted path (Azure coordinator → GCP agent), the remote Foundry endpoint completed all three modes: `mcp_only`

in 0.71 s, `verified`

in 2.7 s with `agreed: true`

and `relative_difference: 0`

. One warm-path anecdote, not a distribution — repeated warm/cold trials and token/cost accounting are the remaining evaluation work.

`a2a-sdk`

major version on `MethodNotFoundError`

is the signature.`protocol`

failure instead of a silently incomplete answer. Design for partial replies.`azd deploy`

died with `AzureDeveloperCLICredential: signal: killed`

and succeeded unchanged on retry. Retry before you bisect.`gpt-5-mini`

and `gemini-2.5-flash`

respected "call the tool, never calculate" — and because all math is `Decimal`

in deterministic code, agreement checks compare numbers, not model prose.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 **independent failure detection**: 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 — `mcp_only`

at 297 ms is your friend.

Everything — coordinator, benchmark agent, deploy script, evaluation cases, and the raw results behind every table above:

[GitHub - xbill9/foundry-adk-a2a-currency](https://github.com/xbill9/foundry-adk-a2a-currency)

Upstream agent pinned at [xbill9/currency-agent@aeef3c4](https://github.com/xbill9/currency-agent).

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.
