# Reference (/aiway/a2a/reference)



The complete API surface of the KubeMQ A2A connector: HTTP endpoints, the JSON-RPC 2.0 wire format, the agent card schema, internal channels, configuration fields, error codes, and Prometheus metrics. The connector is a transparent JSON-RPC proxy — agents are plain HTTP servers registered by URL, reached through a per-agent virtual subscriber over the broker.

All HTTP endpoints are served on the [shared HTTP server](/connectors/concepts/shared-http-server) (default port `9090`), which also hosts the REST, MCP, and CloudEvents connectors. Prometheus metrics are exposed separately on port `8080` (see [Observability](/connectors/concepts/observability)).

## HTTP Endpoints [#http-endpoints]

### A2A endpoints [#a2a-endpoints]

| Method | Path                     | Description                           | Request body         | Success                       | Error                  |
| ------ | ------------------------ | ------------------------------------- | -------------------- | ----------------------------- | ---------------------- |
| POST   | `/a2a/{agent_id}`        | JSON-RPC 2.0 request (sync or stream) | JSON-RPC 2.0 payload | JSON-RPC result or SSE stream | JSON-RPC error         |
| GET    | `/a2a/{agent_id}`        | Not supported                         | —                    | —                             | 405 Method Not Allowed |
| GET    | `/a2a/{agent_id}/stream` | SSE stream via GET                    | —                    | `text/event-stream`           | —                      |

`POST /a2a/{agent_id}` is the primary endpoint. Its behavior depends on the JSON-RPC `method`: `message/stream` opens an SSE proxy; every other method is forwarded synchronously to the agent. `GET /a2a/{agent_id}` always returns **405** — use POST for JSON-RPC or `GET /a2a/{agent_id}/stream` for a server-pushed stream.

```bash
# Synchronous message/send
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!"}]}}}'

# SSE stream via GET
curl -N http://localhost:9090/a2a/echo-agent-01/stream \
  -H 'Accept: text/event-stream'
```

### Registry endpoints [#registry-endpoints]

Standard HTTP JSON responses (not JSON-RPC). See [Agent registry](/aiway/a2a/registry) for the full workflow.

| Method | Path                 | Description                                   | Request body       | Success                               | Error           |
| ------ | -------------------- | --------------------------------------------- | ------------------ | ------------------------------------- | --------------- |
| POST   | `/agents/register`   | Register an agent                             | Agent card JSON    | 200 + enriched card                   | 400 / 403 / 409 |
| GET    | `/agents`            | List agents (filter by `skill_tags`, `limit`) | —                  | 200 + `[<AgentCard>, …]` (bare array) | —               |
| GET    | `/agents/{agent_id}` | Get one agent                                 | —                  | 200 + agent card                      | 404             |
| POST   | `/agents/deregister` | Deregister (JSON body)                        | `{"agent_id":"…"}` | 200 + `{"ok":true}`                   | 404             |
| DELETE | `/agents/{agent_id}` | Deregister (REST, backward compat)            | —                  | 200 + `{"ok":true}`                   | 404             |
| POST   | `/agents/heartbeat`  | Refresh liveness                              | `{"agent_id":"…"}` | 200 + `{"ok":true}`                   | 400             |

`POST /agents/register` errors: **400** (card validation), **403** (ownership conflict — a different principal owns this `agent_id`), **409** (`MaxAgents` limit reached). `registered_by` is set automatically from the caller's JWT claims.

```bash
# List agents tagged "search" or "nlp", capped at 10
curl 'http://localhost:9090/agents?skill_tags=search,nlp&limit=10'
```

### Agent card endpoints [#agent-card-endpoints]

| Method | Path                                          | Description                           | Success                | Error |
| ------ | --------------------------------------------- | ------------------------------------- | ---------------------- | ----- |
| GET    | `/.well-known/agent-card.json`                | Platform agent card (KubeMQ metadata) | 200 (`name: "kubemq"`) | —     |
| GET    | `/a2a/{agent_id}/.well-known/agent-card.json` | Individual agent card from registry   | 200 + enriched card    | 404   |

Both `.well-known/agent-card.json` paths are public — they bypass authentication. See [Agent cards](/aiway/a2a/agent-cards).

```bash
# Platform card — confirms the A2A connector is reachable
curl http://localhost:9090/.well-known/agent-card.json
```

## JSON-RPC 2.0 wire format [#json-rpc-20-wire-format]

The connector uses JSON-RPC 2.0 for all client-to-agent communication. A request carries three required fields plus an optional `params` object.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "message/send",
  "params": {
    "message": { "parts": [{ "text": "Hello, agent!" }] },
    "contextId": "optional-correlation-id",
    "configuration": { "timeout": 30 }
  }
}
```

### Request fields [#request-fields]

| Field                          | Type              | Required | Description                                                                                       |
| ------------------------------ | ----------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `jsonrpc`                      | string            | Yes      | Must be `"2.0"`                                                                                   |
| `id`                           | integer or string | Yes      | Request identifier, echoed in the response                                                        |
| `method`                       | string            | Yes      | JSON-RPC method name (missing/empty → `-32600`)                                                   |
| `params`                       | object            | No       | Method parameters                                                                                 |
| `params.message.parts`         | array             | No       | Message content; each part has a `text` field                                                     |
| `params.contextId`             | string            | No       | Correlation ID, passed to the agent unmodified                                                    |
| `params.configuration.timeout` | number            | No       | Request timeout in seconds (falls back to `DefaultTimeoutSeconds`, capped at `MaxTimeoutSeconds`) |

### Methods [#methods]

KubeMQ is method-agnostic — it forwards any method to the agent unchanged. The five methods below are recognized for metrics labeling; all others are recorded as `method="unknown"`.

| Method               | Behavior                                    | Response            |
| -------------------- | ------------------------------------------- | ------------------- |
| `message/send`       | Synchronous proxy to the agent              | JSON-RPC response   |
| `message/stream`     | SSE stream proxy via the virtual subscriber | `text/event-stream` |
| `tasks/get`          | Forwarded to the agent                      | JSON-RPC response   |
| `tasks/cancel`       | Forwarded to the agent                      | JSON-RPC response   |
| `tasks/send`         | Forwarded to the agent                      | JSON-RPC response   |
| *(any other method)* | Forwarded to the agent                      | JSON-RPC response   |

<Callout type="info">
  JSON-RPC batch requests (an array of request objects) are **not supported** — send individual requests. Notifications (requests without an `id`) are forwarded to the agent but receive no response.
</Callout>

### Success and error responses [#success-and-error-responses]

On success, the agent's return value is placed verbatim in `result` — KubeMQ does not modify the payload. On failure, the response carries an `error` object instead.

```json
{ "jsonrpc": "2.0", "id": 1, "result": { "…": "agent output, unmodified" } }
```

```json
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32002, "message": "agent not found: nonexistent-agent" } }
```

## Agent card schema [#agent-card-schema]

The `AgentCard` is submitted on registration and returned, enriched with server-managed timestamps, from the list/get/well-known endpoints. Required fields are `agent_id`, `name`, and `url`.

| Field                 | JSON key              | Type      | Required | Description                                                                |
| --------------------- | --------------------- | --------- | -------- | -------------------------------------------------------------------------- |
| `AgentID`             | `agent_id`            | string    | Yes      | Unique ID; 2–128 chars, `^[a-z0-9][a-z0-9-]{0,126}[a-z0-9]$`               |
| `Name`                | `name`                | string    | Yes      | Human-readable name (max 256 chars)                                        |
| `Description`         | `description`         | string    | No       | Agent description (max 2048 chars)                                         |
| `Version`             | `version`             | string    | No       | Agent version (max 64 chars)                                               |
| `URL`                 | `url`                 | string    | Yes      | Absolute `http://` or `https://` URL (max 2048 chars)                      |
| `RegisteredBy`        | `registered_by`       | string    | No       | JWT principal that registered the agent (server-set)                       |
| `Capabilities`        | `capabilities`        | object    | No       | Free-form capability map                                                   |
| `Skills`              | `skills`              | array     | No       | List of `AgentSkill` (see below)                                           |
| `DefaultInputModes`   | `defaultInputModes`   | string\[] | No       | Default input modes                                                        |
| `DefaultOutputModes`  | `defaultOutputModes`  | string\[] | No       | Default output modes                                                       |
| `SupportedInterfaces` | `supportedInterfaces` | JSON      | No       | Opaque JSON                                                                |
| `SecuritySchemes`     | `securitySchemes`     | JSON      | No       | Opaque JSON                                                                |
| `Security`            | `security`            | JSON      | No       | Opaque JSON                                                                |
| `ProtocolVersions`    | `protocolVersions`    | string\[] | No       | Supported versions (default `["1.0"]`)                                     |
| `Metadata`            | `metadata`            | object    | No       | Key-value metadata                                                         |
| `LastSeen`            | `last_seen`           | timestamp | —        | Last heartbeat/registration (server-set)                                   |
| `RegisteredAt`        | `registered_at`       | timestamp | —        | Original registration time, preserved across re-registrations (server-set) |

### AgentSkill [#agentskill]

| Field         | JSON key      | Type      | Required | Description                                 |
| ------------- | ------------- | --------- | -------- | ------------------------------------------- |
| `ID`          | `id`          | string    | Yes      | Skill identifier                            |
| `Name`        | `name`        | string    | Yes      | Skill name                                  |
| `Description` | `description` | string    | No       | Skill description                           |
| `Tags`        | `tags`        | string\[] | No       | Skill tags, used by the `skill_tags` filter |

```json
{
  "agent_id": "echo-agent-01",
  "name": "Echo Agent",
  "url": "http://localhost:18080/",
  "skills": [{ "id": "echo", "name": "Echo", "tags": ["test", "echo"] }]
}
```

## Internal channel map [#internal-channel-map]

The connector and registry use the reserved `_AGENTS_.` prefix internally. User channels carrying this prefix are rejected (`IsReservedChannel`). Callers never address these channels directly — the connector and virtual subscriber own them.

| Channel pattern               | Purpose                                                                          | Transport    |
| ----------------------------- | -------------------------------------------------------------------------------- | ------------ |
| `_AGENTS_.agents/{agent_id}`  | Request/reply to an agent via its virtual subscriber (including `stream_cancel`) | Query        |
| `_AGENTS_.stream/{stream_id}` | Temporary SSE stream events relayed by the virtual subscriber                    | Events       |
| `_AGENTS_.discovery`          | Registry replication across cluster nodes                                        | Events Store |

Caller HTTP headers are forwarded to the agent as `a2a_hdr_*` tags on the broker request; the virtual subscriber always sets `X-KubeMQ-Caller-ID` to the original caller's identity. See [Architecture](/aiway/a2a/architecture).

## SSE event types [#sse-event-types]

Streaming responses (`message/stream` or `GET /a2a/{agent_id}/stream`) are delivered as Server-Sent Events. Each event has an `event:` line and a `data:` line carrying the stream envelope. A keepalive comment (`: keepalive`) is sent every 30 seconds; the idle timeout is `MaxSSEIdleSeconds` (default 300s). See [Streaming](/aiway/a2a/streaming).

| SSE event       | Envelope `type` | Description             | Terminal |
| --------------- | --------------- | ----------------------- | -------- |
| `task.status`   | `status_update` | Progress update         | No       |
| `task.artifact` | `artifact`      | Intermediate artifact   | No       |
| `task.done`     | `done`          | Successful completion   | Yes      |
| `task.error`    | `error`         | Failure                 | Yes      |
| `message`       | *(default)*     | Any other envelope type | No       |

```text
event: task.status
data: {"stream_id":"…","type":"status_update","payload":{"status":"working"}}

event: task.done
data: {"stream_id":"…","type":"done","payload":{"final_result":"completed"}}
```

## Error codes [#error-codes]

The connector returns standard JSON-RPC 2.0 base codes plus four A2A-specific codes. See [Error handling](/aiway/a2a/error-handling) for the transport-vs-application distinction and retry guidance.

| Code     | Name                   | Trigger                                                                   |
| -------- | ---------------------- | ------------------------------------------------------------------------- |
| `-32700` | Parse Error            | Malformed JSON body, or `Content-Type` is not `application/json`          |
| `-32600` | Invalid Request        | Missing `method` field, `jsonrpc` != `"2.0"`, or empty/invalid `agent_id` |
| `-32601` | Method Not Found       | Reserved — KubeMQ forwards all methods to agents and does not raise this  |
| `-32602` | Invalid Params         | Malformed `params` object                                                 |
| `-32603` | Internal Error         | Server-side failure                                                       |
| `-32010` | Authentication Failure | JWT auth error on `/a2a/*` (REST endpoints return HTTP 401 instead)       |
| `-32001` | Agent Timeout          | Agent did not respond within the timeout                                  |
| `-32002` | Agent Not Found        | No agent registered with the given `agent_id`                             |
| `-32003` | Agent Unavailable      | Agent rejected the request                                                |
| `-32004` | Invalid Response       | Invalid response from the agent                                           |

### Transport vs application errors [#transport-vs-application-errors]

The virtual subscriber sets `Executed: false` on **transport** failures (the agent never processed the request) and `Executed: true` on **application** errors (the agent processed it and returned an error body). This lets callers choose a retry strategy.

| Agent result                                 | `pb.Response`                                                        | Category    |
| -------------------------------------------- | -------------------------------------------------------------------- | ----------- |
| Connection refused / DNS failure             | `Executed: false`, `Error: "agent unreachable: …"`                   | Transport   |
| HTTP timeout                                 | `Executed: false`, `Error: "agent timeout"`                          | Transport   |
| HTTP 502 / 503 / 504                         | `Executed: false`, `Error: "agent unavailable: …"`                   | Transport   |
| Response exceeds `AgentMaxResponseBytes`     | `Executed: false`, `Error: "agent response too large"`               | Transport   |
| Concurrency limit reached                    | `Executed: false`, `Error: "server busy: concurrency limit reached"` | Transport   |
| HTTP 200–299                                 | `Executed: true`, `Body: response`                                   | Success     |
| HTTP 400 / 401 / 403 / 404 / 409 / 422 / 500 | `Executed: true`, `Body: response`                                   | Application |

## Configuration fields [#configuration-fields]

`A2aConfig` is configured in `Connectors.A2A.*`. The connector is **enabled by default** — set `CONNECTORSA2_A_ENABLE=false` to disable it. See [Configuration](/aiway/a2a/configuration) and the [shared HTTP server](/connectors/concepts/shared-http-server#enable-model-on-by-default) enable model for the irregular env-var naming.

| Field                   | Default            | Disable/override env var                  | Description                                                           |
| ----------------------- | ------------------ | ----------------------------------------- | --------------------------------------------------------------------- |
| `Enable`                | `true`             | `CONNECTORSA2_A_ENABLE`                   | Enable the A2A connector (set `=false` to disable)                    |
| `AgentTTLSeconds`       | `300`              | `CONNECTORSA2_A_AGENT_TTL_SECONDS`        | Agent expiry TTL (liveness check)                                     |
| `DefaultTimeoutSeconds` | `300`              | `CONNECTORSA2_A_DEFAULT_TIMEOUT_SECONDS`  | Default request timeout                                               |
| `MaxTimeoutSeconds`     | `3600`             | `CONNECTORSA2_A_MAX_TIMEOUT_SECONDS`      | Maximum allowed request timeout (must be ≥ `DefaultTimeoutSeconds`)   |
| `MaxAgents`             | `0` (unlimited)    | `CONNECTORSA2_A_MAX_AGENTS`               | Maximum registered agents                                             |
| `MaxSSEIdleSeconds`     | `300`              | `CONNECTORSA2_A_MAX_SSE_IDLE_SECONDS`     | SSE stream idle timeout                                               |
| `TrustedOrigins`        | `["auto"]`         | `CONNECTORSA2_A_TRUSTED_ORIGINS`          | Trusted origins for browser requests                                  |
| `AgentMaxResponseBytes` | `10485760` (10 MB) | `CONNECTORSA2_A_AGENT_MAX_RESPONSE_BYTES` | Max agent HTTP response size; larger → `Executed: false`              |
| `AgentTLSSkipVerify`    | `false`            | `CONNECTORSA2_A_AGENT_TLS_SKIP_VERIFY`    | Skip TLS verification for outbound agent calls (dev only)             |
| `AgentMaxConcurrency`   | `100`              | `CONNECTORSA2_A_AGENT_MAX_CONCURRENCY`    | Max concurrent in-flight requests per agent; overflow → "server busy" |

The gateway adds a `GatewayTimeoutBuffer` of 10 seconds on top of the caller-specified timeout so it does not time out before the downstream agent.

## Metrics [#metrics]

Prometheus metrics are exposed on port `8080` at `/metrics`. See [Observability](/connectors/concepts/observability) for the full surface and the web AI dashboard.

| Metric                                 | Type      | Labels                         | Description                                                   |
| -------------------------------------- | --------- | ------------------------------ | ------------------------------------------------------------- |
| `kubemq_a2a_requests_total`            | Counter   | `agent_id`, `method`, `status` | A2A requests forwarded to agents                              |
| `kubemq_a2a_request_duration_seconds`  | Histogram | `agent_id`                     | A2A request duration                                          |
| `kubemq_a2a_errors_total`              | Counter   | `agent_id`, `error_code`       | A2A gateway errors                                            |
| `kubemq_a2a_registry_operations_total` | Counter   | `op`, `status`                 | Registry operations (register, deregister, heartbeat, expire) |
| `kubemq_a2a_sse_streams_active`        | Gauge     | —                              | Currently active SSE streams                                  |

The `method` label is sanitized against the five standard methods; any other value is recorded as `method="unknown"` to bound Prometheus label cardinality.

```bash
curl http://localhost:8080/metrics 2>/dev/null | grep kubemq_a2a_
```
