Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

a2a-cpp C++20 Agent2Agent (A2A) SDK Documentation

a2a-cpp is a C++20 SDK for building Agent2Agent (A2A) protocol clients and servers. The current documented release focuses on production-oriented protocol coverage: REST, JSON-RPC, and gRPC transports; Agent Card discovery; task lifecycle APIs; streaming; push notification configuration APIs; authentication metadata propagation; interceptors; CMake package exports; and vcpkg-oriented packaging.

What is included

  • Client API: SendMessage, GetTask, ListTasks, CancelTask, streaming send/subscribe, and task push-notification config lifecycle calls.
  • Server API: executor-driven dispatch for REST, JSON-RPC, and gRPC transports.
  • Discovery: public and extended Agent Card fetch plus preferred-interface resolution.
  • Streaming: client observers, cancellable stream handles, and server stream sessions.
  • Authentication hooks: client credential providers and server request metadata extraction.
  • Operational extensions: client/server interceptors, required-extension validation, task stores, task history ordering, UUIDv7 task IDs, and optional PostgreSQL stores.
  • Build integration: CMake 3.25+, C++20, installable CMake package exports, generated protobuf headers, and vcpkg overlay/public-registry preparation.
  1. Installation and Build for toolchain requirements and CMake options.
  2. Quickstart for a copy/paste example flow.
  3. Client Overview or Server Overview, depending on your integration role.
  4. Transports, Streaming, and Authentication for runtime design decisions.
  5. API Reference when you need generated public-header details.

Version and support notes

  • See Releases and Versions for current release details and versioning guidance.
  • The SDK is C++20-only and exports CMake targets under the a2a:: namespace.
  • Package documentation describes vcpkg workflows only.
  • Examples are deterministic and are intended to run without external services unless the example README says otherwise.

Installation and Build

This page reflects the current build surface for the latest documented release.

Choose an integration method

Use the workflow that matches your application:

  • Build the SDK directly from source as described below.
  • Use CMake FetchContent and pin a release tag or reviewed commit.
  • Install the SDK as a CMake package and consume it with find_package.
  • Consume the repository-local vcpkg overlay port.

Prerequisites

Install these tools before configuring the repository:

  • CMake 3.25 or newer.
  • A C++20 compiler: GCC, Clang, AppleClang, or Visual Studio 2022.
  • Protobuf with protoc.
  • gRPC C++ with grpc_cpp_plugin.
  • libcurl when using the built-in HTTP, JSON-RPC, or SSE clients.
  • clang-format and clang-tidy for contributor validation.
  • Optional: Doxygen for API reference generation.
  • Optional: PostgreSQL client libraries when building PostgreSQL-backed stores.

Debian or Ubuntu

./scripts/install_build_deps.sh
cmake --version

The installer supports Debian and Ubuntu. Confirm that the installed CMake version is 3.25 or newer. Some older distributions, including Ubuntu 22.04, provide an older CMake package and require a newer CMake installation from another source.

The same script also supports Windows when run from Git Bash.

macOS

brew install cmake ninja protobuf grpc re2 abseil curl

When enabling PostgreSQL-backed stores, also install the PostgreSQL client package:

brew install libpq

Windows Git Bash

Install Visual Studio 2022 with the Desktop development with C++ workload and Git for Windows. Open Git Bash in the repository root and run:

./scripts/install_build_deps.sh

The script first checks VCPKG_ROOT, then searches for vcpkg.exe or vcpkg on PATH, and then checks $HOME/vcpkg. It clones and bootstraps vcpkg under ${VCPKG_ROOT:-$HOME/vcpkg} only when an existing installation is not found. It installs the dependencies declared by the root vcpkg.json and prints the resolved VCPKG_ROOT, target triplet, and host triplet when it finishes.

Override VCPKG_ROOT, VCPKG_TARGET_TRIPLET, or VCPKG_HOST_TRIPLET before running the script when needed. Keep the resolved VCPKG_ROOT value for the CMake configuration step below; do not reset it to $HOME/vcpkg when the script found vcpkg elsewhere.

The root manifest does not install PostgreSQL client libraries. For PostgreSQL-enabled Windows builds, use a vcpkg manifest that includes libpq, or consume the SDK through the repository overlay port with its postgres-store feature. See Build with vcpkg.

Configure from source

On Linux:

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

On macOS, use the same Homebrew prefixes validated by CI:

cmake -S . -B build -G Ninja \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
  -DCMAKE_PREFIX_PATH="$(brew --prefix);$(brew --prefix curl)"

When A2A_ENABLE_POSTGRES_STORE=ON, append $(brew --prefix libpq) to CMAKE_PREFIX_PATH.

On Windows Git Bash, set VCPKG_ROOT to the value printed by install_build_deps.sh or to an existing vcpkg checkout:

export VCPKG_ROOT="/path/to/vcpkg"
export VCPKG_TARGET_TRIPLET="${VCPKG_TARGET_TRIPLET:-x64-windows}"
export VCPKG_HOST_TRIPLET="${VCPKG_HOST_TRIPLET:-$VCPKG_TARGET_TRIPLET}"
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 \
  -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
  -DVCPKG_TARGET_TRIPLET="$VCPKG_TARGET_TRIPLET" \
  -DVCPKG_HOST_TRIPLET="$VCPKG_HOST_TRIPLET"

If a build directory was first configured without the vcpkg toolchain, delete it before reconfiguring. CMake caches the toolchain during the first configure.

Useful options:

OptionDefaultPurpose
A2A_ENABLE_TESTINGONBuilds unit and integration tests.
A2A_BUILD_EXAMPLESONKeeps the root project compatible with example-related CI messaging; curated examples are built as standalone consumers.
A2A_BUILD_BENCHMARKSOFFBuilds the optional Google Benchmark suite.
A2A_ENABLE_LIBCURLONEnables default outbound HTTP and SSE support when CURL::libcurl is found.
A2A_ENABLE_POSTGRES_STOREOFFBuilds PostgreSQL task and push-notification stores.

Build and test

Linux or macOS:

cmake --build build --parallel
ctest --test-dir build --output-on-failure

Windows Visual Studio generators are multi-configuration; run these commands from Git Bash:

cmake --build build --config RelWithDebInfo --parallel
ctest --test-dir build -C RelWithDebInfo --output-on-failure

Generate only protobuf outputs when needed:

cmake --build build --target a2a_proto_codegen

Generated A2A headers are written under build/generated/a2a/v1/. Generated Google API annotation headers are written under build/generated/google/api/. Both sets are installed with the SDK.

Install CMake package artifacts

cmake --install build --prefix /tmp/a2a-cpp-install

On a Visual Studio build, also pass --config RelWithDebInfo.

The install tree includes public headers, generated protobuf headers, libraries, and CMake package files under ${CMAKE_INSTALL_LIBDIR}/cmake/a2a_cpp, commonly lib/cmake/a2a_cpp.

Contributor validation

For code changes in a Linux CI-compatible environment, run the canonical validation script before opening or updating a PR:

./scripts/verify_changes.sh

The script runs the same main validation categories as CI: formatting, configure/build, tests, and clang-tidy. It runs clang-format -i first and can modify tracked C and C++ files, so review git diff after it completes.

The current script assumes a Unix environment with nproc and a single-configuration build. On macOS or Windows, use the platform-specific configure, build, and test commands above and run the required formatting and clang-tidy checks separately.

For documentation-only mdBook changes, build the book:

mdbook build book

Quickstart: Build and Run Examples

The curated examples build app sources the same way downstream applications consume the SDK.

1. Install dependencies

Follow the platform instructions in Installation and Build. On Windows, run the repository helper from Git Bash before the example runner:

./scripts/install_build_deps.sh

2. Run the smallest end-to-end example

Linux or macOS:

./scripts/run_examples.sh build-example hello_agent

Windows Git Bash cleanup and rebuild:

rm -rf build-example-hello_agent
./scripts/run_examples.sh build-example hello_agent

The executable is written to build-example-hello_agent/a2a_example on single-configuration generators and to build-example-hello_agent/RelWithDebInfo/a2a_example.exe with Visual Studio.

hello_agent creates a minimal in-process client/server flow and exits deterministically.

3. Try transport-specific examples

./scripts/run_examples.sh build-example rest_server json_rpc_server grpc_server

These examples cover server transport setup and deterministic request handling across REST, JSON-RPC, and gRPC.

4. Try streaming, push, and auth examples

./scripts/run_examples.sh build-example streaming_client streaming_server push_notifications auth_policy_server

Use these when validating event streams, webhook configuration flows, or server-side auth metadata policy shapes. On Windows, the runner automatically forwards the vcpkg toolchain and locates the Visual Studio configuration output when VCPKG_ROOT is set.

5. Consume an installed SDK package

After installing the SDK into a prefix, build the same app source with find_package(a2a_cpp CONFIG REQUIRED):

cmake -S examples/installed_package_consumer -B build-installed-example \
  -DCMAKE_PREFIX_PATH=/tmp/a2a-cpp-install \
  -DA2A_EXAMPLE_APP=hello_agent
cmake --build build-installed-example --parallel
./build-installed-example/a2a_example

For Visual Studio, add --config RelWithDebInfo to the build and run build-installed-example/RelWithDebInfo/a2a_example.exe.

6. Validate your local checkout

./scripts/verify_changes.sh

For documentation-only edits, use mdbook build book instead of the full code validation flow.

Client Overview

Use a2a::client::A2AClient with a concrete ClientTransport to call A2A servers.

Client capabilities

  • SendMessage
  • GetTask
  • ListTasks
  • CancelTask
  • SendStreamingMessage
  • SubscribeTask
  • CreateTaskPushNotificationConfig
  • GetTaskPushNotificationConfig
  • ListTaskPushNotificationConfigs
  • DeleteTaskPushNotificationConfig
  • Client interceptors via ClientInterceptor
  • Per-call settings through CallOptions

Typical flow

  1. Fetch an Agent Card with DiscoveryClient.
  2. Select an endpoint with AgentCardResolver::SelectPreferredInterface.
  3. Construct HttpJsonTransport, JsonRpcTransport, or GrpcTransport.
  4. Create A2AClient with the transport.
  5. Invoke task, streaming, or push-notification APIs.
  6. Call Destroy() during shutdown when you need transport cleanup.

Default outbound HTTP

When libcurl is available and A2A_ENABLE_LIBCURL=ON, default constructors/factories can perform buffered outbound REST, JSON-RPC, and discovery calls. For tests, custom TLS policy, mTLS, retries, or embedded runtimes, inject your own requester/fetcher callbacks.

Runnable examples

  • examples/apps/hello_agent/main.cpp
  • examples/apps/simple_client/main.cpp
  • examples/apps/streaming_client/main.cpp
  • examples/apps/push_notifications/main.cpp

Sending Messages

A2AClient::SendMessage starts or continues a task by sending a protobuf lf::a2a::v1::SendMessageRequest through the configured transport.

Request construction guidance

  • Set a stable message.message_id for idempotency and diagnostics.
  • Let the server generate a task ID for new work unless you are intentionally continuing an existing task.
  • Attach push-notification config only when the target Agent Card advertises support and your server policy allows it.
  • Use CallOptions for per-call deadlines, metadata, or auth settings where supported by the transport.

Response handling

The response can contain immediate task state and output data. Persist task identifiers in logs/telemetry so later GetTask, CancelTask, streaming subscription, or push-notification calls can be correlated.

Failure paths to test

  • Network or HTTP/gRPC transport failure.
  • Serialization or protocol validation failure.
  • Auth policy rejection.
  • Unsupported operation or required extension mismatch.
  • Duplicate/retry behavior for repeated message IDs.

Discovery and Agent Cards

Discovery resolves server capabilities before constructing a client transport.

Public and extended cards

DiscoveryClient supports:

  • Fetch(base_url) for the standard Agent Card.
  • FetchExtendedAgentCard(base_url) for the extended Agent Card endpoint.

Fetched cards are cached for kDefaultDiscoveryCacheTtl (300 seconds) unless a different TTL is supplied.

Interface resolution

AgentCardResolver::SelectPreferredInterface(card, preferred) selects a ResolvedInterface for one of:

  • PreferredTransport::kRest
  • PreferredTransport::kJsonRpc
  • PreferredTransport::kGrpc

The result includes the transport, URL, security requirements, and security schemes needed to configure a client.

Operational guidance

  • Validate and log the selected endpoint during startup.
  • Prefer an explicit fallback order when cards advertise multiple transports.
  • Treat discovery metadata as untrusted input until validated.
  • Refresh cached cards after deployment or capability changes.
  • include/a2a/client/discovery.h
  • include/a2a/core/agent_card/agent_card_provider.h
  • include/a2a/server/agent_card/agent_card_serializer.h

Get Task and List Tasks

GetTask retrieves a single task by ID. ListTasks returns a paginated list of tasks through the client abstraction and supported transports.

GetTask flow

  1. Store the task ID from SendMessage or a stream event.
  2. Build lf::a2a::v1::GetTaskRequest.
  3. Call A2AClient::GetTask.
  4. Interpret task status, artifacts, and history according to your application policy.

ListTasks flow

a2a::client::ListTasksRequest contains:

  • page_size
  • page_token

The response includes task values plus next_page_token.

An omitted page size requests the protocol default of at most 50 tasks. Explicit page sizes must be between 1 and 100. Continue until next_page_token is empty:

a2a::client::ListTasksRequest request{.page_size = 50};
do {
  const auto page = client.ListTasks(request);
  if (!page.ok()) {
    return page.error();
  }
  for (const auto& task : page.value().tasks) {
    ProcessTask(task);
  }
  request.page_token = page.value().next_page_token;
} while (!request.page_token.empty());

Keep pages bounded (normally no more than 100 tasks) and request a small history_length, often 0 or 1, unless deeper history is required. CPU cost grows with both the returned task count and retained history. Oversized results also increase protobuf allocations and copies, response memory, serialized payload size, transport latency, and timeout risk. In-memory stores hold their shared read lock longer while materializing a result, so writes may wait longer. Full history projection can dominate the list operation.

Operational guidance

  • Use bounded polling with backoff when not using streaming.
  • Enforce authorization checks on server-side task visibility.
  • Avoid exposing full task history to clients that do not need it.

Cancel Task

A2AClient::CancelTask asks the server to cancel in-flight work.

When to use cancellation

  • A user aborts an operation.
  • A deadline expires upstream.
  • Supervising logic needs to reclaim compute or queue capacity.

Semantics

Cancellation is best-effort. A task can complete before the cancellation request is processed, and servers can reject cancellation for completed, unknown, or policy-protected tasks.

  • Make higher-level cancellation idempotent.
  • Return or display the latest task state after cancellation attempts.
  • Preserve enough audit data to explain whether the task was canceled, completed, or rejected.

Server Overview

Server integrations implement a2a::server::AgentExecutor and route protocol requests through Dispatcher plus one or more transports.

Server capabilities

  • REST server transport.
  • JSON-RPC server transport.
  • gRPC service transport.
  • Public and extended Agent Card dispatch when a provider is installed.
  • Required extension validation.
  • Server interceptors.
  • Streaming through ServerStreamSession.
  • Task lifecycle helpers, in-memory task store, and optional PostgreSQL stores.
  • Push-notification config CRUD and delivery service abstractions.

Core flow

  1. Implement an executor for task and message behavior.
  2. Create a Dispatcher with the executor and optional Agent Card provider/interceptors.
  3. Attach REST, JSON-RPC, or gRPC transport adapters.
  4. Convert inbound framework requests into transport calls.
  5. Return structured protocol errors instead of transport-specific ad hoc errors.

Task IDs

When an incoming message does not carry message.task_id, the SDK service layer can generate server-side task IDs using UUIDv7. UUIDv7 is sortable and operationally useful, but it leaks approximate creation time. Inject a custom task ID generator if your deployment needs opaque identifiers.

Custom Executors

Implement a2a::server::AgentExecutor to define application behavior.

Required methods

Executors implement task/message operations including:

  • SendMessage
  • SendStreamingMessage
  • GetTask
  • ListTasks
  • CancelTask

Push-notification config methods have default PushNotificationNotSupported behavior and should be overridden only when the server actually supports them.

Request context

Every executor method receives RequestContext. Use it for auth metadata, transport metadata, and request-scoped policy decisions. Treat metadata as untrusted until validated.

Design guidance

  • Keep executor methods small and deterministic.
  • Validate inputs at the boundary.
  • Return a2a::core::Result<T> with structured errors.
  • Avoid shared mutable state unless synchronized and documented.
  • Unit-test executor behavior separately from transport mapping.

REST Server Transport

RestServerTransport maps HTTP+JSON requests onto dispatcher operations.

Responsibilities

  • Validate HTTP method, path, and content type.
  • Parse JSON request bodies into protobuf messages.
  • Populate RequestContext, including auth metadata.
  • Serialize protocol responses and errors back to HTTP responses.

Supported operation shape

REST transport covers core task lifecycle operations, streaming-compatible endpoints where wired by the hosting environment, Agent Card routes, and push-notification config APIs exposed by the dispatcher/executor.

Hosting guidance

The SDK provides protocol mapping, not a full production web server framework. In production, place it behind an HTTP runtime that owns TLS, connection limits, request-size limits, access logs, and graceful shutdown.

Example

See examples/apps/rest_server/main.cpp.

JSON-RPC Server Transport

JsonRpcServerTransport maps JSON-RPC 2.0 method calls to dispatcher operations.

Responsibilities

  • Validate the jsonrpc, id, method, and params envelope fields.
  • Route supported A2A methods to the dispatcher.
  • Return JSON-RPC error objects for invalid requests, unknown methods, and executor failures.
  • Preserve request metadata for auth and audit policy.

Use cases

Use JSON-RPC when method-based routing is easier to integrate than resource-oriented REST paths or when another system already standardizes on JSON-RPC 2.0.

Example

See examples/apps/json_rpc_server/main.cpp.

Storage Backends

The SDK storage layer is pluggable. TaskLifecycleService uses TaskStore, and PushNotificationService uses PushNotificationStore.

In-memory stores

Use InMemoryStoreFactory for examples, tests, and single-process development:

a2a::server::stores::InMemoryStoreFactory factory;
auto stores = factory.CreateStoreBundle();

In-memory stores do not persist data or coordinate across processes.

PostgreSQL stores

PostgreSQL support is optional. Enable and link the PostgreSQL store target:

cmake -S . -B build-postgres -DA2A_ENABLE_POSTGRES_STORE=ON
cmake --build build-postgres
a2a::server::stores::PostgresStoreFactory factory({
    .connection_string = "postgresql://a2a:a2a@127.0.0.1:5432/a2a",
    .schema = "public",
    .auto_create_schema = true,
    .connection_pool_size = 8,
});
auto stores = factory.CreateStoreBundle();

Task and push-notification stores returned by CreateStoreBundle() share one connection pool. Separately constructed stores own separate pools. Storage matching uses libpq’s active connection target (selected host, resolved server address, active port, and database) plus the configured target_session_attrs value and schema. Exact coordinate equality confirms local authority; a different database or schema confirms external authority; other endpoint differences are uncertain. Separately constructed stores can set PostgresStoreOptions::storage_authority_id: matching non-empty IDs prove local authority, different non-empty IDs prove external authority, and a one-sided ID remains uncertain. Database/schema differences still remain external. The ID is a stable, non-secret deployment identifier and must reflect the real storage authority because it affects provenance and cleanup. Passwords and raw connection strings are not part of the identity. The effective role is tracked as pool-consistency metadata, but matching endpoint/schema/role metadata does not prove that independent sessions have the same row-security context. The one-command list shortcut therefore requires the task and push stores to share the exact connection pool and local storage authority. Separately constructed stores validate the task through the authoritative task store first, even when their DSNs and roles look equivalent. Do not leave pooled connections with out-of-band session state such as SET ROLE or custom policy GUCs.

Task-aware PostgreSQL behavior

For same-storage task and push stores, create/update uses one push-store acquisition and one PostgreSQL command. That statement calls the SECURITY DEFINER task-lock helper, holds a shared transaction advisory lock derived from the schema-qualified task table and complete task ID, validates task existence, and performs the upsert atomically. Task deletion obtains the matching exclusive advisory lock in a BEFORE DELETE trigger. It has no separate task precheck, revalidation, compensating cleanup, or explicit multi-command transaction. Missing tasks return TaskNotFound, and concurrent deletion cannot leave a locally owned orphan. The local task-aware path rejects repeatable-read and serializable transaction isolation before locking because their transaction-scoped snapshots can remain stale after a concurrent delete commits. Under PostgreSQL read-committed semantics, the VOLATILE helper rechecks task existence with a fresh snapshot after a lock wait. Equal task IDs in different schemas use different advisory keys. Hash collisions can still serialize unrelated task IDs; data access still uses the original task ID. PushConfig_CreateMany at fan-out eight therefore performs eight create commands rather than sixteen.

GetConfig remains push-store-only and uses one PostgreSQL statement. List uses one combined task/count/page statement when task and push stores share a connection pool and local storage authority; separate pools, different storage authority, or an external task store use the authoritative task lookup first and then one combined push-list statement. External-authority creates are marked with local_postgres_task=FALSE; direct non-task-aware CreateOrUpdate calls use the same external provenance. Only locally owned rows are removed by the task-delete cleanup trigger.

When auto_create_schema=true, the SDK installs the provenance column, shared push-lock helper, exclusive task-delete lock helper with a BEFORE DELETE trigger, cleanup helper with an AFTER DELETE trigger, sequences, and indexes, removes the legacy push-to-task foreign key, and writes the task-aware-push-config-v3 migration markers last.

Externally managed PostgreSQL schemas

When auto_create_schema=false, the push-notification store always validates the provenance column and absence of the legacy push-to-task foreign key. If the push-lock helper, task-delete lock helper and trigger, cleanup helper, and cleanup trigger are all absent, construction is allowed for push-only use with an external authoritative TaskStore; local PostgreSQL task-aware creation then fails before writing until the migration is installed. Capability detection happens during store construction rather than on the request path.

If any task-aware helper or trigger is present, the whole task-aware-push-config-v3 migration is required. Validation checks all helper implementations and markers, owner privileges, absence of PUBLIC EXECUTE, the enabled advisory-lock BEFORE DELETE and cleanup AFTER DELETE row triggers without WHEN clauses, and the cleanup owner’s ability to bypass any row-level security enabled on the push-config table. Partial or stale installations fail construction.

Apply the following migration before using local PostgreSQL task-aware create/update. The example uses the public schema and an SDK role named a2a_sdk; replace both names for your deployment. Grant the push-lock helper only to roles authorized to create push configurations. The invoking push role needs task-table SELECT plus helper EXECUTE; neither the caller nor the helper owner needs task-table UPDATE. Push-only roles paired with an external authoritative TaskStore do not need task-table access or helper execution. Task-table row-level security is allowed for those external-authority paths but is rejected when the local task-aware create helper is invoked. If push-table row-level security is enabled, the cleanup helper owner must bypass it via table ownership without FORCE ROW LEVEL SECURITY, or via BYPASSRLS or superuser status, so task deletion cannot silently retain local push configurations.

BEGIN;

ALTER TABLE public.a2a_push_notification_configs
  ADD COLUMN IF NOT EXISTS local_postgres_task BOOLEAN NOT NULL DEFAULT FALSE;

ALTER TABLE public.a2a_push_notification_configs
  DROP CONSTRAINT IF EXISTS a2a_push_configs_task_fk;

DROP INDEX IF EXISTS public.idx_a2a_push_configs_task;
DROP INDEX IF EXISTS public.idx_a2a_push_configs_created_sequence;

CREATE OR REPLACE FUNCTION public.a2a_lock_task_for_push_config(requested_task_id TEXT)
RETURNS BOOLEAN
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog
AS $a2a$
DECLARE
  caller_role NAME;
  lock_key BIGINT;
BEGIN
  IF pg_catalog.current_setting('transaction_isolation') IN ('repeatable read', 'serializable') THEN
    RAISE EXCEPTION USING ERRCODE = '0A000', MESSAGE =
      'PostgreSQL task-aware push configuration requires read-committed transaction isolation';
  END IF;
  lock_key := pg_catalog.hashtextextended(
    requested_task_id,
    pg_catalog.hashtextextended('public.a2a_tasks', 0));
  IF NOT pg_catalog.pg_try_advisory_xact_lock_shared(lock_key) THEN
    PERFORM pg_catalog.pg_advisory_xact_lock_shared(lock_key);
  END IF;
  caller_role := NULLIF(pg_catalog.current_setting('role', true), 'none');
  IF caller_role IS NULL THEN
    caller_role := session_user;
  END IF;
  IF EXISTS (
    SELECT 1 FROM pg_catalog.pg_class
    WHERE oid = pg_catalog.to_regclass('public.a2a_tasks') AND relrowsecurity
  ) THEN
    RAISE EXCEPTION USING ERRCODE = '0A000', MESSAGE =
      'PostgreSQL task-aware push configuration does not support row-level security on a2a_tasks';
  END IF;
  IF NOT pg_catalog.has_table_privilege(caller_role, 'public.a2a_tasks', 'SELECT') THEN
    RAISE EXCEPTION USING ERRCODE = '42501', MESSAGE =
      'PostgreSQL push store role requires SELECT on a2a_tasks for task-aware creation';
  END IF;
  PERFORM 1
  FROM public.a2a_tasks
  WHERE id = requested_task_id;
  RETURN FOUND;
END
$a2a$;

REVOKE ALL ON FUNCTION public.a2a_lock_task_for_push_config(TEXT) FROM PUBLIC;
GRANT SELECT ON public.a2a_tasks TO a2a_sdk;
GRANT EXECUTE ON FUNCTION public.a2a_lock_task_for_push_config(TEXT) TO a2a_sdk;

CREATE OR REPLACE FUNCTION public.a2a_lock_task_for_delete()
RETURNS TRIGGER
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog
AS $a2a$
BEGIN
  PERFORM pg_catalog.pg_advisory_xact_lock(
    pg_catalog.hashtextextended(
      OLD.id,
      pg_catalog.hashtextextended('public.a2a_tasks', 0)));
  RETURN OLD;
END
$a2a$;

REVOKE ALL ON FUNCTION public.a2a_lock_task_for_delete() FROM PUBLIC;

DROP TRIGGER IF EXISTS a2a_lock_task_for_delete_trigger ON public.a2a_tasks;
CREATE TRIGGER a2a_lock_task_for_delete_trigger
BEFORE DELETE ON public.a2a_tasks
FOR EACH ROW
EXECUTE FUNCTION public.a2a_lock_task_for_delete();

CREATE OR REPLACE FUNCTION public.a2a_delete_task_push_configs()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = pg_catalog
AS $a2a$
BEGIN
  DELETE FROM public.a2a_push_notification_configs
  WHERE task_id = OLD.id AND local_postgres_task;
  RETURN OLD;
END
$a2a$;

REVOKE ALL ON FUNCTION public.a2a_delete_task_push_configs() FROM PUBLIC;

DROP TRIGGER IF EXISTS a2a_delete_task_push_configs_trigger ON public.a2a_tasks;
CREATE TRIGGER a2a_delete_task_push_configs_trigger
AFTER DELETE ON public.a2a_tasks
FOR EACH ROW
EXECUTE FUNCTION public.a2a_delete_task_push_configs();

COMMENT ON FUNCTION public.a2a_lock_task_for_delete()
  IS 'task-aware-push-config-v3';

COMMENT ON FUNCTION public.a2a_delete_task_push_configs()
  IS 'task-aware-push-config-v3';

COMMENT ON FUNCTION public.a2a_lock_task_for_push_config(TEXT)
  IS 'task-aware-push-config-v3';

COMMIT;

The migration markers are written last inside the transaction, so partial migrations are rejected. Existing rows receive the conservative external or unknown provenance value from the new column’s FALSE default. The lock-helper owner needs schema USAGE plus task-table SELECT; the task-delete lock helper owner needs schema USAGE; and the cleanup helper owner needs schema USAGE plus push-table SELECT and DELETE. These are SECURITY DEFINER owner requirements, not privileges for every push-store role.

Sensitive push-notification data

Push configurations can contain callback URLs, tokens, and authentication metadata. The SDK does not implement application-level encryption at rest; use database encryption, least-privilege access, protected backups, and appropriate audit logging.

Push Notifications

The SDK includes task push-notification configuration APIs and server-side delivery abstractions.

Client API surface

A2AClient exposes:

  • CreateTaskPushNotificationConfig
  • GetTaskPushNotificationConfig
  • ListTaskPushNotificationConfigs
  • DeleteTaskPushNotificationConfig

Use these only when the target Agent Card and server policy indicate push-notification support.

Server components

  • PushNotificationService coordinates stored configs and delivery.
  • PushNotificationStore persists webhook configs.
  • PushNotificationDeliveryClient abstracts outbound delivery.
  • HttpPushNotificationDeliveryClient provides a simple libcurl-backed synchronous delivery path when libcurl support is enabled.
  1. Persist task state in your TaskStore or TaskLifecycleService.
  2. Register inline configs from SendMessageRequest when present.
  3. Call NotifyTaskUpdated(task) after status changes.
  4. Propagate delivery failures instead of silently dropping them.
  5. Override push-config CRUD executor methods only when push support is fully configured.

Production guidance

For production, prefer a durable queued delivery client with retries, backoff, webhook URL validation, SSRF controls, credential protection, and delivery telemetry. Keep TLS policy at least as strong as the built-in delivery client, which requires TLS 1.2 or newer for HTTPS URLs.

Example

See examples/apps/push_notifications/main.cpp.

Server Interceptors

Server interceptors let deployments attach cross-cutting behavior around dispatcher operations.

Common uses

  • Authorization and policy checks.
  • Request/response telemetry.
  • Audit logging.
  • Metrics and latency measurement.
  • Required extension enforcement in combination with transport validators.

Design guidance

  • Keep interceptors deterministic and low-latency.
  • Avoid logging secrets or raw credential values.
  • Use structured errors so transports can map failures consistently.
  • Prefer composition: keep business behavior in AgentExecutor and cross-cutting behavior in interceptors.
  • a2a::server::ServerInterceptor
  • a2a::server::Dispatcher::AddInterceptor
  • a2a::client::ClientInterceptor for client-side call hooks

Transports Overview

The SDK separates protocol operations from transport adapters. Applications choose the transport that best matches their deployment boundary and interoperability needs.

Available transports

  • REST: HTTP+JSON resource-oriented integration for clients and servers.
  • JSON-RPC: JSON-RPC 2.0 method dispatch over HTTP-style request handling.
  • gRPC: protobuf/gRPC service integration with unary and streaming RPCs.

Choosing a transport

  • Choose REST when your platform already standardizes on HTTP routing, gateways, or resource-style APIs.
  • Choose JSON-RPC when method-style dispatch is easier to interoperate with or proxy.
  • Choose gRPC when you want protobuf-native contracts, gRPC streaming, or a service mesh that already supports gRPC well.

Shared operational concerns

Regardless of transport, define these policies explicitly:

  • Authentication and authorization boundaries.
  • Protocol version and required-extension handling.
  • Request deadlines and payload limits.
  • Retry and idempotency behavior.
  • Telemetry, audit logs, and stable task/request identifiers.

REST Transport

REST is the HTTP+JSON transport path for clients and servers.

Client side

Use HttpJsonTransport with A2AClient. When libcurl is enabled and found, default outbound buffered HTTP can be used. Otherwise inject an HttpRequester implementation.

Server side

Use RestServerTransport to translate inbound framework requests into dispatcher calls.

Operational considerations

  • Enforce TLS and auth policy at your edge or hosting layer.
  • Set explicit request deadlines and payload limits.
  • Log stable task IDs and request IDs.
  • Validate protocol version headers where required.
  • Prefer injected requesters for custom retry, proxy, mTLS, or observability policy.

JSON-RPC Transport

JSON-RPC transport supports A2A method dispatch over JSON-RPC 2.0 envelopes.

Client side

Use JsonRpcTransport with A2AClient. Default buffered outbound HTTP is available when libcurl is enabled and found; otherwise inject a requester.

Server side

Use JsonRpcServerTransport to decode envelopes, dispatch operations, and encode JSON-RPC responses.

Operational considerations

  • Validate envelope fields strictly.
  • Preserve client-provided request IDs in responses.
  • Map structured SDK errors to consistent JSON-RPC error responses.
  • Include auth metadata in RequestContext only after policy validation.

gRPC Transport

gRPC support is first-class for client and server integrations.

Client side

Use a2a::client::GrpcTransport with a resolved interface and either:

  • a std::shared_ptr<grpc::Channel> for real gRPC calls, or
  • a custom GrpcTransport::RpcClient for tests and embedded adapters.

The transport supports unary task operations, streaming send, task subscription, and push-notification config lifecycle calls.

Server side

a2a::server::GrpcServerTransport implements lf::a2a::v1::A2AService::Service and routes RPCs to the dispatcher.

It supports:

  • SendMessage
  • SendStreamingMessage
  • GetTask
  • ListTasks
  • CancelTask
  • SubscribeToTask
  • Push-notification config RPCs
  • GetExtendedAgentCard

Metadata and extensions

The server transport recognizes A2A metadata such as a2a-version and can validate required extensions through GrpcServerTransportOptions::required_extensions.

Example

See examples/apps/grpc_server/main.cpp.

Streaming

Streaming is available through all production client transports:

  • gRPC native server streaming;
  • HTTP+JSON SSE;
  • JSON-RPC response envelopes carried in SSE data: fields.

Servers should advertise capabilities.streaming: true in their Agent Card.

Client APIs

  • A2AClient::SendStreamingMessage(request, observer, options)
  • A2AClient::SubscribeTask(request, observer, options)

Implement a2a::client::StreamObserver:

  • OnEvent(const StreamResponse&) for each decoded event.
  • OnError(const core::Error&) once for transport or protocol failure.
  • OnCompleted() once after a clean remote close.

Both calls return a StreamHandle. Call Cancel() to request cancellation and IsActive() to check handle state. Destruction also cancels the request and joins its worker.

Threading contract

Observer callbacks run on transport-managed background threads. Keep observers alive until stream completion, cancellation, or handle destruction. Callback code should be thread-safe, fast, and non-blocking.

Build the streaming client example

The SDK requires gRPC and Protobuf at configure time. The default HTTP streaming transports also require libcurl.

Linux or macOS:

./scripts/run_examples.sh build-example streaming_client

Windows Git Bash:

./scripts/install_build_deps.sh
rm -rf build-example-streaming_client
./scripts/run_examples.sh build-example streaming_client
./build-example-streaming_client/RelWithDebInfo/a2a_example.exe --help

See Installation and Build and vcpkg for dependency and toolchain details.

Server side

Executors return std::unique_ptr<ServerStreamSession> from SendStreamingMessage and, optionally, SubscribeTask. A stream session publishes StreamResponse values until it returns an empty optional or an error.

Examples

  • examples/apps/streaming_client/main.cpp
  • examples/apps/streaming_server/main.cpp

Authentication and Authorization Hooks

The SDK provides hooks for carrying credentials and auth metadata. Your application remains responsible for credential storage, verification, authorization decisions, and audit policy.

Client credential providers

include/a2a/client/auth.h includes providers for:

  • API key headers.
  • Bearer token headers.
  • Custom header credentials.
  • OAuth2 bearer token extension points.

Use these with transport call options or request construction paths that support metadata injection.

Server metadata

REST, JSON-RPC, and gRPC server paths populate RequestContext with transport metadata. Executor or interceptor code can read this metadata to make authorization decisions.

Policy guidance

  • Never hardcode secrets in source, examples, or tests.
  • Validate credentials before trusting request metadata.
  • Normalize auth failures so callers receive consistent protocol errors.
  • Avoid logging raw credentials.
  • Prefer short-lived tokens and managed secret storage.
  • Document how TLS or mTLS identity is terminated and propagated into the SDK boundary.

Example

See examples/apps/auth_policy_server/main.cpp for an auth-policy shape.

Releases and Versions

This documentation is intended to stay version-aware without embedding a release number in every page title.

Current documented release

The current documented release is v0.4.1 and is recommended for new consumers.

Highlights since v0.4.0

  • Optimized PostgreSQL push-configuration create, get, list, and cleanup paths, reducing redundant task lookups, commands, and connection acquisitions while preserving authoritative task validation and concurrent-deletion safety.
  • Added optimistic revision-aware task persistence to the built-in stores so the example/TCK SendMessage path can persist complete task mutations with fewer PostgreSQL round trips while preserving concurrent history updates.
  • Added HTTP/1.1 persistent connections and connection-scoped request buffering, eliminating per-request connection churn for ordinary unary HTTP+JSON and JSON-RPC operations.
  • Optimized REST query parsing and reorganized transport implementation sources under dedicated client/server transport directories without changing public include paths.
  • Optimized HTTP+JSON and JSON-RPC ListTasks parsing and serialization through typed protobuf JSON paths, with focused parser/scanner benchmarks and CI thresholds.
  • Hardened typed ListTasks compatibility validation for null values, duplicate fields, protobuf-name aliases, nested duplicate message members, escaped JSON-RPC result keys, HTTP-status classification, and protobuf integer ranges.
  • Added component-level transport benchmarks for JSON-RPC envelopes, ProtoJSON, REST query parsing, response construction, and ListTasks client parsing.
  • Isolated performance fixtures from measured operations and restructured reports around concrete workload coordinates rather than mixed scenario averages.
  • Expanded PostgreSQL command-level diagnostics and documented storage, performance, and transport behavior.
  • Added repository-wide Conventional Commit validation in CI.

Compatibility notes

There are no intentional public API removals in v0.4.1.

The transport source-tree reorganization is internal; installed public include paths and exported CMake targets remain unchanged.

HTTP+JSON and JSON-RPC unary traffic can now reuse HTTP/1.1 connections. Explicit Connection: close remains supported, and the existing streaming/SSE behavior is preserved.

Valid ListTasks responses remain compatible across HTTP+JSON and JSON-RPC. Malformed or ambiguous JSON that protobuf 3.21 could otherwise accept with last-value-wins or null-as-unset behavior is now rejected consistently.

PostgreSQL users should allow SDK-managed schemas to migrate and validate the task-aware push-configuration helpers. Externally managed schemas should follow the storage documentation for the required schema objects and privileges.

Previous release: v0.4.0

v0.4.0 focused on configurable PostgreSQL pool sizing, bounded protocol-facing ListTasks pagination, optimized in-memory task listing, interruptible gRPC stream cancellation, expanded performance validation, and centralized HTTP server response construction.

Its important compatibility changes remain in effect:

  • omitted protocol-facing ListTasks.page_size defaults to 50;
  • explicit page sizes must be between 1 and 100;
  • the PostgreSQL connection pool defaults to 4 unless configured otherwise.

Earlier release: v0.3.0

v0.3.0 introduced production-ready SSE streaming for HTTP+JSON and JSON-RPC, end-to-end streaming and subscriptions across all supported transports, report-only performance testing, repository-local vcpkg overlay packaging, and expanded build and platform documentation.

Its TCK validation snapshot was:

  • 249 tests passed
  • 16 tests skipped
  • 0 executed test failures
  • 100.0% compatibility for tested, non-skipped requirements

Versioning guidance

  • Pin CMake FetchContent integrations to a release tag such as v0.4.1 or to a reviewed commit.
  • Prefer find_package(a2a_cpp CONFIG REQUIRED) for installed SDK packages.
  • Keep generated protobuf headers and linked SDK libraries from the same installed package or build tree.
  • Review release notes before upgrading between versions.

Documentation policy

Page titles and navigation should remain mostly version agnostic. Release-specific notes belong on this page or in clearly marked compatibility sections.

Build with CMake

a2a-cpp is a C++20 SDK built and packaged with CMake. The project can be used directly from source with FetchContent, installed into a CMake package prefix, or built with dependencies supplied by vcpkg.

Requirements

  • CMake 3.25 or newer.
  • A C++20 compiler.
  • Protobuf and gRPC development packages.
  • Optional: libcurl for the default buffered outbound HTTP implementation.
  • Optional: PostgreSQL client libraries when A2A_ENABLE_POSTGRES_STORE=ON.

On Ubuntu-like systems, the repository helper installs the dependencies used by CI:

./scripts/install_build_deps.sh

On macOS, install equivalent packages with Homebrew:

brew install cmake ninja protobuf grpc re2 abseil curl

On Windows, install Visual Studio 2022 with the Desktop development with C++ workload and Git for Windows. Then run the repository dependency helper from Git Bash:

./scripts/install_build_deps.sh

Configure from source

The default source build enables tests, keeps the curated example apps out of the top-level build, and enables libcurl-backed HTTP support when CMake can find CURL::libcurl.

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
  -DA2A_ENABLE_TESTING=ON

When dependencies are installed outside standard search paths, pass a CMake prefix path:

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DCMAKE_PREFIX_PATH="/opt/homebrew;/opt/homebrew/opt/curl"

Build and test

Single-configuration generators:

cmake --build build --parallel
ctest --test-dir build --output-on-failure

Visual Studio generators from Git Bash:

cmake --build build --config RelWithDebInfo --parallel
ctest --test-dir build -C RelWithDebInfo --output-on-failure

For the repository’s full local code validation flow, run:

./scripts/verify_changes.sh

That script runs the same main gates expected before a code PR: formatting, configure/build, tests, and clang-tidy.

CMake options

OptionDefaultDescription
A2A_ENABLE_TESTINGONBuilds unit and integration tests and enables CTest.
A2A_BUILD_EXAMPLESONCompatibility/message-only option for the root build today; curated examples are built as standalone consumers from examples/fetch_content_consumer or examples/installed_package_consumer.
A2A_BUILD_BENCHMARKSOFFBuilds benchmark targets under benchmarks/.
A2A_ENABLE_LIBCURLONEnables the default libcurl-backed outbound HTTP implementation when libcurl is found. Disable it to require injected requesters/fetchers.
A2A_ENABLE_POSTGRES_STOREOFFBuilds PostgreSQL-backed store targets when PostgreSQL dependencies are available.

Generated protobuf headers

The SDK generates A2A protocol C++ sources during the build. Primary generated A2A headers are written under build/generated/a2a/v1/, and generated Google API annotation headers are written under build/generated/google/api/.

Those generated headers are installed with the SDK, so downstream projects should include headers from the installed package rather than copying build-tree generated files.

Install as a CMake package

Install the SDK to a prefix:

cmake --install build --prefix /tmp/a2a-cpp-install

The install tree includes public headers, generated protobuf headers, libraries, and package configuration files under lib/cmake/a2a_cpp.

A downstream project can then consume the installed package:

cmake_minimum_required(VERSION 3.25)
project(my_a2a_app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(a2a_cpp CONFIG REQUIRED)

add_executable(my_a2a_app main.cpp)
target_link_libraries(my_a2a_app PRIVATE a2a::client a2a::server a2a::core)

Configure that downstream project with CMAKE_PREFIX_PATH pointing at the install prefix:

cmake -S path/to/app -B build-app \
  -DCMAKE_PREFIX_PATH=/tmp/a2a-cpp-install
cmake --build build-app --parallel

FetchContent consumer

For application projects that prefer source integration, use CMake FetchContent and pin GIT_TAG to a release tag or reviewed commit:

include(FetchContent)

set(A2A_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(A2A_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(A2A_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
set(A2A_ENABLE_POSTGRES_STORE OFF CACHE BOOL "" FORCE)

FetchContent_Declare(
  a2a_cpp
  GIT_REPOSITORY https://github.com/MisterVVP/a2a-cpp.git
  GIT_TAG v0.4.1
)
FetchContent_MakeAvailable(a2a_cpp)

target_link_libraries(my_a2a_app PRIVATE a2a::client a2a::server a2a::core)

See examples/fetch_content_consumer/ for a minimal runnable consumer.

Exported targets

Common exported targets include:

  • a2a::core for shared core types and utilities.
  • a2a::client for client APIs.
  • a2a::server for server APIs.
  • a2a::http for HTTP support internals used by higher-level targets.
  • a2a::proto_generated for generated protobuf bindings.
  • a2a::store_postgres when PostgreSQL store support is enabled.

Most applications should link the smallest set they use. The examples link a2a::client, a2a::server, and a2a::core for a combined client/server sample.

Build the curated examples

Use the repository runner for FetchContent examples:

./scripts/run_examples.sh build-example hello_agent

When VCPKG_ROOT is set, the runner forwards the vcpkg toolchain and optional target/host triplets. It builds RelWithDebInfo by default and finds either build-example-hello_agent/a2a_example or the Visual Studio output at build-example-hello_agent/RelWithDebInfo/a2a_example.exe.

Windows Git Bash:

./scripts/install_build_deps.sh
rm -rf build-example-hello_agent
./scripts/run_examples.sh build-example hello_agent

Platform notes

  • Linux CI configures with CMake and validates build, tests, examples, clang-format, clang-tidy, coverage, and selected sanitizer/interop flows.
  • macOS CI builds with Homebrew-provided dependencies and Ninja.
  • Windows CI and local Windows builds use vcpkg manifest dependencies and the Visual Studio 2022 generator. See vcpkg for the helper script, manifest, triplet, and overlay details.

Build with vcpkg

a2a-cpp provides vcpkg metadata for two related workflows:

  1. Manifest dependency mode for building this repository with vcpkg-supplied third-party dependencies.
  2. Overlay port mode for consuming a2a-cpp itself as a vcpkg package before it is available from a public registry.

The repository root vcpkg.json pins the dependency baseline and declares the SDK’s third-party dependencies: protobuf, gRPC, and curl.

Prerequisites

Install or clone vcpkg and bootstrap it:

git clone https://github.com/microsoft/vcpkg.git "$HOME/vcpkg"
"$HOME/vcpkg/bootstrap-vcpkg.sh"

On Windows PowerShell:

git clone https://github.com/microsoft/vcpkg.git C:\vcpkg
C:\vcpkg\bootstrap-vcpkg.bat

Set VCPKG_ROOT for convenience:

export VCPKG_ROOT="$HOME/vcpkg"
$env:VCPKG_ROOT = 'C:\vcpkg'

Repository helper on Windows Git Bash

From the repository root, the Bash helper clones, bootstraps, and installs the vcpkg manifest dependencies:

./scripts/install_build_deps.sh

The default vcpkg checkout is $HOME/vcpkg; no fixed C:/vcpkg location is required. Override the root or triplets with environment variables:

export VCPKG_ROOT=/d/tools/vcpkg
export VCPKG_TARGET_TRIPLET=x64-windows-static
export VCPKG_HOST_TRIPLET=x64-windows
./scripts/install_build_deps.sh

Build this repository with manifest dependencies

From the repository root, let vcpkg install the manifest dependencies and then configure CMake with the vcpkg toolchain file:

"$VCPKG_ROOT/vcpkg" install
cmake -S . -B build-vcpkg \
  -DVCPKG_MANIFEST_MODE=ON \
  -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DA2A_ENABLE_TESTING=ON
cmake --build build-vcpkg --parallel
ctest --test-dir build-vcpkg --output-on-failure

On multi-config generators such as Visual Studio, pass the configuration during build and test:

& "$env:VCPKG_ROOT\vcpkg.exe" install
cmake -S . -B build-vcpkg -G "Visual Studio 17 2022" -A x64 `
  -DVCPKG_MANIFEST_MODE=ON `
  -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake"
cmake --build build-vcpkg --config RelWithDebInfo --parallel
ctest --test-dir build-vcpkg -C RelWithDebInfo --output-on-failure

Use a specific triplet

Pass the same target triplet to vcpkg and CMake. For native builds, use the same value for the host triplet so host tools such as protoc and grpc_cpp_plugin are resolved consistently:

"$VCPKG_ROOT/vcpkg" install --triplet x64-linux --host-triplet x64-linux
cmake -S . -B build-vcpkg \
  -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" \
  -DVCPKG_TARGET_TRIPLET=x64-linux \
  -DVCPKG_HOST_TRIPLET=x64-linux

Windows CI uses the repository triplet triplets/ci-x64-windows-release.cmake to build release-only dependencies and reduce dependency build time:

$env:VCPKG_OVERLAY_TRIPLETS = "$PWD\triplets"
& "$env:VCPKG_ROOT\vcpkg.exe" install --triplet ci-x64-windows-release --host-triplet ci-x64-windows-release
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 `
  -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" `
  -DVCPKG_TARGET_TRIPLET=ci-x64-windows-release `
  -DVCPKG_HOST_TRIPLET=ci-x64-windows-release

Build standalone FetchContent examples on Windows

The example runner uses the already-installed root manifest dependencies. It automatically passes the vcpkg toolchain and triplets when VCPKG_ROOT is set:

./scripts/install_build_deps.sh
rm -rf build-example-streaming_client
./scripts/run_examples.sh build-example streaming_client
./build-example-streaming_client/RelWithDebInfo/a2a_example.exe --help

Delete a build directory that was configured before the toolchain was supplied; changing CMAKE_TOOLCHAIN_FILE in an existing CMake cache is not reliable.

Consume a2a-cpp through the repository overlay port

The repository includes an overlay port at vcpkg-overlay-ports/a2a-cpp. A downstream manifest can depend on a2a-cpp and point vcpkg at that overlay.

vcpkg.json:

{
  "name": "my-a2a-app",
  "version-string": "0.4.1",
  "dependencies": [
    "a2a-cpp"
  ]
}

vcpkg-configuration.json:

{
  "default-registry": {
    "kind": "builtin",
    "baseline": "3426db05b996481ca31e95fff3734cf23e0f51bc"
  },
  "overlay-ports": [
    "path/to/a2a-cpp/vcpkg-overlay-ports"
  ]
}

Then configure the application with the vcpkg toolchain file on the first CMake configure and use the installed CMake package:

find_package(a2a_cpp CONFIG REQUIRED)
target_link_libraries(my_a2a_app PRIVATE a2a::client a2a::server a2a::core)

A complete example is available in examples/installed_package_consumer/.

Enable PostgreSQL store support

The overlay port exposes a postgres-store feature. Enable it in manifest mode when your application needs PostgreSQL-backed stores:

{
  "name": "my-a2a-app",
  "version-string": "0.4.1",
  "dependencies": [
    {
      "name": "a2a-cpp",
      "features": ["postgres-store"]
    }
  ]
}

When the feature is enabled, link the additional target where needed:

target_link_libraries(my_a2a_app PRIVATE a2a::store_postgres)

Classic mode smoke install

For a direct overlay smoke test, run classic mode from a directory that does not contain a vcpkg.json manifest:

mkdir -p /tmp/a2a-vcpkg-smoke
cd /tmp/a2a-vcpkg-smoke
"$VCPKG_ROOT/vcpkg" install a2a-cpp --overlay-ports=/path/to/a2a-cpp/vcpkg-overlay-ports

Add a triplet if needed:

"$VCPKG_ROOT/vcpkg" install a2a-cpp:x64-linux --overlay-ports=/path/to/a2a-cpp/vcpkg-overlay-ports

Binary caching

Large dependencies such as gRPC and protobuf can take time to build. Enable binary caching for local and CI runs:

export VCPKG_BINARY_SOURCES="clear;files,$HOME/.cache/vcpkg-binary-cache,readwrite"
mkdir -p "$HOME/.cache/vcpkg-binary-cache"

On Windows PowerShell:

$env:VCPKG_BINARY_SOURCES = 'clear;files,C:\vcpkg-binary-cache,readwrite'
New-Item -ItemType Directory -Force C:\vcpkg-binary-cache | Out-Null

Troubleshooting

  • CMake cannot find gRPC or Protobuf: confirm CMAKE_TOOLCHAIN_FILE points to scripts/buildsystems/vcpkg.cmake before the first configure. If you configured without it, delete the build directory and configure again.
  • Unexpected manifest behavior in classic mode: classic vcpkg install a2a-cpp should be run outside directories containing vcpkg.json, otherwise vcpkg switches to manifest mode.
  • Different host and target triplets: pass both VCPKG_TARGET_TRIPLET and VCPKG_HOST_TRIPLET when cross-compiling or when CI uses a custom host triplet.
  • Slow clean builds: enable binary caching and prefer release-only dependency triplets for CI jobs that only link release configurations.
  • Example executable is not in the build root on Windows: Visual Studio is multi-configuration; use build-example-<app>/RelWithDebInfo/a2a_example.exe, or let scripts/run_examples.sh locate and run it.
  • The dependency helper reports an unsupported shell on Windows: run scripts/install_build_deps.sh from Git Bash rather than another Windows shell.

API Reference

The generated C++ API reference is built from public headers under include/a2a/**.

Generated reference

When published with the documentation site, open:

Generate locally

./scripts/generate_api_reference.sh

The script writes generated pages to book-build/api/cpp.

Public API areas

  • a2a::core: results, errors, protocol constants, JSON/protobuf helpers, Agent Card support, task state helpers, and versioning.
  • a2a::client: A2AClient, transports, discovery, auth hooks, call options, interceptors, and streaming observers.
  • a2a::server: executor, dispatcher, transports, interceptors, task stores, task lifecycle helpers, push notifications, and Agent Card serialization.
  • a2a::http: shared outbound HTTP client abstraction used by default REST/JSON-RPC/discovery/push paths when libcurl is enabled.