Bangun agent A2A sungguhan memakai SDK resmi. Kita membedah a2a-sdk Python dari AgentCard sampai executor, lalu SDK TypeScript @a2a-js/sdk dengan pattern handler dan streaming, lengkap dengan contoh client.

Di episode 5 kalian sudah menguasai method JSON-RPC inti — message/send, tasks/get, tasks/cancel, tasks/pushNotificationConfig, messages/list — beserta binding HTTP-nya. Sekarang waktunya naik ke lapisan yang lebih nyaman: SDK resmi. Dengan SDK, kerumitan serialisasi, task store, SSE streaming, dan penyajian Agent Card dibungkus rapi sehingga kalian cukup fokus pada logika agent. Roadmap episode ini: mulai dari a2a-sdk Python — install, struktur server, sampai client — lalu @a2a-js/sdk TypeScript dengan pattern handler dan streaming.
a2a-sdk Pythona2a-sdk adalah SDK Python resmi untuk A2A, menyediakan komponen server dan client sekaligus. Instalasi cukup satu baris, dengan ekstra opsional sesuai kebutuhan:
pip install a2a-sdk "a2a-sdk[fastapi]" "a2a-sdk[grpc]"Ekstra fastapi menambahkan integrasi server web, grpc untuk binding yang kita bedah di episode 11, telemetry untuk tracing OpenTelemetry, dan sql untuk task store berbasis database. a2a-sdk juga mencakup semua tipe inti protokol — Task, Message, TextPart, DataPart, FilePart, Artifact — sebagai model Pydantic.
Seperti yang sudah dipelajari, A2A server butuh empat komponen: AgentCard, TaskStore, executor, dan request handler. Mari kita bangun agent pemberi skor lead:
import uvicorn
from starlette.applications import Starlette
from a2a.helpers import get_message_text, new_task_from_user_message, new_text_message, new_text_part
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill, TaskState
class LeadScorerExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
task = new_task_from_user_message(context.message)
await event_queue.enqueue_event(task)
updater = TaskUpdater(event_queue=event_queue, task_id=task.id, context_id=task.context_id)
await updater.update_status(TaskState.TASK_STATE_WORKING, message=new_text_message("Menganalisis lead..."))
query = get_message_text(context.message)
result = f"Skor lead untuk '{query or 'kosong'}': 87 (prioritas tinggi)"
await updater.add_artifact(parts=[new_text_part(text=result, media_type="text/plain")])
await updater.update_status(TaskState.TASK_STATE_COMPLETED, message=new_text_message("Analisis selesai."))
card = AgentCard(
name="Lead Scorer",
description="Memberikan skor lead dari deskripsi singkat.",
url="http://127.0.0.1:9999",
version="1.0.0",
default_input_modes=["text/plain"],
default_output_modes=["text/plain"],
capabilities=AgentCapabilities(streaming=True),
supported_interfaces=[AgentInterface(protocol_binding="JSONRPC", url="http://127.0.0.1:9999", protocol_version="1.0")],
skills=[AgentSkill(id="skor_lead", name="Skor Lead", description="Menghitung skor lead dari deskripsi.", input_modes=["text/plain"], output_modes=["text/plain"])],
)
handler = DefaultRequestHandler(agent_executor=LeadScorerExecutor(), task_store=InMemoryTaskStore(), agent_card=card)
routes = create_agent_card_routes(card) + create_jsonrpc_routes(handler, "/")
app = Starlette(routes=routes)
uvicorn.run(app, host="127.0.0.1", port=9999)Alurnya jelas: LeadScorerExecutor adalah jantung logika — method execute menerima RequestContext dan EventQueue, lalu mendorong status lewat TaskUpdater; setiap update_status dan add_artifact diterjemahkan SDK menjadi event JSON-RPC yang tepat, termasuk streaming bila client meminta. Untuk agent satu-fungsi, SDK dan ADK juga menyediakan pintasan decorator task handler seperti @agent.task_method — cukup satu fungsi per skill yang mengembalikan Task berstatus completed. create_agent_card_routes menyajikan kartu di /.well-known/agent-card.json, sedangkan create_jsonrpc_routes memasang method JSON-RPC di root /.
A2AClientA2AClient menangani fetch Agent Card, penyusunan JSON-RPC, parsing respons, hingga streaming. Contoh klien untuk agent di atas:
from a2a.client import A2AClient
async def main() -> None:
async with A2AClient(url="http://127.0.0.1:9999") as client:
card = await client.get_agent_card()
print(f"Agent: {card.name}, streaming={card.capabilities.streaming}")
response = await client.send_message(
message={"role": "user", "parts": [{"kind": "text", "text": "Lead: PT Nusantara, budget besar"}]}
)
task = response.result
print(f"State: {task.status.state}")
asyncio.run(main())send_message mengirim method message/send dan mengembalikan task lengkap — cocok untuk pekerjaan cepat. Untuk tugas lama, ada send_message_subscribe yang membuka streaming; kita bedah khusus di episode 7.
@a2a-js/sdkDi sisi JavaScript, SDK resminya adalah @a2a-js/sdk, berjalan di Node.js dengan dukungan TypeScript penuh. SDK dibagi beberapa entry point: root export berisi tipe bersama (Message, Task, AgentCard), @a2a-js/sdk/server berisi executor dan request handler, dan @a2a-js/sdk/client berisi client factory.
npm install @a2a-js/sdk express uuid
npm install -D typescript tsx @types/express @types/uuidInti server TypeScript adalah interface AgentExecutor dengan method execute yang menerima RequestContext dan IExecutionEventBus. Versi TypeScript dari agent skor lead:
import type { AgentExecutor, RequestContext, IExecutionEventBus } from "@a2a-js/sdk";
class LeadScorerExecutor implements AgentExecutor {
async execute(requestContext: RequestContext, eventBus: IExecutionEventBus): Promise<void> {
const text = requestContext.message.parts.find((p) => p.kind === "text")?.text ?? "";
const score = `Skor lead untuk '${text}': 87 (prioritas tinggi)`;
const artifacts = [{ artifactId: "skor-1", parts: [{ kind: "text", text: score }] }];
await eventBus.publish({
kind: "task",
id: requestContext.taskId,
contextId: requestContext.contextId,
status: { state: "completed" },
artifacts,
});
}
async cancelTask(taskId: string, eventBus: IExecutionEventBus): Promise<void> {
console.log(`Membatalkan task ${taskId}`);
}
}Publish event kind: "task" langsung ke state completed setara dengan rangkaian update_status di Python. Untuk laporan progres, tambahkan publish kind: "status-update" dengan final: false di sela-sela pekerjaan. Wiring ke Express nyaris tanpa "rasa protokol":
import express from "express";
import { AGENT_CARD_PATH } from "@a2a-js/sdk";
import { DefaultRequestHandler, InMemoryTaskStore } from "@a2a-js/sdk/server";
import { agentCardHandler, jsonRpcHandler } from "@a2a-js/sdk/server/express";
import { leadScorerCard } from "./card";
import { LeadScorerExecutor } from "./executor";
const requestHandler = new DefaultRequestHandler(leadScorerCard, new InMemoryTaskStore(), new LeadScorerExecutor());
const app = express();
app.use(express.json());
app.get(AGENT_CARD_PATH, agentCardHandler(leadScorerCard));
app.post("/", jsonRpcHandler(requestHandler));
app.listen(4000, () => console.log("Lead Scorer siap di :4000"));Perhatikan AGENT_CARD_PATH — konstanta yang merujuk /.well-known/agent-card.json, path yang sama dengan versi Python. Konsistensi ini bukan kebetulan: kedua SDK mengikuti spesifikasi yang sama, sehingga agent Python dan TypeScript saling memanggil tanpa modifikasi.
@a2a-js/sdk/client menyediakan ClientFactory yang membaca Agent Card dari URL. Streaming ditawarkan lewat async generator, sehingga kalian bisa pakai for await secara alami:
import { ClientFactory } from "@a2a-js/sdk/client";
import { v4 as uuidv4 } from "uuid";
const client = await new ClientFactory().createFromUrl("http://localhost:4000");
const stream = client.sendMessageStream({
message: { kind: "message", messageId: uuidv4(), role: "user", parts: [{ kind: "text", text: "Lead: PT Nusantara, budget besar" }] },
});
for await (const event of stream) {
if (event.kind === "task") {
console.log(`[task] ${event.id} - ${event.status.state}`);
} else if (event.kind === "status-update") {
console.log(`[status] ${event.status.state}`);
}
if (event.kind === "task" && event.status.state === "completed") break;
}Client ini tidak peduli server di baliknya berbahasa Python atau TypeScript — ia hanya tahu bahwa URL tersebut menyajikan Agent Card yang valid. Justru itulah poin utama episode ini: SDK membuat perbedaan bahasa menjadi detail implementasi, bukan halangan integrasi.
Episode 6 membekali kalian dua SDK resmi untuk membangun dan mengonsumsi agent A2A. Di Python, a2a-sdk menyediakan komponen lengkap — AgentCard, DefaultRequestHandler, InMemoryTaskStore, AgentExecutor, TaskUpdater — dengan decorator task handler sebagai pintasan. Di TypeScript, @a2a-js/sdk menawarkan hal yang sama lewat AgentExecutor, DefaultRequestHandler, dan client streaming berbasis async generator.
Inti yang harus dibawa pulang:
a2a-sdk Python mencakup server dan client dalam satu paket, dengan model Pydantic untuk semua tipe protokol.TaskUpdater.@a2a-js/sdk dibagi ke entry point tipe, server, dan client, dengan Express middleware siap pakai.Kedua SDK tadi sudah mendukung streaming dan push notification, tapi kita baru menyentuh permukaannya. Di episode 7 kita membedah sampai dalam: Streaming & Push Notifications — cara kerja SSE untuk aliran event task secara real-time, konfigurasi pushNotificationConfig, strategi retry, hingga fallback ke polling. Sampai jumpa!