<?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: Marwa Sahli</title>
    <description>The latest articles on DEV Community by Marwa Sahli (@marwa_sahli_273c3e83f3039).</description>
    <link>https://dev.to/marwa_sahli_273c3e83f3039</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%2F4213592%2F45d926a1-60a6-4971-92e0-a29621e9c921.png</url>
      <title>DEV Community: Marwa Sahli</title>
      <link>https://dev.to/marwa_sahli_273c3e83f3039</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marwa_sahli_273c3e83f3039"/>
    <language>en</language>
    <item>
      <title>The A2A protocol: how it works, step by step, and how it compares with MCP</title>
      <dc:creator>Marwa Sahli</dc:creator>
      <pubDate>Thu, 05 Nov 2026 08:28:43 +0000</pubDate>
      <link>https://dev.to/marwa_sahli_273c3e83f3039/the-a2a-protocol-how-it-works-step-by-step-and-how-it-compares-with-mcp-4o9n</link>
      <guid>https://dev.to/marwa_sahli_273c3e83f3039/the-a2a-protocol-how-it-works-step-by-step-and-how-it-compares-with-mcp-4o9n</guid>
      <description>&lt;p&gt;AI systems are no longer just a single model answering questions. We now build specialised &lt;strong&gt;agents&lt;/strong&gt; (an HR agent, a booking agent, a code-review agent), often developed by different teams, on different frameworks (LangGraph, CrewAI, ADK, OpenAI Agents SDK…), and hosted by different providers. That raises a new question: &lt;strong&gt;how do these agents talk to each other?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the question &lt;strong&gt;A2A (Agent2Agent)&lt;/strong&gt; answers. This article introduces the protocol, walks through how it works step by step, and then compares it with &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;, with which it is often confused.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is A2A?&lt;/li&gt;
&lt;li&gt;History and governance&lt;/li&gt;
&lt;li&gt;Key concepts&lt;/li&gt;
&lt;li&gt;A three-layer architecture&lt;/li&gt;
&lt;li&gt;How it works, step by step&lt;/li&gt;
&lt;li&gt;Task lifecycle&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;A2A vs MCP&lt;/li&gt;
&lt;li&gt;Using A2A and MCP together&lt;/li&gt;
&lt;li&gt;Current limitations and best practices&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What is A2A?
&lt;/h2&gt;

&lt;p&gt;A2A is an &lt;strong&gt;open specification&lt;/strong&gt; that defines how independent AI agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;discover each other&lt;/strong&gt; (who can do what, and how to reach them),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;delegate tasks&lt;/strong&gt; to one another, including long-running, multi-step ones,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;exchange data&lt;/strong&gt; (text, JSON, files) securely,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;track progress&lt;/strong&gt; on work in flight (streaming, notifications).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core idea: every agent is treated as a &lt;strong&gt;black box&lt;/strong&gt;. A client agent does not need to know the remote agent's model, prompt, tools or memory. It hands over a task, the way you would hand a file to a colleague in another department, and gets a result back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In one sentence:&lt;/strong&gt; MCP connects an agent to its &lt;em&gt;tools&lt;/em&gt;; A2A connects an agent to &lt;em&gt;other agents&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. History and governance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;April 2025&lt;/td&gt;
&lt;td&gt;Protocol announced by Google, with around fifty partners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 2025&lt;/td&gt;
&lt;td&gt;Project donated to the &lt;strong&gt;Linux Foundation&lt;/strong&gt;, which now provides neutral governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summer 2025&lt;/td&gt;
&lt;td&gt;Version 0.3: gRPC binding added, Agent Card signatures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 2026&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stable version 1.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026&lt;/td&gt;
&lt;td&gt;Production support in several platforms (Microsoft Copilot Studio, Azure AI Foundry, Amazon Bedrock AgentCore, ServiceNow, Salesforce…)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Version 1.0 introduced several breaking changes compared with 0.x (method names, state names, removed fields). If you are reading tutorials from 2025, check which version they target.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Key concepts
&lt;/h2&gt;

&lt;p&gt;A2A relies on a small vocabulary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The agent that initiates the request (often an orchestrator).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remote agent (A2A server)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The agent that receives and executes the task. Roles are fluid: an agent can be the client in one interaction and the server in another.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent Card&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The agent's public JSON identity card: name, description, version, skills, endpoints, authentication methods.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A capability declared in the Agent Card (e.g. &lt;code&gt;book_flight&lt;/code&gt;), with a description, tags, examples and accepted formats.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Task&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A unit of work with an identifier and a state, generated by the server. It can last a few milliseconds or several days.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Message&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One turn of conversation, carried by a role (&lt;code&gt;user&lt;/code&gt; or &lt;code&gt;agent&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A piece of content inside a message: text, structured JSON data, a URL to a file, or raw bytes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Artifact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The deliverable produced by the task (a report, a JSON itinerary, a PDF…), also made of Parts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An identifier used to group several tasks and messages belonging to the same conversation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. A three-layer architecture
&lt;/h2&gt;

&lt;p&gt;The v1.0 specification is split into three layers, so you can change transport without rewriting the agent's logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│ Layer 3 — Protocol bindings                             │
│   JSON-RPC 2.0 over HTTPS  │  gRPC  │  HTTP+JSON (REST) │
├─────────────────────────────────────────────────────────┤
│ Layer 2 — Abstract operations                           │
│   SendMessage, SendStreamingMessage, GetTask,           │
│   ListTasks, CancelTask, SubscribeToTask,               │
│   push notification config, Agent Card retrieval        │
├─────────────────────────────────────────────────────────┤
│ Layer 1 — Canonical data model                          │
│   AgentCard, AgentSkill, Task, Message, Part,           │
│   Artifact, Extension (Protocol Buffers + JSON Schema)  │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each agent advertises the binding(s) it supports in its Agent Card (the &lt;code&gt;supportedInterfaces&lt;/code&gt; field). JSON-RPC is the most common for publicly exposed agents; gRPC is attractive for low-latency internal traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. How it works, step by step
&lt;/h2&gt;

&lt;p&gt;Let's take a concrete scenario: an &lt;strong&gt;HR agent&lt;/strong&gt; needs to organise an interview and delegates the booking to a &lt;strong&gt;travel agent&lt;/strong&gt; run by another team.&lt;/p&gt;

&lt;p&gt;![A2A task delegation sequence: discovery, authentication, sending, tracking, multi-turn exchange and result]&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%2Fwwq3k09xakcjlvtdte8v.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%2Fwwq3k09xakcjlvtdte8v.png" alt=" " width="800" height="744"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Discovery via the Agent Card
&lt;/h3&gt;

&lt;p&gt;The remote agent publishes its card at a standard URL: &lt;code&gt;https://&amp;lt;domain&amp;gt;/.well-known/agent-card.json&lt;/code&gt; (an RFC 8615 convention). A simplified, v1.0-compliant example:&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;"Travel booking agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Books flights and hotels for employees."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&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.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"organization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyCompany"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mycompany.example"&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;span class="nl"&gt;"supportedInterfaces"&lt;/span&gt;&lt;span class="p"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://travel.mycompany.example/a2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"protocolBinding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSONRPC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"protocolVersion"&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.0"&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;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pushNotifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extendedAgentCard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="nl"&gt;"defaultInputModes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"text/plain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"defaultOutputModes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book_flight"&lt;/span&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;"Book a flight"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Books a flight given an origin, a destination and dates."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"travel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"booking"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"examples"&lt;/span&gt;&lt;span class="p"&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;span class="s2"&gt;"Book a Paris–Lyon flight on 12 November for employee 4432"&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;span class="p"&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;span class="nl"&gt;"securitySchemes"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"corp_oauth"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oauth2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"flows"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"clientCredentials"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"tokenUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://auth.mycompany.example/token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"flights:write"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Book flights"&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;span class="p"&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;span class="p"&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;span class="nl"&gt;"security"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"corp_oauth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"flights:write"&lt;/span&gt;&lt;span class="p"&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;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 client reads the card, checks that the &lt;code&gt;book_flight&lt;/code&gt; skill exists, picks an interface and identifies the expected authentication method.&lt;/p&gt;

&lt;p&gt;Discovery can also go through an internal &lt;strong&gt;registry&lt;/strong&gt; (a catalogue of vetted agents) or &lt;strong&gt;direct configuration&lt;/strong&gt; (a hard-coded URL) for tightly coupled systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Authentication
&lt;/h3&gt;

&lt;p&gt;Credentials &lt;strong&gt;never&lt;/strong&gt; travel in the body of the A2A message: they are sent in HTTP headers (&lt;code&gt;Authorization: Bearer …&lt;/code&gt;). Here, the client runs an OAuth2 &lt;em&gt;client credentials&lt;/em&gt; flow against the declared &lt;code&gt;tokenUrl&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — Sending the task (&lt;code&gt;SendMessage&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/a2a&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;travel.mycompany.example&lt;/span&gt;
&lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer eyJhbGciOi...&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json, text/event-stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SendMessage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ROLE_USER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parts"&lt;/span&gt;&lt;span class="p"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Book a direct Paris–Lyon flight on 12 November for employee 4432."&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;span class="p"&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;span class="nl"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&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;span class="nl"&gt;"returnImmediately"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;span class="p"&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;Things to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client &lt;strong&gt;does not supply&lt;/strong&gt; a task identifier: the server generates it and returns it.&lt;/li&gt;
&lt;li&gt;For a trivial answer, the server can return a &lt;code&gt;Message&lt;/code&gt; directly instead of creating a &lt;code&gt;Task&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;returnImmediately: true&lt;/code&gt; asks the server to hand back the newly created task right away, so the client can follow progress in parallel (in v1.0 the default behaviour is blocking).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4 — Tracking progress
&lt;/h3&gt;

&lt;p&gt;Three mechanisms, depending on duration and topology:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;th&gt;Operations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Polling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Short tasks, simple client&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GetTask&lt;/code&gt; at regular intervals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE streaming&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The client stays connected and wants real-time updates&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SendStreamingMessage&lt;/code&gt; (send + stream) or &lt;code&gt;SubscribeToTask&lt;/code&gt; (subscribe to an existing task)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Push notifications&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very long tasks, disconnected client (mobile, serverless)&lt;/td&gt;
&lt;td&gt;The client registers a webhook; the server POSTs updates to it, authenticated (signed JWT, HMAC or mTLS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example of a received SSE stream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data: {"task": {"id": "task-9876", "status": {"state": "TASK_STATE_SUBMITTED"}}}

data: {"statusUpdate": {"taskId": "task-9876", "status": {"state": "TASK_STATE_WORKING"}}}

data: {"artifactUpdate": {"taskId": "task-9876", "artifact": {"artifactId": "a1", "name": "itinerary", "parts": [ ... ]}}}

data: {"statusUpdate": {"taskId": "task-9876", "status": {"state": "TASK_STATE_COMPLETED"}}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stream closes automatically when the task reaches a terminal state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 — Multi-turn exchange (if needed)
&lt;/h3&gt;

&lt;p&gt;If the remote agent needs clarification ("economy or business class?"), the task moves to &lt;code&gt;INPUT_REQUIRED&lt;/code&gt;. The client replies with a new &lt;code&gt;SendMessage&lt;/code&gt; carrying the same task identifier, and work resumes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 — Retrieving the result
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Artifacts&lt;/strong&gt; hold the final deliverable. A client that missed the stream can always call &lt;code&gt;GetTask&lt;/code&gt; to fetch the full task, with its history and artifacts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: a minimal Python client
&lt;/h3&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;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://travel.mycompany.example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Discovery
&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="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;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/.well-known/agent-card.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;book_flight&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;card&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;skills&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;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The agent does not offer the book_flight skill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;supportedInterfaces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;headers&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;Authorization&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;Bearer &amp;lt;token&amp;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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Send the task
&lt;/span&gt;&lt;span class="n"&gt;payload&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;jsonrpc&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;2.0&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method&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;SendMessage&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;params&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;ROLE_USER&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;parts&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&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;Book a Paris–Lyon flight on 12/11 for employee 4432.&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configuration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;returnImmediately&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&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;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&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="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&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="c1"&gt;# 3. Read the state (simple polling)
&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&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;jsonrpc&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;2.0&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method&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;GetTask&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;params&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;taskId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In production, prefer the official SDKs (Python, JavaScript, Java, Go, .NET) published by the A2A project over hand-written HTTP calls. The exact shape of &lt;code&gt;result&lt;/code&gt; depends on the server's response (&lt;code&gt;task&lt;/code&gt; or &lt;code&gt;message&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Task lifecycle
&lt;/h2&gt;

&lt;p&gt;![A2A task lifecycle: in-progress, interrupted and terminal states]&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%2Fy9i4l3w9uw9a1e1k9iy1.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%2Fy9i4l3w9uw9a1e1k9iy1.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State (v1.0)&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_SUBMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;in progress&lt;/td&gt;
&lt;td&gt;Accepted, not yet started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_WORKING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;in progress&lt;/td&gt;
&lt;td&gt;Actively being processed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_INPUT_REQUIRED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;interrupted&lt;/td&gt;
&lt;td&gt;Waiting for information from the client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_AUTH_REQUIRED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;interrupted&lt;/td&gt;
&lt;td&gt;The client must provide additional credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_COMPLETED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;terminal&lt;/td&gt;
&lt;td&gt;Finished successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_FAILED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;terminal&lt;/td&gt;
&lt;td&gt;Unrecoverable error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_CANCELED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;terminal&lt;/td&gt;
&lt;td&gt;Cancelled (often via &lt;code&gt;CancelTask&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TASK_STATE_REJECTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;terminal&lt;/td&gt;
&lt;td&gt;Refused by the agent (policy, quota, capability)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  7. Security
&lt;/h2&gt;

&lt;p&gt;A2A builds on web standards rather than inventing its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted transport&lt;/strong&gt;: HTTPS (TLS 1.2 minimum) is mandatory in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication schemes&lt;/strong&gt; aligned with OpenAPI: OAuth 2.0, OpenID Connect, API key, mTLS. Declared in &lt;code&gt;securitySchemes&lt;/code&gt; and required via &lt;code&gt;security&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signed Agent Cards&lt;/strong&gt;: a JWS signature (RFC 7515) computed over a canonical form of the card (RFC 8785) makes any tampering detectable. Otherwise, a modified card could redirect calls or inject malicious content into the client agent's prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended Agent Card&lt;/strong&gt;: an agent can expose a minimal public card and a more detailed one, available only after authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability&lt;/strong&gt;: the specification recommends OpenTelemetry and propagating W3C Trace Context headers (&lt;code&gt;traceparent&lt;/code&gt;) to follow a request across the whole chain of agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For high assurance: combine mTLS and OAuth2, or use sender-constrained tokens (DPoP, RFC 9449), so that a stolen token is useless from another machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. A2A vs MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reminder: what is MCP?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;, launched by Anthropic in late 2024 and now widely adopted, standardises the connection between an AI application (the &lt;em&gt;client/host&lt;/em&gt;) and &lt;strong&gt;servers&lt;/strong&gt; that expose &lt;strong&gt;tools&lt;/strong&gt;, &lt;strong&gt;resources&lt;/strong&gt; (data) and &lt;strong&gt;prompts&lt;/strong&gt;. It runs on JSON-RPC 2.0, locally over &lt;code&gt;stdio&lt;/code&gt; or remotely over HTTP. Its specifications are named by date; the &lt;strong&gt;2026-07-28&lt;/strong&gt; revision notably made the protocol &lt;strong&gt;stateless&lt;/strong&gt; (sessions and the initialisation handshake were removed), which makes horizontal scaling of servers easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;A2A&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Make autonomous agents collaborate&lt;/td&gt;
&lt;td&gt;Give an agent access to tools and data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A colleague you delegate a file to&lt;/td&gt;
&lt;td&gt;A toolbox you use yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nature of the counterpart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Opaque, autonomous, reasons on its own&lt;/td&gt;
&lt;td&gt;A deterministic function or exposed data source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit of exchange&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A task (&lt;code&gt;Task&lt;/code&gt;) with a state and a lifecycle&lt;/td&gt;
&lt;td&gt;A tool call / resource read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typical duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Seconds to days, multi-turn&lt;/td&gt;
&lt;td&gt;Usually short, request/response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interaction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Negotiation possible (&lt;code&gt;INPUT_REQUIRED&lt;/code&gt;, &lt;code&gt;AUTH_REQUIRED&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The model calls, the tool answers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Public Agent Card (&lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;), registries&lt;/td&gt;
&lt;td&gt;Listing tools/resources on the connected server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transports&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JSON-RPC 2.0, gRPC, HTTP+JSON&lt;/td&gt;
&lt;td&gt;JSON-RPC 2.0 over stdio or HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Async / long-running&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native: SSE, webhook push notifications&lt;/td&gt;
&lt;td&gt;Possible but less central&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multimodal via Parts (text, JSON, files, bytes)&lt;/td&gt;
&lt;td&gt;Structured results, text, images, resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Topology&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Peer-to-peer, interchangeable client/server roles&lt;/td&gt;
&lt;td&gt;Host → client → server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin / governance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google → Linux Foundation&lt;/td&gt;
&lt;td&gt;Anthropic → open governance by the MCP community&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maturity (Oct. 2026)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;v1.0 stable (March 2026)&lt;/td&gt;
&lt;td&gt;2026-07-28 revision stable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The right question to ask
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Do I want to &lt;strong&gt;perform a precise action&lt;/strong&gt;, or &lt;strong&gt;hand over a goal&lt;/strong&gt; to someone who will figure out how to reach it?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A precise, deterministic action&lt;/strong&gt; (an SQL query, reading a file, calling a weather API) → &lt;strong&gt;MCP&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A goal to reach&lt;/strong&gt; by a system that reasons, can ask questions, and whose implementation you don't want to know about → &lt;strong&gt;A2A&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a grey area: you can expose a whole agent as an MCP "tool". That works for simple cases, but you lose what makes A2A strong: state tracking, multi-turn exchanges, long-running tasks and cross-organisation discovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Using A2A and MCP together
&lt;/h2&gt;

&lt;p&gt;The two protocols are &lt;strong&gt;complementary&lt;/strong&gt; and together form the communication stack of multi-agent systems:&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%2F7jh0k95zjpqqw3x37qsu.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%2F7jh0k95zjpqqw3x37qsu.png" alt="An orchestrator delegates via A2A to two agents, each of which uses its own tools via MCP" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user asks the orchestrator: "Organise Marie's interview in Lyon on Thursday."&lt;/li&gt;
&lt;li&gt;The orchestrator delegates via &lt;strong&gt;A2A&lt;/strong&gt; to the HR agent ("schedule the interview") and to the travel agent ("book the trip").&lt;/li&gt;
&lt;li&gt;Each of these agents uses &lt;strong&gt;MCP&lt;/strong&gt; internally to query its own tools: calendar, HRIS, booking API.&lt;/li&gt;
&lt;li&gt;The agents return their &lt;strong&gt;artifacts&lt;/strong&gt; (confirmed slot, ticket) to the orchestrator via A2A.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rule of thumb: &lt;strong&gt;A2A between agents, MCP between an agent and its tools.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Current limitations and best practices
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Limitations to be aware of:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-skill data schema&lt;/strong&gt;: a skill declares that it accepts &lt;code&gt;application/json&lt;/code&gt;, but the specification does not standardise the exact schema expected. Workaround: publish a JSON Schema in the skill description or through an &lt;em&gt;Extension&lt;/em&gt; declared in the Agent Card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downscoping across delegation chains&lt;/strong&gt;: A2A says how to pass credentials, not how to narrow them when a highly privileged orchestrator delegates to a less privileged agent. Workaround: OAuth 2.0 Token Exchange (RFC 8693) to issue short-lived, narrowly scoped tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No standard registry&lt;/strong&gt;: discovery through a central catalogue is still something you build yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrating from 0.x to 1.0&lt;/strong&gt;: method names (&lt;code&gt;message/send&lt;/code&gt; → &lt;code&gt;SendMessage&lt;/code&gt;), prefixed state names, removal of the &lt;code&gt;kind&lt;/code&gt; and &lt;code&gt;final&lt;/code&gt; fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat the Agent Card as a &lt;strong&gt;contract&lt;/strong&gt;: any change to a skill, URL or capability must bump the version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign&lt;/strong&gt; Agent Cards and verify signatures on the client side.&lt;/li&gt;
&lt;li&gt;Serve the card with &lt;code&gt;Content-Type: application/json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Put an &lt;strong&gt;API gateway&lt;/strong&gt; in front of agents to centralise authentication, quotas, logging and long-lived connection handling.&lt;/li&gt;
&lt;li&gt;Instrument with &lt;strong&gt;OpenTelemetry&lt;/strong&gt; from day one: debugging a chain of five agents without distributed traces is painful.&lt;/li&gt;
&lt;li&gt;Never blindly trust content received from another agent: it is data, not instructions (prompt-injection risk).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. Conclusion
&lt;/h2&gt;

&lt;p&gt;A2A does for agents what HTTP did for documents or SMTP for email: it provides a &lt;strong&gt;common, neutral language&lt;/strong&gt; that lets heterogeneous systems cooperate without bespoke integration. With a stable v1.0, Linux Foundation governance and adoption by the major platforms, it is now ready for production use.&lt;/p&gt;

&lt;p&gt;MCP and A2A are not competitors: &lt;strong&gt;MCP gives an agent hands; A2A gives it colleagues.&lt;/strong&gt; A modern multi-agent architecture uses both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://a2a-protocol.org/latest/specification/" rel="noopener noreferrer"&gt;Official A2A specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/" rel="noopener noreferrer"&gt;A2A and MCP: how the two protocols relate (official A2A site)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/latest" rel="noopener noreferrer"&gt;MCP specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tyk.io/learning-center/a2a-protocol-architecture-and-technical-specification/" rel="noopener noreferrer"&gt;A2A v1.0 technical guide — Tyk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://metorial.com/for-ai-crawlers/mcp-vs-a2a" rel="noopener noreferrer"&gt;MCP vs A2A — Metorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://marwashli.github.io/articles-tech/en/2026/10/a2a-protocol/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. Une version française est disponible &lt;a href="https://marwashli.github.io/articles-tech/2026/10/a2a-protocole/" rel="noopener noreferrer"&gt;ici&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Authentication protocols for applications: REST APIs, MCP servers and beyond</title>
      <dc:creator>Marwa Sahli</dc:creator>
      <pubDate>Thu, 05 Nov 2026 08:28:32 +0000</pubDate>
      <link>https://dev.to/marwa_sahli_273c3e83f3039/authentication-protocols-for-applications-rest-apis-mcp-servers-and-beyond-58lb</link>
      <guid>https://dev.to/marwa_sahli_273c3e83f3039/authentication-protocols-for-applications-rest-apis-mcp-servers-and-beyond-58lb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There is no single ideal authentication protocol, only the right protocol for each situation: who is calling (a human, a service, an AI agent), on whose behalf, and at what level of risk. An API key is enough for an internal script; a consumer application requires OAuth 2.0 and OpenID Connect; a remote MCP server now follows a precise OAuth 2.1 profile.&lt;/p&gt;

&lt;p&gt;Two notions are often confused:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication (AuthN)&lt;/strong&gt;: proving &lt;em&gt;who&lt;/em&gt; you are (a user, an application, a machine).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization (AuthZ)&lt;/strong&gt;: determining &lt;em&gt;what&lt;/em&gt; you are allowed to do (read, write, administer).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OAuth 2.0 is a delegated &lt;strong&gt;authorization&lt;/strong&gt; protocol; OpenID Connect adds the &lt;strong&gt;authentication&lt;/strong&gt; layer on top. That distinction explains why the two are almost always used together.&lt;/p&gt;

&lt;p&gt;This article reviews the methods, from the simplest to the most robust, then looks in detail at MCP (Model Context Protocol) servers, which connect AI agents to tools and data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple methods: Basic Auth and API keys
&lt;/h2&gt;

&lt;p&gt;Both methods rely on a static secret sent with every request. They are easy to implement and suit internal or server-to-server exchanges, but never an application acting on behalf of third-party users.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Basic Authentication
&lt;/h3&gt;

&lt;p&gt;The client sends the username and password, Base64-encoded, in the &lt;code&gt;Authorization&lt;/code&gt; header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/api/orders&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Basic YWxpY2U6czNjcjN0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: standard (RFC 7617), supported everywhere, no infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits&lt;/strong&gt;: Base64 is not encryption, so HTTPS is mandatory; the password travels with every call; no expiry, no scope, revocation = changing the password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasonable use&lt;/strong&gt;: internal tools, admin endpoints behind a VPN, the initial exchange to obtain a token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API keys
&lt;/h3&gt;

&lt;p&gt;A long random string identifies the calling application, sent in a dedicated header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/v1/weather?city=Lyon&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;X-API-Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sk_live_4f9a…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: easy to issue, revoke and rotate; ideal for per-customer billing and quotas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits&lt;/strong&gt;: the key identifies an &lt;em&gt;application&lt;/em&gt;, not a &lt;em&gt;user&lt;/em&gt;; it is a "bearer" secret (whoever holds it can use it); often no expiry and no fine-grained scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best practices&lt;/strong&gt;: send it in a header, never in the URL (it would end up in logs); store only a hash on the server side; prefix keys (&lt;code&gt;sk_live_&lt;/code&gt;, &lt;code&gt;sk_test_&lt;/code&gt;) to make leaks easier to detect; allow several active keys so they can be rotated without downtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tokens: sessions, Bearer and JWT
&lt;/h2&gt;

&lt;p&gt;Instead of sending a permanent secret, the client exchanges proof of identity for a &lt;strong&gt;short-lived token&lt;/strong&gt;. This is the basis of every modern method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sessions and cookies
&lt;/h3&gt;

&lt;p&gt;After login, the server creates a session and returns its identifier in a cookie. This is the historical model for server-rendered web applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strengths&lt;/strong&gt;: immediate revocation (delete the session); &lt;code&gt;HttpOnly&lt;/code&gt;, &lt;code&gt;Secure&lt;/code&gt; and &lt;code&gt;SameSite&lt;/code&gt; cookies protect against theft by JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits&lt;/strong&gt;: server-side state (shared storage needed across instances), CSRF protection required, poorly suited to mobile clients or APIs consumed by third parties.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bearer tokens
&lt;/h3&gt;

&lt;p&gt;A bearer token (RFC 6750) is sent in the &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header. Whoever holds it can use it, so it must be short-lived, sent over HTTPS and never logged. The token can be &lt;strong&gt;opaque&lt;/strong&gt; (a string the server checks with the issuer through introspection, RFC 7662) or &lt;strong&gt;self-contained&lt;/strong&gt; (a JWT).&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Web Token (JWT)
&lt;/h3&gt;

&lt;p&gt;A JWT (RFC 7519) is made of three Base64URL-encoded parts: &lt;code&gt;header.payload.signature&lt;/code&gt;. The payload contains &lt;em&gt;claims&lt;/em&gt;:&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;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://auth.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user_8241"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1793011200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orders:read"&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;The API verifies the signature with the issuer's public key (published as a JWKS), then checks &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;aud&lt;/code&gt; and &lt;code&gt;exp&lt;/code&gt;, with no network call.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strengths&lt;/strong&gt;: fast local verification, ideal for microservices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pitfalls&lt;/strong&gt;: a JWT cannot be revoked before it expires (hence short lifetimes, 5 to 15 minutes, plus refresh tokens); reject the &lt;code&gt;none&lt;/code&gt; algorithm and pin the list of accepted algorithms (RS256, ES256…); the payload is readable by anyone, it is not encrypted; never forget to check the &lt;code&gt;aud&lt;/code&gt; audience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A JWT is a token &lt;strong&gt;format&lt;/strong&gt;, not a protocol: &lt;em&gt;how&lt;/em&gt; you obtain that token is what OAuth 2.0 defines.&lt;/p&gt;

&lt;h2&gt;
  
  
  OAuth 2.0 and OAuth 2.1: delegated authorization
&lt;/h2&gt;

&lt;p&gt;OAuth 2.0 (RFC 6749) lets an application access an API &lt;strong&gt;on behalf of a user, without ever knowing their password&lt;/strong&gt;. It is the de facto standard for public APIs (Google, GitHub, Microsoft, Salesforce…). OAuth 2.1, being finalised at the IETF, consolidates a decade of best practice: PKCE becomes mandatory, and the implicit and "password" flows are removed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The four roles
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Resource owner&lt;/td&gt;
&lt;td&gt;The user who gives consent&lt;/td&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;The application requesting access&lt;/td&gt;
&lt;td&gt;A mobile app, an MCP client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization server&lt;/td&gt;
&lt;td&gt;Authenticates and issues tokens&lt;/td&gt;
&lt;td&gt;Keycloak, Auth0, Entra ID, Okta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource server&lt;/td&gt;
&lt;td&gt;The protected API that validates tokens&lt;/td&gt;
&lt;td&gt;The REST API, the MCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The recommended flow: Authorization Code + PKCE
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The client generates a random secret (&lt;code&gt;code_verifier&lt;/code&gt;) and its SHA-256 hash (&lt;code&gt;code_challenge&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It redirects the user to the authorization server with &lt;code&gt;client_id&lt;/code&gt;, &lt;code&gt;redirect_uri&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;code_challenge&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The user authenticates and gives consent.&lt;/li&gt;
&lt;li&gt;The authorization server redirects back to the client with a single-use &lt;strong&gt;authorization code&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The client exchanges this code, together with the &lt;code&gt;code_verifier&lt;/code&gt;, for an &lt;strong&gt;access token&lt;/strong&gt; (and often a &lt;strong&gt;refresh token&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;The client calls the API with &lt;code&gt;Authorization: Bearer &amp;lt;access_token&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PKCE (RFC 7636) prevents an attacker who intercepts the code from exchanging it, since they don't know the &lt;code&gt;code_verifier&lt;/code&gt;. It is now required for every client, including server-side applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other flows (grant types)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flow&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;User present?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authorization Code + PKCE&lt;/td&gt;
&lt;td&gt;Web apps, mobile apps, SPAs, MCP clients&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client Credentials&lt;/td&gt;
&lt;td&gt;Service to service, scheduled jobs, back ends&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device Authorization (RFC 8628)&lt;/td&gt;
&lt;td&gt;TVs, CLIs, connected devices without a browser&lt;/td&gt;
&lt;td&gt;Yes, on another device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refresh Token&lt;/td&gt;
&lt;td&gt;Renew an expired access token without logging in again&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Exchange (RFC 8693)&lt;/td&gt;
&lt;td&gt;A service swaps a token it received for a token for a downstream API&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;del&gt;Implicit&lt;/del&gt;, &lt;del&gt;Password&lt;/del&gt;
&lt;/td&gt;
&lt;td&gt;Removed in OAuth 2.1&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Scopes, audiences and refresh tokens
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scopes&lt;/strong&gt; (&lt;code&gt;orders:read&lt;/code&gt;, &lt;code&gt;profile&lt;/code&gt;) limit what the token allows; request the bare minimum.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;resource&lt;/strong&gt; parameter (RFC 8707) binds the token to a specific API: a token issued for API A must be rejected by API B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh tokens&lt;/strong&gt; must be rotated for public clients (each use issues a new one and invalidates the old one).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OpenID Connect and SAML: federated identity and SSO
&lt;/h2&gt;

&lt;p&gt;When the application needs to know &lt;strong&gt;who the user is&lt;/strong&gt; (single sign-on, "Sign in with Google"), you add an identity layer on top of OAuth: OpenID Connect for modern applications, SAML for existing enterprise systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenID Connect (OIDC)
&lt;/h3&gt;

&lt;p&gt;OIDC reuses OAuth 2.0's Authorization Code flow and adds the &lt;code&gt;openid&lt;/code&gt; scope. The authorization server (called the &lt;em&gt;OpenID Provider&lt;/em&gt;) returns, in addition to the access token, an &lt;strong&gt;ID token&lt;/strong&gt;: a signed JWT describing the user (&lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;auth_time&lt;/code&gt;…).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic discovery&lt;/strong&gt;: &lt;code&gt;/.well-known/openid-configuration&lt;/code&gt; publishes the endpoints, the algorithms and the public key (JWKS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UserInfo endpoint&lt;/strong&gt; to obtain additional attributes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Golden rule&lt;/strong&gt;: the ID token is for the &lt;em&gt;client&lt;/em&gt; to learn who the user is; the access token is for calling the &lt;em&gt;API&lt;/em&gt;. Never send an ID token to an API as an access token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SAML 2.0
&lt;/h3&gt;

&lt;p&gt;SAML (Security Assertion Markup Language) exchanges &lt;strong&gt;signed XML assertions&lt;/strong&gt; between an identity provider (IdP: ADFS, Okta, Entra ID) and a service provider (SP), through browser redirects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strengths&lt;/strong&gt;: very widespread in large enterprises and B2B SaaS, mature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits&lt;/strong&gt;: verbose, hard to implement correctly (XML signature wrapping attacks), designed for the browser rather than for APIs or mobile apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trend&lt;/strong&gt;: new projects favour OIDC; SAML is still needed to integrate with existing corporate directories, often through an identity broker (Keycloak, Auth0) that translates SAML into OIDC.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What about the users themselves?
&lt;/h3&gt;

&lt;p&gt;OIDC and SAML delegate identity verification to the identity provider, which can then use multi-factor authentication (MFA) or &lt;strong&gt;passkeys&lt;/strong&gt; (WebAuthn/FIDO2), today the most phishing-resistant method.&lt;/p&gt;

&lt;h2&gt;
  
  
  mTLS, HMAC signatures and DPoP: strong authentication between machines
&lt;/h2&gt;

&lt;p&gt;These mechanisms address the main weakness of bearer tokens (a stolen token can be used by anyone) by requiring &lt;strong&gt;proof of possession&lt;/strong&gt; of a key that only the legitimate client holds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mutual TLS (mTLS)
&lt;/h3&gt;

&lt;p&gt;In a classic TLS handshake, only the server presents a certificate. With mTLS, &lt;strong&gt;the client presents one too&lt;/strong&gt;, signed by a certificate authority the server trusts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Uses&lt;/strong&gt;: microservice communication (often automated by a &lt;em&gt;service mesh&lt;/em&gt; such as Istio or Linkerd), banking APIs and Open Banking, zero-trust architectures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With OAuth&lt;/strong&gt;: RFC 8705 binds an access token to the client's certificate (a &lt;em&gt;certificate-bound token&lt;/em&gt;); a stolen token is useless without the private key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraints&lt;/strong&gt;: certificate lifecycle management (issuance, rotation, revocation); harder to deploy in browsers or on mobile.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Request signing (HMAC)
&lt;/h3&gt;

&lt;p&gt;The client and the server share a secret key. The client computes a signature (HMAC-SHA256) over parts of the request (method, path, timestamp, body hash) and sends it in a header. The server recomputes it and compares.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Examples&lt;/strong&gt;: AWS Signature Version 4, webhook verification (Stripe, GitHub, Slack).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strengths&lt;/strong&gt;: the secret never travels; message integrity is guaranteed; the timestamp blocks replay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardisation&lt;/strong&gt;: RFC 9421 (HTTP Message Signatures) now standardises this approach, including with asymmetric keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DPoP (Demonstrating Proof of Possession)
&lt;/h3&gt;

&lt;p&gt;DPoP (RFC 9449) brings proof of possession to the application layer, without a certificate infrastructure. The client generates a key pair and attaches a small signed JWT (the &lt;code&gt;DPoP&lt;/code&gt; header) to every request, covering the method, the URL and a timestamp. The authorization server binds the access token to that public key.&lt;/p&gt;

&lt;p&gt;DPoP is a good fit for SPAs and mobile apps, where mTLS is hard to deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The case of MCP servers
&lt;/h2&gt;

&lt;p&gt;A remote MCP server is an &lt;strong&gt;OAuth 2.1 resource server&lt;/strong&gt;: under the 2026-07-28 revision of the specification, the client discovers the authorization server on its own, identifies itself with a URL (Client ID Metadata Document) and obtains a token valid for that server only. A local (stdio) MCP server, on the other hand, simply reads its credentials from the environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reminder: what is MCP?
&lt;/h3&gt;

&lt;p&gt;The Model Context Protocol is an open protocol that connects AI applications (Claude, ChatGPT, VS Code, Cursor…) to tools and data sources through "MCP servers". What makes it special from a security standpoint: an agent can connect to a server it has never seen before, with no prior relationship between the client's vendor and the server's. The authentication profile is designed for exactly that scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two transports, two approaches
&lt;/h3&gt;

&lt;p&gt;The specification makes authorization &lt;strong&gt;optional&lt;/strong&gt;, but sets out what to do depending on the transport.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transport&lt;/th&gt;
&lt;th&gt;Where the server runs&lt;/th&gt;
&lt;th&gt;Authentication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;stdio&lt;/td&gt;
&lt;td&gt;Local process launched by the host application&lt;/td&gt;
&lt;td&gt;Do not follow the OAuth specification; read credentials from the environment (variables, config files)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streamable HTTP&lt;/td&gt;
&lt;td&gt;Remote server reachable by URL&lt;/td&gt;
&lt;td&gt;The MCP specification's OAuth 2.1 profile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The old HTTP+SSE transport (2024-11-05 revision) is deprecated.&lt;/p&gt;

&lt;h3&gt;
  
  
  The authorization flow, step by step
&lt;/h3&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%2Fzd4twcmxl5m9jedzbv9g.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%2Fzd4twcmxl5m9jedzbv9g.png" alt="MCP authorization flow: 10 exchanges between the MCP client, the MCP server and the authorization server" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The client only needs the MCP server's URL: an initial &lt;code&gt;401&lt;/code&gt; rejection tells it where to find the metadata; it then runs a standard Authorization Code + PKCE flow and replays its request with the token.&lt;/p&gt;

&lt;p&gt;The server's &lt;code&gt;401&lt;/code&gt; response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;401&lt;/span&gt; &lt;span class="ne"&gt;Unauthorized&lt;/span&gt;
&lt;span class="na"&gt;WWW-Authenticate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The standard building blocks involved
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Building block&lt;/th&gt;
&lt;th&gt;Reference&lt;/th&gt;
&lt;th&gt;Role in MCP&lt;/th&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protected Resource Metadata&lt;/td&gt;
&lt;td&gt;RFC 9728&lt;/td&gt;
&lt;td&gt;The MCP server states which authorization server to use&lt;/td&gt;
&lt;td&gt;Mandatory (server and client)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization server metadata&lt;/td&gt;
&lt;td&gt;RFC 8414 and OpenID Connect Discovery&lt;/td&gt;
&lt;td&gt;Endpoint discovery&lt;/td&gt;
&lt;td&gt;The client must support both&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PKCE&lt;/td&gt;
&lt;td&gt;RFC 7636&lt;/td&gt;
&lt;td&gt;Protects the code exchange&lt;/td&gt;
&lt;td&gt;Mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource Indicators&lt;/td&gt;
&lt;td&gt;RFC 8707&lt;/td&gt;
&lt;td&gt;Binds the token to the target MCP server&lt;/td&gt;
&lt;td&gt;Mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client ID Metadata Documents (CIMD)&lt;/td&gt;
&lt;td&gt;IETF draft&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;client_id&lt;/code&gt; is an HTTPS URL pointing to a JSON document describing the client&lt;/td&gt;
&lt;td&gt;Recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic Client Registration&lt;/td&gt;
&lt;td&gt;RFC 7591&lt;/td&gt;
&lt;td&gt;Automatic client registration&lt;/td&gt;
&lt;td&gt;Optional, &lt;strong&gt;deprecated&lt;/strong&gt; in 2026-07-28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Issuer validation&lt;/td&gt;
&lt;td&gt;RFC 9207&lt;/td&gt;
&lt;td&gt;The client checks &lt;code&gt;iss&lt;/code&gt; before exchanging the code&lt;/td&gt;
&lt;td&gt;Mandatory since 2026-07-28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Client registration: the order of precedence
&lt;/h3&gt;

&lt;p&gt;How does an MCP client obtain a &lt;code&gt;client_id&lt;/code&gt; from an authorization server it doesn't know? The specification recommends this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;strong&gt;pre-registered&lt;/strong&gt; credentials for that server, if it has any.&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;Client ID Metadata Document&lt;/strong&gt; if the authorization server advertises &lt;code&gt;client_id_metadata_document_supported&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fall back to &lt;strong&gt;Dynamic Client Registration&lt;/strong&gt; if the server exposes a &lt;code&gt;registration_endpoint&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Otherwise, ask the user to enter the client details themselves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CIMD has the advantage of being portable: the identifier is a URL hosted by the client's vendor, resolved on demand by any authorization server, with no registration database to maintain.&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;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://app.example.com/oauth/client-metadata.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_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;"My MCP client"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redirect_uris"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:3000/callback"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"grant_types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"authorization_code"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_endpoint_auth_method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&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;h3&gt;
  
  
  MCP-specific security points
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No token passthrough&lt;/strong&gt;: the MCP server must never forward the token it received from the client to a downstream API; it obtains its own token (for example via Token Exchange).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audience validation&lt;/strong&gt;: the MCP server only accepts tokens issued specifically for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuer-bound credentials&lt;/strong&gt;: a client must associate its credentials with the authorization server that issued them and never reuse them with another one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental consent&lt;/strong&gt;: since the 2025-11-25 revision, a client can request additional scopes at the moment a tool needs them, rather than asking for everything up front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stdio servers&lt;/strong&gt;: secrets in the environment should have minimal scope, because the local process runs with the user's permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparison and decision guide
&lt;/h2&gt;

&lt;p&gt;The right choice depends first on whether a human user is involved, then on the level of risk.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Identifies&lt;/th&gt;
&lt;th&gt;Expiry / revocation&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;th&gt;Typical use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic Auth&lt;/td&gt;
&lt;td&gt;A user or a technical account&lt;/td&gt;
&lt;td&gt;None / password change&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Internal tools, administration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API key&lt;/td&gt;
&lt;td&gt;An application&lt;/td&gt;
&lt;td&gt;Manual / simple revocation&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Public APIs with quotas, server integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session + cookie&lt;/td&gt;
&lt;td&gt;A user&lt;/td&gt;
&lt;td&gt;Configurable / immediate&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Classic web applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JWT&lt;/td&gt;
&lt;td&gt;User or application&lt;/td&gt;
&lt;td&gt;Short / hard before expiry&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Microservices, stateless APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OAuth 2.0 / 2.1&lt;/td&gt;
&lt;td&gt;An application acting for a user&lt;/td&gt;
&lt;td&gt;Short + refresh / via the authorization server&lt;/td&gt;
&lt;td&gt;Medium to high&lt;/td&gt;
&lt;td&gt;APIs open to third parties, remote MCP clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenID Connect&lt;/td&gt;
&lt;td&gt;A user (identity)&lt;/td&gt;
&lt;td&gt;Short&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;SSO, "Sign in with…"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAML 2.0&lt;/td&gt;
&lt;td&gt;A user (identity)&lt;/td&gt;
&lt;td&gt;Short&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Legacy enterprise SSO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mTLS&lt;/td&gt;
&lt;td&gt;A machine&lt;/td&gt;
&lt;td&gt;Certificate lifetime / CRL, OCSP&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Zero trust, service mesh, finance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMAC signature&lt;/td&gt;
&lt;td&gt;An application + message integrity&lt;/td&gt;
&lt;td&gt;Per request (timestamp)&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Webhooks, cloud APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DPoP&lt;/td&gt;
&lt;td&gt;Application, token bound to a key&lt;/td&gt;
&lt;td&gt;Short&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High-security SPAs and mobile apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Decision shortcut:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user signs in to &lt;em&gt;your&lt;/em&gt; application → &lt;strong&gt;OIDC&lt;/strong&gt; (with passkeys or MFA at the identity provider).&lt;/li&gt;
&lt;li&gt;A third-party application accesses a user's data → &lt;strong&gt;OAuth 2.1 Authorization Code + PKCE&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A service calls another service → &lt;strong&gt;Client Credentials&lt;/strong&gt;, hardened with &lt;strong&gt;mTLS&lt;/strong&gt; or &lt;strong&gt;DPoP&lt;/strong&gt; if the data is sensitive.&lt;/li&gt;
&lt;li&gt;You receive webhooks → &lt;strong&gt;HMAC signature&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An internal script or a prototype → &lt;strong&gt;API key&lt;/strong&gt;, with rotation.&lt;/li&gt;
&lt;li&gt;A remote MCP server → &lt;strong&gt;the MCP specification's OAuth 2.1 profile&lt;/strong&gt; (previous section); a local MCP server → &lt;strong&gt;environment variables&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best practices and conclusion
&lt;/h2&gt;

&lt;p&gt;Whatever protocol you pick, most incidents come from implementation details rather than from the protocol itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS everywhere&lt;/strong&gt;, no exceptions, including internally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't reinvent&lt;/strong&gt;: rely on a proven authorization server (Keycloak, Auth0, Entra ID, Okta, Cognito…) and maintained libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short-lived tokens&lt;/strong&gt; (minutes) and rotated refresh tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate every token&lt;/strong&gt;: signature, issuer &lt;code&gt;iss&lt;/code&gt;, audience &lt;code&gt;aud&lt;/code&gt;, expiry &lt;code&gt;exp&lt;/code&gt;, scopes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege&lt;/strong&gt;: minimal scopes, one token per target API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets out of the code&lt;/strong&gt;: a vault (Vault, AWS Secrets Manager…) or environment variables, never in Git.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing sensitive in URLs or logs&lt;/strong&gt; (keys, tokens, authorization codes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; on authentication endpoints to slow down credential stuffing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log and monitor&lt;/strong&gt; token issuance, failures and revocations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For AI agents&lt;/strong&gt;: explicit user consent, tokens limited to the target MCP server, no token passthrough to downstream APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, API keys and Basic Auth remain useful for simple cases; OAuth 2.1 and OpenID Connect are the norm as soon as users and third parties are involved; mTLS, HMAC and DPoP harden machine-to-machine exchanges. MCP servers reinvent nothing: they apply these standards through a strict profile, designed so that AI agents can connect to thousands of services without prior registration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization" rel="noopener noreferrer"&gt;MCP — Authorization, 2026-07-28 revision&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/latest/basic/authorization/client-registration" rel="noopener noreferrer"&gt;MCP — Client Registration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ssojet.com/blog/mcp-authorization-spec-2026-07-28-what-changed" rel="noopener noreferrer"&gt;MCP Authorization Spec 2026-07-28: what changed (SSOJet)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ssojet.com/blog/mcp-authentication-oauth-tokens-security-ai-connections.md" rel="noopener noreferrer"&gt;MCP Authentication: requirements of the 2026-07-28 revision (SSOJet)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://den.dev/blog/mcp-november-authorization-spec/" rel="noopener noreferrer"&gt;What's new in the MCP 2025-11-25 authorization spec (den.dev)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;RFCs cited: 6749 (OAuth 2.0), 6750 (Bearer), 7519 (JWT), 7617 (Basic), 7636 (PKCE), 7662 (introspection), 8414, 8628, 8693, 8705, 8707, 9207, 9421, 9449, 9728 — available at &lt;a href="https://www.rfc-editor.org" rel="noopener noreferrer"&gt;rfc-editor.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://marwashli.github.io/articles-tech/en/2026/10/authentication-protocols-api-mcp/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. Une version française est disponible &lt;a href="https://marwashli.github.io/articles-tech/2026/10/protocoles-authentification-api-mcp/" rel="noopener noreferrer"&gt;ici&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>oauth</category>
      <category>api</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
