A2A Protocol: Cara AI Agent Menemukan dan Delegasi ke Peer

A2A, singkatan dari Agent2Agent, adalah standar terbuka yang memungkinkan satu AI agent mendelegasikan pekerjaan ke agent lain buatan tim atau vendor berbeda. Standar ini diumumkan Google pada April 2025, didonasikan ke Linux Foundation, dan mencapai versi 1.0.0 sebagai spesifikasi stabil pertamanya. Asumsi utamanya adalah opaque execution: agent bekerja sama lewat capability yang dideklarasikan tanpa membuka rencana atau tool internalnya.
MCP menghubungkan agent ke tools yang schema-nya boleh ia baca, biasanya di dalam satu organisasi. A2A menghubungkan agent ke agent lain yang isinya tidak bisa ia lihat, umumnya melintasi batas kepercayaan, sehingga authentication dan task lifecycle menjadi bagian dari protocol itu sendiri. Keduanya lapisan yang saling melengkapi, bukan pesaing, dan kebanyakan sistem nyata akan memakai keduanya.
Agent Card adalah dokumen JSON yang menjelaskan identitas, provider, version, skill, transport yang didukung, serta security scheme yang diwajibkan sebuah agent. Mekanisme discovery utama dalam spesifikasi adalah path tetap di domain agent itu sendiri, yaitu /.well-known/agent-card.json, walaupun registry dan konfigurasi langsung juga diperbolehkan. Card boleh ditandatangani dengan JSON Web Signature setelah dikanonikalisasi, sehingga client bisa memverifikasi daftar skill-nya tidak diubah.
Keduanya adalah interrupted state dalam task lifecycle A2A. TASK_STATE_INPUT_REQUIRED berarti agent di seberang masih butuh informasi dari manusia sebelum bisa lanjut, sedangkan TASK_STATE_AUTH_REQUIRED berarti ia butuh credential. Keduanya berbeda dari empat terminal state, dan keduanya datang bersama task id yang dipakai untuk melanjutkan task yang sama, bukan memulai task baru.
Mempublikasikan Agent Card itu murah dan layak dilakukan: cukup satu file JSON statis ditambah endpoint yang menjawab SendMessage untuk satu skill sempit. Sisi pemanggil yang mahal, karena harus menyimpan task id, menangani interrupted state, menjaga kanal update tetap hidup, dan mengurus credential. Aturan yang masuk akal adalah publikasikan dulu, lalu bangun sisi konsumen hanya setelah ada partner bernama yang ingin memanggil Anda.

Ringkasan Utama
A2A protocol, kini proyek Linux Foundation di versi 1.0.0, membuat satu AI agent bisa mendelegasikan pekerjaan ke agent lain yang isinya tidak bisa ia lihat. Agent mempublikasikan Agent Card berformat JSON di URL well-known, lalu bertukar task yang lifecycle-nya punya interrupted state untuk input dan credential yang belum ada, bukan berpura-pura delegasi selalu selesai.
Setiap MCP server yang pernah saya kirim berhenti di batas yang sama. Server itu mengekspos function milik saya, berjalan di infrastruktur yang saya kendalikan, dengan schema yang saya tulis sendiri. Itu cukup selama kedua ujung koneksinya milik saya. Begitu pekerjaannya milik sistem orang lain, cara itu gugur, karena Anda tidak bisa menerbitkan tool schema untuk proses yang isinya memang tidak boleh Anda lihat.
Jadi saya membaca spesifikasi A2A seperti seorang ERP developer membaca standar integrasi mana pun: mencari apa yang dikatakannya tentang bagian yang gagal. Tulisan ini adalah hasil bacaan itu. Semua hal teknis di sini berasal dari spesifikasi A2A v1.0.0 dan rilis satu tahun dari Linux Foundation, sedangkan kesimpulan di akhir adalah milik saya.
Dua protocol ini terlihat bersebelahan, padahal tidak. MCP server adalah permukaan yang boleh Anda baca: setiap tool punya nama, JSON schema dan tipe hasil, dan tugas client hanya memilih lalu memanggilnya. A2A berangkat dari premis sebaliknya. Guiding principles-nya memuat Opaque Execution, yang menurut spesifikasi berarti agent berkolaborasi lewat capability yang dideklarasikan dan informasi yang dipertukarkan, tanpa perlu membuka isi pikiran, rencana, atau implementasi tool masing-masing.
| Pertanyaan | MCP | A2A |
|---|---|---|
| Apa yang ada di ujung sana | Permukaan tool: function bernama dengan schema yang dideklarasikan | Agent yang menyusun rencana, dan bisa mendelegasikan lagi ke pihak lain |
| Bagaimana menemukannya | Config client, entry di registry, atau perintah yang menjalankannya | Agent Card yang diambil dari URL well-known di domain peer |
| Apa yang dikembalikan satu panggilan | Hasil tool, saat itu juga | Task dengan lifecycle yang bisa mandek berjam-jam |
| Siapa yang memiliki kegagalan | Anda, karena itu server Anda sendiri | Peer, dan Anda hanya melihat state yang ia pilih untuk dipublikasikan |
| Di mana batas kepercayaannya | Umumnya di dalam satu organisasi | Umumnya lintas dua organisasi, karena itu auth masuk ke protocol |
Satu prinsip itu mengubah seluruh keputusan desain sesudahnya. Anda tidak bisa memvalidasi argumen milik peer, mengulang langkah-langkahnya satu per satu, atau memperkirakan berapa lama seharusnya, karena semuanya memang tidak diekspos. Yang Anda dapat adalah skill yang dideklarasikan, sebuah task id, dan sebuah state. Tugas protocol ini hanya satu: membuat antarmuka setipis itu tetap bertahan menghadapi outage, antrean approval, dan identity provider milik partner.
Sebuah A2A server wajib mempublikasikan Agent Card, dan mekanisme discovery pertama dalam spesifikasi adalah path tetap di domain server itu sendiri: /.well-known/agent-card.json. Card tersebut memuat identitas, provider, version, skill yang ditawarkan, transport yang didukung dalam urutan preferensi, serta security scheme yang harus dipenuhi pemanggil. Discovery dan authentication tinggal di dokumen yang sama, dan bagian ini yang tidak saya duga.
# The entire discovery surface: one cacheable document at a fixed path.
GET /.well-known/agent-card.json HTTP/1.1
Host: erp.contoh.co.id
{
"name": "Delivery Scheduling Agent",
"description": "Reserves dock time against warehouse capacity for a confirmed purchase order.",
"provider": { "organization": "Contoh Logistik", "url": "https://erp.contoh.co.id" },
"version": "1.4.0",
// Ordered and normative: a client MUST take the first binding it speaks,
// not the one it would rather use.
"supportedInterfaces": [
{ "url": "https://erp.contoh.co.id/a2a/v1", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" },
{ "url": "https://erp.contoh.co.id/a2a/json", "protocolBinding": "HTTP+JSON", "protocolVersion": "1.0" }
],
"capabilities": { "streaming": true, "pushNotifications": true, "extendedAgentCard": false },
// Discovery and auth are the same document. A caller learns what you can do
// and which token you want in one GET.
"securitySchemes": {
"partner-oidc": {
"openIdConnectSecurityScheme": {
"openIdConnectUrl": "https://sso.contoh.co.id/.well-known/openid-configuration"
}
}
},
"securityRequirements": [
{ "schemes": { "partner-oidc": { "list": ["openid", "delivery.write"] } } }
],
"defaultInputModes": ["application/json", "text/plain"],
"defaultOutputModes": ["application/json"],
"skills": [
{
"id": "book-delivery-slot",
"name": "Book a delivery slot",
"description": "Reserves a dock and a time window for a confirmed purchase order.",
"tags": ["logistics", "scheduling", "erp"],
"examples": ["Book the earliest slot for PO-2026-0184 at the Semarang DC."],
"inputModes": ["application/json"],
"outputModes": ["application/json"]
}
]
}Comment di dalam card itu tambahan saya; file aslinya JSON polos, dan nama field-nya diambil langsung dari contoh di spesifikasi. Dua detail layak dicontek meski Anda tidak pernah memakai A2A. Array supportedInterfaces bersifat berurutan dan normatif, sehingga client harus memilih binding pertama yang ia dukung, bukan yang paling ia sukai. Selain itu card memang dirancang cacheable: server sebaiknya mengirim Cache-Control dan ETag yang diturunkan dari field version, dan client sebaiknya melakukan revalidasi dengan If-None-Match alih-alih mengunduh ulang dokumen yang setahun hanya berubah beberapa kali.
Card juga boleh ditandatangani dengan JSON Web Signature, dan spesifikasinya tegas soal langkah yang paling sering keliru. Sebelum ditandatangani, isinya harus dikanonikalisasi memakai JSON Canonicalization Scheme, supaya card yang sama diserialisasi oleh dua library berbeda menghasilkan byte yang sama, dan karenanya signature yang sama. Card bertanda tangan adalah cara partner membuktikan daftar skill itu datang dari mereka, bukan dari siapa pun yang terakhir mengedit config Anda.

Perlakukan field version sebagai cache key Anda. Naikkan nilainya setiap kali skill, transport atau security scheme berubah, turunkan ETag dari sana, dan setiap request If-None-Match dari partner cukup dibayar dengan 304, bukan card utuh.
A2A v1.0.0 mendefinisikan tiga protocol binding di atas satu data model, dan mengizinkan binding kustom: JSON-RPC 2.0, gRPC, serta HTTP+JSON/REST. Nama method-nya sama di ketiganya, dan itulah gunanya memisahkan binding dari model: SendMessage dan SendStreamingMessage, GetTask dan ListTasks, CancelTask, SubscribeToTask, method untuk push notification config, serta GetExtendedAgentCard. Memilih binding adalah urusan pipa. Memilih pola interaksi bukan.
# 1. Delegate. SendMessage is BLOCKING by default, and "blocking" means it
# returns when the task reaches a terminal state OR an interrupted one --
# not when the work is finished.
POST /a2a/v1 HTTP/1.1
Host: erp.contoh.co.id
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"messageId": "8f31c0d2-4c1e-4f9a-9a3e-1b70b0c2e5aa",
"parts": [{ "text": "Book the earliest slot for PO-2026-0184 at the Semarang DC." }]
},
"configuration": {
"returnImmediately": false,
"acceptedOutputModes": ["application/json"]
}
}
}
# 2. It did not complete. It stalled -- and said so, with a question attached.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"task": {
"id": "9f2c1e40-6d55-4d29-9d2f-3b6c9a7f1e04",
"contextId": "3a77b512-1f0c-49a4-bd6e-2f2a0e5d77c1",
"status": {
"state": "TASK_STATE_INPUT_REQUIRED",
"message": {
"role": "ROLE_AGENT",
"parts": [{ "text": "Two docks are free on 11 Sep. Which one, and is a tail lift needed?" }]
}
}
}
}
}
# 3. Resume the SAME task by echoing its id. Sending a fresh message with no
# taskId opens a DIFFERENT task -- and an opaque peer will not tell you it
# just booked a second slot.
{
"jsonrpc": "2.0",
"id": 2,
"method": "SendMessage",
"params": {
"message": {
"taskId": "9f2c1e40-6d55-4d29-9d2f-3b6c9a7f1e04",
"contextId": "3a77b512-1f0c-49a4-bd6e-2f2a0e5d77c1",
"role": "ROLE_USER",
"messageId": "b41d7f66-90a2-4c7d-8f1b-5c2d9e3a4f09",
"parts": [{ "text": "Dock 2, tail lift required." }]
}
}
}Baris yang paling gampang terlewat justru yang paling penting. SendMessage bersifat blocking secara default, dan blocking di sini berarti ia kembali saat task mencapai terminal state atau interrupted state. Task yang mandek adalah response yang sukses, bukan error. Setel returnImmediately ke true dan Anda langsung menerima task id, dengan konsekuensi harus melakukan polling GetTask, menahan stream SubscribeToTask, atau mendaftarkan webhook sendiri.
Ada delapan task state, dan dua yang paling berguna justru dua yang bukan akhir. TASK_STATE_INPUT_REQUIRED berarti agent masih butuh sesuatu dari manusia sebelum bisa lanjut. TASK_STATE_AUTH_REQUIRED berarti ia butuh credential. Spesifikasi menyebut keduanya interrupted state dan memisahkannya dari empat terminal state, yaitu completed, failed, canceled dan rejected. Pembedaan itu bagian paling jujur dari dokumen ini.
Siapa pun yang pernah membangun approval chain di ERP langsung mengenali polanya. Sebuah purchase requisition tidak gagal hanya karena managernya cuti; ia menunggu. Kebanyakan protocol integrasi tidak punya kosakata untuk menunggu, jadi keadaan itu diterjemahkan menjadi timeout, padahal timeout adalah kebohongan tentang giliran siapa sekarang. A2A memberi kemandekan itu sebuah state, status message yang menjelaskan apa yang dibutuhkan, dan task id untuk melanjutkannya. Aturan in-task authorization layak dibaca utuh, tetapi empat di antaranya menentukan desain Anda:

Kemandekan bisa berakhir tanpa memberi tahu Anda. Credential sering tiba out of band, dan agent boleh langsung melanjutkan proses tanpa message susulan dari client, sehingga pemanggil yang tidak memegang stream, webhook atau polling akan melewatkan penyelesaiannya dan melaporkan task menggantung yang sebenarnya sudah selesai berjam-jam lalu.
Begitu sebuah task mencapai completed, canceled, rejected atau failed, ia tidak bisa dijalankan ulang. Setiap perbaikan adalah task baru yang membawa contextId yang sama, dan boleh menyebut task lama lewat referenceTaskIds. Alasannya di spesifikasi adalah kejelasan audit dan saya setuju, tetapi konsekuensinya memindahkan kerja nyata ke sisi pemanggil: Anda kini memiliki state machine kecil yang durable untuk setiap delegasi, bukan sekadar satu request dan satu response.
// Terminal is terminal: a completed, failed, canceled or rejected task cannot
// be restarted. Follow-up work is a NEW task carrying the same contextId.
const TERMINAL = new Set([
"TASK_STATE_COMPLETED",
"TASK_STATE_FAILED",
"TASK_STATE_CANCELED",
"TASK_STATE_REJECTED",
]);
const INTERRUPTED = new Set([
"TASK_STATE_INPUT_REQUIRED", // a human still has to answer something
"TASK_STATE_AUTH_REQUIRED", // a credential still has to be fetched
]);
async function onTaskUpdate(task: A2ATask): Promise<void> {
const state = task.status.state;
if (TERMINAL.has(state)) {
// Never send to task.id again. A revision is a new task, same contextId,
// with the old id listed in referenceTaskIds so the audit trail survives.
await closeDelegation(task.id, state);
return;
}
if (INTERRUPTED.has(state)) {
// The stall is the normal case, not the error path. Park it against the
// ERP document and notify a human -- but keep the subscription open:
// an out-of-band credential can un-stall the task with no message from us.
await parkForHuman(task.id, task.contextId, task.status.message);
return;
}
// TASK_STATE_SUBMITTED / TASK_STATE_WORKING: keep exactly one of the three
// update channels alive -- SubscribeToTask, a push-notification webhook, or
// a GetTask poll. Drop all three and the completion arrives nowhere.
}Itulah ongkos jujur dari mendelegasikan ke sesuatu yang isinya tidak bisa Anda lihat. Pada tool MCP, crash hanya menghilangkan satu panggilan yang bisa Anda ulang. Pada task A2A, crash menghilangkan satu-satunya pegangan Anda atas pekerjaan yang masih dikerjakan partner atas nama Anda.
Soal tata kelola sekarang sudah jelas, berbeda dengan situasi 2025. Google mengumumkan A2A pada 9 April 2025 bersama lebih dari 50 launch partner lalu mendonasikannya ke Linux Foundation. Rilis satu tahun dari Linux Foundation, bertanggal 9 April 2026, menyebut lebih dari 150 organisasi pendukung, lebih dari 22.000 bintang di GitHub, dan versi 1.0 sebagai spesifikasi stabil pertama. Rilis yang sama mencatat Agent Payments Protocol punya lebih dari 60 organisasi pendukung.
Dukungan platform mudah diverifikasi: Microsoft mengintegrasikan A2A ke Azure AI Foundry dan Copilot Studio, AWS menambahkan dukungan lewat Amazon Bedrock AgentCore Runtime, dan Google Cloud juga mengirimkannya. Lima SDK disebut sudah production-ready, mencakup Python, JavaScript, Java, Go dan .NET. Pemakaian produksi disebut per vertikal, bukan per pelanggan, yaitu supply chain, financial services, insurance dan IT operations. Perhatikan apa yang tidak dikatakan kalimat itu. Tidak ada nama perusahaan, dan saya tidak akan menyusun roadmap di atas referensi tanpa nama.
Spesifikasi, kelima SDK dan contoh agent-nya berada di organisasi a2aproject di GitHub, jadi dokumen dan kode yang mengimplementasikannya bergerak bersama. Kalau Anda hanya sempat membaca satu bagian sebelum memutuskan apa pun, baca bagian task lifecycle; isinya pendek, dan di situlah keputusan desainnya berada.
Untuk tim kecil, jawabannya sengaja asimetris: publikasikan Agent Card, jangan bangun peer mesh dulu. Mempublikasikan berarti satu file JSON statis ditambah endpoint yang bisa menjawab SendMessage untuk satu skill sempit. Sisi konsumsi yang mahal, karena di sana ada task store yang durable, penanganan interrupted state, urusan credential, dan partner yang mau menandatangani kesepakatan tentang apa yang boleh dilakukan agent mereka terhadap data Anda.
State machine-nya adalah bukti untuk urutan itu. Semua yang mahal dari A2A berada di sisi pemanggil: interrupted state, aturan melanjutkan task, dan kenyataan bahwa terminal berarti final. Sisi penyedia hanya sebuah dokumen dan dua method.
MCP membuat tool milik sebuah agent jadi portabel. A2A berusaha membuat rekan kerja sebuah agent jadi portabel, dan itu lebih sulit, karena seorang rekan bisa menjawab belum sekarang. Nilailah protocol ini dari task lifecycle-nya, bukan dari jumlah partnernya. Standar yang menjadikan input-required dan auth-required sebagai state kelas satu adalah standar yang memang memperkirakan pekerjaan nyata akan mandek, dan perkiraan itulah alasan ia pantas diperhatikan tim ERP kecil sekarang, sebagai card untuk dipublikasikan, bukan mesh untuk dibangun.
Sumber dan bacaan lanjutan