A2A PHP SDK¶
Build and call A2A agents in PHP. The SDK has the same classes, in the same places, as the official Python SDK. There is a Laravel bridge for queued, streaming agents.
Targets A2A 1.0, the current version of the spec.
Early development
The client and the server are done. The server passes the official A2A test kit (Conformance), and the SDK talks to the official Python SDK in both directions. The Laravel bridge is next, and the API may still change before 1.0. Progress is on the roadmap.
-
Same shape as Python
AgentExecutor,RequestContext,TaskUpdater,DefaultRequestHandler,ClientFactory… The same names, withcamelCasemethods. If you know the Python SDK, you already know this one. -
Spec-exact on the wire
Types are generated from the official
a2a.proto(v1.0.0), so the JSON on the wire is exactly what the spec defines. The server passes the official A2A test kit at every level, checked in CI (Conformance). -
Laravel bridge
Route::a2a(), queued execution on your workers, live SSE streaming through Redis Streams or the database, and owner-scoped storage. PHP-FPM can't keep work running after a request ends, so the bridge moves long tasks to a queue. It passes the A2A TCK too. -
Works with any framework
The core is built only on PSR standards (PSR-7/15/17/18), so it runs under Symfony, Slim, Laravel or plain PHP. The Laravel bridge is a separate package.
A taste¶
An agent that answers "hello", written the same way in both SDKs:
final class HelloExecutor implements AgentExecutor
{
public function execute(RequestContext $context, EventQueue $eventQueue): void
{
$updater = new TaskUpdater($eventQueue, $context->taskId(), $context->contextId());
$updater->startWork();
$updater->addArtifact([new Part(['text' => 'Hello, ' . $context->getUserInput()])], name: 'response');
$updater->complete();
}
public function cancel(RequestContext $context, EventQueue $eventQueue): void
{
(new TaskUpdater($eventQueue, $context->taskId(), $context->contextId()))->cancel();
}
}
class HelloExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.start_work()
await updater.add_artifact([Part(text=f'Hello, {context.get_user_input()}')], name='response')
await updater.complete()
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
await TaskUpdater(event_queue, context.task_id, context.context_id).cancel()
Pick a tab once
The Plain PHP / Laravel / Python tabs are linked across the whole site. Choose one and every page follows.
Packages¶
| Package | Install |
|---|---|
praveendias1180/a2a-php: the SDK |
composer require praveendias1180/a2a-php |
praveendias1180/a2a-laravel: Laravel bridge |
composer require praveendias1180/a2a-laravel |
Requires PHP 8.2 or newer.