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

common-tasks.mddocs/guides/

Common Tasks

Quick solutions for frequently needed operations.

Change Endpoint Path

@Configuration
class CustomA2AConfig {
    @Bean
    fun defaultAgentCardHandler(
        agentPlatform: AgentPlatform,
        requestHandler: AutonomyA2ARequestHandler
    ): AgentCardHandler {
        return EmbabelServerGoalsAgentCardHandler(
            path = "custom-path",  // Changes /a2a to /custom-path
            agentPlatform = agentPlatform,
            a2ARequestHandler = requestHandler,
            goalFilter = { true }
        )
    }
}

Result: Endpoints at /custom-path/.well-known/agent.json and /custom-path


Filter Exposed Goals

By Tag:

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

Exclude Internal:

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

By Name Pattern:

goalFilter = { goal -> goal.name.startsWith("external_") }

Complex Logic:

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

Create Multiple Endpoints

Expose different capability sets at different paths:

@Configuration
class MultiAgentConfig {

    @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-api") }
        )
    }
}

Result:

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

Add Security

Allow Public AgentCard, Secure JSON-RPC:

@Configuration
@EnableWebSecurity
class SecurityConfig {
    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeHttpRequests { auth ->
            auth
                .requestMatchers("/*/.well-known/agent.json").permitAll()
                .requestMatchers("/*/").authenticated()
        }
        return http.build()
    }
}

Monitor Requests and Responses

@Component
class A2ALogger {
    @EventListener
    fun logRequest(event: A2ARequestEvent) {
        logger.info(
            "[A2A Request] method={}, id={}, platform={}",
            event.request.method,
            event.request.id,
            event.agentPlatform.name
        )
    }

    @EventListener
    fun logResponse(event: A2AResponseEvent) {
        logger.info(
            "[A2A Response] id={}, success={}",
            event.response.id,
            event.response !is JSONRPCErrorResponse
        )
    }
}

Collect Metrics

@Component
class A2AMetrics(private val meterRegistry: MeterRegistry) {

    @EventListener
    fun recordRequest(event: A2ARequestEvent) {
        meterRegistry.counter(
            "a2a.requests",
            "method", event.request.method,
            "platform", event.agentPlatform.name
        ).increment()
    }

    @EventListener
    fun recordResponse(event: A2AResponseEvent) {
        val status = if (event.response is JSONRPCErrorResponse) "error" else "success"
        meterRegistry.counter("a2a.responses", "status", status).increment()
    }
}

Enable Only in Specific Profiles

Profile-Based:

@Configuration
@Profile("a2a")
class A2AProfileConfiguration {
    @Bean
    fun agentCardHandler(...): AgentCardHandler { /* ... */ }
}

Property-Based:

@Configuration
@ConditionalOnProperty(
    prefix = "embabel.a2a",
    name = ["enabled"],
    havingValue = "true"
)
class ConditionalA2AConfig {
    @Bean
    fun agentCardHandler(...): AgentCardHandler { /* ... */ }
}

application.yml:

embabel:
  a2a:
    enabled: true

Configure Endpoint Path from Properties

@Configuration
class ConfigurableA2APath {

    @Value("\${a2a.endpoint.path:a2a}")
    private lateinit var endpointPath: String

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

application.yml:

a2a:
  endpoint:
    path: my-agent-endpoint

Track Request Duration

@Component
class A2AMonitor {
    private val activeRequests = ConcurrentHashMap<String, Instant>()

    @EventListener
    fun onRequest(event: A2ARequestEvent) {
        val requestId = event.request.id?.toString() ?: return
        activeRequests[requestId] = event.timestamp
    }

    @EventListener
    fun onResponse(event: A2AResponseEvent) {
        val responseId = event.response.id?.toString() ?: return
        val requestTime = activeRequests.remove(responseId) ?: return
        val duration = Duration.between(requestTime, event.timestamp)

        logger.info("Request {} completed in {}ms", responseId, duration.toMillis())

        if (duration.toSeconds() > 30) {
            logger.warn("Slow request: {} took {}s", responseId, duration.toSeconds())
        }
    }
}

Clean Up Old Tasks

@Component
class TaskCleanup(private val taskStateManager: TaskStateManager) {

    @Scheduled(cron = "0 0 2 * * ?")  // Daily at 2 AM
    fun cleanupOldTasks() {
        val cutoff = Instant.now().minus(Duration.ofDays(7))
        taskStateManager.cleanupOldTasks(cutoff)
        logger.info("Cleaned up tasks older than {}", cutoff)
    }
}

Handle Errors with Notifications

@Component
class A2AErrorNotifier(private val notificationService: NotificationService) {

    @EventListener
    fun notifyOnError(event: A2AResponseEvent) {
        if (event.response is JSONRPCErrorResponse) {
            val error = event.response.error
            notificationService.send(
                subject = "A2A Request Failed",
                message = """
                    Error Code: ${error.code}
                    Message: ${error.message}
                    Request ID: ${event.response.id}
                    Platform: ${event.agentPlatform.name}
                """.trimIndent()
            )
        }
    }
}

See Also

  • Integration Patterns - Architectural patterns
  • API Reference - Complete API documentation
  • Troubleshooting - Common issues and solutions
tessl i tessl/maven-com-embabel-agent--embabel-agent-a2a@0.3.3

docs

index.md

tile.json