# Getting Started (/aiway/a2a/getting-started)



The A2A connector turns any HTTP server into a callable agent. You register an agent's
URL with the gateway, then route JSON-RPC `message/send` requests to it through
`POST /a2a/<agent_id>` — no KubeMQ SDK on the agent. This walkthrough takes you from a
running server to a verified round-trip.

## Prerequisites [#prerequisites]

* A running **kubemq-server** with the shared HTTP server reachable on **port 9090**.
* An HTTP endpoint to register as your agent. For this guide, run one of the example
  echo agents from `.kb/integration-a2a/examples` (each agent registers itself on
  startup and echoes the request body back).
* `curl` (or one of the language clients below) to send the message.

Confirm the gateway is live by fetching the platform agent card:

```bash
curl http://localhost:9090/.well-known/agent-card.json
```

## Enable / disable [#enable--disable]

The A2A connector is **enabled by default**. Start kubemq-server and the `/a2a/*` and
`/agents/*` routes are live immediately — there is **no `=true` flag to set**.

To **disable** A2A, set its enable variable to `false`:

<RunKubeMQ variant="disable" env="{ CONNECTORSA2_A_ENABLE: 'false' }" />

<Callout type="info">
  The disable variable name is irregular by design — the config key `Connectors.A2A.Enable`
  snake-cases to `CONNECTORSA2_A_ENABLE`, not `KUBEMQ_A2A_ENABLE`. See
  [Shared HTTP server](/connectors/concepts/shared-http-server) for why, and never rely on the
  older off-by-default behavior.
</Callout>

## How it works [#how-it-works]

A `message/send` request travels from the caller to the gateway, across the broker to the
agent's virtual subscriber, out to the agent over HTTP, and back the same way — the
gateway relays the agent's JSON-RPC response to the caller unchanged.

<Mermaid
  chart="`
sequenceDiagram
participant C as Caller
participant G as A2A gateway<br/>:9090
participant V as Virtual subscriber
participant A as Agent<br/>HTTP server
C->>G: POST /a2a/echo-agent-01<br/>message/send
G->>V: Query<br/>_AGENTS_.agents/echo-agent-01
V->>A: HTTP POST (JSON-RPC 2.0)
A-->>V: JSON-RPC result
V-->>G: broker reply
G-->>C: JSON-RPC response
`"
/>

*The gateway proxies the request to the agent over the broker and relays the reply unchanged.*

## Steps [#steps]

<Steps>
  <Step>
    ### Register an agent [#register-an-agent]

    An agent registers itself by POSTing its **agent card** — including the absolute
    `url` the gateway will call — to `POST /agents/register`. The example echo agents do
    this on startup; the snippets below show the registration call.

    <Tabs groupId="language" items="['curl','C#','Go','Java','Python','TypeScript']">
      <Tab value="curl">
        ```bash
        curl -X POST http://localhost:9090/agents/register \
          -H "Content-Type: application/json" \
          -d '{
            "agent_id": "echo-agent-01",
            "name": "Echo Agent",
            "description": "A simple echo agent for testing",
            "version": "1.0.0",
            "url": "http://localhost:18080/",
            "skills": [
              {"id": "echo", "name": "Echo", "description": "Echoes back the received message", "tags": ["test", "echo"]}
            ],
            "defaultInputModes": ["text"],
            "defaultOutputModes": ["text"],
            "protocolVersions": ["1.0"]
          }'
        ```
      </Tab>

      <Tab value="C#">
        ```csharp
        var card = new JsonObject
        {
            ["agent_id"] = "echo-agent-01",
            ["name"] = "Echo Agent",
            ["description"] = "A simple echo agent for testing",
            ["version"] = "1.0.0",
            ["url"] = "http://localhost:18080/",
            ["skills"] = new JsonArray(new JsonObject
            {
                ["id"] = "echo", ["name"] = "Echo",
                ["description"] = "Echoes back the received message",
                ["tags"] = new JsonArray("test", "echo")
            }),
            ["defaultInputModes"] = new JsonArray("text"),
            ["defaultOutputModes"] = new JsonArray("text"),
            ["protocolVersions"] = new JsonArray("1.0")
        };

        using var client = new HttpClient();
        var resp = await client.PostAsync(
            "http://localhost:9090/agents/register",
            new StringContent(card.ToJsonString(), System.Text.Encoding.UTF8, "application/json"));
        Console.WriteLine($"Registered: {(int)resp.StatusCode}");
        ```
      </Tab>

      <Tab value="Go">
        ```go
        card := map[string]interface{}{
            "agent_id":    "echo-agent-01",
            "name":        "Echo Agent",
            "description": "A simple echo agent for testing",
            "version":     "1.0.0",
            "url":         "http://localhost:18080/",
            "skills": []map[string]interface{}{
                {
                    "id":          "echo",
                    "name":        "Echo",
                    "description": "Echoes back the received message",
                    "tags":        []string{"test", "echo"},
                },
            },
            "defaultInputModes":  []string{"text"},
            "defaultOutputModes": []string{"text"},
            "protocolVersions":   []string{"1.0"},
        }
        data, _ := json.Marshal(card)
        resp, err := http.Post("http://localhost:9090/agents/register", "application/json", bytes.NewReader(data))
        if err != nil {
            fmt.Fprintf(os.Stderr, "Registration failed: %v\n", err)
            return
        }
        defer resp.Body.Close()
        fmt.Printf("Registered: %d\n", resp.StatusCode)
        ```
      </Tab>

      <Tab value="Java">
        ```java
        var card = Map.of(
            "agent_id", "echo-agent-01",
            "name", "Echo Agent",
            "description", "A simple echo agent for testing",
            "version", "1.0.0",
            "url", "http://localhost:18080/",
            "skills", List.of(Map.of(
                "id", "echo", "name", "Echo",
                "description", "Echoes back the received message",
                "tags", List.of("test", "echo")
            )),
            "defaultInputModes", List.of("text"),
            "defaultOutputModes", List.of("text"),
            "protocolVersions", List.of("1.0")
        );

        var client = HttpClient.newHttpClient();
        var req = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:9090/agents/register"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(MAPPER.writeValueAsString(card)))
            .build();
        var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println("Registered: " + resp.statusCode());
        ```
      </Tab>

      <Tab value="Python">
        ```python
        card = {
            "agent_id": "echo-agent-01",
            "name": "Echo Agent",
            "description": "A simple echo agent for testing",
            "version": "1.0.0",
            "url": "http://localhost:18080/",
            "skills": [
                {
                    "id": "echo",
                    "name": "Echo",
                    "description": "Echoes back the received message",
                    "tags": ["test", "echo"],
                }
            ],
            "defaultInputModes": ["text"],
            "defaultOutputModes": ["text"],
            "protocolVersions": ["1.0"],
        }
        async with httpx.AsyncClient() as client:
            resp = await client.post("http://localhost:9090/agents/register", json=card)
            print(f"Registered: {resp.status_code}")
        ```
      </Tab>

      <Tab value="TypeScript">
        ```typescript
        const card = {
          agent_id: "echo-agent-01",
          name: "Echo Agent",
          description: "A simple echo agent for testing",
          version: "1.0.0",
          url: "http://localhost:18080/",
          skills: [
            {
              id: "echo",
              name: "Echo",
              description: "Echoes back the received message",
              tags: ["test", "echo"],
            },
          ],
          defaultInputModes: ["text"],
          defaultOutputModes: ["text"],
          protocolVersions: ["1.0"],
        };

        const resp = await fetch("http://localhost:9090/agents/register", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(card),
        });
        console.log("Registered:", resp.status);
        ```
      </Tab>
    </Tabs>

    A `200` response means the agent is registered. The gateway returns the stored card
    with server-managed `registered_at` and `last_seen` fields populated.
  </Step>

  <Step>
    ### Send a message [#send-a-message]

    Route a JSON-RPC 2.0 `message/send` request to the agent through
    `POST /a2a/<agent_id>`. The gateway forwards it to the agent and relays the reply back.

    <Tabs groupId="language" items="['curl','C#','Go','Java','Python','TypeScript']">
      <Tab value="curl">
        ```bash
        curl -X POST http://localhost:9090/a2a/echo-agent-01 \
          -H "Content-Type: application/json" \
          -d '{
            "jsonrpc": "2.0",
            "id": 1,
            "method": "message/send",
            "params": {
              "message": {
                "parts": [{"text": "Hello, agent!"}]
              }
            }
          }'
        ```
      </Tab>

      <Tab value="C#">
        ```csharp
        var payload = new JsonObject
        {
            ["jsonrpc"] = "2.0",
            ["id"] = 1,
            ["method"] = "message/send",
            ["params"] = new JsonObject
            {
                ["message"] = new JsonObject
                {
                    ["parts"] = new JsonArray(new JsonObject { ["text"] = "Hello, agent!" })
                }
            }
        };

        using var client = new HttpClient();
        var resp = await client.PostAsync(
            "http://localhost:9090/a2a/echo-agent-01",
            new StringContent(payload.ToJsonString(), Encoding.UTF8, "application/json"));

        Console.WriteLine($"Status: {(int)resp.StatusCode}");
        var body = await resp.Content.ReadAsStringAsync();
        var data = JsonNode.Parse(body)!;
        Console.WriteLine(JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }));
        ```
      </Tab>

      <Tab value="Go">
        ```go
        payload := map[string]interface{}{
            "jsonrpc": "2.0",
            "id":      1,
            "method":  "message/send",
            "params": map[string]interface{}{
                "message": map[string]interface{}{
                    "parts": []map[string]interface{}{{"text": "Hello, agent!"}},
                },
            },
        }

        data, _ := json.Marshal(payload)
        resp, err := http.Post("http://localhost:9090/a2a/echo-agent-01", "application/json", bytes.NewReader(data))
        if err != nil {
            fmt.Fprintf(os.Stderr, "Request failed: %v\n", err)
            os.Exit(1)
        }
        defer resp.Body.Close()

        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("Status: %d\n", resp.StatusCode)
        fmt.Println(string(body))
        ```
      </Tab>

      <Tab value="Java">
        ```java
        var payload = Map.of(
            "jsonrpc", "2.0",
            "id", 1,
            "method", "message/send",
            "params", Map.of(
                "message", Map.of(
                    "parts", List.of(Map.of("text", "Hello, agent!"))
                )
            )
        );

        var client = HttpClient.newHttpClient();
        var req = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:9090/a2a/echo-agent-01"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(MAPPER.writeValueAsString(payload)))
            .build();
        var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println("Status: " + resp.statusCode());

        var data = MAPPER.readTree(resp.body());
        System.out.println(MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(data));
        ```
      </Tab>

      <Tab value="Python">
        ```python
        payload = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "message/send",
            "params": {
                "message": {
                    "parts": [{"text": "Hello, agent!"}],
                },
            },
        }

        async with httpx.AsyncClient() as client:
            resp = await client.post("http://localhost:9090/a2a/echo-agent-01", json=payload)
            print(f"Status: {resp.status_code}")
            data = resp.json()
            print(json.dumps(data, indent=2))
            assert "result" in data
        ```
      </Tab>

      <Tab value="TypeScript">
        ```typescript
        const request = {
          jsonrpc: "2.0",
          id: 1,
          method: "message/send",
          params: {
            message: {
              parts: [{ text: "Hello, agent!" }],
            },
          },
        };

        const resp = await fetch("http://localhost:9090/a2a/echo-agent-01", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(request),
        });

        const data = await resp.json();
        console.log("Response:", JSON.stringify(data, null, 2));
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Verify the reply [#verify-the-reply]

    The echo agent returns the full request body inside `result.echo`, confirming the
    round-trip through the gateway:

    ```json
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "echo": {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "message/send",
          "params": {
            "message": { "parts": [{ "text": "Hello, agent!" }] }
          }
        }
      }
    }
    ```

    A response containing `result` is a successful round-trip. A response with an `error`
    object instead means the gateway or agent reported a problem — see
    [Error handling](/aiway/a2a/error-handling) for the codes and how to
    distinguish transport failures from application errors.
  </Step>
</Steps>

## What's next [#whats-next]

<Cards>
  <Card title="Configuration" href="/aiway/a2a/configuration" description="Tune timeouts, concurrency limits, TTL, and the agent response-size cap." />

  <Card title="Synchronous messaging" href="/aiway/a2a/sync-messaging" description="Context IDs, custom methods, header forwarding, and concurrent requests." />

  <Card title="Agent registry" href="/aiway/a2a/registry" description="Register, list, heartbeat, and deregister agents over the REST management API." />

  <Card title="Building agents" href="/aiway/a2a/guides/building-agents" description="Write a compliant HTTP agent server — no KubeMQ SDK required." />
</Cards>
