diff --git a/docs/content/about.md b/docs/content/about.md
deleted file mode 100644
index b7ffc9c1a..000000000
--- a/docs/content/about.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-title: About
-description: A static site generator built with Java and Quarkus. Zero config to get started, full power of the JVM when you need it.
-layout: page
----
-
-# About this site
-
-This site is built with [Roq](https://iamroq.dev), a static site generator powered by [Quarkus](https://quarkus.io). It combines the best of tools like Jekyll and Hugo with the Java ecosystem: zero configuration to get started, blazing fast live-reload in dev mode, and full access to Java when you need it.
-
-## Authors
-
-
- {#for id in cdi:authors.fields}
- {#let author=cdi:authors.get(id)}
- {#roq/authorCard name=author.name avatar=author.avatar?? nickname=author.nickname profile=author.profile /}
- {/for}
-
-
diff --git a/docs/content/authorization.md b/docs/content/authorization.md
new file mode 100644
index 000000000..10aa2e6d5
--- /dev/null
+++ b/docs/content/authorization.md
@@ -0,0 +1,67 @@
+---
+title: Task Authorization
+description: Per-user access control for A2A tasks — ownership, read/write checks, and user identity across transports.
+layout: page
+---
+
+# Task Authorization
+
+> **Security note:** For multi-user deployments, a `TaskAuthorizationProvider` **must** be configured. Without one, all operations are permitted regardless of authentication — any authenticated user can read, modify, or cancel any task. Production deployments should use a fail-closed ownership policy (deny access when ownership is unknown).
+
+## Implementing TaskAuthorizationProvider
+
+Implement `TaskAuthorizationProvider` to control per-user access:
+
+```java
+@ApplicationScoped
+public class MyTaskAuthorizationProvider implements TaskAuthorizationProvider {
+
+ @Override
+ public boolean checkRead(ServerCallContext context, String taskId, TaskOperation op) {
+ return isOwner(context.getUser(), taskId);
+ }
+
+ @Override
+ public boolean checkWrite(ServerCallContext context, String taskId, TaskOperation op) {
+ return isOwner(context.getUser(), taskId);
+ }
+
+ @Override
+ public boolean checkCreate(ServerCallContext context, TaskOperation op) {
+ return context.getUser() != null && context.getUser().isAuthenticated();
+ }
+
+ @Override
+ public boolean isTaskRecorded(String taskId) {
+ return ownershipStore.contains(taskId);
+ }
+
+ @Override
+ public void recordOwnership(ServerCallContext context, String taskId, TaskOperation op) {
+ if (context.getUser() != null) {
+ ownershipStore.put(taskId, context.getUser().getUsername());
+ }
+ }
+}
+```
+
+The SDK discovers the bean via CDI automatically — no additional wiring needed.
+
+> **Note:** When task authorization is required, always obtain `RequestHandler` through CDI injection. Manual instantiation via `DefaultRequestHandler.create()` bypasses the `AuthorizationRequestHandlerDecorator` and all authorization checks.
+
+## User Identity in ServerCallContext
+
+Authorization decisions rely on `context.getUser()` returning the authenticated user. How the user is populated depends on the transport:
+
+- **JSON-RPC and REST**: The Quarkus route handler extracts the user from the Vert.x routing context (`rc.userContext()`) and sets it on `ServerCallContext` directly.
+- **gRPC**: The reference server includes a `QuarkusCallContextFactory` CDI bean that injects the Quarkus `SecurityIdentity` and maps it to the `ServerCallContext` `User`. This happens automatically when using the reference gRPC module. If you provide your own `CallContextFactory`, you are responsible for populating the user.
+
+## Authorization Checks
+
+| Operation | Authorization check |
+|-----------|---------------------|
+| `getTask`, `subscribeToTask`, `getTaskPushNotificationConfig`, `listTaskPushNotificationConfigs` | `checkRead` |
+| `cancelTask`, `createTaskPushNotificationConfig`, `deleteTaskPushNotificationConfig` | `checkWrite` |
+| `messageSend` / `messageSendStream` (existing task) | `checkWrite` |
+| `messageSend` / `messageSendStream` (new task) | `checkCreate`, then `recordOwnership` |
+| `listTasks` | `checkRead` per task |
diff --git a/docs/content/boms.md b/docs/content/boms.md
new file mode 100644
index 000000000..e0dc6e1b4
--- /dev/null
+++ b/docs/content/boms.md
@@ -0,0 +1,120 @@
+---
+title: Bill of Materials (BOM)
+description: Dependency management BOMs for the A2A Java SDK — SDK, Extras, and Reference BOMs.
+layout: page
+---
+
+# Bill of Materials (BOM)
+
+The A2A Java SDK provides three BOMs for different use cases, so you can manage dependency versions in one place.
+
+## BOM Modules
+
+### SDK BOM
+
+**Artifact:** `org.a2aproject.sdk:a2a-java-sdk-bom`
+
+Includes all A2A SDK core modules (spec, server, client, transport), core third-party dependencies, Jakarta APIs, and test utilities.
+
+**Use this BOM when:** Building A2A agents with any framework (Quarkus, Spring Boot, vanilla Java, etc.)
+
+### Extras BOM
+
+**Artifact:** `org.a2aproject.sdk:a2a-java-sdk-extras-bom`
+
+Includes everything from the SDK BOM plus server-side enhancement modules (database persistence, distributed queue management, etc.).
+
+**Use this BOM when:** Building production A2A servers needing advanced server-side features beyond the core SDK.
+
+### Reference BOM
+
+**Artifact:** `org.a2aproject.sdk:a2a-java-sdk-reference-bom`
+
+Includes everything from the SDK BOM plus the Quarkus BOM (complete Quarkus platform), A2A reference implementation modules, and the TCK module for testing.
+
+**Use this BOM when:** Building Quarkus-based A2A agents or reference implementations.
+
+## Usage
+
+### SDK BOM (Any Framework)
+
+```xml
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-bom
+ $\{org.a2aproject.sdk.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-server-common
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-transport-jsonrpc
+
+
+```
+
+### Extras BOM (Database Persistence, Distributed Deployments)
+
+```xml
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-extras-bom
+ $\{org.a2aproject.sdk.version}
+ pom
+ import
+
+
+
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-server-common
+
+
+ org.a2aproject.sdk
+ a2a-java-extras-task-store-database-jpa
+
+
+```
+
+### Reference BOM (Quarkus)
+
+```xml
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-reference-bom
+ $\{org.a2aproject.sdk.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-reference-jsonrpc
+
+
+ io.quarkus
+ quarkus-arc
+
+
+```
diff --git a/docs/content/client.md b/docs/content/client.md
index 94ca0492b..c39bb24f9 100644
--- a/docs/content/client.md
+++ b/docs/content/client.md
@@ -190,42 +190,8 @@ Client client = Client
## Communicating with v0.3 Agents
-Use `Client_v0_3` to communicate with agents that only support protocol v0.3:
-
-```xml
-
- org.a2aproject.sdk
- a2a-java-sdk-compat-0.3-client
- $\{org.a2aproject.sdk.version}
-
-
- org.a2aproject.sdk
- a2a-java-sdk-compat-0.3-client-transport-jsonrpc
- $\{org.a2aproject.sdk.version}
-
-```
-
-gRPC and REST transports are also available:
-- `a2a-java-sdk-compat-0.3-client-transport-grpc`
-- `a2a-java-sdk-compat-0.3-client-transport-rest`
-
-```java
-AgentCard card = A2ACardResolver.builder().baseUrl("http://localhost:1234")
- .build().getAgentCard();
-
-AgentInterface v03Interface = card.supportedInterfaces().stream()
- .filter(i -> A2AProtocol_v0_3.PROTOCOL_VERSION.equals(i.protocolVersion()))
- .findFirst().orElseThrow();
-
-Client_v0_3 client = ClientBuilder_v0_3.forUrl(v03Interface.url())
- .withTransport(JSONRPCTransport_v0_3.class, new JSONRPCTransportConfigBuilder_v0_3())
- .build();
-```
-
-**Note:** `Client_v0_3` exposes only operations available in protocol v0.3. For example, `listTasks()` is not available (it was added in v1.0). Return types use v0.3 domain objects from the `org.a2aproject.sdk.compat03.spec` package.
+See [Backward Compatibility](compatibility#client-communicating-with-v03-agents) for using `Client_v0_3` with older protocol agents.
## Examples
-- [Hello World Client](https://github.com/a2aproject/a2a-java/blob/main/examples/helloworld/client/README.md) — Java client talking to a Python A2A server
-- [Hello World Server](https://github.com/a2aproject/a2a-java/blob/main/examples/helloworld/server/README.md) — Python client talking to a Java A2A server
-- [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/java/agents) — More agent examples
+See [Examples](examples) for Hello World walkthroughs and sample applications.
diff --git a/docs/content/compatibility.md b/docs/content/compatibility.md
new file mode 100644
index 000000000..aae404e3e
--- /dev/null
+++ b/docs/content/compatibility.md
@@ -0,0 +1,124 @@
+---
+title: Backward Compatibility
+description: Serve v1.0 and v0.3 A2A protocol versions simultaneously — multi-version modules, version routing, and v0.3 client support.
+layout: page
+---
+
+# Backward Compatibility with v0.3
+
+Add compat modules alongside v1.0 modules to serve both protocol versions simultaneously. No changes to your `AgentExecutor` are needed.
+
+## Server: Multi-Version Module (recommended)
+
+```xml
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-reference-multiversion-jsonrpc
+ $\{org.a2aproject.sdk.version}
+
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-reference-multiversion-rest
+ $\{org.a2aproject.sdk.version}
+
+```
+
+## Server: Individual Compat Modules
+
+```xml
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-compat-0.3-reference-jsonrpc
+ $\{org.a2aproject.sdk.version}
+
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-compat-0.3-reference-rest
+ $\{org.a2aproject.sdk.version}
+
+
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-compat-0.3-reference-grpc
+ $\{org.a2aproject.sdk.version}
+
+```
+
+## How Version Routing Works
+
+- **JSON-RPC and REST**: When serving multiple protocol versions, version routing inspects the `A2A-Version` HTTP header on each request. If the header is `"1.0"`, the request is routed to the v1.0 handler. If it is `"0.3"` or absent, the request is routed to the v0.3 handler.
+- **gRPC**: Version dispatch is implicit — v0.3 clients use the `a2a.v1` protobuf package and v1.0 clients use `lf.a2a.v1`, so requests are routed to the correct service automatically.
+- **Agent card**: When both v1.0 and v0.3 are enabled, the v1.0 `AgentCard` takes precedence and is served at `/.well-known/agent-card.json`. The v0.3 `AgentCard_v0_3` is ignored. If only v0.3 is enabled, the v0.3 agent card is used. If only v1.0 is enabled, the v1.0 agent card is used as-is.
+
+## Making the v1.0 Agent Card Compatible with v0.3 Clients
+
+When serving both protocol versions, you need to ensure the v1.0 agent card contains fields that v0.3 clients expect. Existing v0.3 client implementations (in any language) look for `url`, `preferredTransport`, and `additionalInterfaces` with `transport`/`url` entries — fields that don't exist in the v1.0 format by default.
+
+To make your v1.0 `AgentCard` parsable by v0.3 clients, set these fields on the builder:
+
+```java
+AgentCard card = AgentCard.builder()
+ .name("My Agent")
+ // ... other v1.0 fields ...
+ .supportedInterfaces(List.of(
+ new AgentInterface("jsonrpc", "http://localhost:9999")))
+ // v0.3 backward-compatibility fields:
+ .url("http://localhost:9999")
+ .preferredTransport("jsonrpc")
+ .additionalInterfaces(List.of(
+ new Legacy_0_3_AgentInterface("jsonrpc", "http://localhost:9999")))
+ .build();
+```
+
+The two interface lists serve different clients:
+
+- `supportedInterfaces` — used by **v1.0 clients** to discover endpoints (uses `AgentInterface` with `protocolBinding`/`url`/`tenant` fields)
+- `additionalInterfaces` — used by **v0.3 clients** to discover endpoints (uses `Legacy_0_3_AgentInterface` with v0.3 field names: `transport`/`url`)
+- `url` and `preferredTransport` — top-level fields that v0.3 clients use to discover the primary endpoint
+
+## Push Notification Behavior
+
+Push notification payloads are automatically formatted to match the protocol version used when the push notification configuration was registered. When a v0.3 client registers a push notification configuration (via any transport), the server records the protocol version alongside the configuration. When a notification is later sent to that webhook, the payload is formatted as a v0.3 Task object. Configurations registered by v1.0 clients receive v1.0 `StreamResponse` payloads as usual. This happens transparently — no additional configuration is needed beyond adding the compat reference module.
+
+## Client: Communicating with v0.3 Agents
+
+Use `Client_v0_3` to communicate with agents that only support protocol v0.3:
+
+```xml
+
+ org.a2aproject.sdk
+ a2a-java-sdk-compat-0.3-client
+ $\{org.a2aproject.sdk.version}
+
+
+ org.a2aproject.sdk
+ a2a-java-sdk-compat-0.3-client-transport-jsonrpc
+ $\{org.a2aproject.sdk.version}
+
+```
+
+gRPC and REST transports are also available:
+- `a2a-java-sdk-compat-0.3-client-transport-grpc`
+- `a2a-java-sdk-compat-0.3-client-transport-rest`
+
+```java
+AgentCard card = A2ACardResolver.builder().baseUrl("http://localhost:1234")
+ .build().getAgentCard();
+
+AgentInterface v03Interface = card.supportedInterfaces().stream()
+ .filter(i -> A2AProtocol_v0_3.PROTOCOL_VERSION.equals(i.protocolVersion()))
+ .findFirst().orElseThrow();
+
+Client_v0_3 client = ClientBuilder_v0_3.forUrl(v03Interface.url())
+ .withTransport(JSONRPCTransport_v0_3.class, new JSONRPCTransportConfigBuilder_v0_3())
+ .build();
+```
+
+**Note:** `Client_v0_3` exposes only operations available in protocol v0.3. For example, `listTasks()` is not available (it was added in v1.0). Return types use v0.3 domain objects from the `org.a2aproject.sdk.compat03.spec` package.
diff --git a/docs/content/configuration.md b/docs/content/configuration.md
new file mode 100644
index 000000000..304598fcb
--- /dev/null
+++ b/docs/content/configuration.md
@@ -0,0 +1,147 @@
+---
+title: Configuration
+description: Configure the A2A Java SDK — properties, MicroProfile Config integration, custom providers.
+layout: page
+---
+
+# Configuration
+
+The A2A Java SDK uses a flexible configuration system that works across different frameworks.
+
+**Default behavior:** Configuration values come from `META-INF/a2a-defaults.properties` files on the classpath (provided by core modules and extras). These defaults work out of the box without any additional setup.
+
+**Customizing configuration:**
+- **Quarkus/MicroProfile Config users**: Add the `microprofile-config` integration to override defaults via `application.properties`, environment variables, or system properties
+- **Spring/other frameworks**: Implement a custom `A2AConfigProvider` (see [Custom Config Providers](#custom-config-providers) below)
+- **Reference implementations**: Already include the MicroProfile Config integration
+
+## Configuration Properties
+
+### Executor Settings
+
+The SDK uses a dedicated executor for async operations like streaming. Default: 5 core threads, 50 max threads.
+
+```properties
+# Core thread pool size for the @Internal executor (default: 5)
+a2a.executor.core-pool-size=5
+
+# Maximum thread pool size (default: 50)
+a2a.executor.max-pool-size=50
+
+# Thread keep-alive time in seconds (default: 60)
+a2a.executor.keep-alive-seconds=60
+```
+
+### Blocking Call Timeouts
+
+```properties
+# Timeout for agent execution in blocking calls (default: 30 seconds)
+a2a.blocking.agent.timeout.seconds=30
+
+# Timeout for event consumption in blocking calls (default: 5 seconds)
+a2a.blocking.consumption.timeout.seconds=5
+```
+
+### Tuning Guidelines
+
+- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load.
+- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool.
+- **Concurrency**: In production with high concurrent streaming, increase pool sizes accordingly.
+- **Agent Timeouts**: LLM-based agents may need longer timeouts (60--120s) compared to simple agents.
+
+## MicroProfile Config Integration
+
+Add the integration dependency to override configuration via standard MicroProfile Config sources:
+
+```xml
+
+ org.a2aproject.sdk
+ a2a-java-sdk-microprofile-config
+ $\{org.a2aproject.sdk.version}
+
+```
+
+Once added, you can set any A2A property through:
+
+**application.properties:**
+```properties
+a2a.executor.core-pool-size=10
+a2a.executor.max-pool-size=100
+a2a.blocking.agent.timeout.seconds=60
+```
+
+**Environment variables:**
+```bash
+export A2A_EXECUTOR_CORE_POOL_SIZE=10
+export A2A_BLOCKING_AGENT_TIMEOUT_SECONDS=60
+```
+
+**System properties:**
+```bash
+java -Da2a.executor.core-pool-size=10 -jar your-app.jar
+```
+
+### Configuration Fallback Chain
+
+```
+MicroProfile Config Sources (application.properties, env vars, -D flags)
+ | (not found?)
+DefaultValuesConfigProvider
+ -> Scans classpath for ALL META-INF/a2a-defaults.properties files
+ -> Merges all discovered properties together
+ -> Throws exception if duplicate keys found
+ | (property exists?)
+Return merged default value
+ | (not found?)
+IllegalArgumentException
+```
+
+All `META-INF/a2a-defaults.properties` files (from server-common, extras modules, etc.) are loaded and merged together by `DefaultValuesConfigProvider` at startup. This is not a sequential fallback chain, but a single merged set of defaults.
+
+### Framework Compatibility
+
+The MicroProfile Config integration works with any MicroProfile Config implementation:
+
+- **Quarkus** -- Built-in MicroProfile Config support
+- **Helidon** -- Built-in MicroProfile Config support
+- **Open Liberty** -- Built-in MicroProfile Config support
+- **WildFly/JBoss EAP** -- Add `smallrye-config` dependency
+- **Other Jakarta EE servers** -- Add MicroProfile Config implementation
+
+## Custom Config Providers
+
+If you're using a different framework (Spring, Micronaut, etc.), implement your own `A2AConfigProvider`:
+
+```java
+@ApplicationScoped
+@Alternative
+@Priority(100) // Higher than MicroProfileConfigProvider's priority of 50
+public class MyConfigProvider implements A2AConfigProvider {
+
+ @Inject
+ Environment env; // Your framework's config source (e.g. Spring Environment)
+
+ @Inject
+ DefaultValuesConfigProvider defaultValues;
+
+ @Override
+ public String getValue(String name) {
+ String value = env.getProperty(name);
+ if (value != null) {
+ return value;
+ }
+ return defaultValues.getValue(name);
+ }
+
+ @Override
+ public Optional getOptionalValue(String name) {
+ String value = env.getProperty(name);
+ if (value != null) {
+ return Optional.of(value);
+ }
+ return defaultValues.getOptionalValue(name);
+ }
+}
+```
+
+**Note:** The reference server implementations (Quarkus-based) automatically include the MicroProfile Config integration, so properties work out of the box in `application.properties`.
diff --git a/docs/content/examples.md b/docs/content/examples.md
new file mode 100644
index 000000000..90d3150e2
--- /dev/null
+++ b/docs/content/examples.md
@@ -0,0 +1,149 @@
+---
+title: Examples
+description: Hello World examples for the A2A Java SDK — server and client walkthroughs with transport selection and OpenTelemetry.
+layout: page
+---
+
+# Examples
+
+## Prerequisites
+
+- Java 17 or higher
+- Python 3.8 or higher
+- [uv](https://github.com/astral-sh/uv) (recommended Python package installer)
+- Git
+
+## Hello World Server
+
+This example runs a Java A2A server that a Python client can talk to.
+
+### Start the Java Server
+
+```bash
+cd examples/helloworld/server
+mvn quarkus:dev
+```
+
+### Transport Protocol Selection
+
+Select the transport protocol via the `quarkus.agentcard.protocol` property:
+
+```bash
+# JSON-RPC (default)
+mvn quarkus:dev
+
+# gRPC
+mvn quarkus:dev -Dquarkus.agentcard.protocol=GRPC
+
+# HTTP+JSON/REST
+mvn quarkus:dev -Dquarkus.agentcard.protocol=HTTP+JSON
+```
+
+You can also set the default in `src/main/resources/application.properties`:
+```properties
+quarkus.agentcard.protocol=HTTP+JSON
+```
+
+### Run the Python Client
+
+The Python client is part of the [a2a-samples](https://github.com/google-a2a/a2a-samples) project:
+
+```bash
+git clone https://github.com/google-a2a/a2a-samples.git
+cd a2a-samples/samples/python/agents/helloworld
+
+uv venv
+source .venv/bin/activate
+uv pip install -e .
+uv run test_client.py
+```
+
+The client connects to `http://localhost:9999`, fetches the agent card, sends a regular message, then sends the same message as a streaming request.
+
+## Hello World Client
+
+This example runs a Java A2A client that talks to a Python server.
+
+### Start the Python Server
+
+```bash
+git clone https://github.com/google-a2a/a2a-samples.git
+cd a2a-samples/samples/python/agents/helloworld
+
+uv venv
+source .venv/bin/activate
+uv pip install -e .
+uv run .
+```
+
+The server starts on `http://localhost:9999`. You can also use the [Java server example](#hello-world-server) instead.
+
+### Run the Java Client
+
+First build the SDK, then run the client:
+
+```bash
+# From the a2a-java root
+mvn clean install
+
+# Run the client
+cd examples/helloworld/client
+mvn exec:java
+```
+
+#### Transport Protocol Selection
+
+```bash
+# JSON-RPC (default)
+mvn exec:java
+
+# gRPC
+mvn exec:java -Dquarkus.agentcard.protocol=GRPC
+
+# HTTP+JSON/REST
+mvn exec:java -Dquarkus.agentcard.protocol=HTTP+JSON
+```
+
+The protocol you select on the client must match the protocol configured on the server.
+
+#### Using JBang
+
+Alternatively, run the client with [JBang](https://www.jbang.dev/) (no Maven required):
+
+```bash
+jbang examples/helloworld/client/src/main/java/org/a2aproject/sdk/examples/helloworld/HelloWorldRunner.java
+```
+
+Pass transport and OpenTelemetry flags the same way:
+```bash
+jbang examples/helloworld/client/src/main/java/org/a2aproject/sdk/examples/helloworld/HelloWorldRunner.java \
+ -Dquarkus.agentcard.protocol=GRPC -Dopentelemetry=true
+```
+
+## OpenTelemetry (Optional)
+
+Both the server and client support distributed tracing with OpenTelemetry.
+
+### Server with OpenTelemetry
+
+```bash
+cd examples/helloworld/server
+mvn quarkus:dev -Popentelemetry
+```
+
+Quarkus Dev Services automatically starts a Grafana observability stack. Open Grafana at `http://localhost:3001` (credentials: admin/admin) and view traces in the "Explore" section using the Tempo data source.
+
+### Client with OpenTelemetry
+
+```bash
+cd examples/helloworld/client
+mvn exec:java -Dopentelemetry=true
+```
+
+The client expects an OpenTelemetry collector on port 5317. The easiest way is to run the Java server with `-Popentelemetry` (which starts the collector automatically), then run the client with `-Dopentelemetry=true` for end-to-end traces.
+
+For more information, see the [OpenTelemetry extras module](extras/opentelemetry).
+
+## More Examples
+
+- [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/java/agents) — Additional agent examples in Java and other languages
diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md
new file mode 100644
index 000000000..230a609dc
--- /dev/null
+++ b/docs/content/getting-started.md
@@ -0,0 +1,47 @@
+---
+title: Getting Started
+description: Get started with the A2A Java SDK — overview, guides, and next steps.
+layout: page
+---
+
+# Getting Started
+
+The A2A Java SDK is a multi-module Maven library for implementing the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/) in Java. It provides both client and server support for agent communication over JSON-RPC, gRPC, and REST transports.
+
+## What You Can Build
+
+- **A2A Servers** — Expose your Java agent as an A2A-compliant service that other agents and clients can discover and communicate with.
+- **A2A Clients** — Connect to any A2A-compliant agent, with streaming, push notifications, and task management.
+
+## Quick Start
+
+Add the reference server dependency to your Maven project:
+
+```xml
+
+ org.a2aproject.sdk
+ a2a-java-sdk-reference-jsonrpc
+ $\{org.a2aproject.sdk.version}
+
+```
+
+Then implement an `AgentExecutor` and define an `AgentCard`. See the [Server Guide](server) for the full walkthrough.
+
+## Guides
+
+| Guide | Description |
+|-------|-------------|
+| [Server Guide](server) | Run your Java application as an A2A server |
+| [Client Guide](client) | Communicate with A2A-compliant agents |
+| [Configuration](configuration) | Config properties, MicroProfile Config, custom providers |
+| [Task Authorization](authorization) | Per-user access control for multi-user deployments |
+| [Backward Compatibility](compatibility) | Serve v1.0 and v0.3 clients simultaneously |
+| [REST API Reference](rest-api-reference) | HTTP+JSON endpoint reference |
+| [Extras](extras) | Optional add-ons: database storage, OpenTelemetry, HTTP clients |
+| [BOMs](boms) | Dependency management with Bill of Materials |
+| [Examples](examples) | Hello World walkthroughs and sample applications |
+
+## Requirements
+
+- Java 17+
+- Maven 3.8+
diff --git a/docs/content/index.html b/docs/content/index.html
index 4bf0db8b3..ebd358091 100644
--- a/docs/content/index.html
+++ b/docs/content/index.html
@@ -42,6 +42,36 @@ Quick Install
<version>$\{org.a2aproject.sdk.version}</version>
</dependency>
- See the Server Guide and Client Guide for full setup instructions, or browse Community Articles for tutorials and examples.
+ See the Getting Started guide, or jump directly to the Server Guide and Client Guide for full setup instructions.
+
+
+
+ Compatibility
+
+
+
+ | SDK Version |
+ A2A Protocol |
+ Java |
+
+
+
+
+ | 1.0.0.Final |
+ 1.0, 0.3 |
+ 17+ |
+
+
+ | 1.1.0.Final |
+ 1.0, 0.3 |
+ 17+ |
+
+
+ | dev (unreleased) |
+ 1.0, 0.3 |
+ 17+ |
+
+
+
diff --git a/docs/content/rest-api-reference.md b/docs/content/rest-api-reference.md
new file mode 100644
index 000000000..6c48fc309
--- /dev/null
+++ b/docs/content/rest-api-reference.md
@@ -0,0 +1,327 @@
+---
+title: REST API Reference
+description: HTTP+JSON endpoint reference for the A2A REST transport — request/response formats, SSE streaming, error handling.
+layout: page
+---
+
+# REST API Reference
+
+REST transport implementation for the A2A Protocol, providing HTTP-based (`HTTP+JSON` protocol binding) communication between agents and clients. All request and response bodies use [Protobuf JSON](https://protobuf.dev/programming-guides/proto3/#json) serialization (camelCase field names).
+
+The `\{tenant}` path prefix is optional on all endpoints. When omitted, the extra slash is also omitted (e.g., `/message:send` instead of `/\{tenant}/message:send`).
+
+## Send Message
+
+Sends a message to the agent and blocks until the agent reaches a terminal or interrupted state, or returns immediately if `returnImmediately` is set.
+
+```
+POST /\{tenant}/message:send
+Content-Type: application/json
+```
+
+**Request body** (`SendMessageRequest`):
+```json
+{
+ "message": {
+ "messageId": "msg-1",
+ "role": "ROLE_USER",
+ "parts": [
+ {"text": "Hello, what can you do?"}
+ ],
+ "contextId": "ctx-1"
+ },
+ "configuration": {
+ "historyLength": 10,
+ "returnImmediately": false,
+ "acceptedOutputModes": ["text/plain"]
+ }
+}
+```
+
+**Response** — one of:
+- `{"task": { ... }}` — a `Task` object when the agent creates/updates a task
+- `{"message": { ... }}` — a `Message` object when the agent replies without a task
+
+## Send Streaming Message
+
+Sends a message and streams task updates as Server-Sent Events (SSE). Requires `capabilities.streaming = true` in the agent card.
+
+```
+POST /\{tenant}/message:stream
+Content-Type: application/json
+Accept: text/event-stream
+```
+
+Request body is identical to `message:send`. Response is a stream of SSE events:
+
+```
+: SSE stream started
+
+id: 0
+data: {"statusUpdate":{"taskId":"task-1","contextId":"ctx-1","status":{"state":"TASK_STATE_WORKING"}}}
+
+id: 1
+data: {"artifactUpdate":{"taskId":"task-1","contextId":"ctx-1","artifact":{"artifactId":"a-1","parts":[{"text":"Hello!"}]}}}
+
+id: 2
+data: {"statusUpdate":{"taskId":"task-1","contextId":"ctx-1","status":{"state":"TASK_STATE_COMPLETED"}}}
+```
+
+Each `data` field contains a JSON-serialized `StreamResponse` with one of the following fields set:
+- `task` — full `Task` snapshot
+- `message` — a `Message` from the agent
+- `statusUpdate` — a `TaskStatusUpdateEvent`
+- `artifactUpdate` — a `TaskArtifactUpdateEvent`
+
+## Get Task
+
+Retrieves the current state of a task by ID.
+
+```
+GET /\{tenant}/tasks/\{taskId}?historyLength=10
+```
+
+| Query parameter | Type | Description |
+|-----------------|---------|-----------------------------------------------------------------------------|
+| `historyLength` | integer | Maximum number of history messages to include. Omit for no limit; `0` for none. |
+
+**Response** — a `Task` object:
+```json
+{
+ "id": "task-1",
+ "contextId": "ctx-1",
+ "status": {
+ "state": "TASK_STATE_COMPLETED",
+ "timestamp": "2023-10-27T10:00:00Z"
+ },
+ "artifacts": [],
+ "history": []
+}
+```
+
+## List Tasks
+
+Lists tasks with optional filtering and pagination.
+
+```
+GET /\{tenant}/tasks
+```
+
+| Query parameter | Type | Description |
+|------------------------|---------|--------------------------------------------------------------------------------------------|
+| `contextId` | string | Filter by context ID. |
+| `status` | string | Filter by task state. One of the `TaskState` enum values (e.g. `TASK_STATE_COMPLETED`). |
+| `pageSize` | integer | Maximum number of tasks to return (server default: 50, max: 100). |
+| `pageToken` | string | Pagination token from a previous `ListTasks` response. |
+| `historyLength` | integer | Maximum history messages to include per task. |
+| `statusTimestampAfter` | string | ISO-8601 timestamp. Only return tasks whose status was updated at or after this time. |
+| `includeArtifacts` | boolean | Whether to include artifacts in results. Defaults to `false`. |
+
+**Response** (`ListTasksResponse`):
+```json
+{
+ "tasks": [ ... ],
+ "nextPageToken": "",
+ "pageSize": 50,
+ "totalSize": 3
+}
+```
+
+### TaskState Values
+
+| Value | Description |
+|-------------------------------|--------------------------------------------------|
+| `TASK_STATE_SUBMITTED` | Task acknowledged, not yet processing. |
+| `TASK_STATE_WORKING` | Task is actively being processed. |
+| `TASK_STATE_COMPLETED` | Task finished successfully (terminal). |
+| `TASK_STATE_FAILED` | Task finished with an error (terminal). |
+| `TASK_STATE_CANCELED` | Task was canceled (terminal). |
+| `TASK_STATE_REJECTED` | Agent declined to perform the task (terminal). |
+| `TASK_STATE_INPUT_REQUIRED` | Agent needs additional input (interrupted). |
+| `TASK_STATE_AUTH_REQUIRED` | Authentication is required to proceed (interrupted). |
+
+## Cancel Task
+
+Requests cancellation of a running task. The agent should transition the task to `TASK_STATE_CANCELED`.
+
+```
+POST /\{tenant}/tasks/\{taskId}:cancel
+Content-Type: application/json
+```
+
+Request body is optional (may be empty or contain a `metadata` field).
+
+**Response** — the updated `Task` object on success.
+
+## Subscribe to Task
+
+Opens an SSE stream to receive real-time updates for an existing task. Requires `capabilities.streaming = true`. Returns `UnsupportedOperationError` if the task is already in a terminal state.
+
+```
+POST /\{tenant}/tasks/\{taskId}:subscribe
+Accept: text/event-stream
+```
+
+Response is an SSE stream with the same event format as `message:stream`.
+
+## Push Notification Configs
+
+### Create Push Notification Config
+
+Creates a webhook configuration for push notifications on a task.
+
+```
+POST /\{tenant}/tasks/\{taskId}/pushNotificationConfigs
+Content-Type: application/json
+```
+
+**Request body** (`TaskPushNotificationConfig`):
+```json
+{
+ "url": "https://example.com/webhook",
+ "token": "optional-token",
+ "authentication": {
+ "scheme": "Bearer",
+ "credentials": "my-token"
+ }
+}
+```
+
+**Response** — the created `TaskPushNotificationConfig` with its generated `id`. HTTP 201.
+
+### Get Push Notification Config
+
+```
+GET /\{tenant}/tasks/\{taskId}/pushNotificationConfigs/\{configId}
+```
+
+**Response** — the `TaskPushNotificationConfig` object.
+
+### List Push Notification Configs
+
+```
+GET /\{tenant}/tasks/\{taskId}/pushNotificationConfigs
+```
+
+| Query parameter | Type | Description |
+|-----------------|---------|------------------------------------|
+| `pageSize` | integer | Maximum configurations to return. |
+| `pageToken` | string | Pagination token. |
+
+**Response** (`ListTaskPushNotificationConfigsResponse`):
+```json
+{
+ "configs": [ ... ],
+ "nextPageToken": ""
+}
+```
+
+### Delete Push Notification Config
+
+```
+DELETE /\{tenant}/tasks/\{taskId}/pushNotificationConfigs/\{configId}
+```
+
+**Response** — HTTP 204 No Content on success.
+
+## Get Agent Card
+
+Public discovery endpoint. Returns the agent's self-describing manifest. No authentication required.
+
+```
+GET /.well-known/agent-card.json
+```
+
+**Response** — an `AgentCard` object:
+```json
+{
+ "name": "My Agent",
+ "description": "An example agent",
+ "version": "1.0.0",
+ "supportedInterfaces": [ ... ],
+ "capabilities": {
+ "streaming": true,
+ "pushNotifications": false
+ },
+ "skills": [ ... ],
+ "defaultInputModes": ["text/plain"],
+ "defaultOutputModes": ["text/plain"]
+}
+```
+
+## Get Extended Agent Card
+
+Returns additional agent metadata for authenticated clients. Requires `capabilities.extendedAgentCard = true` in the public agent card.
+
+```
+GET /\{tenant}/extendedAgentCard
+```
+
+**Response** — an `AgentCard` object (same structure as the public card, potentially with additional fields).
+
+## Request Headers
+
+| Header | Description |
+|--------------------|-------------------------------------------------------------------------------------------------|
+| `X-A2A-Version` | Requested A2A protocol version (e.g., `1.0`). Validated against the agent's supported versions. |
+| `X-A2A-Extensions` | Comma-separated list of extension URIs the client supports. Required when the agent declares required extensions. |
+
+## Error Handling
+
+All error responses use [RFC 7807 Problem Details](https://tools.ietf.org/html/rfc7807) with `Content-Type: application/problem+json`.
+
+```json
+{
+ "type": "https://a2a-protocol.org/errors/task-not-found",
+ "title": "Task not found",
+ "status": 404,
+ "details": ""
+}
+```
+
+| Field | Type | Description |
+|-----------|---------|---------------------------------------------|
+| `type` | string | URI identifying the error type. |
+| `title` | string | Human-readable summary of the error. |
+| `status` | integer | HTTP status code. |
+| `details` | string | Additional error context (may be empty). |
+
+### Error Types
+
+| `type` URI | HTTP Status | Description |
+|----------------------------------------------------------------|-------------|------------------------------------------------------------|
+| `https://a2a-protocol.org/errors/task-not-found` | 404 | The requested task does not exist. |
+| `https://a2a-protocol.org/errors/method-not-found` | 404 | The endpoint does not exist. |
+| `https://a2a-protocol.org/errors/invalid-request` | 400 | Malformed request, missing required fields, or JSON parse error. |
+| `https://a2a-protocol.org/errors/invalid-params` | 422 | Invalid parameter values (e.g., negative `historyLength`). |
+| `https://a2a-protocol.org/errors/extension-support-required` | 400 | The agent requires an extension the client did not declare.|
+| `https://a2a-protocol.org/errors/task-not-cancelable` | 409 | The task cannot be canceled in its current state. |
+| `https://a2a-protocol.org/errors/content-type-not-supported` | 415 | The requested content type is not supported. |
+| `https://a2a-protocol.org/errors/push-notification-not-supported` | 501 | Push notifications are not configured for this agent. |
+| `https://a2a-protocol.org/errors/unsupported-operation` | 501 | The operation is not implemented or not applicable. |
+| `https://a2a-protocol.org/errors/version-not-supported` | 400 | The requested protocol version is not supported. |
+| `https://a2a-protocol.org/errors/invalid-agent-response` | 502 | The agent produced an invalid response. |
+| `https://a2a-protocol.org/errors/extended-agent-card-not-configured` | 400 | The agent does not have an extended agent card configured. |
+| `https://a2a-protocol.org/errors/internal-error` | 500 | An unexpected server-side error occurred. |
+
+## Client Integration
+
+The REST client (`client/transport/rest`) automatically maps error responses to typed A2A exceptions:
+
+```java
+try {
+ Task task = client.getTask(new TaskQueryParams("task-123"));
+} catch (A2AClientException e) {
+ if (e.getCause() instanceof TaskNotFoundError) {
+ // Handle task not found
+ } else if (e.getCause() instanceof UnsupportedOperationError) {
+ // Handle unsupported operation
+ }
+}
+```
+
+## See Also
+
+- [A2A Protocol Specification](https://a2a-protocol.org/)
+- [RFC 7807 Problem Details](https://tools.ietf.org/html/rfc7807)
+- [Protobuf JSON Encoding](https://protobuf.dev/programming-guides/proto3/#json)
diff --git a/docs/content/server.md b/docs/content/server.md
index 315e8bd71..249200645 100644
--- a/docs/content/server.md
+++ b/docs/content/server.md
@@ -152,190 +152,15 @@ public class WeatherAgentExecutorProducer {
## 4. Configuration
-The A2A Java SDK uses a flexible configuration system that works across different frameworks.
-
-**Default behavior:** Configuration values come from `META-INF/a2a-defaults.properties` files on the classpath (provided by core modules and extras). These defaults work out of the box without any additional setup.
-
-**Customizing configuration:**
-- **Quarkus/MicroProfile Config users**: Add the [`microprofile-config`](https://github.com/a2aproject/a2a-java/blob/main/integrations/microprofile-config/README.md) integration to override defaults via `application.properties`, environment variables, or system properties
-- **Spring/other frameworks**: See the [integration module README](https://github.com/a2aproject/a2a-java/blob/main/integrations/microprofile-config/README.md#custom-config-providers) for how to implement a custom `A2AConfigProvider`
-- **Reference implementations**: Already include the MicroProfile Config integration
-
-### Configuration Properties
-
-**Executor Settings** (Optional)
-
-The SDK uses a dedicated executor for async operations like streaming. Default: 5 core threads, 50 max threads.
-
-```properties
-# Core thread pool size for the @Internal executor (default: 5)
-a2a.executor.core-pool-size=5
-
-# Maximum thread pool size (default: 50)
-a2a.executor.max-pool-size=50
-
-# Thread keep-alive time in seconds (default: 60)
-a2a.executor.keep-alive-seconds=60
-```
-
-**Blocking Call Timeouts** (Optional)
-
-```properties
-# Timeout for agent execution in blocking calls (default: 30 seconds)
-a2a.blocking.agent.timeout.seconds=30
-
-# Timeout for event consumption in blocking calls (default: 5 seconds)
-a2a.blocking.consumption.timeout.seconds=5
-```
-
-**Why this matters:**
-- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load.
-- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool.
-- **Concurrency**: In production with high concurrent streaming, increase pool sizes accordingly.
-- **Agent Timeouts**: LLM-based agents may need longer timeouts (60–120s) compared to simple agents.
-
-**Note:** The reference server implementations (Quarkus-based) automatically include the MicroProfile Config integration, so properties work out of the box in `application.properties`.
+See [Configuration](configuration) for all config properties and tuning.
## 5. Task Authorization (Optional)
-> **⚠ Security note:** For multi-user deployments, a `TaskAuthorizationProvider` **must** be configured. Without one, all operations are permitted regardless of authentication — any authenticated user can read, modify, or cancel any task. Production deployments should use a fail-closed ownership policy (deny access when ownership is unknown).
-
-Implement `TaskAuthorizationProvider` to control per-user access:
-
-```java
-@ApplicationScoped
-public class MyTaskAuthorizationProvider implements TaskAuthorizationProvider {
-
- @Override
- public boolean checkRead(ServerCallContext context, String taskId, TaskOperation op) {
- return isOwner(context.getUser(), taskId);
- }
-
- @Override
- public boolean checkWrite(ServerCallContext context, String taskId, TaskOperation op) {
- return isOwner(context.getUser(), taskId);
- }
-
- @Override
- public boolean checkCreate(ServerCallContext context, TaskOperation op) {
- return context.getUser().isAuthenticated();
- }
-
- @Override
- public boolean isTaskRecorded(String taskId) {
- return ownershipStore.contains(taskId);
- }
-
- @Override
- public void recordOwnership(ServerCallContext context, String taskId, TaskOperation op) {
- ownershipStore.put(taskId, context.getUser().getUsername());
- }
-}
-```
-
-The SDK discovers the bean via CDI automatically — no additional wiring needed.
-
-> **Note:** When task authorization is required, always obtain `RequestHandler` through CDI injection. Manual instantiation via `DefaultRequestHandler.create()` bypasses the `AuthorizationRequestHandlerDecorator` and all authorization checks.
-
-### User Identity in ServerCallContext
-
-Authorization decisions rely on `context.getUser()` returning the authenticated user. How the user is populated depends on the transport:
-
-- **JSON-RPC and REST**: The Quarkus route handler extracts the user from the Vert.x routing context (`rc.userContext()`) and sets it on `ServerCallContext` directly.
-- **gRPC**: The reference server includes a `QuarkusCallContextFactory` CDI bean that injects the Quarkus `SecurityIdentity` and maps it to the `ServerCallContext` `User`. This happens automatically when using the reference gRPC module. If you provide your own `CallContextFactory`, you are responsible for populating the user.
-
-### Authorization Checks
-
-| Operation | Authorization check |
-|-----------|---------------------|
-| `getTask`, `subscribeToTask`, `getTaskPushNotificationConfig`, `listTaskPushNotificationConfigs` | `checkRead` |
-| `cancelTask`, `createTaskPushNotificationConfig`, `deleteTaskPushNotificationConfig` | `checkWrite` |
-| `messageSend` / `messageSendStream` (existing task) | `checkWrite` |
-| `messageSend` / `messageSendStream` (new task) | `checkCreate`, then `recordOwnership` |
-| `listTasks` | `checkRead` per task |
+See [Task Authorization](authorization) for per-user access control.
## Backward Compatibility with v0.3
-Add compat modules alongside v1.0 modules to serve both protocol versions simultaneously. No changes to your `AgentExecutor` are needed.
-
-### Multi-Version Module (recommended)
-
-```xml
-
-
- org.a2aproject.sdk
- a2a-java-sdk-reference-multiversion-jsonrpc
- $\{org.a2aproject.sdk.version}
-
-
-
-
- org.a2aproject.sdk
- a2a-java-sdk-reference-multiversion-rest
- $\{org.a2aproject.sdk.version}
-
-```
-
-### Individual Compat Modules
-
-```xml
-
-
- org.a2aproject.sdk
- a2a-java-sdk-compat-0.3-reference-jsonrpc
- $\{org.a2aproject.sdk.version}
-
-
-
-
- org.a2aproject.sdk
- a2a-java-sdk-compat-0.3-reference-rest
- $\{org.a2aproject.sdk.version}
-
-
-
-
- org.a2aproject.sdk
- a2a-java-sdk-compat-0.3-reference-grpc
- $\{org.a2aproject.sdk.version}
-
-```
-
-### How Version Routing Works
-
-- **JSON-RPC and REST**: When serving multiple protocol versions, version routing inspects the `A2A-Version` HTTP header on each request. If the header is `"1.0"`, the request is routed to the v1.0 handler. If it is `"0.3"` or absent, the request is routed to the v0.3 handler.
-- **gRPC**: Version dispatch is implicit — v0.3 clients use the `a2a.v1` protobuf package and v1.0 clients use `lf.a2a.v1`, so requests are routed to the correct service automatically.
-- **Agent card**: When both v1.0 and v0.3 are enabled, the v1.0 `AgentCard` takes precedence and is served at `/.well-known/agent-card.json`. The v0.3 `AgentCard_v0_3` is ignored. If only v0.3 is enabled, the v0.3 agent card is used. If only v1.0 is enabled, the v1.0 agent card is used as-is.
-
-### Making the v1.0 Agent Card Compatible with v0.3 Clients
-
-When serving both protocol versions, you need to ensure the v1.0 agent card contains fields that v0.3 clients expect. Existing v0.3 client implementations (in any language) look for `url`, `preferredTransport`, and `additionalInterfaces` with `transport`/`url` entries — fields that don't exist in the v1.0 format by default.
-
-To make your v1.0 `AgentCard` parsable by v0.3 clients, set these fields on the builder:
-
-```java
-AgentCard card = AgentCard.builder()
- .name("My Agent")
- // ... other v1.0 fields ...
- .supportedInterfaces(List.of(
- new AgentInterface("jsonrpc", "http://localhost:9999")))
- // v0.3 backward-compatibility fields:
- .url("http://localhost:9999")
- .preferredTransport("jsonrpc")
- .additionalInterfaces(List.of(
- new Legacy_0_3_AgentInterface("jsonrpc", "http://localhost:9999")))
- .build();
-```
-
-The two interface lists serve different clients:
-
-- `supportedInterfaces` — used by **v1.0 clients** to discover endpoints (uses `AgentInterface` with `protocolBinding`/`url`/`tenant` fields)
-- `additionalInterfaces` — used by **v0.3 clients** to discover endpoints (uses `Legacy_0_3_AgentInterface` with v0.3 field names: `transport`/`url`)
-- `url` and `preferredTransport` — top-level fields that v0.3 clients use to discover the primary endpoint
-
-### Push Notification Behavior
-
-Push notification payloads are automatically formatted to match the protocol version used when the push notification configuration was registered. When a v0.3 client registers a push notification configuration (via any transport), the server records the protocol version alongside the configuration. When a notification is later sent to that webhook, the payload is formatted as a v0.3 Task object. Configurations registered by v1.0 clients receive v1.0 `StreamResponse` payloads as usual. This happens transparently — no additional configuration is needed beyond adding the compat reference module.
+See [Backward Compatibility](compatibility) for multi-version modules, version routing, and v0.3 client support.
## Server Integrations
diff --git a/docs/data/menu.yml b/docs/data/menu.yml
index 551048d95..61fe0d225 100644
--- a/docs/data/menu.yml
+++ b/docs/data/menu.yml
@@ -5,6 +5,9 @@ items:
- title: "Announcements"
path: "/announces"
icon: "fa-regular fa-newspaper"
+ - title: "Getting Started"
+ path: "/getting-started"
+ icon: "fa-solid fa-rocket"
- title: "Documentation"
type: "group"
icon: "fa-solid fa-book"
@@ -16,10 +19,34 @@ items:
path: "/client"
icon: "fa-solid fa-plug"
group: "documentation"
+ - title: "Configuration"
+ path: "/configuration"
+ icon: "fa-solid fa-sliders"
+ group: "documentation"
+ - title: "Authorization"
+ path: "/authorization"
+ icon: "fa-solid fa-shield-halved"
+ group: "documentation"
+ - title: "REST API"
+ path: "/rest-api-reference"
+ icon: "fa-solid fa-globe"
+ group: "documentation"
+ - title: "Compatibility"
+ path: "/compatibility"
+ icon: "fa-solid fa-code-branch"
+ group: "documentation"
- title: "Extras"
path: "/extras"
icon: "fa-solid fa-puzzle-piece"
group: "documentation"
+ - title: "BOMs"
+ path: "/boms"
+ icon: "fa-solid fa-cubes"
+ group: "documentation"
+ - title: "Examples"
+ path: "/examples"
+ icon: "fa-solid fa-flask"
+ group: "documentation"
- title: "Javadoc"
path: "https://javadoc.io/doc/org.a2aproject.sdk"
icon: "fa-solid fa-file-code"
@@ -38,4 +65,4 @@ items:
- title: "GitHub"
path: "https://github.com/a2aproject/a2a-java"
icon: "fa-brands fa-github"
- target: "_blank"
\ No newline at end of file
+ target: "_blank"
diff --git a/docs/src/main/resources/templates/layouts/page.html b/docs/src/main/resources/templates/layouts/page.html
new file mode 100644
index 000000000..2dd083319
--- /dev/null
+++ b/docs/src/main/resources/templates/layouts/page.html
@@ -0,0 +1,5 @@
+---
+theme-layout: page
+link: /:raw-path
+---
+{#insert /}
diff --git a/docs/web/_custom.css b/docs/web/_custom.css
index b662a727e..c801967ec 100644
--- a/docs/web/_custom.css
+++ b/docs/web/_custom.css
@@ -69,7 +69,7 @@
.menu-group-children-wrapper {
overflow: hidden;
- max-height: 300px;
+ max-height: 2000px;
transition: max-height 0.25s ease;
padding: 0;
margin: 0;
@@ -107,6 +107,35 @@
max-width: var(--container-5xl);
}
+/* ── Tables ──────────────────────────────────────────────────────────────── */
+
+.page-content table,
+.roq-section table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1.5rem 0;
+}
+
+.page-content th,
+.page-content td,
+.roq-section th,
+.roq-section td {
+ padding: 0.6rem 1rem;
+ text-align: left;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+.page-content th,
+.roq-section th {
+ font-weight: 600;
+ background: rgba(255, 255, 255, 0.06);
+}
+
+.page-content tr:nth-child(even),
+.roq-section tr:nth-child(even) {
+ background: rgba(255, 255, 255, 0.03);
+}
+
/* ── Quick Install section ────────────────────────────────────────────────── */
.roq-section pre {