Cross Cloud A2A Agent Card Field Comparison — Stable…
    Neura Market
    Neura Market
    /Stable Diffusion
    Marketplace
    Directories
    Resources
    Stable Diffusion
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeekCoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    OverviewPromptsBlogVideosGuidesCoursesCommunityModelsLoRAsComfyUI WorkflowsTrending
    Stable DiffusionBlogCross Cloud A2A Agent Card Field Comparison
    Back to Blog
    Cross Cloud A2A Agent Card Field Comparison
    a2a

    Cross Cloud A2A Agent Card Field Comparison

    xbill August 26, 2026
    0 views

    Comparing Agent Cards with A2A - This tutorial aims to fetch the agent card from A2A agents running...

    Comparing Agent Cards with A2A - This tutorial aims to fetch the agent card from A2A agents running on several mainstream Cloud providers and compare the fields they publish.

    Same Protocol - Different Cards!

    Why do I care what is in the Agent Card? Can't I just call the Agent?

    What do the field differences actually mean for a client?

    What is this Approach actually Comparing?

    This project fetches the agent card from every agent in a mesh, stores the exact bytes each server sent, and compares the fields side by side.

    It never invokes anything. There is no model, no prompt and no token spend in the measurement path. It stops at discovery.

    The targets are three local specimens on 127.0.0.1 and three deployed agents on Cloud Run, Bedrock AgentCore and Azure Container Apps. All results below were measured on 2026-08-25.

    What is an A2A Agent Card?

    The agent card is a JSON document published at a well known path. It names the agent, lists its skills, and declares how to reach it.

    It is the only thing a client reads before committing to a runtime. If two vendors describe the same agent differently, every routing decision downstream inherits that difference.

    The spec places the card at /.well-known/agent-card.json, with /.well-known/agent.json as the older path.

    How is Discovery Privileged Against Invocation?

    Differently on each cloud, and the three answers do not resemble each other:

    cloudwhat gates the cardseparable from invocation
    Bedrock AgentCorebedrock-agentcore:GetAgentCardyes, a distinct IAM action
    Cloud Runroles/run.invokerno, the same role invokes
    Container Appsplatform auth on every pathno, and not configurable per path here

    Only AgentCore lets you grant discovery without granting invocation. On Cloud Run the card and the call sit behind one role, so anything that can read the card can also run the agent. On Container Apps the platform intercepts every path ahead of the container, which is stricter still and is covered later.

    The AgentCore case is the one that produces a confusing failure. A policy granting only InvokeAgentRuntime denies the card fetch, and the denial surfaces as a transport or protocol error nowhere near auth.

    This is why the credential is attached to the httpx client rather than to a single request: the card fetch needs authenticating in its own right, not as a side effect of the call it is preparing for.

    Tool Chain Setup

    Verify that the prerequisite packages are installed - and clone the sample Github repo:

    $ git clone https://github.com/xbill9/multicloud-agentcard
    $ cd multicloud-agentcard
    $ uv pip install --system -e '.[specimens,dev]'
    

    There is no virtualenv and nothing is pinned. The versions in use are a2a-sdk 1.1.2, google-adk 2.6.3, httpx 0.28.1 and starlette 1.3.1, on Python 3.13.14.

    Checking the Developer Environment

    Once you have all the tools in place - you can test the installation:

    $ python3 -m pytest -q
    125 passed in 0.52s
    
    $ ruff check .
    All checks passed!
    

    The suite is hermetic. The fetcher transport is injectable, so it covers cases a live cloud will not produce on demand: a card served only on the older path, a 403 carrying a real AgentCore denial, and a 200 carrying an HTML login page.

    Starting the Local Card Specimens

    Start with the local control, not the clouds. Two SDKs run on one machine and serve cards that no cloud and no model has touched:

    $ ./infra/run_mesh.sh start
    gcp starting on :11001 (pid 171954)
    aws starting on :11002 (pid 171955)
    azure starting on :11003 (pid 171956)
    waiting for health...
      gcp ready
      aws ready
      azure ready
    

    The start script verifies that its own process survived, not merely that something answers on the port. A health check alone reports that some server answered, and sharing a port range with another project produces three ready agents and a comparison of the wrong ones.

    A2A Agent Card (Cloud Run / google-adk)

    The GCP agent runs on Cloud Run behind IAM. Fetch the card with an identity token:

    $ TOK=$(gcloud auth print-identity-token)
    $ curl -s -D - -H "Authorization: Bearer $TOK" \
        https://research-gcp-...run.app/.well-known/agent-card.json
    HTTP/2 200
    content-type: application/json
    x-cloud-trace-context: b398ae03c356ad8b7addc408937df911;o=1
    server: Google Frontend
    

    The card is served at the well known path on the service hostname. Discovery is gated by roles/run.invoker at the Google Frontend.

    The card itself is 528 bytes:

    {
      "capabilities": { "pushNotifications": false, "streaming": false },
      "defaultInputModes": ["text/plain"],
      "defaultOutputModes": ["text/plain"],
      "description": "An agent that writes a short, sourced research brief",
      "name": "research_agent",
      "skills": [{ "description": "An agent that writes a short, sourced...",
                   "id": "research_agent", "name": "custom",
                   "tags": ["custom_agent"] }],
      "supportedInterfaces": [{ "protocolBinding": "JSONRPC",
                                "protocolVersion": "1.0",
                                "url": "http://0.0.0.0:8080" }],
      "version": "0.0.1"
    }
    

    to_a2a() writes the bind address onto the card. On Cloud Run the process binds 0.0.0.0:8080, so a public HTTPS endpoint advertises unroutable plaintext to every client that routes by card URL. This does not reproduce on a local mesh, where the bind address and the dial address are the same.

    A2A Agent Card (Bedrock AgentCore / a2a-sdk)

    AgentCore has no per-agent hostname. The runtime is addressed by URL escaped ARN, and the card sits beneath the same /invocations/ path the calls use:

    https://bedrock-agentcore.us-west-2.amazonaws.com
      /runtimes/arn%3Aaws%3Abedrock-agentcore%3A...%3Aruntime%2Fresearch_aws-...
      /invocations/.well-known/agent-card.json
    

    List the runtime and confirm it is ready:

    $ aws bedrock-agentcore-control list-agent-runtimes --region us-west-2
    [ { "name": "research_aws",
        "arn": "arn:aws:bedrock-agentcore:us-west-2:...:runtime/research_aws-...",
        "status": "READY" } ]
    

    The card is 2,109 bytes, four times the size of the Cloud Run card for the same agent:

    {
      "capabilities": { "streaming": false },
      "defaultInputModes": ["text/plain"],
      "defaultOutputModes": ["text/plain"],
      "description": "An agent that writes a short, sourced research brief",
      "name": "research_agent",
      "preferredTransport": "JSONRPC",
      "protocolVersion": "0.3",
      "skills": [{ "description": "You are a research assistant with a
                                   web_search tool...",
                   "id": "research_brief", "name": "research brief",
                   "tags": ["research","writing","analysis","brain:llm",
                            "model:us.amazon.nova-micro-v1:0"] }],
      "supportedInterfaces": [{ "protocolBinding": "JSONRPC",
                                "url": "https://bedrock-agentcore..." }],
      "url": "https://bedrock-agentcore...",
      "version": "0.1.0"
    }
    

    The path shape has a consequence for any tracer. A round trip classified as discovery only when the path equals a known card path, or begins with /.well-known/, files every AgentCore card fetch as an invocation. The rule must match on the suffix.

    A2A Agent Card (Container Apps / Agent Framework)

    The Azure agent runs on Container Apps behind the platform's built-in auth. Fetching the card without a credential returns a 401:

    $ curl -s -D - https://research-azure....azurecontainerapps.io/.well-known/agent-card.json
    HTTP/2 401
    www-authenticate: Bearer realm="research-azure....azurecontainerapps.io"
    x-ms-middleware-request-id: a2e2bf7d-458b-4197-a9ce-fae82666d986
    

    There is no server header from the application. The response comes from the Container Apps auth middleware, not from the agent.

    The request never reaches the container. Two requests, one to the card path and one to /health, both returned 401, and uvicorn logged neither:

    $ az containerapp logs show -n research-azure -g research-mesh-rg --tail 40
    "F INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)"
    

    The app is up and serving. It registers the card route through the same a2a-sdk helper the AWS agent uses. The 401 is interception, not absence.

    With a credential the card is 1,924 bytes:

    {
      "name": "research_agent",
      "description": "An agent that writes a short, sourced research brief on a given topic",
      "supportedInterfaces": [{ "url": "https://research-azure....azurecontainerapps.io",
                                "protocolBinding": "JSONRPC" }],
      "version": "0.1.0",
      "capabilities": { "streaming": false },
      "defaultInputModes": ["text/plain"],
      "defaultOutputModes": ["text/plain"],
      "skills": [{ "id": "research_brief", "name": "research brief",
                   "description": "You are a research assistant with a web_search
                                   tool...",
                   "tags": ["research","writing","analysis","brain:llm",
                            "model:research-reasoning"] }],
      "preferredTransport": "JSONRPC",
      "protocolVersion": "0.3",
      "url": "https://research-azure....azurecontainerapps.io"
    }
    

    Note the interface URL. Container Apps sits behind a platform ingress exactly as Cloud Run does, and this card advertises the routable public hostname.

    Debugging API Permission Errors

    Requesting a token for the Azure application from the workstation fails before any card is fetched:

    $ az account get-access-token --resource be143e2d-...
    ERROR: AADSTS65001: The user or administrator has not consented to use the
    application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' named
    'Microsoft Azure CLI'.
    

    The workstation is signed in to the correct tenant, so this is not a missing login. The app registration exposes no API scopes, has no identifier URIs and no pre-authorized applications, which means no user and no other application can obtain a token for it at all.

    It holds exactly one credential, and that is the whole allowlist:

    $ az ad app federated-credential list --id be143e2d-...
      issuer    https://accounts.google.com
      subject   1049501159…            <- a GCP service account numeric id
      audiences api://AzureADTokenExchange
    

    That subject is the numeric id of a GCP service account. One identity on one other cloud can read this agent's card.

    A workstation can still become that identity, because impersonation produces a token carrying the service account's sub rather than the developer's:

    $ gcloud auth print-identity-token \
        --impersonate-service-account=research-coordinator@...iam.gserviceaccount.com \
        --audiences=api://AzureADTokenExchange --include-email
    

    That needs roles/iam.serviceAccountTokenCreator on the target. Project Owner is not sufficient, because Owner does not include iam.serviceAccounts.getAccessToken. Exchange the result at the Entra token endpoint for <client-id>/.default and the card returns 200.

    The GCP leg has a related issue. Pinning the audience is refused for a user account:

    $ gcloud auth print-identity-token --audiences=https://research-gcp-...run.app
    ERROR: (gcloud.auth.print-identity-token) Invalid account type for
    `--audiences`. Requires valid service account.
    

    The token that comes back carries aud set to the gcloud OAuth client id, 32555940559.apps.googleusercontent.com, and not the Cloud Run service URL. Cloud Run accepts it anyway, honouring the allowlisted CLI client id. The result proves IAM role membership only, so the tool logs a warning on every fallback.

    What Fields are Actually in the Cards?

    This is the full top level field inventory across the three clouds. The spec groups fields into required core, 1.0 era, legacy 0.x, and optional:

    categoryfieldCloud Run (ADK)AgentCore (a2a-sdk)Container Apps (Agent Framework)
    requiredcapabilitiesyesyesyes
    requireddefaultInputModesyesyesyes
    requireddefaultOutputModesyesyesyes
    requireddescriptionyesyesyes
    requirednameyesyesyes
    requiredskillsyesyesyes
    requiredversionyesyesyes
    1.0supportedInterfacesyesyesyes
    1.0securityRequirementsnonono
    legacy 0.xurlnoyesyes
    legacy 0.xpreferredTransportnoyesyes
    legacy 0.xadditionalInterfacesnonono
    legacy 0.xsecuritynonono
    legacy 0.xsupportsAuthenticatedExtendedCardnonono
    optionalprotocolVersionnoyesyes
    optionaldocumentationUrlnonono
    optionaliconUrlnonono
    optionalprovidernonono
    optionalsecuritySchemesnonono
    optionalsignaturesnonono

    Read the last two columns again. AgentCore and Container Apps agree on every single row.

    That holds all the way down. Same capabilities keys, same skill keys, same interface keys, the same version string 0.1.0, the same skill id research_brief, and the same 1,258 character skill description. The only differences anywhere in the two cards are the hostnames and one tag value: model:us.amazon.nova-micro-v1:0 against model:research-reasoning.

    Those are two different clouds running two different agent frameworks. Strands on AgentCore, Microsoft Agent Framework on Container Apps. They emit structurally identical cards because both hand card construction to the same a2a-sdk route helper.

    Cloud Run is the only column that differs, and it differs on every structural row. ADK does not delegate; to_a2a() builds its own card.

    That is the honest width of this result. What varies with the cloud is nothing; what varies is whether the framework builds the card itself or delegates. Two delegating frameworks agreeing does not prove the framework is irrelevant, since neither is doing the work. Separating the two properly would need one framework across two SDKs, or a non-delegating framework on two clouds, and neither is in this mesh.

    Required Fields - Where the Clouds Agree

    All three cards carry all seven required fields. None is malformed, and a validator checking only the required set passes all three.

    Every difference between these runtimes is in optional or legacy territory. That is why a schema validator is not sufficient for cross cloud work, and why the comparison has to happen at the field level.

    The 1.0 and 0.x Split

    The Cloud Run card carries supportedInterfaces and none of the legacy keys. It is a clean 1.0 shape.

    The AgentCore and Container Apps cards carry supportedInterfaces and also url and preferredTransport, which the 1.0 spec replaced. Both are hybrids, serving two generations at once.

    A client written for 0.x reads url, works against two of the three clouds, and finds nothing on Cloud Run. A client written for 1.0 reads supportedInterfaces and works everywhere.

    client generationCloud RunAgentCoreContainer Apps
    reads urlfails, key absentworksworks
    reads supportedInterfacesworksworksworks

    The hybrid cards are the more compatible of the two shapes. They are also the ones declaring a version they do not match.

    Where is protocolVersion Declared?

    The two SDKs put the protocol version in opposite places:

    locationCloud Run (ADK)AgentCoreContainer Apps
    top level protocolVersionabsent0.30.3
    supportedInterfaces[].protocolVersion1.0absentabsent

    A client that reads the top level field sees nothing from Cloud Run and 0.3 from the other two. A client that reads the per interface field sees 1.0 from Cloud Run and nothing from the other two.

    Both readings are wrong in one direction each. Two of the three cards declare 0.3 while carrying the 1.0 supportedInterfaces key, so trusting the declared value routes a client into the wrong protocol generation on two clouds.

    The reliable test is structural. Branch on the presence of supportedInterfaces, not on any declared version string.

    Capabilities - Absent is not False

    The capabilities object differs by one key:

    capabilityCloud Run (ADK)AgentCoreContainer Apps
    streamingfalsefalsefalse
    pushNotificationsfalseabsentabsent
    stateTransitionHistoryabsentabsentabsent
    extensionsabsentabsentabsent

    Cloud Run states that push notifications are unsupported. The other two say nothing.

    For a client these are different answers. An explicit false is a commitment, and an absent key is an unknown that a strict client has to probe or assume. Three agents with identical behaviour are described with different degrees of confidence.

    Skills - What Counts as a Skill?

    All three cards carry exactly one skill with the same four keys. Two of the three agree on every value; the third has almost nothing in common with them:

    skill fieldCloud Run (ADK)AgentCoreContainer Apps
    idresearch_agentresearch_briefresearch_brief
    namecustomresearch briefresearch brief
    description69 chars, the agent description1,258 chars, the system prompt1,258 chars, the system prompt
    tagscustom_agentresearch, writing, analysis, brain:llm, model:us.amazon.nova-micro-v1:0research, writing, analysis, brain:llm, model:research-reasoning

    The id fields name different things. ADK uses the agent name, so the skill id and the agent name are the same string. The other two use the capability name.

    The name fields are not comparable at all. ADK emits the literal string custom, which is a category rather than a label. The other two emit a human readable name.

    The description fields diverge the most. ADK repeats the agent description. The other two publish the agent's entire system prompt, 1,258 characters of instruction text, and their tags name the model behind it.

    A router selecting agents by skill description is reading a one line summary from one cloud and a full system prompt from the other. Ranking those by text similarity compares documents of different kinds.

    Which Fields Come from the SDK and Which from the Author?

    Not every difference is the runtime's doing. The same a2a-sdk version produces different fields in two deployments:

    local    aws skills[0]: description, examples, id, inputModes, name, outputModes, tags
    deployed aws skills[0]: description, id, name, tags
    

    The local specimen sets examples, inputModes and outputModes. The deployed agent does not. documentationUrl behaves the same way, present locally and absent when deployed.

    So card richness has two independent sources. Structural fields such as protocolVersion placement, url and preferredTransport come from the SDK. Descriptive fields such as examples, inputModes and documentationUrl come from whoever wrote the agent.

    Attributing a missing examples array to the cloud is a mistake. Attributing a missing url key to the author is also a mistake.

    What is Missing from Every Card

    Six optional fields are absent from both deployed cards:

    fieldwhat a client loses
    securitySchemesno declaration of how to authenticate
    securityRequirementsno statement of what is required
    providerno organisation behind the agent
    documentationUrlnowhere to send a human
    iconUrlnothing to render in a catalogue
    signaturesnothing binds the card to the agent it describes

    The first two matter most. All three agents reject unauthenticated requests, and not one card declares a security scheme. A client that discovers any of these agents learns nothing from the card about why its next request will be rejected. This is unanimous across three clouds, three frameworks and two SDKs.

    The absence of signatures means an agent card is an unauthenticated claim. Any party able to serve that path can assert any capability.

    What the Field Differences Mean for a Client

    Collecting the field analysis into the decisions a client actually makes:

    client decisionfield it readsCloud RunAgentCoreContainer Appssafe approach
    which protocol generationprotocolVersionabsent0.3, incorrect0.3, incorrecttest for supportedInterfaces
    where to send the requesturl or interfaces0.0.0.0:8080, unroutablecorrect public URLcorrect public URLnever route by card URL alone
    which transportpreferredTransportabsentJSONRPCJSONRPCread protocolBinding on the interface
    can it streamcapabilities.streamingfalsefalsefalsereliable
    can it pushcapabilities.pushNotificationsfalseabsentabsenttreat absent as unknown
    what can it doskills[].descriptionone linefull system promptfull system promptdo not compare by text length
    how do I authenticatesecuritySchemesabsentabsentabsentout of band knowledge required
    is this card genuinesignaturesabsentabsentabsentnot answerable

    Two of these are actively dangerous rather than merely incomplete. Routing by the Cloud Run card URL sends traffic to 0.0.0.0:8080. Trusting the declared protocol version selects 0.3 semantics for a 1.0 shaped card, on two clouds.

    The bind address row also settles a question the single cloud view cannot. Container Apps sits behind a platform ingress exactly as Cloud Run does, and it advertises the routable hostname. The unroutable URL is ADK's doing, not a consequence of being deployed behind an ingress.

    Time to Start Comparing some Cards!

    One command dials every peer, reviews each card, contrasts the results and stores the run:

    $ agentcard fetch --save
    run 2aca64eb85d6  3/3 card(s)  55ms
    
      gcp    200  1.0          675B  none              0 err  0 warn
      aws    200  hybrid       717B  none              0 err  1 warn
      azure  200  hybrid       719B  none              0 err  1 warn
    

    The local mesh reproduces the same split. The gcp specimen runs google-adk and produces the 1.0 shape. The other two run a2a-sdk and produce the hybrid shape.

    The agents are identical echo agents, so the shape difference is the SDK.

    Results

    Point the tool at a peers file and fetch the deployed agents:

    $ agentcard --corpus-dir .cards-deployed fetch --peers-file peers.toml --save
    run 23cff0f73098  3/3 card(s)  17660ms
    
      gcp    200  1.0          528B  gcloud-id-token   1 err  2 warn
      aws    200  hybrid      2109B  aws-sigv4-local   0 err  2 warn
      azure  200  hybrid      1924B  entra-fic         0 err  2 warn
    

    Three clouds, three cards. The run produced seven findings:

    sevpeercodedetail
    errorgcpbind-address-on-cardadvertises http://0.0.0.0:8080
    warninggcpplaintext-urlhttp:// for a remote agent
    warninggcpundeclared-authfetched with a credential, names no securitySchemes
    warningawsundeclared-authsame, on the second cloud
    warningawsversion-shape-mismatchdeclares 0.3, shaped like hybrid
    warningazureundeclared-authsame, on the third cloud
    warningazureversion-shape-mismatchdeclares 0.3, shaped like hybrid

    The only error belongs to one cloud. The two hybrid cards produce identical findings as well as identical fields.

    A Card that Changed

    Five runs of the same peer, at the same endpoint, with the same credential, across three and a half hours:

    runtime (UTC)versionskillsbytes
    8c201b95f34914:10:240.0.141533
    2a4992c4372b15:58:280.0.141533
    05bb15448c6316:01:040.0.141533
    07f23fc6df3f17:17:390.0.11528
    6dedd20d0b0517:40:460.0.11528

    Three runs sit on one side and two on the other, and each group is byte for byte stable. The card lost two thirds of its bytes and three of its four skills, and the version field did not move.

    The cause is a deployment, and the platform will tell you so:

    $ gcloud run revisions list --service=research-gcp --region=us-central1
    NAME                    CREATION_TIMESTAMP
    research-gcp-00020-6hd  2026-08-25T17:08:19Z
    research-gcp-00019-dq6  2026-08-14T19:20:15Z
    

    Revision 00020 was created at 17:08:19, between the last four skill reading at 16:01 and the first one skill reading at 17:17. The revision before it was eleven days old. The agent was redeployed with different composition, which is ordinary and expected.

    So this is not a card mutating on its own, and it would be wrong to read it that way. What it does show is narrower and still worth having: a redeploy changed the card materially while version stayed at 0.0.1. A client cannot use version to decide whether a card it cached is still current, because the field does not track the content. That is an agent authoring gap rather than a platform or protocol one, and it is invisible from the card alone.

    Checking the revision list is one command, and it is the difference between reporting a fact and reporting a mechanism. Any drift a corpus catches should be matched against the platform's own deployment history before it is called anything stronger than a change.

    The flattened composition is what was removed:

    05bb15448c63 -> ['research_agent',
                     'research_agent-sub-agents',
                     'research_agent_gemini_research_agent_gemini',
                     'research_agent_gemini_research_agent_gemini-web_search']
    
    07f23fc6df3f -> ['research_agent']
    

    A client that discovered this agent at 16:01 and cached "it can search the web" held a claim the card no longer made by 17:17, and nothing in the card told it so. The redeploy is the explanation; it is not the excuse, because a client has no way to see a redeploy.

    The review output is identical across the change. Diffing the two defect blocks produces no output:

    $ diff <(agentcard replay 05bb15448c63) <(agentcard replay 07f23fc6df3f)
    

    Both runs report 1 error and 2 warnings on this peer, and the same six defects run wide at the same severities. A checker that asks only whether a card is conformant reports no difference, because on that question there is none.

    Gating on Drift

    Conformance and change are different questions, so they use different exit codes:

    $ agentcard fetch --save --fail-on-change
    $ agentcard fetch --fail-on-defect
    

    Restarting a specimen so it advertises a different URL produces exit 0 from --fail-on-defect and exit 4 from --fail-on-change.

    exitmeaning
    0ran, nothing gated
    1bad invocation
    2--fail-on-defect, a card is wrong now
    3no peer served a card at all
    4--fail-on-change, a card differs from the previous run

    Exit 3 is distinct from 1 and 2. A harness must separate an instrument failure from a working instrument reporting bad news.

    A peer name is not an identity. The local specimens and the deployed agents are both named gcp, aws and azure, so a corpus holding both would compare a local specimen against Cloud Run and report it as vendor drift. A peer whose endpoint changed prints as [not compared] and does not trip the gate.

    Discovery Cost

    A card fetched over a federated credential costs round trips to another cloud identity provider first, and arrives byte identical to one fetched from an open port:

    peerauthkeylessround tripsdiscovery ms
    gcp (local)noneyes116
    aws (local)noneyes19
    azure (local)noneyes16
    gcp (deployed)gcloud-id-tokenyes116785
    aws (deployed)aws-sigv4-localno1771
    azure (deployed)entra-ficyes2683

    The local mesh answers in 55 ms. The deployed run is dominated by whichever container is cold, and the Azure leg costs two round trips rather than one because the Google assertion has to be exchanged at Entra before the card can be requested.

    Validating the Results

    Each result was re-checked with curl and python3, independent of the tool:

    resultvalidation methodoutcome
    Two SDKs, two card shapescurl both local cardsconfirmed
    protocolVersion in opposite placescurl, read the fieldconfirmed
    Both local shapes reproduce when deployedparsed the server raw bytesconfirmed
    Live card advertises 0.0.0.0:8080curl the deployed cardconfirmed
    No deployed card declares authcurl and raw bytesconfirmed
    capabilities differ by one keyfield inventory across all three cardsconfirmed
    skill fields carry different meaningsfield inventory across all three cardsconfirmed
    skill richness is author setsame SDK, two deploymentsconfirmed
    AgentCore and Container Apps are field identicalfull field inventory of both cardsconfirmed
    The Azure app serves a card200 with an impersonated federated tokenconfirmed
    The Azure 401 is interception, not absence2 requests, 401 each, zero uvicorn log linesconfirmed
    A card changed with no version bumpfive stored runs over 3.5 hconfirmed
    The review did not report the changediff of both review outputsidentical
    The two gates catch different thingsboth run on one changed card0 vs 4
    replay never dialsevery server killed, then replayedconfirmed

    The replay path is tested by removing the network. Stop every server, confirm nothing is listening, then replay a run that fetched three cards:

    $ ss -ltn | grep -c '1100[123]'
    0
    $ agentcard replay
    run 1657b2be9bff  3/3 card(s)  46ms
      gcp    200  1.0          675B  none              0 err  0 warn
      aws    200  hybrid       717B  none              0 err  1 warn
      azure  200  hybrid       719B  none              0 err  1 warn
    

    Three cards are reviewed with every server down.

    Final Results

    approachcatches invalid cardscatches field divergencecatches driftcost
    read the specnononofree
    a JSON schema validatoryesnonofree
    invoke the agent and inspectpartlynonomodel spend
    this harnessyesyesyeszero spend

    All three deployed cards are valid. Every meaningful difference sits in optional and legacy fields that a schema validator does not examine, which is the gap a field level comparison fills.

    The three way result is sharper than a two way one. Two clouds and two agent frameworks produced structurally identical cards, and the third differed on every structural row. Across this mesh the card shape tracks the card builder, not the cloud: the two frameworks that delegate to a2a-sdk agree exactly, and the one that builds its own card is the outlier on every structural row.

    Summary

    The goal of this article was to fetch A2A agent cards from multiple clouds and compare the fields they publish. The key to the solution was storing the exact bytes each server returned and comparing every field at every nesting level. Three local specimens and three remote agents were presented, covering three deployment clouds, three agent frameworks and two A2A SDKs. Finally, a drift gate was added to compare each run against the previous stored corpus.

    The field comparison produced these results:

    • All three deployed cards carry the seven required fields, so all three are valid and every difference sits in optional or legacy territory.
    • AgentCore and Container Apps emit structurally identical cards. Same fields, same version string, same skill id, same 1,258 character description. Two clouds and two agent frameworks, one shape.
    • Cloud Run is the outlier on every structural row, and it is the only card carrying an error.
    • Across this mesh the shape tracks the card builder rather than the cloud. Both identical cards come from frameworks that delegate to a2a-sdk; the outlier builds its own. Whether the framework matters independently of that is not separable here, because no framework appears on two SDKs.
    • The two SDKs declare protocolVersion in opposite places, and the 0.3 on the two hybrid cards contradicts the 1.0 shape they carry.
    • capabilities differ by one key, and an absent key is not the same answer as an explicit false.
    • ADK emits the literal skill name custom and a 69 character description. The other two emit a human name, the agent's full system prompt and the model id.
    • Not one card declares securitySchemes, though all three reject unauthenticated requests.
    • A redeploy changed a card from four skills to one while version stayed at 0.0.1, and the conformance review was identical on both sides. The cause was a Cloud Run revision created between the two readings, not a card mutating on its own; the point is that version does not track content, so a client cannot use it to tell whether a cached card is current.

    Discovery is privileged differently on each cloud, and the range is wide. Cloud Run gates the card behind an invoker role. AgentCore makes discovery a separately grantable IAM action. Container Apps intercepts every path at the platform, so the card is readable only by the single federated identity the app registration trusts, and a client must already hold the credential before it can read the document describing which credential to use.

    What this does not establish

    One agent per runtime, one region per cloud, one tenant, and a single reading of each card apart from the Cloud Run series. Two SDKs and three frameworks, with no framework appearing on more than one SDK.

    So the field differences are properties of these deployments on this date. They are not a survey of what Cloud Run, AgentCore or Container Apps do in general, and the two identical cards agree because of a shared card builder rather than anything the clouds have in common.

    The field categories used throughout come from this repo's model of the spec in cards/spec.py, not from an independent conformance suite. A field sorted into the wrong category there would move consistently across every table here.

    Cards change across deploys without the version field moving, so every result in this article names the date it was measured on.

    The code is at xbill9/multicloud-agentcard.

    Tags

    a2amulticloudaipython

    Comments

    More Blog

    View all
    Overcoming Dart's Single Inheritance Wall: Composable CubitSignalMixin & BlocSignalMixin in Flutterflutter

    Overcoming Dart's Single Inheritance Wall: Composable CubitSignalMixin & BlocSignalMixin in Flutter

    Discover how CubitSignalMixin and BlocSignalMixin allow any existing Flutter controller, domain repository, or enterprise class to gain full reactive state container capabilities without occupying its single inheritance slot.

    R
    Randal L. Schwartz
    Taking Advantage of Gemini Managed Agents with Google Apps Scriptgoogleappsscript

    Taking Advantage of Gemini Managed Agents with Google Apps Script

    Breaking the Limits of GAS with Direct Cloud-to-Cloud Streaming in Persistent Linux...

    T
    Tanaike
    Grand Central Station: Why BLoC, Riverpod, and BlocSignal Are Now True Peersflutter

    Grand Central Station: Why BLoC, Riverpod, and BlocSignal Are Now True Peers

    Discover why Flutter state management is no longer an all-or-nothing choice. Explore how BlocSignal, Classic BLoC, and Riverpod now operate as first-class bidirectional peers at the Grand Central State Terminal.

    R
    Randal L. Schwartz
    Unlocking workload rightsizing visibility on GKE: How VPA decision logs bring observability to autoscalingkubernetes

    Unlocking workload rightsizing visibility on GKE: How VPA decision logs bring observability to autoscaling

    Learn how to troubleshoot and audit GKE Vertical Pod Autoscaler actions with structured decision logs in Cloud Logging.

    O
    Olivier Bourgeois
    Accelerating JVM startup on GKE: How VPA CPU startup boost eliminates ongoing resource wastekubernetes

    Accelerating JVM startup on GKE: How VPA CPU startup boost eliminates ongoing resource waste

    Learn how GKE VerticalPodAutoscaler (VPA) CPU Startup Boost cuts JVM cold starts and eliminates ongoing CPU waste using in-place Pod resizing.

    O
    Olivier Bourgeois
    Why AI Websites All Look the Same and How to Build Something Differentai

    Why AI Websites All Look the Same and How to Build Something Different

    If you've built a website with AI recently, there is a good chance it looks familiar. Maybe you have...

    M
    Mfonobong Umondia

    Stay up to date

    Get the latest Stable Diffusion prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for Stable Diffusion and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Neura Market

    Custom AI Systems & Services

    Our team of experienced AI builders will help build custom AI systems, workflows, and solutions.

    Request custom work

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this Stable Diffusion resource

    • Learn n8n Keyboard Shortcuts with an Interactive Hands-On Tutorial Workflown8n · $24.99 · Related topic
    • Learn API Fundamentals with an Interactive Hands-On Tutorial Workflown8n · $24.99 · Related topic
    • Auto-Generate Product Comparison Pages with OpenAI & Google Sheetsn8n · $24.99 · Related topic
    • Learn n8n Expressions with an Interactive Step-by-Step Tutorial for Beginnersn8n · $14.99 · Related topic
    Browse all workflows