CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-a2a

A2A protocol integration for Embabel Agent Framework enabling agent-to-agent communication

Overview
Eval results
Files

agent-card.mddocs/api/

AgentCard API

AgentCard exposure and capability advertising for A2A agent discovery.

AgentCardHandler

Core interface combining AgentCard exposure with request handling.

/**
 * Exposes A2A AgentCard and handles JSON-RPC requests.
 * Extends A2ARequestHandler and HasInfoString.
 */
interface AgentCardHandler : A2ARequestHandler, HasInfoString {
    /**
     * Relative path where endpoint is exposed (e.g., "a2a" → "/a2a").
     */
    val path: String

    /**
     * Returns AgentCard for the A2A server.
     * @param scheme URL scheme ("http" or "https")
     * @param host Server hostname
     * @param port Server port number
     * @return AgentCard with computed URLs
     */
    fun agentCard(scheme: String, host: String, port: Int): AgentCard
}

EmbabelServerGoalsAgentCardHandler

Default implementation exposing Embabel AgentPlatform as A2A AgentCard.

/**
 * Exposes agent platform capabilities as A2A AgentCard.
 * Converts filtered Goals to A2A Skills.
 *
 * @param path Relative endpoint path (default: "a2a")
 * @param agentPlatform Embabel agent platform
 * @param a2ARequestHandler Request handler to delegate to
 * @param goalFilter Filter function for goal exposure
 */
class EmbabelServerGoalsAgentCardHandler(
    override val path: String = DEFAULT_A2A_PATH,
    private val agentPlatform: AgentPlatform,
    private val a2ARequestHandler: A2ARequestHandler,
    private val goalFilter: GoalFilter
) : AgentCardHandler, A2ARequestHandler by a2ARequestHandler {

    override fun agentCard(scheme: String, host: String, port: Int): AgentCard

    override fun infoString(verbose: Boolean?, indent: Int): String
}

Constants:

const val DEFAULT_A2A_PATH = "a2a"

Type Aliases:

typealias GoalFilter = (Goal) -> Boolean

AgentCard Structure

AgentCard.Builder()
    .name(agentPlatform.name)
    .description(agentPlatform.description)
    .url("$scheme://$host:$port/$path")
    .provider(AgentProvider("Embabel", "https://embabel.com"))
    .version(Semver.DEFAULT_VERSION)
    .documentationUrl("https://embabel.com/docs")
    .capabilities(
        AgentCapabilities.Builder()
            .streaming(true)
            .pushNotifications(false)
            .stateTransitionHistory(false)
            .extensions(emptyList())
            .build()
    )
    .defaultInputModes(listOf("application/json", "text/plain"))
    .defaultOutputModes(listOf("application/json", "text/plain"))
    .skills(/* goals converted to skills via FromGoalsAgentSkillFactory */)
    .supportsAuthenticatedExtendedCard(false)
    .protocolVersion("0.3.0")
    .build()

Goal Filtering

Allow All Goals

val handler = EmbabelServerGoalsAgentCardHandler(
    path = "a2a",
    agentPlatform = agentPlatform,
    a2ARequestHandler = requestHandler,
    goalFilter = { true }
)

Filter by Tag

goalFilter = { goal -> goal.tags.contains("public") }

Exclude Internal

goalFilter = { goal ->
    goal.tags.contains("external") && !goal.tags.contains("internal")
}

Complex Filter

goalFilter = { goal ->
    goal.tags.contains("a2a-enabled") &&
    !goal.tags.contains("internal") &&
    goal.description.isNotEmpty()
}

Basic Usage

@Configuration
class A2AConfig {

    @Bean
    fun agentCardHandler(
        agentPlatform: AgentPlatform,
        requestHandler: AutonomyA2ARequestHandler
    ): AgentCardHandler {
        return EmbabelServerGoalsAgentCardHandler(
            path = "a2a",
            agentPlatform = agentPlatform,
            a2ARequestHandler = requestHandler,
            goalFilter = { true }
        )
    }
}

// Endpoints registered automatically:
// GET  http://localhost:8080/a2a/.well-known/agent.json
// POST http://localhost:8080/a2a

Multiple Endpoints

Expose different capability sets at different paths:

@Configuration
class MultiEndpointConfig {

    @Bean
    fun publicHandler(
        agentPlatform: AgentPlatform,
        requestHandler: AutonomyA2ARequestHandler
    ): AgentCardHandler {
        return EmbabelServerGoalsAgentCardHandler(
            path = "a2a-public",
            agentPlatform = agentPlatform,
            a2ARequestHandler = requestHandler,
            goalFilter = { goal -> goal.tags.contains("public") }
        )
    }

    @Bean
    fun partnerHandler(
        agentPlatform: AgentPlatform,
        requestHandler: AutonomyA2ARequestHandler
    ): AgentCardHandler {
        return EmbabelServerGoalsAgentCardHandler(
            path = "a2a-partner",
            agentPlatform = agentPlatform,
            a2ARequestHandler = requestHandler,
            goalFilter = { goal -> goal.tags.contains("partner") }
        )
    }
}

Result:

  • /a2a-public/.well-known/agent.json and /a2a-public
  • /a2a-partner/.well-known/agent.json and /a2a-partner

Custom Implementation

class CustomAgentCardHandler(
    override val path: String,
    private val customSkills: List<AgentSkill>
) : AgentCardHandler {

    override fun agentCard(scheme: String, host: String, port: Int): AgentCard {
        return AgentCard.Builder()
            .name("Custom Agent")
            .description("Custom A2A agent")
            .url("$scheme://$host:$port/$path")
            .provider(AgentProvider("MyCompany", "https://example.com"))
            .skills(customSkills)
            .capabilities(
                AgentCapabilities.Builder()
                    .streaming(false)
                    .build()
            )
            .protocolVersion("0.3.0")
            .build()
    }

    override fun handleJsonRpc(request: NonStreamingJSONRPCRequest<*>): JSONRPCResponse<*> {
        // Custom request handling
        TODO()
    }

    override fun handleJsonRpcStream(request: StreamingJSONRPCRequest<*>): SseEmitter {
        throw UnsupportedOperationException("Streaming not supported")
    }

    override fun infoString(verbose: Boolean?, indent: Int): String {
        return "CustomAgentCardHandler(path='$path')"
    }
}

Request Delegation

EmbabelServerGoalsAgentCardHandler delegates request handling:

class EmbabelServerGoalsAgentCardHandler(
    /* ... */
) : AgentCardHandler, A2ARequestHandler by a2ARequestHandler {
    // AgentCard-specific methods implemented
    // Request handling delegated to a2ARequestHandler
}

Separation of Concerns:

  • AgentCard generation → EmbabelServerGoalsAgentCardHandler
  • Request processing → AutonomyA2ARequestHandler

Accessing AgentCard

Via HTTP:

curl http://localhost:8080/a2a/.well-known/agent.json

Via Code:

@Autowired
lateinit var agentCardHandler: AgentCardHandler

fun getAgentCard(): AgentCard {
    return agentCardHandler.agentCard("http", "localhost", 8080)
}

See Also

  • Skill Factory API - Goal-to-skill conversion
  • Configuration API - Spring auto-configuration
  • Endpoint Registration API - Dynamic endpoint setup
  • Common Tasks - Configuration examples
tessl i tessl/maven-com-embabel-agent--embabel-agent-a2a@0.3.3

docs

api

agent-card.md

configuration.md

endpoint-registration.md

events.md

overview.md

request-handling.md

skill-factory.md

streaming.md

task-state.md

index.md

tile.json