adk-server 2.1.0

HTTP server and A2A protocol for Rust Agent Development Kit (ADK-Rust) agents
Documentation

adk-server

HTTP server and A2A v1.0.0 protocol for Rust Agent Development Kit (ADK-Rust) agents.

Crates.io Documentation License

Overview

adk-server provides HTTP infrastructure for the Rust Agent Development Kit (ADK-Rust):

  • REST API - Standard HTTP endpoints for agent interaction
  • A2A Protocol - Agent-to-Agent v1.0.0 communication (JSON-RPC 2.0, all 11 operations)
  • SSE Streaming - Server-Sent Events for real-time responses
  • Runtime UI - Built-in responsive interface for agent and team execution
  • RemoteA2aAgent - Connect to remote agents as sub-agents
  • Auth Bridge - Flow authenticated identity from HTTP headers into agent execution
  • Artifacts - Binary artifact storage and retrieval per session
  • Debug/Tracing - Trace inspection and graph visualization endpoints
  • YAML Agent Config (feature: yaml-agent) - Declarative agent definitions with:
    • AgentConfigLoader — load agents from YAML files
    • HotReloadWatcher — filesystem watching with debounce
    • Environment variable interpolation (${VAR} and ${VAR:-default})
    • Plugin, session, and memory backend configuration in YAML
    • Round-trip serialization (serialize_definition())

Installation

[dependencies]
adk-server = "2.1.0"

Or use the meta-crate:

[dependencies]
adk-rust = { version = "2.1.0", features = ["server"] }

Quick Start

Basic Server

use adk_server::{create_app, ServerConfig};
use std::sync::Arc;

let config = ServerConfig::new(
    Arc::new(SingleAgentLoader::new(Arc::new(agent))),
    Arc::new(InMemorySessionService::new()),
);

let app = create_app(config);

let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await?;

Security Configuration

Configure CORS, timeouts, and other security settings:

use adk_server::{ServerConfig, SecurityConfig};
use std::time::Duration;

// Development mode (permissive CORS, detailed errors)
let config = ServerConfig::new(agent_loader, session_service)
    .with_security(SecurityConfig::development());

// Production mode (restricted CORS, sanitized errors)
let config = ServerConfig::new(agent_loader, session_service)
    .with_security(SecurityConfig::production(vec!["https://myapp.com".to_string()]));

// Or configure individual settings
let config = ServerConfig::new(agent_loader, session_service)
    .with_allowed_origins(vec!["https://myapp.com".to_string()])
    .with_request_timeout(Duration::from_secs(60))
    .with_max_body_size(5 * 1024 * 1024)  // 5MB
    .with_error_details(false);

Optional Services

let config = ServerConfig::new(agent_loader, session_service)
    .with_artifact_service(Arc::new(artifact_service))
    .with_memory_service(Arc::new(memory_service))
    .with_span_exporter(Arc::new(span_exporter))
    .with_request_context(Arc::new(my_auth_extractor));

Runner Configuration Passthrough

ServerConfig can now forward runner-level compaction and prompt-cache settings:

let config = ServerConfig::new(agent_loader, session_service)
    .with_compaction(compaction_config)
    .with_context_cache(context_cache_config, cache_capable_model);

This applies to both the standard SSE runtime endpoints and the A2A runtime controller.

A2A v1.0.0 Server

use adk_server::create_app_with_a2a;

let app = create_app_with_a2a(config, Some("http://localhost:8080"));

ServerBuilder — Custom Routes

ServerBuilder lets you register custom Axum controllers alongside the built-in routes. Custom routes share the same middleware stack (auth, CORS, tracing, timeout, security headers).

use adk_server::{ServerBuilder, ServerConfig};
use axum::{Router, routing::get, Json};

let app = ServerBuilder::new(config)
    // Routes nested under /api — get auth middleware automatically
    .add_api_routes(
        Router::new()
            .route("/projects", get(list_projects))
            .route("/projects/{id}", get(get_project))
    )
    // Root-level routes — CORS/tracing/security headers, but no auth middleware
    .add_root_routes(
        Router::new()
            .route("/webhook", axum::routing::post(handle_webhook))
    )
    // Enable A2A protocol
    .with_a2a("http://localhost:8080")
    .build();

let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await?;
Method Description
ServerBuilder::new(config) Create a builder from a ServerConfig
.add_api_routes(router) Add routes nested under /api with auth middleware
.add_root_routes(router) Add routes at the root level (no auth middleware)
.with_a2a(base_url) Enable A2A protocol endpoints
.enable_shutdown_endpoint() Enable POST /api/shutdown for graceful shutdown
.build() Build the final axum::Router with all middleware
.build_with_shutdown() Build and return a ShutdownHandle for graceful shutdown

See examples/server_builder/ for a complete working example.

Graceful Shutdown Endpoint

Enable POST /api/shutdown for clean process termination (e.g., from an Electron shell):

use adk_server::{ServerBuilder, ServerConfig};

let (app, shutdown_handle) = ServerBuilder::new(config)
    .enable_shutdown_endpoint()
    .build_with_shutdown();

let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app)
    .with_graceful_shutdown(shutdown_handle.signal())
    .await?;

When POST /api/shutdown is called, the server:

  1. Stops accepting new connections
  2. Completes in-flight requests
  3. Flushes pending writes (SQLite WAL, etc.)
  4. Exits with code 0

The ShutdownHandle::signal() also responds to Ctrl+C and SIGTERM, combining all shutdown triggers into one future.

// Exposes: // GET /.well-known/agent-card.json - Agent card with capabilities // POST /jsonrpc - JSON-RPC endpoint (all 11 v1 operations) // REST routes for all operations // A2A-Version header negotiation


The A2A v1.0.0 implementation includes: RFC 3339 timestamps, capabilities declaration, message ID idempotency, push notification authentication, INPUT_REQUIRED multi-turn flow, input validation, `application/a2a+json` Content-Type, and Task-as-first-SSE-event. See [A2A docs](../docs/official_docs/deployment/a2a.md) for details.

### Remote Agent Client

```rust
use adk_server::RemoteA2aAgent;

let remote = RemoteA2aAgent::builder("weather_agent")
    .description("Remote weather service")
    .agent_url("http://weather-service:8080")
    .build()?;

// Use as sub-agent
let coordinator = LlmAgentBuilder::new("coordinator")
    .sub_agent(Arc::new(remote))
    .build()?;

Auth Bridge

Flow authenticated identity from HTTP requests into agent execution:

use adk_server::auth_bridge::{RequestContextExtractor, RequestContextError};
use adk_core::RequestContext;
use async_trait::async_trait;

struct MyExtractor;

#[async_trait]
impl RequestContextExtractor for MyExtractor {
    async fn extract(
        &self,
        parts: &axum::http::request::Parts,
    ) -> Result<RequestContext, RequestContextError> {
        let auth = parts.headers
            .get("authorization")
            .and_then(|v| v.to_str().ok())
            .ok_or(RequestContextError::MissingAuth)?;
        // validate token, build RequestContext ...
        todo!()
    }
}

let config = ServerConfig::new(agent_loader, session_service)
    .with_request_context(Arc::new(MyExtractor));

When configured, the extracted RequestContext flows into InvocationContext, making scopes available to tools via ToolContext::user_scopes(). Session and artifact endpoints enforce user_id authorization against the authenticated identity.

Background Runs and Cron (feature: background)

Runs a workflow without holding the request open, and schedules one on a cron expression. Neither is compiled unless the feature is on.

use adk_server::background::{FileRunPersistence, RunRetention, RunStore};
use std::sync::Arc;

// Runs are held in memory. Attach persistence so a restart can still see them.
let store = RunStore::new()
    .with_persistence(Arc::new(FileRunPersistence::new("/var/lib/adk/runs.json")))
    .with_retention(RunRetention::keep_finished(1000));

// At startup, report what the last stop interrupted.
for run_id in store.restore().await? {
    tracing::warn!(run.id = %run_id, "this run did not finish before the last restart");
}
Endpoint Method Purpose
/runs POST Start a run, returning its id immediately
/runs/{id} GET Read a run's status and result
/runs/{id} DELETE Cancel a run
/cron POST / GET / PATCH / DELETE Manage scheduled jobs

Cron jobs take a concurrency policy — skip, allow or queue — for what happens when the previous run is still going.

What survives a restart. With a RunPersistence attached, run records do. A run that was still going cannot still be going, so restore returns it as Failed with a reason and gives it a live cancellation token. The graph state behind it is separate: a checkpointed adk-graph thread can still be resumed by its id.

Finished runs are bounded by default — the newest 1000 — because a store that keeps every finished run forever is a leak that only appears after weeks. RunRetention::unlimited() opts out. A run still in flight is never discarded.

FileRunPersistence writes one JSON file and is enough for a single node. A deployment across several needs a shared store; implement RunPersistence for one.

API Endpoints

Health

Endpoint Method Description
/api/health GET Health check with component status

Apps

Endpoint Method Description
/api/apps GET List available agents
/api/list-apps GET adk-go compatible app listing

Sessions

Endpoint Method Description
/api/sessions POST Create session
/api/sessions/{app_name}/{user_id}/{session_id} GET, DELETE Get or delete session
/api/apps/{app_name}/users/{user_id}/sessions GET, POST List or create sessions
/api/apps/{app_name}/users/{user_id}/sessions/{session_id} GET, POST, DELETE Get, create, or delete session

Runtime

Endpoint Method Description
/api/run/{app_name}/{user_id}/{session_id} POST Run agent with SSE
/api/run_sse POST adk-go compatible SSE runtime

Artifacts

Endpoint Method Description
/api/sessions/{app_name}/{user_id}/{session_id}/artifacts GET List artifacts for a session
/api/sessions/{app_name}/{user_id}/{session_id}/artifacts/{artifact_name} GET Get a specific artifact

Debug and Tracing

Endpoint Method Description
/api/debug/trace/{event_id} GET Get trace by event ID (admin only when auth configured)
/api/debug/trace/session/{session_id} GET Get all spans for a session
/api/debug/graph/{app_name}/{user_id}/{session_id}/{event_id} GET Get graph visualization
/api/apps/{app_name}/users/{user_id}/sessions/{session_id}/events/{event_id} GET Get event data
/api/apps/{app_name}/users/{user_id}/sessions/{session_id}/events/{event_id}/graph GET Get graph (path-style)
/api/apps/{app_name}/eval_sets GET Get evaluation sets (stub)

UI Protocol

Endpoint Method Description
/api/ui/capabilities GET Supported UI protocols plus capability metadata (versions, features, implementationTier, specTrack, summary, limitations)
/api/ui/agents/{name} GET Agent capabilities, hierarchy, and exact portable team topology
/api/ui/initialize POST Additive MCP Apps host-bridge initialize helper (direct body or JSON-RPC-like envelope)
/api/ui/message POST Additive MCP Apps host-bridge message helper
/api/ui/update-model-context POST Additive MCP Apps host-bridge model-context helper
/api/ui/notifications/poll POST Poll queued MCP Apps host-bridge notifications
/api/ui/notifications/resources-list-changed POST Queue an MCP Apps resource-list-changed notification
/api/ui/notifications/tools-list-changed POST Queue an MCP Apps tool-list-changed notification
/api/ui/resources GET List MCP UI resources (ui:// entries)
/api/ui/resources/read?uri=... GET Read a registered MCP UI resource
/api/ui/resources/register POST Register an MCP UI resource (validated ui:// + mime/meta)

Runtime endpoints support protocol negotiation via:

  • request body field uiProtocol / ui_protocol
  • header x-adk-ui-protocol (takes precedence)
  • request body field uiTransport / ui_transport
  • header x-adk-ui-transport (takes precedence)

Supported runtime profile values: adk_ui (default), a2ui, ag_ui, mcp_apps.

Current support is intentionally tiered:

  • a2ui is a draft-aligned hybrid subset exposed through protocol-aware UI tool payloads.
  • ag_ui is a hybrid subset: the default stream remains the generic ADK wrapper, but clients can opt into protocol_native transport plus AG-UI run input fields on /api/run_sse.
  • mcp_apps is a compatibility subset with ui:// resource registration plus additive initialize / message / update-model-context bridge helpers, notification polling, list-changed host flows, and runtime request fields, not a full browser postMessage host bridge yet.

Runtime transport values:

  • legacy_wrapper (default) preserves the existing generic ADK SSE envelope.
  • protocol_native is currently available for ag_ui only.

Use /api/ui/capabilities instead of assuming full upstream protocol parity.

For MCP Apps tool responses, adk-server::ui_types now exposes a canonical additive helper:

  • McpUiBridgeSnapshot for typed host/app bridge state that can be promoted into tool responses
  • McpUiToolResult for the shared tool-result envelope
  • McpUiToolResultBridge for typed bridge metadata (protocolVersion, structuredContent, hostInfo, hostCapabilities, hostContext, appInfo, appCapabilities, initialized)

Use McpUiBridgeSnapshot::build_tool_result(...) as the preferred constructor path when promoting framework bridge state into an MCP Apps tool response. resourceUri and inline html fallbacks remain available for compatibility-oriented hosts.

For embedded-host mappings, the additive HTTP bridge corresponds to MCP Apps host/app methods as follows:

  • ui/initialize -> /api/ui/initialize
  • ui/message -> /api/ui/message
  • ui/update-model-context -> /api/ui/update-model-context
  • notifications/resources/list_changed -> /api/ui/notifications/resources-list-changed
  • notifications/tools/list_changed -> /api/ui/notifications/tools-list-changed
  • queued host notifications -> /api/ui/notifications/poll

A2A Endpoints

Endpoint Method Description
/.well-known/agent.json GET A2A agent card
/a2a POST A2A JSON-RPC
/a2a/stream POST A2A streaming

Web UI

The ADK-Rust-owned interface uses the Studio Next visual language while staying focused on execution. It displays Markdown-formatted conversations and multimodal attachments, streaming tool activity, handoffs, animated exact team delegation/handoff topology, realtime transcripts and completed audio, event timelines, dedicated telemetry spans, session and shared state, artifacts, prior sessions, configured runtime services, A2A discovery, and negotiated UI and MCP Apps protocols. It is responsive, keyboard accessible, supports system/light/dark themes, and has no external font or asset dependency. The ADK Runtime brand links to adk-rust.com.

See examples/advanced_agents/ for one OpenAI-backed server demonstrating ambient scheduling, realtime voice, A2A, MCP discovery/tasks, and telemetry.

Endpoint Method Description
/ GET Redirect to /ui/
/ui/ GET Built-in agent and team runtime interface
/ui/assets/config/runtime-config.json GET Runtime configuration
/ui/{*path} GET Static UI assets

Security

The server applies the following security layers automatically:

  • CORS (configurable allowed origins)
  • Request body size limits (default 10MB)
  • Request timeouts (default 30s)
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Request ID tracking via x-request-id header
  • User ID authorization on session/artifact/debug endpoints when auth is configured

Features

  • Axum-based async HTTP server
  • CORS support with configurable origins
  • Embedded web UI assets
  • Multi-agent routing via AgentLoader
  • Health checks with component status
  • OpenTelemetry trace integration
  • Auth middleware bridge for identity propagation
  • Artifact storage and retrieval
  • A2A v1.0.0 protocol with JSON-RPC 2.0 (all 11 operations, idempotency, multi-turn, push auth)

Related Crates

License

Apache-2.0

Part of ADK-Rust

This crate is part of the ADK-Rust framework for building AI agents in Rust.