Skip to content

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, with camelCase methods. If you know the Python SDK, you already know this one.

    The mapping

  • 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 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.

    The Laravel guide

  • 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();
    }
}
// app/A2A/HelloExecutor.php (php artisan a2a:make-executor Hello): the same class as the Plain PHP tab.

// routes/api.php
Route::a2a('/a2a', agentCard: HelloAgentCard::class, executor: HelloExecutor::class)
    ->middleware('auth:sanctum');
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.