Satu agent melayani banyak tenant dengan isolasi konteks yang ketat, lalu bagaimana client dan server menentukan versi protokol yang sama. Termasuk strategi migrasi dari v0.3 ke v1.0 tanpa downtime.

Di episode 9 agent kalian sudah diamankan autentikasi dan signed agent card. Namun ada masalah praktis yang belum tersentuh: bagaimana satu instance agent melayani puluhan atau ratusan klien sekaligus — bank A, marketplace B, logistik C — tanpa mencampuradukkan data mereka? Dan bagaimana agent yang ditulis di v1.0 tetap bisa bicara dengan client yang masih v0.3? Episode ini menjawab keduanya, dua fitur yang justru menjadi pilar kematangan A2A v1.0.
Roadmap episode ini: kita bedah multi-tenancy — dari konsep, pemakaian tenant di wire protocol, hingga isolasi data — lalu version negotiation dan strategi migrasi antar versi.
Tenant adalah unit isolasi logis — biasanya satu organisasi atau satu proyek yang berlangganan agent. Dalam model multi-tenant, satu proses agent melayani banyak tenant sekaligus, dan setiap tenant melihat dirinya seolah-olah satu-satunya pengguna layanan. Bayangkan satu agent "Analisis Penjualan" yang melayani Bank A dan Marketplace B: keduanya memanggil endpoint yang sama, tapi task, pesan, dan hasil mereka tidak boleh bocor satu sama lain.
Konteks per-tenant memengaruhi tiga hal sekaligus: state (task apa yang sedang berjalan), data (dataset mana yang boleh diakses), dan konfigurasi (kunci, batas kuota, kebijakan tiap tenant). Ketiganya harus di-scope ke tenant, bukan dibagikan global. Tanpa pemisahan ini, satu bug kecil bisa menampilkan data Bank A kepada Marketplace B — insiden klasik SaaS.
A2A v1.0 mendukung multi-tenancy secara eksplisit: request JSON-RPC dapat membawa parameter tenant yang menandai pemilik task. Server membaca nilai ini dan memastikan seluruh lifecycle task berjalan dalam konteks tenant tersebut:
{
"jsonrpc": "2.0",
"id": 7,
"method": "message/send",
"params": {
"tenant": "acme-corp",
"message": {
"role": "user",
"parts": [{ "kind": "text", "text": "Analisis tren penjualan Q3" }]
}
}
}Di sisi SDK, tenant tidak hanya jadi label di payload — ia ikut menjadi bagian dari kunci penyimpanan task. Server bisa memakai task store yang memisahkan task berdasarkan tenant, sehingga tasks/get untuk tenant A tidak akan pernah menemukan task milik tenant B:
class TenantScopedTaskStore(InMemoryTaskStore):
def __init__(self) -> None:
self.tasks: dict[str, dict[str, Task]] = {}
def _key(self, tenant: str, task_id: str) -> str:
return f"{tenant}:{task_id}"
def get_task(self, tenant: str, task_id: str) -> Task | None:
return self.tasks.get(tenant, {}).get(task_id)Pola ini menegaskan prinsip penting: tenant adalah bagian dari identitas task, bukan metadata sekunder. Ia ikut dibawa dalam operasi tasks/get, tasks/cancel, dan push notification agar semua jalur akses memakai lensa tenant yang sama.
Info
Saat membangun agent multi-tenant, jadikan tenant bagian dari setiap jalur: autentikasi (token membawa klaim tenant), task store (kunci gabungan tenant dan task id), artefak (disimpan di namespace tenant), dan monitoring (label tenant pada metrik). Jika ada satu jalur yang lupa, di situ kebocoran akan terjadi.
Isolasi bukan hanya tentang task — ia mencakup seluruh permukaan data agent:
Dua prinsip teknis yang wajib dipegang. Pertama, tenant harus deterministik — nilai tenant selalu berasal dari sumber tepercaya (klaim token hasil autentikasi), bukan dari input task yang bisa dimanipulasi client. Kedua, default deny — tanpa pemetaan eksplisit, task tidak boleh mengakses data tenant lain.
Ekosistem A2A berkembang cepat — dari v0.1 sampai v1.0 dalam kurun waktu setahun — dan tidak semua orang bisa upgrade serentak. Version negotiation adalah mekanisme agar client dan server menentukan versi protokol yang sama sebelum transaksi dimulai, dan A2A menjadikannya bagian resmi dari Agent Card.
Dua titik yang dipakai berunding:
protocol_version yang dipahami server, dan tiap supported_interfaces bisa mencantumkan versi pada endpoint-nya.{
"agentName": "Analisis Lead",
"protocol_version": "1.0",
"supported_interfaces": [
{ "protocol_binding": "JSONRPC", "url": "https://lead-agent.example.com/", "protocol_version": "1.0" },
{ "protocol_binding": "JSONRPC", "url": "https://lead-agent.example.com/v0.3", "protocol_version": "0.3" }
]
}Karena perundingan terjadi di level kartu yang bisa di-cache, klien v0.3 dan v1.0 bisa hidup berdampingan memanggil agent yang sama — masing-masing ke endpoint versi mereka. Inilah kontrak back-compat yang membuat rollout bertahap menjadi mungkin.
A2A v1.0 memperkenalkan breaking changes dibanding v0.3 — penyesuaian nama method (tasks/send menjadi message/send), struktur event, dan mekanisme discovery baru. Migrasi yang aman tidak dilakukan dalam satu hari. Strategi bertahap yang umum:
a2a.compat.v0_3 untuk handler lama, sehingga logika agent ditulis sekali.Warning
Jangan menunda migrasi selamanya dengan alasan "masih kompatibel". Semakin lama dua versi berjalan, semakin besar biaya perawatan dan makin banyak client yang menggantung di endpoint lama. Tetapkan tanggal pensiun sejak hari pertama peluncuran endpoint baru.
Episode 10 membawa agent kalian ke level produksi multi-klien: multi-tenancy dengan parameter tenant yang menjadi bagian identitas task, isolasi data dan konfigurasi per tenant dengan prinsip deterministic tenant dan default deny, lalu version negotiation lewat protocol_version pada Agent Card yang memungkinkan client lama dan baru hidup berdampingan, ditutup strategi migrasi v0.3 ke v1.0 yang bertahap dan terukur.
Inti yang harus dibawa pulang:
tenant adalah unit isolasi logis; satu agent melayani banyak tenant tanpa campur data.tenant, dan task store memakai kombinasi tenant plus task id sebagai kunci.protocol_version di Agent Card dan supported_interfaces.Semakin banyak tenant dan semakin beragam client, satu hal mulai terasa mahal: overhead HTTP+JSON untuk setiap request. Di episode 11 kita membahas solusinya — gRPC Support — binding ketiga A2A dengan protobuf, keunggulan streaming dan backpressure, serta kapan gRPC lebih tepat daripada HTTP/JSON. Sampai jumpa!