This is an automated email from the ASF dual-hosted git repository.

xwm1992 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/eventmesh.git


The following commit(s) were added to refs/heads/master by this push:
     new 4f9491126 [ISSUE #5247] Add A2A Agent Card Registry to EventMesh 
(#5246)
4f9491126 is described below

commit 4f9491126e17321c3cf5df32e7943d9916d9961f
Author: Eason Chen <[email protected]>
AuthorDate: Mon May 11 10:58:13 2026 +0800

    [ISSUE #5247] Add A2A Agent Card Registry to EventMesh (#5246)
    
    * update architecture
    
    * update architecture image
    
    * Test: Add unit test for SendAsyncEventProcessor to verify V1 and V2 logic 
integration
    
    * Test & Docs: Add unit tests for core engines and documentation for plugin 
configuration
    
    * 1.12.0-prepare (#5222)
    
    * Update copyright year to 2025
    
    * update release version
    
    * feat: add A2A Agent Card Registry based on EMQX reference implementation
    
    - Add Agent Card Java model classes matching A2A spec (AgentCard, 
AgentInterface,
      AgentProvider, AgentCapabilities, AgentSkill, SecurityScheme, etc.)
    - Add Agent Card JSON Schema from EMQX for validation
    - Add AgentCardValidator with JSON Schema validation support
    - Add AgentIdentity with hierarchical ID (org_id/unit_id/agent_id) and
      discovery topic construction/parsing
    - Update A2AProtocolConstants with Agent Card operations, status constants,
      CE extension keys, and ID validation pattern
    - Update EnhancedA2AProtocolAdaptor for Agent Card operation routing and
      discovery topic support
    - Implement A2APublishSubscribeService with full Card Registry (CRUD, status
      tracking, event metadata augmentation)
    - Add A2ACardHttpHandler REST API for card management
    - Add AgentCardDemo example
    - Add json-schema-validator and protocol-a2a dependencies
    
    * fix: remove test files that reference refactoring code moved to separate 
PR
    
    Remove RouterEngineTest.java and SendAsyncEventProcessorTest.java which
    test RouterEngine/IngressProcessor pipeline code that was moved to the
    refactor/unified-runtime-pipeline branch. These files were added as part
    of the refactoring commits that have been separated into their own PR.
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * fix: resolve checkstyle violations blocking CI
    
    - Remove unused import java.nio.charset.StandardCharsets in 
A2APublishSubscribeService
    - Use try-with-resources for EventMeshTCPClient in example files to fix
      resource leak checkstyle warnings (AsyncPublish, SyncRequest,
      cloudevents AsyncPublish)
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * fix: resolve checkstyle import ordering and PMD violations
    
    - Remove unused java.util.Collections import and reorder imports to match
      checkstyle groups (org.apache.eventmesh, java, io, com, lombok) in
      A2APublishSubscribeService
    - Reorder java.* imports before io.* in A2ACardHttpHandler to match
      checkstyle ImportOrder rule
    - Inline EventMeshTCPClientConfig.builder() into factory calls in example
      files to avoid PMD DU anomaly warnings from standalone config variables
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * fix: remove redundant and unused imports in AgentCardDemo
    
    - Remove redundant import for A2AAbstractDemo (same package)
    - Remove unused import for AgentIdentity
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * fix: reorder imports in AgentCardValidator to match checkstyle ImportOrder
    
    Move java.* imports before com.* and lombok.* to comply with the project's
    import group ordering (org.apache.eventmesh, java, com, lombok).
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    * fix: update FilterEngineTest and TransformerEngineTest for new 
constructors
    
    FilterEngine and TransformerEngine now require ProducerManager and
    ConsumerManager in addition to MetaStorage. Update test files to mock
    these additional dependencies.
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
    
    ---------
    
    Co-authored-by: mike_xwm <[email protected]>
    Co-authored-by: Claude Opus 4.7 <[email protected]>
---
 docs/plugins/core-engines-configuration.md         | 135 ++++
 eventmesh-examples/build.gradle                    |   1 +
 .../apache/eventmesh/a2a/demo/AgentCardDemo.java   | 199 ++++++
 .../tcp/demo/pub/cloudevents/AsyncPublish.java     |  32 +-
 .../demo/pub/eventmeshmessage/AsyncPublish.java    |  30 +-
 .../tcp/demo/pub/eventmeshmessage/SyncRequest.java |  16 +-
 .../function/router/RouterBuilderTest.java         |  34 +
 .../eventmesh-protocol-a2a/build.gradle            |   1 +
 .../protocol/a2a/A2AProtocolConstants.java         |  57 +-
 .../eventmesh/protocol/a2a/AgentCardValidator.java | 145 ++++
 .../eventmesh/protocol/a2a/AgentIdentity.java      | 160 +++++
 .../protocol/a2a/EnhancedA2AProtocolAdaptor.java   | 199 +++---
 .../protocol/a2a/model/AgentCapabilities.java      |  52 ++
 .../eventmesh/protocol/a2a/model/AgentCard.java    |  79 +++
 .../protocol/a2a/model/AgentCardSignature.java     |  47 ++
 .../protocol/a2a/model/AgentExtension.java         |  49 ++
 .../protocol/a2a/model/AgentInterface.java         |  51 ++
 .../protocol/a2a/model/AgentProvider.java          |  44 ++
 .../eventmesh/protocol/a2a/model/AgentSkill.java   |  61 ++
 .../protocol/a2a/model/SecurityRequirement.java    |  56 ++
 .../protocol/a2a/model/SecurityScheme.java         | 255 +++++++
 .../src/main/resources/a2a/agent_card_schema.json  | 732 +++++++++++++++++++++
 .../eventmesh/runtime/a2a/A2ACardHttpHandler.java  | 196 ++++++
 .../runtime/a2a/A2APublishSubscribeService.java    | 352 ++++++++++
 .../eventmesh/runtime/boot/FilterEngineTest.java   |  75 +++
 .../runtime/boot/TransformerEngineTest.java        |  76 +++
 26 files changed, 3009 insertions(+), 125 deletions(-)

diff --git a/docs/plugins/core-engines-configuration.md 
b/docs/plugins/core-engines-configuration.md
new file mode 100644
index 000000000..758efd9de
--- /dev/null
+++ b/docs/plugins/core-engines-configuration.md
@@ -0,0 +1,135 @@
+# EventMesh Core Engines Configuration Guide
+
+EventMesh provides powerful core engines (`Filter`, `Transformer`, `Router`) 
to dynamically process messages. These engines are configured via 
**MetaStorage** (Governance Center, e.g., Nacos, Etcd), supporting on-demand 
loading and hot-reloading.
+
+## 0. Core Concepts
+
+Before configuration, it is important to understand the specific role of each 
engine in the message flow:
+
+*   **Filter (The Gatekeeper)**: Decides **"Whether to pass"**.
+    *   It inspects the message (CloudEvent) attributes. If the message 
matches the rules, it passes; otherwise, it is dropped.
+    *   *Use Case*: Block debug logs from production traffic; Only subscribe 
to specific event types.
+
+*   **Transformer (The Translator)**: Decides **"What it looks like"**.
+    *   It modifies the message content (Payload or Metadata) according to 
templates or scripts.
+    *   *Use Case*: Convert XML to JSON; Mask sensitive data (PII); Adapt 
legacy protocols to new standards.
+
+*   **Router (The Dispatcher)**: Decides **"Where to go"**.
+    *   It dynamically changes the destination (Topic) of the message.
+    *   *Use Case*: Route traffic to a Canary/Gray release topic; Route 
high-priority orders to a dedicated queue.
+
+---
+
+## 1. Overview
+
+The configuration is not in local property files but distributed via the 
MetaStorage. EventMesh listens to specific **Keys** based on client Groups.
+
+- **Data Source**: Configured via `eventMesh.metaStorage.plugin.type`.
+- **Loading Mechanism**: Lazy loading & Hot-reloading.
+- **Key Format**: `{EnginePrefix}-{GroupName}`.
+- **Value Format**: JSON Array.
+
+| Engine | Prefix | Scope | Description |
+| :--- | :--- | :--- | :--- |
+| **Router** | `router-` | Pub Only | Routes messages to different topics. |
+| **Filter** | `filter-` | Pub & Sub | Filters messages based on CloudEvent 
attributes. |
+| **Transformer** | `transformer-` | Pub & Sub | Transforms message content 
(Payload/Header). |
+
+---
+
+## 2. Router (Routing)
+
+**Scope**: Publish Only (Upstream)  
+**Key**: `router-{producerGroup}`
+
+Decides the target storage topic for a message sent by a producer.
+
+### Configuration Example (JSON)
+
+```json
+[
+  {
+    "topic": "original-topic",
+    "routerConfig": {
+      "targetTopic": "redirect-topic",
+      "expression": "data.type == 'urgent'"
+    }
+  }
+]
+```
+
+*   **topic**: The original topic the producer sends to.
+*   **targetTopic**: The actual topic to write to Storage.
+*   **expression**: Condition to trigger routing (e.g., SpEL).
+
+---
+
+## 3. Filter (Filtering)
+
+**Scope**: Both Publish (Upstream) & Subscribe (Downstream)
+
+### A. Publish Side (Upstream)
+**Key**: `filter-{producerGroup}`  
+**Effect**: Intercepts messages **before** they are sent to Storage.
+
+### B. Subscribe Side (Downstream)
+**Key**: `filter-{consumerGroup}`  
+**Effect**: Intercepts messages **before** they are pushed to the Consumer.
+
+### Configuration Example (JSON)
+
+```json
+[
+  {
+    "topic": "test-topic",
+    "filterPattern": {
+      "source": ["app-a", "app-b"],
+      "type": [{"prefix": "com.example"}]
+    }
+  }
+]
+```
+
+*   **filterPattern**: Rules matching CloudEvent attributes. If a message 
doesn't match, it is dropped.
+
+---
+
+## 4. Transformer (Transformation)
+
+**Scope**: Both Publish (Upstream) & Subscribe (Downstream)
+
+### A. Publish Side (Upstream)
+**Key**: `transformer-{producerGroup}`  
+**Effect**: Modifies message content **before** sending to Storage.
+
+### B. Subscribe Side (Downstream)
+**Key**: `transformer-{consumerGroup}`  
+**Effect**: Modifies message content **before** pushing to the Consumer.
+
+### Configuration Example (JSON)
+
+```json
+[
+  {
+    "topic": "raw-topic",
+    "transformerConfig": {
+      "transformerType": "template",
+      "template": "{\"id\": \"${id}\", \"new_content\": \"${data.content}\"}"
+    }
+  }
+]
+```
+
+*   **transformerType**: e.g., `original`, `template`.
+*   **template**: The transformation template definition.
+
+---
+
+## 5. Verification
+
+1.  **Publish Config**: Add the JSON config to your Governance Center (e.g., 
Nacos) with the Data ID `router-MyGroup`.
+2.  **Send Message**: Use EventMesh SDK to send a message from `MyGroup`.
+3.  **Observe**:
+    *   For **Router**: Check if the message appears in the `targetTopic` in 
your MQ.
+    *   For **Filter**: Check if blocked messages are skipped.
+    *   For **Transformer**: Check if the message body in MQ (for Pub) or 
Consumer (for Sub) is modified.
diff --git a/eventmesh-examples/build.gradle b/eventmesh-examples/build.gradle
index bd90b8349..5815a1b9b 100644
--- a/eventmesh-examples/build.gradle
+++ b/eventmesh-examples/build.gradle
@@ -21,6 +21,7 @@ dependencies {
     implementation project(":eventmesh-sdks:eventmesh-sdk-java")
     implementation project(":eventmesh-common")
     implementation project(":eventmesh-storage-plugin:eventmesh-storage-api")
+    implementation project(":eventmesh-protocol-plugin:eventmesh-protocol-a2a")
     implementation project(":eventmesh-connectors:eventmesh-connector-spring")
     implementation('org.springframework.boot:spring-boot-starter-web') {
         exclude group: 'org.springframework.boot', module: 
'spring-boot-starter-logging'
diff --git 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/a2a/demo/AgentCardDemo.java
 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/a2a/demo/AgentCardDemo.java
new file mode 100644
index 000000000..c3418dc19
--- /dev/null
+++ 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/a2a/demo/AgentCardDemo.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.a2a.demo;
+
+import org.apache.eventmesh.client.http.conf.EventMeshHttpClientConfig;
+import org.apache.eventmesh.client.http.producer.EventMeshHttpProducer;
+import org.apache.eventmesh.common.Constants;
+import org.apache.eventmesh.common.ExampleConstants;
+import org.apache.eventmesh.common.utils.JsonUtils;
+import org.apache.eventmesh.protocol.a2a.A2AProtocolConstants;
+import org.apache.eventmesh.protocol.a2a.model.AgentCapabilities;
+import org.apache.eventmesh.protocol.a2a.model.AgentCard;
+import org.apache.eventmesh.protocol.a2a.model.AgentInterface;
+import org.apache.eventmesh.protocol.a2a.model.AgentProvider;
+import org.apache.eventmesh.protocol.a2a.model.AgentSkill;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Demo showing A2A Agent Card registration, discovery, and deletion via 
EventMesh.
+ */
+@Slf4j
+public class AgentCardDemo extends A2AAbstractDemo {
+
+    public static void main(String[] args) throws Exception {
+        EventMeshHttpClientConfig config = 
initEventMeshHttpClientConfig("a2a-agent-card-demo");
+        try (EventMeshHttpProducer producer = new 
EventMeshHttpProducer(config)) {
+
+            // 1. Register an Agent Card
+            registerAgentCard(producer, "my.org", "my.unit", "weather-agent");
+
+            // 2. List Agent Cards
+            listAgentCards(producer);
+
+            // 3. Get specific Agent Card
+            getAgentCard(producer, "my.org", "my.unit", "weather-agent");
+
+            // 4. Delete Agent Card
+            deleteAgentCard(producer, "my.org", "my.unit", "weather-agent");
+
+            log.info("AgentCardDemo completed.");
+        }
+    }
+
+    private static void registerAgentCard(EventMeshHttpProducer producer,
+                                           String orgId, String unitId, String 
agentId) throws Exception {
+        AgentCard card = buildSampleCard(agentId);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("org_id", orgId);
+        params.put("unit_id", unitId);
+        params.put("agent_id", agentId);
+        params.put("card", card);
+
+        Map<String, Object> request = new HashMap<>();
+        request.put("jsonrpc", "2.0");
+        request.put("method", A2AProtocolConstants.OP_REGISTER_AGENT_CARD);
+        request.put("params", params);
+        request.put("id", UUID.randomUUID().toString());
+
+        CloudEvent event = buildA2ACardEvent(request);
+        producer.publish(event);
+        log.info("Registered agent card: {}/{}/{}", orgId, unitId, agentId);
+    }
+
+    private static void listAgentCards(EventMeshHttpProducer producer) throws 
Exception {
+        Map<String, Object> params = new HashMap<>();
+        params.put("org_id", "my.org");
+
+        Map<String, Object> request = new HashMap<>();
+        request.put("jsonrpc", "2.0");
+        request.put("method", A2AProtocolConstants.OP_LIST_AGENT_CARDS);
+        request.put("params", params);
+        request.put("id", UUID.randomUUID().toString());
+
+        CloudEvent event = buildA2ACardEvent(request);
+        producer.publish(event);
+        log.info("Listed agent cards for org: my.org");
+    }
+
+    private static void getAgentCard(EventMeshHttpProducer producer,
+                                      String orgId, String unitId, String 
agentId) throws Exception {
+        Map<String, Object> params = new HashMap<>();
+        params.put("org_id", orgId);
+        params.put("unit_id", unitId);
+        params.put("agent_id", agentId);
+
+        Map<String, Object> request = new HashMap<>();
+        request.put("jsonrpc", "2.0");
+        request.put("method", A2AProtocolConstants.OP_GET_AGENT_CARD);
+        request.put("params", params);
+        request.put("id", UUID.randomUUID().toString());
+
+        CloudEvent event = buildA2ACardEvent(request);
+        producer.publish(event);
+        log.info("Got agent card: {}/{}/{}", orgId, unitId, agentId);
+    }
+
+    private static void deleteAgentCard(EventMeshHttpProducer producer,
+                                         String orgId, String unitId, String 
agentId) throws Exception {
+        Map<String, Object> params = new HashMap<>();
+        params.put("org_id", orgId);
+        params.put("unit_id", unitId);
+        params.put("agent_id", agentId);
+
+        Map<String, Object> request = new HashMap<>();
+        request.put("jsonrpc", "2.0");
+        request.put("method", A2AProtocolConstants.OP_DELETE_AGENT_CARD);
+        request.put("params", params);
+        request.put("id", UUID.randomUUID().toString());
+
+        CloudEvent event = buildA2ACardEvent(request);
+        producer.publish(event);
+        log.info("Deleted agent card: {}/{}/{}", orgId, unitId, agentId);
+    }
+
+    private static AgentCard buildSampleCard(String agentId) {
+        AgentInterface iface = AgentInterface.builder()
+            .url("http://localhost:8080/a2a";)
+            .protocolBinding("JSONRPC")
+            .protocolVersion(A2AProtocolConstants.PROTOCOL_VERSION)
+            .build();
+
+        AgentProvider provider = AgentProvider.builder()
+            .url("https://example.org";)
+            .organization("Example Org")
+            .build();
+
+        AgentCapabilities capabilities = AgentCapabilities.builder()
+            .streaming(true)
+            .pushNotifications(true)
+            .build();
+
+        AgentSkill skill = AgentSkill.builder()
+            .id("weather-query")
+            .name("Weather Query")
+            .description("Queries weather information for a given location")
+            .tags(Arrays.asList("weather", "query"))
+            .examples(Arrays.asList("What's the weather in Beijing?"))
+            .build();
+
+        return AgentCard.builder()
+            .name(agentId)
+            .description("A weather query agent")
+            .version("1.0.0")
+            .supportedInterfaces(Collections.singletonList(iface))
+            .provider(provider)
+            .capabilities(capabilities)
+            .skills(Collections.singletonList(skill))
+            .defaultInputModes(Collections.singletonList("text/plain"))
+            .defaultOutputModes(Collections.singletonList("text/plain"))
+            .build();
+    }
+
+    private static CloudEvent buildA2ACardEvent(Map<String, Object> 
jsonRpcBody) {
+        String content = JsonUtils.toJSONString(jsonRpcBody);
+        String method = (String) jsonRpcBody.get("method");
+        String ceType = A2AProtocolConstants.CE_TYPE_PREFIX + 
method.replace("/", ".") + ".req";
+
+        return CloudEventBuilder.v1()
+            .withId(UUID.randomUUID().toString())
+            .withSource(URI.create("a2a-agent-card-demo"))
+            .withDataContentType(ExampleConstants.CLOUDEVENT_CONTENT_TYPE)
+            .withType(ceType)
+            .withData(content.getBytes(StandardCharsets.UTF_8))
+            .withExtension(A2AProtocolConstants.CE_EXTENSION_PROTOCOL, "A2A")
+            .withExtension(A2AProtocolConstants.CE_EXTENSION_PROTOCOL_VERSION, 
"2.0")
+            .withExtension(A2AProtocolConstants.CE_EXTENSION_A2A_METHOD, 
method)
+            .withExtension(A2AProtocolConstants.CE_EXTENSION_MCP_TYPE, 
"request")
+            .withExtension(Constants.EVENTMESH_MESSAGE_CONST_TTL, 
String.valueOf(4_000))
+            .build();
+    }
+}
diff --git 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/cloudevents/AsyncPublish.java
 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/cloudevents/AsyncPublish.java
index 666e2e373..8160fc861 100644
--- 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/cloudevents/AsyncPublish.java
+++ 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/cloudevents/AsyncPublish.java
@@ -42,24 +42,26 @@ public class AsyncPublish {
         final String eventMeshIp = 
properties.getProperty(ExampleConstants.EVENTMESH_IP);
         final int eventMeshTcpPort = 
Integer.parseInt(properties.getProperty(ExampleConstants.EVENTMESH_TCP_PORT));
         try {
-            UserAgent userAgent = EventMeshTestUtils.generateClient1();
-            EventMeshTCPClientConfig eventMeshTcpClientConfig = 
EventMeshTCPClientConfig.builder()
-                .host(eventMeshIp)
-                .port(eventMeshTcpPort)
-                .userAgent(userAgent)
-                .build();
-            final EventMeshTCPClient<CloudEvent> client =
-                
EventMeshTCPClientFactory.createEventMeshTCPClient(eventMeshTcpClientConfig, 
CloudEvent.class);
-            client.init();
+            final UserAgent userAgent = EventMeshTestUtils.generateClient1();
+            try (final EventMeshTCPClient<CloudEvent> client =
+                     EventMeshTCPClientFactory.createEventMeshTCPClient(
+                         EventMeshTCPClientConfig.builder()
+                             .host(eventMeshIp)
+                             .port(eventMeshTcpPort)
+                             .userAgent(userAgent)
+                             .build(),
+                         CloudEvent.class)) {
+                client.init();
 
-            for (int i = 0; i < 2; i++) {
-                CloudEvent event = 
EventMeshTestUtils.generateCloudEventV1Async();
-                log.info("begin send async msg[{}]: {}", i, event);
-                client.publish(event, EventMeshCommon.DEFAULT_TIME_OUT_MILLS);
+                for (int i = 0; i < 2; i++) {
+                    final CloudEvent event = 
EventMeshTestUtils.generateCloudEventV1Async();
+                    log.info("begin send async msg[{}]: {}", i, event);
+                    client.publish(event, 
EventMeshCommon.DEFAULT_TIME_OUT_MILLS);
 
-                ThreadUtils.sleep(1, TimeUnit.SECONDS);
+                    ThreadUtils.sleep(1, TimeUnit.SECONDS);
+                }
+                ThreadUtils.sleep(2, TimeUnit.SECONDS);
             }
-            ThreadUtils.sleep(2, TimeUnit.SECONDS);
         } catch (Exception e) {
             log.error("AsyncPublish failed", e);
         }
diff --git 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/AsyncPublish.java
 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/AsyncPublish.java
index 222fbb7cd..16ebbc458 100644
--- 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/AsyncPublish.java
+++ 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/AsyncPublish.java
@@ -42,24 +42,26 @@ public class AsyncPublish {
         final int eventMeshTcpPort = 
Integer.parseInt(properties.getProperty(ExampleConstants.EVENTMESH_TCP_PORT));
         try {
             final UserAgent userAgent = EventMeshTestUtils.generateClient1();
-            final EventMeshTCPClientConfig eventMeshTcpClientConfig = 
EventMeshTCPClientConfig.builder()
-                .host(eventMeshIp)
-                .port(eventMeshTcpPort)
-                .userAgent(userAgent)
-                .build();
-            final EventMeshTCPClient<EventMeshMessage> client =
-                
EventMeshTCPClientFactory.createEventMeshTCPClient(eventMeshTcpClientConfig, 
EventMeshMessage.class);
-            client.init();
+            try (final EventMeshTCPClient<EventMeshMessage> client =
+                     EventMeshTCPClientFactory.createEventMeshTCPClient(
+                         EventMeshTCPClientConfig.builder()
+                             .host(eventMeshIp)
+                             .port(eventMeshTcpPort)
+                             .userAgent(userAgent)
+                             .build(),
+                         EventMeshMessage.class)) {
+                client.init();
 
-            for (int i = 0; i < 5; i++) {
-                final EventMeshMessage eventMeshMessage = 
EventMeshTestUtils.generateAsyncEventMqMsg();
+                for (int i = 0; i < 5; i++) {
+                    final EventMeshMessage eventMeshMessage = 
EventMeshTestUtils.generateAsyncEventMqMsg();
 
-                log.info("begin send async msg[{}]: {}", i, eventMeshMessage);
-                client.publish(eventMeshMessage, 
EventMeshCommon.DEFAULT_TIME_OUT_MILLS);
+                    log.info("begin send async msg[{}]: {}", i, 
eventMeshMessage);
+                    client.publish(eventMeshMessage, 
EventMeshCommon.DEFAULT_TIME_OUT_MILLS);
 
-                ThreadUtils.sleep(1, TimeUnit.SECONDS);
+                    ThreadUtils.sleep(1, TimeUnit.SECONDS);
+                }
+                ThreadUtils.sleep(2, TimeUnit.SECONDS);
             }
-            ThreadUtils.sleep(2, TimeUnit.SECONDS);
         } catch (Exception e) {
             log.error("AsyncPublish failed", e);
         }
diff --git 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/SyncRequest.java
 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/SyncRequest.java
index ed303e8d3..459fc9678 100644
--- 
a/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/SyncRequest.java
+++ 
b/eventmesh-examples/src/main/java/org/apache/eventmesh/tcp/demo/pub/eventmeshmessage/SyncRequest.java
@@ -40,14 +40,14 @@ public class SyncRequest {
         final String eventMeshIp = 
properties.getProperty(ExampleConstants.EVENTMESH_IP);
         final int eventMeshTcpPort = 
Integer.parseInt(properties.getProperty(ExampleConstants.EVENTMESH_TCP_PORT));
         final UserAgent userAgent = EventMeshTestUtils.generateClient1();
-        final EventMeshTCPClientConfig eventMeshTcpClientConfig = 
EventMeshTCPClientConfig.builder()
-            .host(eventMeshIp)
-            .port(eventMeshTcpPort)
-            .userAgent(userAgent)
-            .build();
-        try {
-            final EventMeshTCPClient<EventMeshMessage> client = 
EventMeshTCPClientFactory.createEventMeshTCPClient(
-                eventMeshTcpClientConfig, EventMeshMessage.class);
+        try (final EventMeshTCPClient<EventMeshMessage> client =
+                 EventMeshTCPClientFactory.createEventMeshTCPClient(
+                     EventMeshTCPClientConfig.builder()
+                         .host(eventMeshIp)
+                         .port(eventMeshTcpPort)
+                         .userAgent(userAgent)
+                         .build(),
+                     EventMeshMessage.class)) {
             client.init();
 
             final EventMeshMessage eventMeshMessage = 
EventMeshTestUtils.generateSyncRRMqMsg();
diff --git 
a/eventmesh-function/eventmesh-function-router/src/test/java/org/apache/eventmesh/function/router/RouterBuilderTest.java
 
b/eventmesh-function/eventmesh-function-router/src/test/java/org/apache/eventmesh/function/router/RouterBuilderTest.java
new file mode 100644
index 000000000..30af93f18
--- /dev/null
+++ 
b/eventmesh-function/eventmesh-function-router/src/test/java/org/apache/eventmesh/function/router/RouterBuilderTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.function.router;
+
+import org.apache.eventmesh.function.api.Router;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RouterBuilderTest {
+
+    @Test
+    public void testBuild() {
+        String targetTopic = "targetTopic";
+        Router router = RouterBuilder.build(targetTopic);
+        Assertions.assertNotNull(router);
+        Assertions.assertEquals(targetTopic, router.route("{}"));
+    }
+}
diff --git a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/build.gradle 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/build.gradle
index 877ec19a7..440788eea 100644
--- a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/build.gradle
+++ b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/build.gradle
@@ -24,6 +24,7 @@ dependencies {
     implementation "io.cloudevents:cloudevents-core"
     implementation "io.cloudevents:cloudevents-json-jackson"
     implementation "com.fasterxml.jackson.core:jackson-databind"
+    implementation "com.networknt:json-schema-validator:1.5.6"
     implementation "org.slf4j:slf4j-api"
     
     compileOnly 'org.projectlombok:lombok'
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/A2AProtocolConstants.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/A2AProtocolConstants.java
index 3db2b7354..1c168e85d 100644
--- 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/A2AProtocolConstants.java
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/A2AProtocolConstants.java
@@ -18,29 +18,62 @@
 package org.apache.eventmesh.protocol.a2a;
 
 /**
- * Standard Operations defined by a2a-protocol.org Specification.
+ * Standard Operations and Constants defined by A2A Protocol Specification.
  * Reference: 
https://a2a-protocol.org/latest/specification/#3-a2a-protocol-operations
  */
 public class A2AProtocolConstants {
-    
+
+    // Protocol version (per A2A spec)
+    public static final String PROTOCOL_VERSION = "0.3";
+
     // Core Messaging
     public static final String OP_SEND_MESSAGE = "message/send";
     public static final String OP_SEND_STREAMING_MESSAGE = 
"message/sendStream";
-    
+
     // Task Management
     public static final String OP_GET_TASK = "task/get";
     public static final String OP_LIST_TASKS = "task/list";
     public static final String OP_CANCEL_TASK = "task/cancel";
     public static final String OP_SUBSCRIBE_TASK = "task/subscribe";
-    
+
     // Notifications
     public static final String OP_NOTIFICATION_CONFIG_SET = 
"notification/config/set";
     public static final String OP_NOTIFICATION_CONFIG_GET = 
"notification/config/get";
     public static final String OP_NOTIFICATION_CONFIG_LIST = 
"notification/config/list";
     public static final String OP_NOTIFICATION_CONFIG_DELETE = 
"notification/config/delete";
-    
-    // Discovery
+
+    // Agent Card / Discovery
     public static final String OP_GET_AGENT_CARD = "agent/card/get";
+    public static final String OP_REGISTER_AGENT_CARD = "agent/card/register";
+    public static final String OP_DELETE_AGENT_CARD = "agent/card/delete";
+    public static final String OP_LIST_AGENT_CARDS = "agent/card/list";
+    public static final String OP_UPDATE_AGENT_CARD = "agent/card/update";
+
+    // Agent Status
+    public static final String STATUS_ONLINE = "online";
+    public static final String STATUS_OFFLINE = "offline";
+
+    // CloudEvent extension keys (matching EMQX conventions)
+    public static final String CE_EXTENSION_A2A_STATUS = "a2astatus";
+    public static final String CE_EXTENSION_A2A_STATUS_SOURCE = 
"a2astatussource";
+    public static final String CE_EXTENSION_TARGET_AGENT = "targetagent";
+    public static final String CE_EXTENSION_A2A_METHOD = "a2amethod";
+    public static final String CE_EXTENSION_COLLABORATION_ID = 
"collaborationid";
+    public static final String CE_EXTENSION_MCP_TYPE = "mcptype";
+    public static final String CE_EXTENSION_PROTOCOL = "protocol";
+    public static final String CE_EXTENSION_PROTOCOL_VERSION = 
"protocolversion";
+    public static final String CE_EXTENSION_SEQ = "seq";
+
+    // Discovery topic components
+    public static final String TOPIC_NAMESPACE = "a2a";
+    public static final String TOPIC_VERSION = "v1";
+    public static final String TOPIC_DISCOVERY = "discovery";
+
+    // ID validation pattern (matching EMQX: ^[A-Za-z0-9._-]+$)
+    public static final String SEGMENT_ID_PATTERN = "^[A-Za-z0-9._-]+$";
+
+    // CloudEvent type prefix
+    public static final String CE_TYPE_PREFIX = "org.apache.eventmesh.a2a.";
 
     /**
      * Checks if the method is a standard A2A Protocol operation.
@@ -54,4 +87,14 @@ public class A2AProtocolConstants {
             || method.startsWith("notification/")
             || method.startsWith("agent/");
     }
-}
\ No newline at end of file
+
+    /**
+     * Checks if the method is an Agent Card operation.
+     */
+    public static boolean isAgentCardOperation(String method) {
+        if (method == null) {
+            return false;
+        }
+        return method.startsWith("agent/card");
+    }
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/AgentCardValidator.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/AgentCardValidator.java
new file mode 100644
index 000000000..f226db9d3
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/AgentCardValidator.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersion;
+import com.networknt.schema.ValidationMessage;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Validates Agent Card JSON against the A2A Agent Card JSON Schema.
+ */
+@Slf4j
+public class AgentCardValidator {
+
+    private static final String SCHEMA_RESOURCE = 
"/a2a/agent_card_schema.json";
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+
+    private final JsonSchema schema;
+    private final boolean schemaValidationEnabled;
+
+    public AgentCardValidator(boolean schemaValidationEnabled) {
+        this.schemaValidationEnabled = schemaValidationEnabled;
+        this.schema = loadSchema();
+    }
+
+    public AgentCardValidator() {
+        this(true);
+    }
+
+    private JsonSchema loadSchema() {
+        try (InputStream is = 
AgentCardValidator.class.getResourceAsStream(SCHEMA_RESOURCE)) {
+            if (is == null) {
+                log.warn("Agent card schema resource not found: {}, schema 
validation will be disabled", SCHEMA_RESOURCE);
+                return null;
+            }
+            JsonNode schemaNode = objectMapper.readTree(is);
+            JsonSchemaFactory factory = 
JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V6);
+            return factory.getSchema(schemaNode);
+        } catch (IOException e) {
+            log.warn("Failed to load agent card schema, validation will be 
disabled: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Validates an Agent Card JSON string.
+     *
+     * @param cardJson the card JSON string
+     * @return ValidationResult with success/failure and error messages
+     */
+    public ValidationResult validate(String cardJson) {
+        if (cardJson == null || cardJson.isEmpty()) {
+            return ValidationResult.failure("Card JSON is null or empty");
+        }
+
+        if (!schemaValidationEnabled || schema == null) {
+            // Only check it's valid JSON object
+            try {
+                JsonNode node = objectMapper.readTree(cardJson);
+                if (!node.isObject()) {
+                    return ValidationResult.failure("Card must be a JSON 
object");
+                }
+                return ValidationResult.success();
+            } catch (Exception e) {
+                return ValidationResult.failure("Card is not valid JSON: " + 
e.getMessage());
+            }
+        }
+
+        try {
+            JsonNode cardNode = objectMapper.readTree(cardJson);
+            if (!cardNode.isObject()) {
+                return ValidationResult.failure("Card must be a JSON object");
+            }
+            Set<ValidationMessage> messages = schema.validate(cardNode);
+            if (messages.isEmpty()) {
+                return ValidationResult.success();
+            }
+            StringBuilder sb = new StringBuilder("Card schema validation 
failed: ");
+            for (ValidationMessage msg : messages) {
+                sb.append(msg.getMessage()).append("; ");
+            }
+            return ValidationResult.failure(sb.toString());
+        } catch (IOException e) {
+            return ValidationResult.failure("Failed to parse card JSON: " + 
e.getMessage());
+        }
+    }
+
+    /**
+     * Validates that an ID segment matches the allowed pattern.
+     */
+    public static boolean validateId(String id) {
+        return id != null && 
id.matches(A2AProtocolConstants.SEGMENT_ID_PATTERN);
+    }
+
+    public static class ValidationResult {
+
+        private final boolean valid;
+        private final String errorMessage;
+
+        private ValidationResult(boolean valid, String errorMessage) {
+            this.valid = valid;
+            this.errorMessage = errorMessage;
+        }
+
+        public static ValidationResult success() {
+            return new ValidationResult(true, null);
+        }
+
+        public static ValidationResult failure(String errorMessage) {
+            return new ValidationResult(false, errorMessage);
+        }
+
+        public boolean isValid() {
+            return valid;
+        }
+
+        public String getErrorMessage() {
+            return errorMessage;
+        }
+    }
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/AgentIdentity.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/AgentIdentity.java
new file mode 100644
index 000000000..9677e6d5e
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/AgentIdentity.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents the hierarchical identity of an A2A agent: org_id / unit_id / 
agent_id.
+ * Also provides discovery topic construction and parsing per the A2A protocol.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentIdentity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String GLOBAL_NAMESPACE = "global";
+    public static final String TOPIC_NAMESPACE = "a2a";
+    public static final String TOPIC_VERSION = "v1";
+    public static final String TOPIC_DISCOVERY = "discovery";
+
+    private String orgId;
+
+    private String unitId;
+
+    private String agentId;
+
+    private String namespace;
+
+    public AgentIdentity(String orgId, String unitId, String agentId) {
+        this.orgId = orgId;
+        this.unitId = unitId;
+        this.agentId = agentId;
+        this.namespace = GLOBAL_NAMESPACE;
+    }
+
+    /**
+     * Builds the discovery topic for this agent identity.
+     * Global namespace: a2a/v1/discovery/{orgId}/{unitId}/{agentId}
+     * Custom namespace: 
{namespace}/a2a/v1/discovery/{orgId}/{unitId}/{agentId}
+     */
+    public String discoveryTopic() {
+        if (GLOBAL_NAMESPACE.equals(namespace)) {
+            return String.join("/", TOPIC_NAMESPACE, TOPIC_VERSION, 
TOPIC_DISCOVERY, orgId, unitId, agentId);
+        }
+        return String.join("/", namespace, TOPIC_NAMESPACE, TOPIC_VERSION, 
TOPIC_DISCOVERY, orgId, unitId, agentId);
+    }
+
+    /**
+     * Parses a discovery topic string into an AgentIdentity.
+     *
+     * @param topic the discovery topic
+     * @return parsed AgentIdentity, or null if the topic does not match the 
expected pattern
+     */
+    public static AgentIdentity fromDiscoveryTopic(String topic) {
+        if (topic == null) {
+            return null;
+        }
+        String[] parts = topic.split("/");
+        // Global: a2a/v1/discovery/{org}/{unit}/{agent} = 6 parts
+        // Namespaced: {ns}/a2a/v1/discovery/{org}/{unit}/{agent} = 7 parts
+        if (parts.length == 6
+            && TOPIC_NAMESPACE.equals(parts[0])
+            && TOPIC_VERSION.equals(parts[1])
+            && TOPIC_DISCOVERY.equals(parts[2])) {
+            return AgentIdentity.builder()
+                .namespace(GLOBAL_NAMESPACE)
+                .orgId(parts[3])
+                .unitId(parts[4])
+                .agentId(parts[5])
+                .build();
+        }
+        if (parts.length == 7
+            && TOPIC_NAMESPACE.equals(parts[1])
+            && TOPIC_VERSION.equals(parts[2])
+            && TOPIC_DISCOVERY.equals(parts[3])) {
+            return AgentIdentity.builder()
+                .namespace(parts[0])
+                .orgId(parts[4])
+                .unitId(parts[5])
+                .agentId(parts[6])
+                .build();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the composite client ID: orgId/unitId/agentId (matching EMQX's 
agent_card_clientid).
+     */
+    public String clientId() {
+        return String.join("/", orgId, unitId, agentId);
+    }
+
+    /**
+     * Validates that all ID segments match the allowed pattern: 
^[A-Za-z0-9._-]+$
+     */
+    public boolean isValid() {
+        return isValidId(orgId) && isValidId(unitId) && isValidId(agentId);
+    }
+
+    private static boolean isValidId(String id) {
+        return id != null && 
id.matches(A2AProtocolConstants.SEGMENT_ID_PATTERN);
+    }
+
+    /**
+     * Checks if this identity matches a wildcard filter.
+     * Wildcard is represented by "+" (matching EMQX's MQTT wildcard 
convention).
+     */
+    public boolean matchesFilter(String filterOrgId, String filterUnitId, 
String filterAgentId) {
+        return (filterOrgId == null || "+".equals(filterOrgId) || 
filterOrgId.equals(orgId))
+            && (filterUnitId == null || "+".equals(filterUnitId) || 
filterUnitId.equals(unitId))
+            && (filterAgentId == null || "+".equals(filterAgentId) || 
filterAgentId.equals(agentId));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        AgentIdentity that = (AgentIdentity) o;
+        return Objects.equals(orgId, that.orgId)
+            && Objects.equals(unitId, that.unitId)
+            && Objects.equals(agentId, that.agentId)
+            && Objects.equals(namespace, that.namespace);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(orgId, unitId, agentId, namespace);
+    }
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/EnhancedA2AProtocolAdaptor.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/EnhancedA2AProtocolAdaptor.java
index 336dc4fd2..fa906f32c 100644
--- 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/EnhancedA2AProtocolAdaptor.java
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/EnhancedA2AProtocolAdaptor.java
@@ -38,11 +38,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.extern.slf4j.Slf4j;
 
 /**
- * Enhanced A2A Protocol Adaptor that implements MCP (Model Context Protocol) 
over CloudEvents.
+ * Enhanced A2A Protocol Adaptor that implements A2A (Agent-to-Agent) protocol 
over CloudEvents.
  *
  * <p>This adaptor supports:
- * 1. Standard MCP JSON-RPC 2.0 messages.
- * 2. Delegation to standard CloudEvents/HTTP protocols.
+ * 1. Standard A2A JSON-RPC 2.0 messages (messaging, task, notification, agent 
card operations).
+ * 2. Agent Card registration and discovery via discovery topics.
+ * 3. Agent status metadata augmentation.
+ * 4. Delegation to standard CloudEvents/HTTP protocols.
  */
 @Slf4j
 public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTransportObject> {
@@ -52,14 +54,14 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
 
     private static final ObjectMapper objectMapper = new ObjectMapper();
 
-    // Reuse existing protocol adaptors
     private ProtocolAdaptor<ProtocolTransportObject> cloudEventsAdaptor;
     private ProtocolAdaptor<ProtocolTransportObject> httpAdaptor;
 
     private volatile boolean initialized = false;
 
+    private AgentCardValidator cardValidator;
+
     public EnhancedA2AProtocolAdaptor() {
-        // Leverage existing protocol infrastructure with null checks
         try {
             this.cloudEventsAdaptor = 
ProtocolPluginFactory.getProtocolAdaptor("cloudevents");
         } catch (Exception e) {
@@ -78,7 +80,8 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
     @Override
     public void initialize() {
         if (!initialized) {
-            log.info("Initializing Enhanced A2A Protocol Adaptor v{} (MCP 
Support)", PROTOCOL_VERSION);
+            log.info("Initializing Enhanced A2A Protocol Adaptor v{} (Agent 
Card Registry Support)", PROTOCOL_VERSION);
+            this.cardValidator = new AgentCardValidator(true);
             if (cloudEventsAdaptor != null) {
                 log.info("Leveraging CloudEvents adaptor: {}", 
cloudEventsAdaptor.getClass().getSimpleName());
             }
@@ -107,24 +110,23 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
                 // ignore
             }
 
-            // 1. Check for MCP / JSON-RPC 2.0
             if (node != null && node.has("jsonrpc") && 
"2.0".equals(node.get("jsonrpc").asText())) {
-                return convertMcpToCloudEvent(node, content);
+                return convertA2AToCloudEvent(node, content);
             }
 
-            // 2. Delegation
             if (protocol.getClass().getName().contains("Http") && httpAdaptor 
!= null) {
                 return httpAdaptor.toCloudEvent(protocol);
             } else if (cloudEventsAdaptor != null) {
                 return cloudEventsAdaptor.toCloudEvent(protocol);
             } else {
-                // Last resort: if it looks like JSON but missing headers, 
treat as MCP Request implicitly if it has 'method'
                 if (node != null && node.has("method")) {
-                    return convertMcpToCloudEvent(node, content);
+                    return convertA2AToCloudEvent(node, content);
                 }
                 throw new ProtocolHandleException("Unknown protocol message 
format");
             }
 
+        } catch (ProtocolHandleException e) {
+            throw e;
         } catch (Exception e) {
             throw new ProtocolHandleException("Failed to convert to 
CloudEvent", e);
         }
@@ -143,12 +145,11 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
                 // ignore
             }
 
-            // Check if this is a Batch (JSON Array)
             if (node != null && node.isArray()) {
                 List<CloudEvent> events = new ArrayList<>();
                 for (JsonNode item : node) {
                     if (item.has("jsonrpc")) {
-                        events.add(convertMcpToCloudEvent(item, 
item.toString()));
+                        events.add(convertA2AToCloudEvent(item, 
item.toString()));
                     }
                 }
                 if (!events.isEmpty()) {
@@ -156,7 +157,6 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
                 }
             }
 
-            // Delegate
             if (cloudEventsAdaptor != null) {
                 try {
                     return cloudEventsAdaptor.toBatchCloudEvent(protocol);
@@ -167,10 +167,11 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
                 }
             }
 
-            // Fallback
             CloudEvent single = toCloudEvent(protocol);
             return Collections.singletonList(single);
 
+        } catch (ProtocolHandleException e) {
+            throw e;
         } catch (Exception e) {
             throw new ProtocolHandleException("Failed to convert batch to 
CloudEvents", e);
         }
@@ -179,12 +180,10 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
     @Override
     public ProtocolTransportObject fromCloudEvent(CloudEvent cloudEvent) 
throws ProtocolHandleException {
         try {
-            // Check if this is an A2A/MCP CloudEvent
             if (isA2ACloudEvent(cloudEvent)) {
                 return convertCloudEventToA2A(cloudEvent);
             }
 
-            // Determine target protocol from CloudEvent extensions
             String targetProtocol = getTargetProtocol(cloudEvent);
 
             switch (targetProtocol.toLowerCase()) {
@@ -234,7 +233,10 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
             "mcp-jsonrpc",
             "agent-communication",
             "workflow-orchestration",
-            "collaboration"
+            "collaboration",
+            "agent-discovery",
+            "agent-card-registry",
+            "agent-status"
         );
     }
 
@@ -246,13 +248,11 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
 
         try {
             String content = protocol.toString();
-            // Fast fail
             if (!content.contains("{")) {
                 return false;
             }
 
             JsonNode node = objectMapper.readTree(content);
-            // Valid if JSON-RPC
             if (node.has("jsonrpc")) {
                 return true;
             }
@@ -271,108 +271,144 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
     }
 
     private boolean isA2ACloudEvent(CloudEvent cloudEvent) {
-        return PROTOCOL_TYPE.equals(cloudEvent.getExtension("protocol"))
-            || cloudEvent.getType().startsWith("org.apache.eventmesh.a2a")
-            || cloudEvent.getExtension("a2amethod") != null;
+        return 
PROTOCOL_TYPE.equals(cloudEvent.getExtension(A2AProtocolConstants.CE_EXTENSION_PROTOCOL))
+            || 
cloudEvent.getType().startsWith(A2AProtocolConstants.CE_TYPE_PREFIX)
+            || 
cloudEvent.getExtension(A2AProtocolConstants.CE_EXTENSION_A2A_METHOD) != null;
     }
 
     /**
-     * Converts a modern MCP / A2A JSON-RPC message to CloudEvent.
-     * Distinguishes between Requests and Responses for Event-Driven Async RPC 
pattern.
+     * Converts an A2A JSON-RPC message to CloudEvent.
+     * Handles Agent Card operations with discovery topic routing.
      */
-    private CloudEvent convertMcpToCloudEvent(JsonNode node, String content) 
throws ProtocolHandleException {
+    private CloudEvent convertA2AToCloudEvent(JsonNode node, String content) 
throws ProtocolHandleException {
         try {
             boolean isRequest = node.has("method");
-            boolean isResponse = node.has("result")
-                || node.has("error");
+            boolean isResponse = node.has("result") || node.has("error");
 
             String id = node.has("id") ? node.get("id").asText() : 
generateMessageId();
             String ceType;
             String mcpType;
             String correlationId = null;
-            String eventId = isRequest ? id : generateMessageId(); // For 
request, CE id = RPC id. For response, CE id is new.
+            String eventId = isRequest ? id : generateMessageId();
 
             CloudEventBuilder builder = CloudEventBuilder.v1()
                 .withSource(java.net.URI.create("eventmesh-a2a"))
                 .withData(content.getBytes(StandardCharsets.UTF_8))
-                .withExtension("protocol", PROTOCOL_TYPE)
-                .withExtension("protocolversion", PROTOCOL_VERSION);
+                .withExtension(A2AProtocolConstants.CE_EXTENSION_PROTOCOL, 
PROTOCOL_TYPE)
+                
.withExtension(A2AProtocolConstants.CE_EXTENSION_PROTOCOL_VERSION, 
PROTOCOL_VERSION);
 
             if (isRequest) {
-                // JSON-RPC Request -> Event
                 String method = node.get("method").asText();
-
-                // Determine suffix based on operation type
-                String suffix = ".req";
-                if 
(A2AProtocolConstants.OP_SEND_STREAMING_MESSAGE.equals(method)) {
-                    suffix = ".stream";
-                }
-
-                ceType = "org.apache.eventmesh.a2a." + method.replace("/", 
".") + suffix;
                 mcpType = "request";
 
-                builder.withExtension("a2amethod", method);
-
-                // Extract optional params for routing
-                if (node.has("params")) {
-                    JsonNode params = node.get("params");
-
-                    // 1. Pub/Sub Routing (Priority): Broadcast to a Topic
-                    if (params.has("_topic")) {
-                        builder.withSubject(params.get("_topic").asText());
-                    } else if (params.has("_agentId")) {
-                        // 2. P2P Routing (Fallback): Unicast to specific Agent
-                        builder.withExtension("targetagent", 
params.get("_agentId").asText());
-                    }
-
-                    // 3. Sequencing for Streaming
-                    if (params.has("_seq")) {
-                        builder.withExtension("seq", 
params.get("_seq").asText());
-                    }
+                if (A2AProtocolConstants.isAgentCardOperation(method)) {
+                    ceType = buildAgentCardCloudEventType(method);
+                    
builder.withExtension(A2AProtocolConstants.CE_EXTENSION_A2A_METHOD, method);
+                    extractAgentCardRouting(node, builder, method);
+                } else if 
(A2AProtocolConstants.OP_SEND_STREAMING_MESSAGE.equals(method)) {
+                    ceType = A2AProtocolConstants.CE_TYPE_PREFIX + 
method.replace("/", ".") + ".stream";
+                    
builder.withExtension(A2AProtocolConstants.CE_EXTENSION_A2A_METHOD, method);
+                    extractStandardRouting(node, builder);
+                } else {
+                    ceType = A2AProtocolConstants.CE_TYPE_PREFIX + 
method.replace("/", ".") + ".req";
+                    
builder.withExtension(A2AProtocolConstants.CE_EXTENSION_A2A_METHOD, method);
+                    extractStandardRouting(node, builder);
                 }
             } else if (isResponse) {
-                // JSON-RPC Response -> Event
-                // We map the RPC ID to correlationId so the requester can 
match it
-                ceType = "org.apache.eventmesh.a2a.common.response";
+                ceType = A2AProtocolConstants.CE_TYPE_PREFIX + 
"common.response";
                 mcpType = "response";
                 correlationId = id;
-
-                builder.withExtension("collaborationid", correlationId);
+                
builder.withExtension(A2AProtocolConstants.CE_EXTENSION_COLLABORATION_ID, 
correlationId);
             } else {
-                // Notification or invalid
-                ceType = "org.apache.eventmesh.a2a.unknown";
+                ceType = A2AProtocolConstants.CE_TYPE_PREFIX + "unknown";
                 mcpType = "unknown";
             }
 
             builder.withId(eventId)
                 .withType(ceType)
-                .withExtension("mcptype", mcpType);
+                .withExtension(A2AProtocolConstants.CE_EXTENSION_MCP_TYPE, 
mcpType);
 
             return builder.build();
 
         } catch (Exception e) {
-            throw new ProtocolHandleException("Failed to convert MCP/A2A 
message to CloudEvent", e);
+            throw new ProtocolHandleException("Failed to convert A2A message 
to CloudEvent", e);
         }
     }
 
-    private ProtocolTransportObject convertCloudEventToA2A(CloudEvent 
cloudEvent)
-        throws ProtocolHandleException {
-        try {
-            if (cloudEventsAdaptor != null) {
-                try {
-                    return cloudEventsAdaptor.fromCloudEvent(cloudEvent);
-                } catch (Exception ignored) {
-                    // ignore
+    private String buildAgentCardCloudEventType(String method) {
+        return A2AProtocolConstants.CE_TYPE_PREFIX + method.replace("/", ".") 
+ ".req";
+    }
+
+    /**
+     * Extracts routing for Agent Card operations.
+     * For register/update: route to discovery topic with 
org_id/unit_id/agent_id from params.
+     * For get/delete: route to discovery topic for lookup.
+     * For list: route to discovery topic with wildcard.
+     */
+    private void extractAgentCardRouting(JsonNode node, CloudEventBuilder 
builder, String method) {
+        if (!node.has("params")) {
+            return;
+        }
+        JsonNode params = node.get("params");
+
+        String orgId = getTextParam(params, "org_id");
+        String unitId = getTextParam(params, "unit_id");
+        String agentId = getTextParam(params, "agent_id");
+
+        if (orgId != null && unitId != null && agentId != null) {
+            AgentIdentity identity = new AgentIdentity(orgId, unitId, agentId);
+            builder.withSubject(identity.discoveryTopic());
+        }
+
+        if (params.has("card")) {
+            // Validate card if validator is available
+            if (cardValidator != null) {
+                String cardJson = params.get("card").toString();
+                AgentCardValidator.ValidationResult result = 
cardValidator.validate(cardJson);
+                if (!result.isValid()) {
+                    log.warn("Agent card validation failed: {}", 
result.getErrorMessage());
                 }
             }
+        }
+    }
+
+    private void extractStandardRouting(JsonNode node, CloudEventBuilder 
builder) {
+        if (!node.has("params")) {
+            return;
+        }
+        JsonNode params = node.get("params");
 
-            byte[] data = cloudEvent.getData() != null ? 
cloudEvent.getData().toBytes() : new byte[0];
-            String content = new String(data, StandardCharsets.UTF_8);
-            return new SimpleA2AProtocolTransportObject(content, cloudEvent);
+        if (params.has("_topic")) {
+            builder.withSubject(params.get("_topic").asText());
+        } else if (params.has("_agentId")) {
+            
builder.withExtension(A2AProtocolConstants.CE_EXTENSION_TARGET_AGENT, 
params.get("_agentId").asText());
+        }
 
-        } catch (Exception e) {
-            throw new ProtocolHandleException("Failed to convert CloudEvent to 
A2A", e);
+        if (params.has("_seq")) {
+            builder.withExtension(A2AProtocolConstants.CE_EXTENSION_SEQ, 
params.get("_seq").asText());
+        }
+    }
+
+    private String getTextParam(JsonNode params, String key) {
+        if (params.has(key)) {
+            JsonNode val = params.get(key);
+            return val.isTextual() ? val.asText() : null;
+        }
+        return null;
+    }
+
+    private ProtocolTransportObject convertCloudEventToA2A(CloudEvent 
cloudEvent) {
+        if (cloudEventsAdaptor != null) {
+            try {
+                return cloudEventsAdaptor.fromCloudEvent(cloudEvent);
+            } catch (Exception ignored) {
+                // ignore
+            }
         }
+
+        byte[] data = cloudEvent.getData() != null ? 
cloudEvent.getData().toBytes() : new byte[0];
+        String content = new String(data, StandardCharsets.UTF_8);
+        return new SimpleA2AProtocolTransportObject(content, cloudEvent);
     }
 
     private String getTargetProtocol(CloudEvent cloudEvent) {
@@ -391,6 +427,7 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
     }
 
     private static class SimpleA2AProtocolTransportObject implements 
ProtocolTransportObject {
+
         private final String content;
         private final CloudEvent sourceCloudEvent;
 
@@ -416,6 +453,6 @@ public class EnhancedA2AProtocolAdaptor implements 
ProtocolAdaptor<ProtocolTrans
     }
 
     private String generateMessageId() {
-        return "a2a-mcp-" + System.currentTimeMillis() + "-" + Math.random();
+        return "a2a-" + System.currentTimeMillis() + "-" + Math.random();
     }
 }
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCapabilities.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCapabilities.java
new file mode 100644
index 000000000..44322146b
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCapabilities.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Defines optional capabilities supported by an agent.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentCapabilities implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Boolean streaming;
+
+    @JsonProperty("pushNotifications")
+    private Boolean pushNotifications;
+
+    @JsonProperty("extendedAgentCard")
+    private Boolean extendedAgentCard;
+
+    private List<AgentExtension> extensions;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCard.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCard.java
new file mode 100644
index 000000000..6f1787874
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCard.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents an A2A Agent Card as defined by the A2A protocol specification.
+ * Reference: https://a2a-protocol.org/latest/specification/#agent-card
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentCard implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String name;
+
+    private String description;
+
+    private String version;
+
+    @JsonProperty("supportedInterfaces")
+    private List<AgentInterface> supportedInterfaces;
+
+    private AgentProvider provider;
+
+    private AgentCapabilities capabilities;
+
+    private List<AgentSkill> skills;
+
+    private List<AgentCardSignature> signatures;
+
+    @JsonProperty("securitySchemes")
+    private Map<String, SecurityScheme> securitySchemes;
+
+    @JsonProperty("securityRequirements")
+    private List<SecurityRequirement> securityRequirements;
+
+    @JsonProperty("defaultInputModes")
+    private List<String> defaultInputModes;
+
+    @JsonProperty("defaultOutputModes")
+    private List<String> defaultOutputModes;
+
+    @JsonProperty("documentationUrl")
+    private String documentationUrl;
+
+    @JsonProperty("iconUrl")
+    private String iconUrl;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCardSignature.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCardSignature.java
new file mode 100644
index 000000000..13cacbd4f
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentCardSignature.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a JWS signature of an AgentCard (RFC 7515).
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentCardSignature implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String protect;
+
+    private String signature;
+
+    private Map<String, Object> header;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentExtension.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentExtension.java
new file mode 100644
index 000000000..ccb19a9c5
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentExtension.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * A declaration of a protocol extension supported by an Agent.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentExtension implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String uri;
+
+    private String description;
+
+    private Boolean required;
+
+    private Map<String, Object> params;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentInterface.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentInterface.java
new file mode 100644
index 000000000..d0f398951
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentInterface.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Declares a combination of a target URL, transport and protocol version for 
interacting with the agent.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentInterface implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String url;
+
+    @JsonProperty("protocolBinding")
+    private String protocolBinding;
+
+    @JsonProperty("protocolVersion")
+    private String protocolVersion;
+
+    private String tenant;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentProvider.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentProvider.java
new file mode 100644
index 000000000..72e53b39e
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents the service provider of an agent.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentProvider implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String url;
+
+    private String organization;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentSkill.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentSkill.java
new file mode 100644
index 000000000..0e484af21
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/AgentSkill.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Represents a distinct capability or function that an agent can perform.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AgentSkill implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String id;
+
+    private String name;
+
+    private String description;
+
+    private List<String> tags;
+
+    private List<String> examples;
+
+    @JsonProperty("inputModes")
+    private List<String> inputModes;
+
+    @JsonProperty("outputModes")
+    private List<String> outputModes;
+
+    @JsonProperty("securityRequirements")
+    private List<SecurityRequirement> securityRequirements;
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/SecurityRequirement.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/SecurityRequirement.java
new file mode 100644
index 000000000..cb8b05381
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/SecurityRequirement.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Defines the security requirements for an agent.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class SecurityRequirement implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Map<String, StringList> schemes;
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class StringList implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private List<String> list;
+    }
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/SecurityScheme.java
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/SecurityScheme.java
new file mode 100644
index 000000000..381594232
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/java/org/apache/eventmesh/protocol/a2a/model/SecurityScheme.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.protocol.a2a.model;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Defines a security scheme that can be used to secure an agent's endpoints.
+ * Discriminated union type based on OpenAPI 3.2 Security Scheme Object.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class SecurityScheme implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty("apiKeySecurityScheme")
+    private APIKeySecurityScheme apiKeySecurityScheme;
+
+    @JsonProperty("httpAuthSecurityScheme")
+    private HTTPAuthSecurityScheme httpAuthSecurityScheme;
+
+    @JsonProperty("oauth2SecurityScheme")
+    private OAuth2SecurityScheme oauth2SecurityScheme;
+
+    @JsonProperty("openIdConnectSecurityScheme")
+    private OpenIdConnectSecurityScheme openIdConnectSecurityScheme;
+
+    @JsonProperty("mtlsSecurityScheme")
+    private MutualTlsSecurityScheme mtlsSecurityScheme;
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class APIKeySecurityScheme implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private String description;
+        private String location;
+        private String name;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class HTTPAuthSecurityScheme implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private String description;
+        private String scheme;
+
+        @JsonProperty("bearerFormat")
+        private String bearerFormat;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class OAuth2SecurityScheme implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private String description;
+        private OAuthFlows flows;
+
+        @JsonProperty("oauth2MetadataUrl")
+        private String oauth2MetadataUrl;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class OpenIdConnectSecurityScheme implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private String description;
+
+        @JsonProperty("openIdConnectUrl")
+        private String openIdConnectUrl;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class MutualTlsSecurityScheme implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private String description;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class OAuthFlows implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("authorizationCode")
+        private AuthorizationCodeOAuthFlow authorizationCode;
+
+        @JsonProperty("clientCredentials")
+        private ClientCredentialsOAuthFlow clientCredentials;
+
+        @JsonProperty("deviceCode")
+        private DeviceCodeOAuthFlow deviceCode;
+
+        @JsonProperty("implicit")
+        private ImplicitOAuthFlow implicit;
+
+        @JsonProperty("password")
+        private PasswordOAuthFlow password;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class AuthorizationCodeOAuthFlow implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("authorizationUrl")
+        private String authorizationUrl;
+
+        @JsonProperty("tokenUrl")
+        private String tokenUrl;
+
+        @JsonProperty("refreshUrl")
+        private String refreshUrl;
+
+        private Map<String, String> scopes;
+
+        @JsonProperty("pkceRequired")
+        private Boolean pkceRequired;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class ClientCredentialsOAuthFlow implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("tokenUrl")
+        private String tokenUrl;
+
+        @JsonProperty("refreshUrl")
+        private String refreshUrl;
+
+        private Map<String, String> scopes;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class DeviceCodeOAuthFlow implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("deviceAuthorizationUrl")
+        private String deviceAuthorizationUrl;
+
+        @JsonProperty("tokenUrl")
+        private String tokenUrl;
+
+        @JsonProperty("refreshUrl")
+        private String refreshUrl;
+
+        private Map<String, String> scopes;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class ImplicitOAuthFlow implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("authorizationUrl")
+        private String authorizationUrl;
+
+        @JsonProperty("refreshUrl")
+        private String refreshUrl;
+
+        private Map<String, String> scopes;
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class PasswordOAuthFlow implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("tokenUrl")
+        private String tokenUrl;
+
+        @JsonProperty("refreshUrl")
+        private String refreshUrl;
+
+        private Map<String, String> scopes;
+    }
+}
diff --git 
a/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/resources/a2a/agent_card_schema.json
 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/resources/a2a/agent_card_schema.json
new file mode 100644
index 000000000..db9b4946f
--- /dev/null
+++ 
b/eventmesh-protocol-plugin/eventmesh-protocol-a2a/src/main/resources/a2a/agent_card_schema.json
@@ -0,0 +1,732 @@
+{
+  "$schema": "http://json-schema.org/draft-06/schema#";,
+  "title": "A2A Agent Card",
+  "type": "object",
+  "required": [
+    "name",
+    "description",
+    "supportedInterfaces",
+    "version",
+    "capabilities",
+    "defaultInputModes",
+    "defaultOutputModes",
+    "skills"
+  ],
+  "patternProperties": {
+    "^(supported_interfaces)$": {
+      "type": "array",
+      "description": "Ordered list of supported interfaces. The first entry is 
preferred.",
+      "items": {
+        "$ref": "#/definitions/AgentInterface"
+      }
+    },
+    "^(documentation_url)$": {
+      "type": "string",
+      "description": "A URL providing additional documentation about the 
agent."
+    },
+    "^(security_schemes)$": {
+      "type": "object",
+      "description": "The security scheme details used for authenticating with 
this agent.",
+      "additionalProperties": {
+        "$ref": "#/definitions/SecurityScheme"
+      },
+      "propertyNames": {
+        "type": "string"
+      }
+    },
+    "^(security_requirements)$": {
+      "type": "array",
+      "description": "Security requirements for contacting the agent.",
+      "items": {
+        "$ref": "#/definitions/SecurityRequirement"
+      }
+    },
+    "^(default_input_modes)$": {
+      "type": "array",
+      "items": { "type": "string" },
+      "description": "The set of interaction modes that the agent supports 
across all skills, defined as media types."
+    },
+    "^(default_output_modes)$": {
+      "type": "array",
+      "items": { "type": "string" },
+      "description": "The media types supported as outputs from this agent."
+    },
+    "^(icon_url)$": {
+      "type": "string",
+      "description": "A URL to an icon for the agent."
+    }
+  },
+  "properties": {
+    "name": {
+      "type": "string",
+      "description": "A human readable name for the agent."
+    },
+    "description": {
+      "type": "string",
+      "description": "A human-readable description of the agent, assisting 
users and other agents in understanding its purpose."
+    },
+    "supportedInterfaces": {
+      "type": "array",
+      "description": "Ordered list of supported interfaces. The first entry is 
preferred.",
+      "items": {
+        "$ref": "#/definitions/AgentInterface"
+      }
+    },
+    "provider": {
+      "$ref": "#/definitions/AgentProvider"
+    },
+    "version": {
+      "type": "string",
+      "description": "The version of the agent (e.g., \"1.0.0\")."
+    },
+    "documentationUrl": {
+      "type": "string",
+      "description": "A URL providing additional documentation about the 
agent."
+    },
+    "capabilities": {
+      "$ref": "#/definitions/AgentCapabilities"
+    },
+    "securitySchemes": {
+      "type": "object",
+      "description": "The security scheme details used for authenticating with 
this agent.",
+      "additionalProperties": {
+        "$ref": "#/definitions/SecurityScheme"
+      },
+      "propertyNames": {
+        "type": "string"
+      }
+    },
+    "securityRequirements": {
+      "type": "array",
+      "description": "Security requirements for contacting the agent.",
+      "items": {
+        "$ref": "#/definitions/SecurityRequirement"
+      }
+    },
+    "defaultInputModes": {
+      "type": "array",
+      "items": { "type": "string" },
+      "description": "The set of interaction modes that the agent supports 
across all skills, defined as media types."
+    },
+    "defaultOutputModes": {
+      "type": "array",
+      "items": { "type": "string" },
+      "description": "The media types supported as outputs from this agent."
+    },
+    "skills": {
+      "type": "array",
+      "description": "Skills represent the abilities of an agent.",
+      "items": {
+        "$ref": "#/definitions/AgentSkill"
+      }
+    },
+    "signatures": {
+      "type": "array",
+      "description": "JSON Web Signatures computed for this AgentCard.",
+      "items": {
+        "$ref": "#/definitions/AgentCardSignature"
+      }
+    },
+    "iconUrl": {
+      "type": "string",
+      "description": "A URL to an icon for the agent."
+    }
+  },
+  "additionalProperties": false,
+  "definitions": {
+    "AgentInterface": {
+      "type": "object",
+      "description": "Declares a combination of a target URL, transport and 
protocol version for interacting with the agent.",
+      "required": ["url", "protocolBinding", "protocolVersion"],
+      "patternProperties": {
+        "^(protocol_binding)$": {
+          "type": "string",
+          "description": "The protocol binding supported at this URL (e.g., 
JSONRPC, GRPC, HTTP+JSON)."
+        },
+        "^(protocol_version)$": {
+          "type": "string",
+          "description": "The version of the A2A protocol this interface 
exposes (e.g., \"0.3\", \"1.0\")."
+        }
+      },
+      "properties": {
+        "url": {
+          "type": "string",
+          "description": "The URL where this interface is available."
+        },
+        "protocolBinding": {
+          "type": "string",
+          "description": "The protocol binding supported at this URL (e.g., 
JSONRPC, GRPC, HTTP+JSON)."
+        },
+        "tenant": {
+          "type": "string",
+          "description": "Tenant ID to be used in the request when calling the 
agent."
+        },
+        "protocolVersion": {
+          "type": "string",
+          "description": "The version of the A2A protocol this interface 
exposes (e.g., \"0.3\", \"1.0\")."
+        }
+      },
+      "additionalProperties": false
+    },
+    "AgentProvider": {
+      "type": "object",
+      "description": "Represents the service provider of an agent.",
+      "required": ["url", "organization"],
+      "properties": {
+        "url": {
+          "type": "string",
+          "description": "A URL for the agent provider's website or relevant 
documentation."
+        },
+        "organization": {
+          "type": "string",
+          "description": "The name of the agent provider's organization."
+        }
+      },
+      "additionalProperties": false
+    },
+    "AgentCapabilities": {
+      "type": "object",
+      "description": "Defines optional capabilities supported by an agent.",
+      "patternProperties": {
+        "^(push_notifications)$": {
+          "type": "boolean",
+          "description": "Indicates if the agent supports sending push 
notifications for asynchronous task updates."
+        },
+        "^(extended_agent_card)$": {
+          "type": "boolean",
+          "description": "Indicates if the agent supports providing an 
extended agent card when authenticated."
+        }
+      },
+      "properties": {
+        "streaming": {
+          "type": "boolean",
+          "description": "Indicates if the agent supports streaming responses."
+        },
+        "pushNotifications": {
+          "type": "boolean",
+          "description": "Indicates if the agent supports sending push 
notifications for asynchronous task updates."
+        },
+        "extensions": {
+          "type": "array",
+          "description": "A list of protocol extensions supported by the 
agent.",
+          "items": {
+            "$ref": "#/definitions/AgentExtension"
+          }
+        },
+        "extendedAgentCard": {
+          "type": "boolean",
+          "description": "Indicates if the agent supports providing an 
extended agent card when authenticated."
+        }
+      },
+      "additionalProperties": false
+    },
+    "AgentExtension": {
+      "type": "object",
+      "description": "A declaration of a protocol extension supported by an 
Agent.",
+      "properties": {
+        "uri": {
+          "type": "string",
+          "description": "The unique URI identifying the extension."
+        },
+        "description": {
+          "type": "string",
+          "description": "A human-readable description of how this agent uses 
the extension."
+        },
+        "required": {
+          "type": "boolean",
+          "description": "If true, the client must understand and comply with 
the extension's requirements."
+        },
+        "params": {
+          "type": "object",
+          "description": "Extension-specific configuration parameters."
+        }
+      },
+      "additionalProperties": false
+    },
+    "AgentSkill": {
+      "type": "object",
+      "description": "Represents a distinct capability or function that an 
agent can perform.",
+      "required": ["id", "name", "description", "tags"],
+      "patternProperties": {
+        "^(input_modes)$": {
+          "type": "array",
+          "items": { "type": "string" },
+          "description": "The set of supported input media types for this 
skill, overriding the agent's defaults."
+        },
+        "^(output_modes)$": {
+          "type": "array",
+          "items": { "type": "string" },
+          "description": "The set of supported output media types for this 
skill, overriding the agent's defaults."
+        },
+        "^(security_requirements)$": {
+          "type": "array",
+          "description": "Security schemes necessary for this skill.",
+          "items": {
+            "$ref": "#/definitions/SecurityRequirement"
+          }
+        }
+      },
+      "properties": {
+        "id": {
+          "type": "string",
+          "description": "A unique identifier for the agent's skill."
+        },
+        "name": {
+          "type": "string",
+          "description": "A human-readable name for the skill."
+        },
+        "description": {
+          "type": "string",
+          "description": "A detailed description of the skill."
+        },
+        "tags": {
+          "type": "array",
+          "items": { "type": "string" },
+          "description": "A set of keywords describing the skill's 
capabilities."
+        },
+        "examples": {
+          "type": "array",
+          "items": { "type": "string" },
+          "description": "Example prompts or scenarios that this skill can 
handle."
+        },
+        "inputModes": {
+          "type": "array",
+          "items": { "type": "string" },
+          "description": "The set of supported input media types for this 
skill, overriding the agent's defaults."
+        },
+        "outputModes": {
+          "type": "array",
+          "items": { "type": "string" },
+          "description": "The set of supported output media types for this 
skill, overriding the agent's defaults."
+        },
+        "securityRequirements": {
+          "type": "array",
+          "description": "Security schemes necessary for this skill.",
+          "items": {
+            "$ref": "#/definitions/SecurityRequirement"
+          }
+        }
+      },
+      "additionalProperties": false
+    },
+    "AgentCardSignature": {
+      "type": "object",
+      "description": "Represents a JWS signature of an AgentCard (RFC 7515).",
+      "required": ["protected", "signature"],
+      "properties": {
+        "protected": {
+          "type": "string",
+          "description": "The protected JWS header for the signature, 
base64url-encoded JSON object."
+        },
+        "signature": {
+          "type": "string",
+          "description": "The computed signature, base64url-encoded."
+        },
+        "header": {
+          "type": "object",
+          "description": "The unprotected JWS header values."
+        }
+      },
+      "additionalProperties": false
+    },
+    "SecurityRequirement": {
+      "type": "object",
+      "description": "Defines the security requirements for an agent.",
+      "properties": {
+        "schemes": {
+          "type": "object",
+          "description": "A map of security scheme names to the required 
scopes.",
+          "additionalProperties": {
+            "$ref": "#/definitions/StringList"
+          },
+          "propertyNames": {
+            "type": "string"
+          }
+        }
+      },
+      "additionalProperties": false
+    },
+    "StringList": {
+      "type": "object",
+      "description": "A list of strings.",
+      "properties": {
+        "list": {
+          "type": "array",
+          "description": "The individual string values.",
+          "items": { "type": "string" }
+        }
+      },
+      "additionalProperties": false
+    },
+    "SecurityScheme": {
+      "type": "object",
+      "description": "Defines a security scheme that can be used to secure an 
agent's endpoints. This is a discriminated union type based on the OpenAPI 3.2 
Security Scheme Object.",
+      "patternProperties": {
+        "^(api_key_security_scheme)$": {
+          "$ref": "#/definitions/APIKeySecurityScheme",
+          "description": "API key-based authentication."
+        },
+        "^(http_auth_security_scheme)$": {
+          "$ref": "#/definitions/HTTPAuthSecurityScheme",
+          "description": "HTTP authentication (Basic, Bearer, etc.)."
+        },
+        "^(oauth2_security_scheme)$": {
+          "$ref": "#/definitions/OAuth2SecurityScheme",
+          "description": "OAuth 2.0 authentication."
+        },
+        "^(open_id_connect_security_scheme)$": {
+          "$ref": "#/definitions/OpenIdConnectSecurityScheme",
+          "description": "OpenID Connect authentication."
+        },
+        "^(mtls_security_scheme)$": {
+          "$ref": "#/definitions/MutualTlsSecurityScheme",
+          "description": "Mutual TLS authentication."
+        }
+      },
+      "properties": {
+        "apiKeySecurityScheme": {
+          "$ref": "#/definitions/APIKeySecurityScheme",
+          "description": "API key-based authentication."
+        },
+        "httpAuthSecurityScheme": {
+          "$ref": "#/definitions/HTTPAuthSecurityScheme",
+          "description": "HTTP authentication (Basic, Bearer, etc.)."
+        },
+        "oauth2SecurityScheme": {
+          "$ref": "#/definitions/OAuth2SecurityScheme",
+          "description": "OAuth 2.0 authentication."
+        },
+        "openIdConnectSecurityScheme": {
+          "$ref": "#/definitions/OpenIdConnectSecurityScheme",
+          "description": "OpenID Connect authentication."
+        },
+        "mtlsSecurityScheme": {
+          "$ref": "#/definitions/MutualTlsSecurityScheme",
+          "description": "Mutual TLS authentication."
+        }
+      },
+      "additionalProperties": false
+    },
+    "APIKeySecurityScheme": {
+      "type": "object",
+      "description": "Defines a security scheme using an API key.",
+      "properties": {
+        "description": {
+          "type": "string",
+          "description": "An optional description for the security scheme."
+        },
+        "location": {
+          "type": "string",
+          "description": "The location of the API key. Valid values are 
\"query\", \"header\", or \"cookie\"."
+        },
+        "name": {
+          "type": "string",
+          "description": "The name of the header, query, or cookie parameter 
to be used."
+        }
+      },
+      "additionalProperties": false
+    },
+    "HTTPAuthSecurityScheme": {
+      "type": "object",
+      "description": "Defines a security scheme using HTTP authentication.",
+      "patternProperties": {
+        "^(bearer_format)$": {
+          "type": "string",
+          "description": "A hint to identify how the bearer token is formatted 
(e.g., \"JWT\")."
+        }
+      },
+      "properties": {
+        "description": {
+          "type": "string",
+          "description": "An optional description for the security scheme."
+        },
+        "scheme": {
+          "type": "string",
+          "description": "The HTTP Authentication scheme (e.g., Bearer, 
Basic)."
+        },
+        "bearerFormat": {
+          "type": "string",
+          "description": "A hint to identify how the bearer token is formatted 
(e.g., \"JWT\")."
+        }
+      },
+      "additionalProperties": false
+    },
+    "OAuth2SecurityScheme": {
+      "type": "object",
+      "description": "Defines a security scheme using OAuth 2.0.",
+      "patternProperties": {
+        "^(oauth2_metadata_url)$": {
+          "type": "string",
+          "description": "URL to the OAuth2 authorization server metadata (RFC 
8414)."
+        }
+      },
+      "properties": {
+        "description": {
+          "type": "string",
+          "description": "An optional description for the security scheme."
+        },
+        "flows": {
+          "$ref": "#/definitions/OAuthFlows",
+          "description": "An object containing configuration information for 
the supported OAuth 2.0 flows."
+        },
+        "oauth2MetadataUrl": {
+          "type": "string",
+          "description": "URL to the OAuth2 authorization server metadata (RFC 
8414)."
+        }
+      },
+      "additionalProperties": false
+    },
+    "OpenIdConnectSecurityScheme": {
+      "type": "object",
+      "description": "Defines a security scheme using OpenID Connect.",
+      "patternProperties": {
+        "^(open_id_connect_url)$": {
+          "type": "string",
+          "description": "The OpenID Connect Discovery URL."
+        }
+      },
+      "properties": {
+        "description": {
+          "type": "string",
+          "description": "An optional description for the security scheme."
+        },
+        "openIdConnectUrl": {
+          "type": "string",
+          "description": "The OpenID Connect Discovery URL."
+        }
+      },
+      "additionalProperties": false
+    },
+    "MutualTlsSecurityScheme": {
+      "type": "object",
+      "description": "Defines a security scheme using mTLS authentication.",
+      "properties": {
+        "description": {
+          "type": "string",
+          "description": "An optional description for the security scheme."
+        }
+      },
+      "additionalProperties": false
+    },
+    "OAuthFlows": {
+      "type": "object",
+      "description": "Defines the configuration for the supported OAuth 2.0 
flows.",
+      "patternProperties": {
+        "^(authorization_code)$": {
+          "$ref": "#/definitions/AuthorizationCodeOAuthFlow",
+          "description": "Configuration for the OAuth Authorization Code flow."
+        },
+        "^(client_credentials)$": {
+          "$ref": "#/definitions/ClientCredentialsOAuthFlow",
+          "description": "Configuration for the OAuth Client Credentials flow."
+        },
+        "^(device_code)$": {
+          "$ref": "#/definitions/DeviceCodeOAuthFlow",
+          "description": "Configuration for the OAuth Device Code flow."
+        }
+      },
+      "properties": {
+        "authorizationCode": {
+          "$ref": "#/definitions/AuthorizationCodeOAuthFlow",
+          "description": "Configuration for the OAuth Authorization Code flow."
+        },
+        "clientCredentials": {
+          "$ref": "#/definitions/ClientCredentialsOAuthFlow",
+          "description": "Configuration for the OAuth Client Credentials flow."
+        },
+        "deviceCode": {
+          "$ref": "#/definitions/DeviceCodeOAuthFlow",
+          "description": "Configuration for the OAuth Device Code flow."
+        },
+        "implicit": {
+          "$ref": "#/definitions/ImplicitOAuthFlow",
+          "description": "Deprecated: Use Authorization Code + PKCE instead."
+        },
+        "password": {
+          "$ref": "#/definitions/PasswordOAuthFlow",
+          "description": "Deprecated: Use Authorization Code + PKCE or Device 
Code."
+        }
+      },
+      "additionalProperties": false
+    },
+    "AuthorizationCodeOAuthFlow": {
+      "type": "object",
+      "description": "Defines configuration details for the OAuth 2.0 
Authorization Code flow.",
+      "patternProperties": {
+        "^(authorization_url)$": {
+          "type": "string",
+          "description": "The authorization URL to be used for this flow."
+        },
+        "^(token_url)$": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "^(refresh_url)$": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        },
+        "^(pkce_required)$": {
+          "type": "boolean",
+          "description": "Indicates if PKCE (RFC 7636) is required for this 
flow."
+        }
+      },
+      "properties": {
+        "authorizationUrl": {
+          "type": "string",
+          "description": "The authorization URL to be used for this flow."
+        },
+        "tokenUrl": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "refreshUrl": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        },
+        "scopes": {
+          "type": "object",
+          "description": "The available scopes for the OAuth2 security 
scheme.",
+          "additionalProperties": { "type": "string" },
+          "propertyNames": { "type": "string" }
+        },
+        "pkceRequired": {
+          "type": "boolean",
+          "description": "Indicates if PKCE (RFC 7636) is required for this 
flow."
+        }
+      },
+      "additionalProperties": false
+    },
+    "ClientCredentialsOAuthFlow": {
+      "type": "object",
+      "description": "Defines configuration details for the OAuth 2.0 Client 
Credentials flow.",
+      "patternProperties": {
+        "^(token_url)$": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "^(refresh_url)$": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        }
+      },
+      "properties": {
+        "tokenUrl": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "refreshUrl": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        },
+        "scopes": {
+          "type": "object",
+          "description": "The available scopes for the OAuth2 security 
scheme.",
+          "additionalProperties": { "type": "string" },
+          "propertyNames": { "type": "string" }
+        }
+      },
+      "additionalProperties": false
+    },
+    "DeviceCodeOAuthFlow": {
+      "type": "object",
+      "description": "Defines configuration details for the OAuth 2.0 Device 
Code flow (RFC 8628).",
+      "patternProperties": {
+        "^(device_authorization_url)$": {
+          "type": "string",
+          "description": "The device authorization endpoint URL."
+        },
+        "^(token_url)$": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "^(refresh_url)$": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        }
+      },
+      "properties": {
+        "deviceAuthorizationUrl": {
+          "type": "string",
+          "description": "The device authorization endpoint URL."
+        },
+        "tokenUrl": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "refreshUrl": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        },
+        "scopes": {
+          "type": "object",
+          "description": "The available scopes for the OAuth2 security 
scheme.",
+          "additionalProperties": { "type": "string" },
+          "propertyNames": { "type": "string" }
+        }
+      },
+      "additionalProperties": false
+    },
+    "ImplicitOAuthFlow": {
+      "type": "object",
+      "description": "Deprecated: Use Authorization Code + PKCE instead.",
+      "patternProperties": {
+        "^(authorization_url)$": {
+          "type": "string",
+          "description": "The authorization URL to be used for this flow."
+        },
+        "^(refresh_url)$": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        }
+      },
+      "properties": {
+        "authorizationUrl": {
+          "type": "string",
+          "description": "The authorization URL to be used for this flow."
+        },
+        "refreshUrl": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        },
+        "scopes": {
+          "type": "object",
+          "description": "The available scopes for the OAuth2 security 
scheme.",
+          "additionalProperties": { "type": "string" },
+          "propertyNames": { "type": "string" }
+        }
+      },
+      "additionalProperties": false
+    },
+    "PasswordOAuthFlow": {
+      "type": "object",
+      "description": "Deprecated: Use Authorization Code + PKCE or Device 
Code.",
+      "patternProperties": {
+        "^(token_url)$": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "^(refresh_url)$": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        }
+      },
+      "properties": {
+        "tokenUrl": {
+          "type": "string",
+          "description": "The token URL to be used for this flow."
+        },
+        "refreshUrl": {
+          "type": "string",
+          "description": "The URL to be used for obtaining refresh tokens."
+        },
+        "scopes": {
+          "type": "object",
+          "description": "The available scopes for the OAuth2 security 
scheme.",
+          "additionalProperties": { "type": "string" },
+          "propertyNames": { "type": "string" }
+        }
+      },
+      "additionalProperties": false
+    }
+  }
+}
diff --git 
a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/a2a/A2ACardHttpHandler.java
 
b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/a2a/A2ACardHttpHandler.java
new file mode 100644
index 000000000..f905ee2b0
--- /dev/null
+++ 
b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/a2a/A2ACardHttpHandler.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.runtime.a2a;
+
+import org.apache.eventmesh.protocol.a2a.AgentIdentity;
+import org.apache.eventmesh.protocol.a2a.model.AgentCard;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.QueryStringDecoder;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * HTTP handler for A2A Agent Card Registry API.
+ * Matches EMQX's API pattern:
+ * - GET  /a2a/cards/list                           - list agent cards
+ * - GET  /a2a/cards/card/{org_id}/{unit_id}/{agent_id}  - get specific card
+ * - POST /a2a/cards/card/{org_id}/{unit_id}/{agent_id}  - register card
+ * - DELETE /a2a/cards/card/{org_id}/{unit_id}/{agent_id} - delete card
+ */
+@Slf4j
+public class A2ACardHttpHandler {
+
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+    private static final String PATH_LIST = "/a2a/cards/list";
+    private static final String PATH_CARD_PREFIX = "/a2a/cards/card/";
+
+    private final A2APublishSubscribeService a2aService;
+
+    public A2ACardHttpHandler(A2APublishSubscribeService a2aService) {
+        this.a2aService = a2aService;
+    }
+
+    public FullHttpResponse handle(HttpRequest httpRequest, 
ChannelHandlerContext ctx) throws Exception {
+        String uri = httpRequest.uri();
+        QueryStringDecoder decoder = new QueryStringDecoder(uri);
+        String path = decoder.path();
+
+        try {
+            if (path.equals(PATH_LIST)) {
+                return handleList(decoder);
+            }
+
+            if (path.startsWith(PATH_CARD_PREFIX)) {
+                String[] segments = 
path.substring(PATH_CARD_PREFIX.length()).split("/");
+                if (segments.length != 3) {
+                    return jsonResponse(HttpResponseStatus.BAD_REQUEST,
+                        errorBody("Invalid path. Expected 
/a2a/cards/card/{org_id}/{unit_id}/{agent_id}"));
+                }
+
+                String orgId = segments[0];
+                String unitId = segments[1];
+                String agentId = segments[2];
+
+                if (httpRequest.method() == HttpMethod.GET) {
+                    return handleGet(orgId, unitId, agentId);
+                } else if (httpRequest.method() == HttpMethod.POST) {
+                    return handleRegister(orgId, unitId, agentId, httpRequest);
+                } else if (httpRequest.method() == HttpMethod.DELETE) {
+                    return handleDelete(orgId, unitId, agentId);
+                }
+            }
+
+            return jsonResponse(HttpResponseStatus.NOT_FOUND, errorBody("Not 
found: " + path));
+        } catch (Exception e) {
+            log.error("A2A card handler error: {}", e.getMessage(), e);
+            return jsonResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR, 
errorBody(e.getMessage()));
+        }
+    }
+
+    private FullHttpResponse handleList(QueryStringDecoder decoder) throws 
Exception {
+        String orgId = getQueryParam(decoder, "org_id");
+        String unitId = getQueryParam(decoder, "unit_id");
+        String agentId = getQueryParam(decoder, "agent_id");
+
+        List<A2APublishSubscribeService.CardEntry> cards = 
a2aService.listCards(orgId, unitId, agentId);
+        String body = objectMapper.writeValueAsString(cards);
+        return jsonResponse(HttpResponseStatus.OK, body);
+    }
+
+    private FullHttpResponse handleGet(String orgId, String unitId, String 
agentId) throws Exception {
+        AgentIdentity identity = AgentIdentity.builder()
+            .orgId(orgId).unitId(unitId).agentId(agentId).build();
+        A2APublishSubscribeService.CardEntry card = 
a2aService.getCard(identity);
+
+        if (card == null) {
+            return jsonResponse(HttpResponseStatus.NOT_FOUND, errorBody("Card 
not found"));
+        }
+
+        String body = objectMapper.writeValueAsString(formatCardOut(card));
+        return jsonResponse(HttpResponseStatus.OK, body);
+    }
+
+    private FullHttpResponse handleRegister(String orgId, String unitId, 
String agentId, HttpRequest request) throws Exception {
+        // Read body from request
+        String requestBody = "";
+        if (request instanceof io.netty.handler.codec.http.FullHttpRequest) {
+            ByteBuf content = ((io.netty.handler.codec.http.FullHttpRequest) 
request).content();
+            requestBody = content.toString(StandardCharsets.UTF_8);
+        }
+
+        AgentCard card;
+        try {
+            card = objectMapper.readValue(requestBody, AgentCard.class);
+        } catch (Exception e) {
+            return jsonResponse(HttpResponseStatus.BAD_REQUEST, 
errorBody("Invalid card JSON: " + e.getMessage()));
+        }
+
+        AgentIdentity identity = AgentIdentity.builder()
+            .orgId(orgId).unitId(unitId).agentId(agentId).build();
+        A2APublishSubscribeService.RegistrationResult result = 
a2aService.registerCard(identity, card);
+
+        if (result.isSuccess()) {
+            return jsonResponse(HttpResponseStatus.NO_CONTENT, "");
+        } else {
+            return jsonResponse(HttpResponseStatus.BAD_REQUEST, 
errorBody(result.getErrorMessage()));
+        }
+    }
+
+    private FullHttpResponse handleDelete(String orgId, String unitId, String 
agentId) throws Exception {
+        AgentIdentity identity = AgentIdentity.builder()
+            .orgId(orgId).unitId(unitId).agentId(agentId).build();
+        boolean deleted = a2aService.deleteCard(identity);
+
+        if (deleted) {
+            return jsonResponse(HttpResponseStatus.NO_CONTENT, "");
+        } else {
+            return jsonResponse(HttpResponseStatus.NOT_FOUND, errorBody("Card 
not found"));
+        }
+    }
+
+    private Map<String, Object> 
formatCardOut(A2APublishSubscribeService.CardEntry entry) {
+        Map<String, Object> out = new LinkedHashMap<>();
+        out.put("namespace", entry.getNamespace());
+        out.put("id", entry.getId());
+        out.put("name", entry.getName());
+        out.put("version", entry.getVersion());
+        out.put("description", entry.getDescription());
+        out.put("status", entry.getStatus());
+        return out;
+    }
+
+    private String getQueryParam(QueryStringDecoder decoder, String key) {
+        List<String> values = decoder.parameters().get(key);
+        return (values != null && !values.isEmpty()) ? values.get(0) : null;
+    }
+
+    private String errorBody(String message) {
+        try {
+            return 
objectMapper.writeValueAsString(Collections.singletonMap("error", message));
+        } catch (Exception e) {
+            return "{\"error\":\"" + message + "\"}";
+        }
+    }
+
+    private FullHttpResponse jsonResponse(HttpResponseStatus status, String 
body) {
+        ByteBuf content = Unpooled.copiedBuffer(body, StandardCharsets.UTF_8);
+        DefaultFullHttpResponse response = new DefaultFullHttpResponse(
+            HttpVersion.HTTP_1_1, status, content);
+        response.headers().set(HttpHeaderNames.CONTENT_TYPE, 
"application/json; charset=utf-8");
+        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, 
content.readableBytes());
+        return response;
+    }
+}
diff --git 
a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/a2a/A2APublishSubscribeService.java
 
b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/a2a/A2APublishSubscribeService.java
new file mode 100644
index 000000000..6dab3f546
--- /dev/null
+++ 
b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/a2a/A2APublishSubscribeService.java
@@ -0,0 +1,352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.runtime.a2a;
+
+import org.apache.eventmesh.protocol.a2a.A2AProtocolConstants;
+import org.apache.eventmesh.protocol.a2a.AgentCardValidator;
+import org.apache.eventmesh.protocol.a2a.AgentIdentity;
+import org.apache.eventmesh.protocol.a2a.model.AgentCard;
+import org.apache.eventmesh.runtime.boot.EventMeshServer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.cloudevents.CloudEvent;
+import io.cloudevents.core.builder.CloudEventBuilder;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * A2APublishSubscribeService: Manages A2A Agent Card Registry and processes 
A2A events.
+ *
+ * <p>Features:
+ * - Agent Card CRUD (register, delete, get, list)
+ * - Agent status tracking (online/offline)
+ * - Event processing with status metadata augmentation
+ * - Hierarchical identity (org_id/unit_id/agent_id) with wildcard queries
+ */
+@Slf4j
+public class A2APublishSubscribeService {
+
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+
+    private final EventMeshServer eventMeshServer;
+    private volatile boolean isStarted = false;
+
+    private final ConcurrentHashMap<AgentIdentity, RegisteredCard> 
cardRegistry = new ConcurrentHashMap<>();
+    private final ConcurrentHashMap<String, AgentStatus> agentStatusMap = new 
ConcurrentHashMap<>();
+
+    private AgentCardValidator cardValidator;
+
+    public A2APublishSubscribeService(EventMeshServer eventMeshServer) {
+        this.eventMeshServer = eventMeshServer;
+    }
+
+    public void init() throws Exception {
+        this.cardValidator = new AgentCardValidator(true);
+        log.info("A2APublishSubscribeService initialized with Agent Card 
Registry.");
+    }
+
+    public void start() throws Exception {
+        isStarted = true;
+        log.info("A2APublishSubscribeService started.");
+    }
+
+    public void shutdown() throws Exception {
+        isStarted = false;
+        cardRegistry.clear();
+        agentStatusMap.clear();
+        log.info("A2APublishSubscribeService shutdown.");
+    }
+
+    // 
=========================================================================
+    // Agent Card Registry Operations
+    // 
=========================================================================
+
+    /**
+     * Registers an Agent Card. Validates the card and identity before storing.
+     *
+     * @param identity the agent identity (org_id/unit_id/agent_id)
+     * @param card     the agent card
+     * @return RegistrationResult with success/failure
+     */
+    public RegistrationResult registerCard(AgentIdentity identity, AgentCard 
card) {
+        if (!isStarted) {
+            throw new IllegalStateException("A2APublishSubscribeService is not 
started");
+        }
+
+        // Validate identity
+        if (!identity.isValid()) {
+            String msg = String.format("Invalid agent identity: orgId=%s, 
unitId=%s, agentId=%s",
+                identity.getOrgId(), identity.getUnitId(), 
identity.getAgentId());
+            log.warn(msg);
+            return RegistrationResult.failure(msg);
+        }
+
+        // Validate card
+        try {
+            String cardJson = objectMapper.writeValueAsString(card);
+            AgentCardValidator.ValidationResult result = 
cardValidator.validate(cardJson);
+            if (!result.isValid()) {
+                log.warn("Agent card schema validation failed for {}: {}", 
identity.clientId(), result.getErrorMessage());
+                return RegistrationResult.failure(result.getErrorMessage());
+            }
+        } catch (Exception e) {
+            log.warn("Failed to serialize/validate agent card for {}: {}", 
identity.clientId(), e.getMessage());
+            return RegistrationResult.failure("Card validation error: " + 
e.getMessage());
+        }
+
+        RegisteredCard existing = cardRegistry.put(identity, new 
RegisteredCard(card, System.currentTimeMillis()));
+        if (existing != null) {
+            log.info("Updated agent card for {}", identity.clientId());
+        } else {
+            log.info("Registered new agent card for {}", identity.clientId());
+        }
+
+        // Set agent online
+        agentStatusMap.put(identity.clientId(), AgentStatus.ONLINE);
+
+        return RegistrationResult.success();
+    }
+
+    /**
+     * Deletes an Agent Card from the registry.
+     */
+    public boolean deleteCard(AgentIdentity identity) {
+        if (!isStarted) {
+            throw new IllegalStateException("A2APublishSubscribeService is not 
started");
+        }
+        RegisteredCard removed = cardRegistry.remove(identity);
+        if (removed != null) {
+            agentStatusMap.remove(identity.clientId());
+            log.info("Deleted agent card for {}", identity.clientId());
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Gets a specific Agent Card.
+     */
+    public CardEntry getCard(AgentIdentity identity) {
+        if (!isStarted) {
+            throw new IllegalStateException("A2APublishSubscribeService is not 
started");
+        }
+        RegisteredCard rc = cardRegistry.get(identity);
+        if (rc == null) {
+            return null;
+        }
+        return new CardEntry(identity, rc.card, lookupAgentStatus(identity));
+    }
+
+    /**
+     * Lists Agent Cards matching the given filters. Use null or "+" for 
wildcard matching.
+     */
+    public List<CardEntry> listCards(String orgId, String unitId, String 
agentId) {
+        if (!isStarted) {
+            throw new IllegalStateException("A2APublishSubscribeService is not 
started");
+        }
+        List<CardEntry> results = new ArrayList<>();
+        for (Map.Entry<AgentIdentity, RegisteredCard> entry : 
cardRegistry.entrySet()) {
+            AgentIdentity id = entry.getKey();
+            if (id.matchesFilter(orgId, unitId, agentId)) {
+                results.add(new CardEntry(id, entry.getValue().card, 
lookupAgentStatus(id)));
+            }
+        }
+        return results;
+    }
+
+    /**
+     * Lists all registered Agent Cards.
+     */
+    public List<CardEntry> listAllCards() {
+        return listCards(null, null, null);
+    }
+
+    // 
=========================================================================
+    // Agent Status
+    // 
=========================================================================
+
+    /**
+     * Looks up the status of an agent (online/offline).
+     */
+    public String lookupAgentStatus(AgentIdentity identity) {
+        AgentStatus status = agentStatusMap.get(identity.clientId());
+        return status != null ? status.value : 
A2AProtocolConstants.STATUS_OFFLINE;
+    }
+
+    /**
+     * Sets the status of an agent.
+     */
+    public void setAgentStatus(AgentIdentity identity, String status) {
+        if (A2AProtocolConstants.STATUS_ONLINE.equals(status)) {
+            agentStatusMap.put(identity.clientId(), AgentStatus.ONLINE);
+        } else {
+            agentStatusMap.put(identity.clientId(), AgentStatus.OFFLINE);
+        }
+    }
+
+    // 
=========================================================================
+    // Event Processing
+    // 
=========================================================================
+
+    /**
+     * Processes an A2A CloudEvent. Augments events with agent status metadata.
+     *
+     * @param event The CloudEvent to process.
+     * @return The processed (potentially modified) CloudEvent.
+     */
+    public CloudEvent process(CloudEvent event) {
+        if (!isStarted) {
+            throw new IllegalStateException("A2APublishSubscribeService is not 
started");
+        }
+
+        log.debug("Processing A2A event: {}", event.getId());
+
+        // Check if this is an A2A discovery topic event
+        String subject = event.getSubject();
+        if (subject != null && 
subject.startsWith(A2AProtocolConstants.TOPIC_NAMESPACE + "/")) {
+            AgentIdentity identity = AgentIdentity.fromDiscoveryTopic(subject);
+            if (identity != null) {
+                return augmentWithStatusMetadata(event, identity);
+            }
+        }
+
+        return event;
+    }
+
+    /**
+     * Augments a CloudEvent with agent status metadata (a2astatus, 
a2astatussource extensions).
+     * Matches EMQX's on_message_delivered hook behavior.
+     */
+    private CloudEvent augmentWithStatusMetadata(CloudEvent event, 
AgentIdentity identity) {
+        String status = lookupAgentStatus(identity);
+        CloudEventBuilder builder = CloudEventBuilder.from(event);
+        builder.withExtension(A2AProtocolConstants.CE_EXTENSION_A2A_STATUS, 
status);
+        
builder.withExtension(A2AProtocolConstants.CE_EXTENSION_A2A_STATUS_SOURCE, 
"eventmesh");
+        return builder.build();
+    }
+
+    // 
=========================================================================
+    // Inner Types
+    // 
=========================================================================
+
+    private enum AgentStatus {
+        ONLINE(A2AProtocolConstants.STATUS_ONLINE),
+        OFFLINE(A2AProtocolConstants.STATUS_OFFLINE);
+
+        final String value;
+
+        AgentStatus(String value) {
+            this.value = value;
+        }
+    }
+
+    private static class RegisteredCard {
+
+        final AgentCard card;
+        final long registeredAt;
+
+        RegisteredCard(AgentCard card, long registeredAt) {
+            this.card = card;
+            this.registeredAt = registeredAt;
+        }
+    }
+
+    /**
+     * Represents a registered agent card entry with identity, card data, and 
status.
+     */
+    public static class CardEntry {
+
+        private final AgentIdentity identity;
+        private final AgentCard card;
+        private final String status;
+
+        public CardEntry(AgentIdentity identity, AgentCard card, String 
status) {
+            this.identity = identity;
+            this.card = card;
+            this.status = status;
+        }
+
+        public AgentIdentity getIdentity() {
+            return identity;
+        }
+
+        public AgentCard getCard() {
+            return card;
+        }
+
+        public String getStatus() {
+            return status;
+        }
+
+        public String getNamespace() {
+            return identity.getNamespace();
+        }
+
+        public String getId() {
+            return identity.clientId();
+        }
+
+        public String getName() {
+            return card.getName();
+        }
+
+        public String getVersion() {
+            return card.getVersion();
+        }
+
+        public String getDescription() {
+            return card.getDescription();
+        }
+    }
+
+    /**
+     * Result of a card registration attempt.
+     */
+    public static class RegistrationResult {
+
+        private final boolean success;
+        private final String errorMessage;
+
+        private RegistrationResult(boolean success, String errorMessage) {
+            this.success = success;
+            this.errorMessage = errorMessage;
+        }
+
+        public static RegistrationResult success() {
+            return new RegistrationResult(true, null);
+        }
+
+        public static RegistrationResult failure(String errorMessage) {
+            return new RegistrationResult(false, errorMessage);
+        }
+
+        public boolean isSuccess() {
+            return success;
+        }
+
+        public String getErrorMessage() {
+            return errorMessage;
+        }
+    }
+}
diff --git 
a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/boot/FilterEngineTest.java
 
b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/boot/FilterEngineTest.java
new file mode 100644
index 000000000..ccfd994fb
--- /dev/null
+++ 
b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/boot/FilterEngineTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.runtime.boot;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.when;
+
+import org.apache.eventmesh.function.filter.pattern.Pattern;
+import 
org.apache.eventmesh.runtime.core.protocol.http.consumer.ConsumerManager;
+import org.apache.eventmesh.runtime.core.protocol.producer.ProducerManager;
+import org.apache.eventmesh.runtime.meta.MetaStorage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class FilterEngineTest {
+
+    @Mock
+    private MetaStorage metaStorage;
+
+    @Mock
+    private ProducerManager producerManager;
+
+    @Mock
+    private ConsumerManager consumerManager;
+
+    @Test
+    public void testStartAndGetFilter() {
+        FilterEngine filterEngine = new FilterEngine(metaStorage, 
producerManager, consumerManager);
+
+        // Mock MetaData
+        Map<String, String> filterMetaData = new HashMap<>();
+        String group = "testGroup";
+        // JSON config for filter
+        // Condition: source == "testSource" (must be array)
+        String filterJson = "[{\"topic\":\"testTopic\", 
\"condition\":{\"source\":[\"testSource\"]}}]";
+        filterMetaData.put("filter-" + group, filterJson);
+
+        when(metaStorage.getMetaData(any(String.class), 
anyBoolean())).thenReturn(filterMetaData);
+
+        // Start Engine
+        filterEngine.start();
+
+        // Get Filter
+        Pattern pattern = filterEngine.getFilterPattern(group + "-testTopic");
+        Assertions.assertNotNull(pattern);
+
+        // Verify Filter behavior (optional, depends on Pattern implementation)
+        // String validEventJson = 
"{"specversion":"1.0","id":"1","source":"testSource","type":"testType"}";
+        // Assertions.assertTrue(pattern.filter(validEventJson));
+    }
+}
diff --git 
a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/boot/TransformerEngineTest.java
 
b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/boot/TransformerEngineTest.java
new file mode 100644
index 000000000..7e436457f
--- /dev/null
+++ 
b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/boot/TransformerEngineTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+package org.apache.eventmesh.runtime.boot;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.when;
+
+import org.apache.eventmesh.function.transformer.Transformer;
+import 
org.apache.eventmesh.runtime.core.protocol.http.consumer.ConsumerManager;
+import org.apache.eventmesh.runtime.core.protocol.producer.ProducerManager;
+import org.apache.eventmesh.runtime.meta.MetaStorage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class TransformerEngineTest {
+
+    @Mock
+    private MetaStorage metaStorage;
+
+    @Mock
+    private ProducerManager producerManager;
+
+    @Mock
+    private ConsumerManager consumerManager;
+
+    @Test
+    public void testStartAndGetTransformer() throws Exception {
+        TransformerEngine transformerEngine = new 
TransformerEngine(metaStorage, producerManager, consumerManager);
+
+        // Mock MetaData
+        Map<String, String> transformerMetaData = new HashMap<>();
+        String group = "testGroup";
+        
+        // JSON config for transformer
+        // Use "original" which passes through
+        String transformerJson = "[{\"topic\":\"testTopic\", 
\"transformerParam\":{\"transformerType\":\"original\"}}]";
+        transformerMetaData.put("transformer-" + group, transformerJson);
+
+        when(metaStorage.getMetaData(any(String.class), 
anyBoolean())).thenReturn(transformerMetaData);
+
+        // Start Engine
+        transformerEngine.start();
+
+        // Get Transformer
+        Transformer transformer = transformerEngine.getTransformer(group + 
"-testTopic");
+        Assertions.assertNotNull(transformer);
+        
+        // Verify transform (original returns content as is)
+        String content = "testContent";
+        Assertions.assertEquals(content, transformer.transform(content));
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to