StackA2A
utilitykotlin

A2A 4K (Kotlin)

44

by a2a-4k

Kotlin implementation of the A2A Protocol with Redis-based task storage support.

38 starsUpdated 2026-02-23
Quality Score44/100
Community
36
Freshness
100
Official
30
Skills
10
Protocol
30
🔒 Security
20

Getting Started

1Clone the repository
$ git clone https://github.com/a2a-4k/a2a-4k
2Navigate to the project
$ cd a2a-4k
3Install dependencies
$ # Check README for install instructions
4Run the agent
$ # Check README for run instructions

README

GitHub Actions Build Status GitHub Actions Publish Status Apache 2.0 License Contributor Covenant

A2A-4K WIP

A2A-4K is a kotlin implementation of the Agent2Agent (A2A) protocol (https://github.com/google/A2A).

Check out the examples in the examples directory for a quick start.

The project is currently very much WIP and looking for contributors.

We have examples uing Arc (https://eclipse.dev/lmos/arc2) and Langchain4j.

Usage Examples

Don't forget to check out the Langchain4j and Arc examples here.

Server-side: Implementing a TaskHandler

The TaskHandler interface is the core component for handling tasks in the A2A system. It's used to call an Agent of your choice. Here's a simple example of implementing a TaskHandler:

import io.github.a2a_4k.TaskHandler
import io.github.a2a_4k.models.*

// Custom TaskHandler that calls your Agent
class MyAgentTaskHandler : TaskHandler {
    override fun handle(task: Task): Task {
        // Extract the message from the task
        val message = task.history?.lastOrNull()

        // Call your Agent with the message
        val response = callYourAgent(message)

        // Create a response Artifact
        val responseArtifact = Artifact(
            name = "agent-response",
            parts = listOf(TextPart(text = response)),
        )

        // Update the task status
        val updatedStatus = TaskStatus(state = TaskState.COMPLETED)

        // Return the updated task
        return task.copy(status = updatedStatus,
            artifacts = listOf(responseArtifact)
        )
    }

    private fun callYourAgent(message: Message?): String {
        // Implement your Agent call here
        // This could be an API call to an LLM, a custom AI service, etc.
        return "Response from your Agent"
    }
}

Setting up the A2A Server

import io.github.a2a_4k.*
import io.github.a2a_4k.models.*

// Create your TaskHandler
val taskHandler = MyAgentTaskHandler()

// Create a TaskManager with your TaskHandler
val taskManager = BasicTaskManager(taskHandler)

// Define your Agent's capabilities
val capabilities = Capabilities(
    streaming = true,
    pushNotifications = true,
    stateTransitionHistory = true
)

// Create an AgentCard for your Agent
val agentCard = AgentCard(
    name = "My Agent",
    description = "A custom A2A Agent",
    url = "https://example.com",
    version = "1.0.0",
    capabilities = capabilities,
    defaultInputModes = listOf("text"),
    defaultOutputModes = listOf("text"),
    skills = listOf(/* Define your Agent's skills here */)
)

// Create and start the A2A Server
val server = A2AServer(
    host = "0.0.0.0",
    port = 5000,
    endpoint = "/",
    agentCard = agentCard,
    taskManager = taskManager
)

// Start the server
server.start()

Client-side: Interacting with an A2A Server

import io.github.a2a_4k.A2AClient
import io.github.a2a_4k.models.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // Create an A2A Client
    val client = A2AClient(baseUrl = "http://localhost:5000")

    try {
        // Get the Agent's metadata
        val agentCard = client.getAgentCard()
        println("Connected to Agent: ${agentCard.name}")

        // Create a message to send to the Agent
        val message = "Hello, Agent!".toUserMessage()

        // Send a task to the Agent
        val response = client.sendTask(
            taskId = "task-123",
            sessionId = "session-456",
            message = message
        )

        // Process the response
        if (response.result != null) {
            println("Agent response: $response")
        } else {
            println("Error: ${response.error?.message}")
        }
    } finally {
        // Close the client
        client.close()
    }
}

Project Structure

The A2A-4K project is organized into several modules, each with a specific purpose:

a2a4k-models

This module contains the data models and serialization logic for the A2A protocol. It defines:

  • JSON-RPC request and response structures
  • Task and message models
  • Agent capabilities and metadata (AgentCard)
  • Serialization/deserialization utilities

a2a4k-server

This module provides the core server-side functionality for the A2A protocol. It includes:

  • TaskManager interface and implementation (BasicTaskManager)
  • TaskHandler interface for processing tasks
  • TaskStorage interface and implementation for storing tasks
  • Core logic for managing task lifecycle

a2a4k-server-ktor

This module implements a Ktor-based server for the A2A protocol, building on the core functionality provided by a2a4k-server. It includes:

  • A2AServer class for creating and managing an A2A server
  • A2AModule for configuring Ktor routing
  • HTTP endpoints for the A2A protocol

a2a4k-client

This module provides client-side functionality for communicating with A2A servers. It includes:

  • A2AClient class for sending requests to A2A servers
  • Methods for all A2A operations (getTask, sendTask, cancelTask, etc.)
  • Support for streaming responses
  • Error handling

a2a4k-storage-redis

This module provides a Redis implementation of the TaskStorage interface, allowing tasks and notification configurations to be stored in Redis.

Using RedisTaskStorage

The A2A-4K project includes a Redis implementation of the TaskStorage interface, which allows you to store tasks and notification configurations in Redis instead of in-memory.

This provides several benefits:

  • Persistence: Tasks and configurations are stored in Redis and survive application restarts
  • Scalability: Multiple instances of your application can share the same Redis database
  • Performance: Redis offers high-performance storage with configurable persistence options

To use RedisTaskStorage, add the following dependency to your project:

// Gradle Kotlin DSL
implementation("org.a2a4k:a2a4k-storage-redis:$a2a4kVersion")

and then set the following environment variables / system properties:

  • A2A_STORAGE_REDIS_HOST: The hostname or IP address of your Redis server (required)
  • A2A_STORAGE_REDIS_PORT: The port number of your Redis server (optional, defaults to Redis standard port)
  • A2A_STORAGE_REDIS_USERNAME: Username for Redis authentication (optional)
  • A2A_STORAGE_REDIS_PASSWORD: Password for Redis authentication (optional)
  • A2A_STORAGE_REDIS_SSL: Set to "true" to enable SSL connection (optional, defaults to false)
  • A2A_STORAGE_REDIS_TLS: Set to "true" to enable TLS connection (optional, defaults to false)

This will automatically load and configure the RedisTaskStorage.

Code of Conduct

This project has adopted the Contributor Covenant in version 2.1 as our code of conduct. Please see the details in our CODE_OF_CONDUCT.md. All contributors must abide by the code of conduct.

By participating in this project, you agree to abide by its Code of Conduct at all times.

Licensing

Copyright (c) 2025

Sourcecode licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License.

This project follows the REUSE standard for software licensing.
Each file contains copyright and license information, and license texts can be found in the ./LICENSES folder. For more information visit https://reuse.software/.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the LICENSE for the specific language governing permissions and limitations under the License.

Capabilities

StreamingPush NotificationsMulti-TurnAuth: none
kotlina2a-sdkredisjvm
View on GitHub