Reference
Complete A2A endpoint, JSON-RPC, agent card schema, internal channel, config, error code, and metrics reference for KubeMQ.
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 (default port 9090), which also hosts the REST, MCP, and CloudEvents connectors. Prometheus metrics are exposed separately on port 8080 (see Observability).
HTTP 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.
# 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
Standard HTTP JSON responses (not JSON-RPC). See Agent 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.
# List agents tagged "search" or "nlp", capped at 10
curl 'http://localhost:9090/agents?skill_tags=search,nlp&limit=10'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.
# Platform card — confirms the A2A connector is reachable
curl http://localhost:9090/.well-known/agent-card.jsonJSON-RPC 2.0 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.
{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": { "parts": [{ "text": "Hello, agent!" }] },
"contextId": "optional-correlation-id",
"configuration": { "timeout": 30 }
}
}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
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 |
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.
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.
{ "jsonrpc": "2.0", "id": 1, "result": { "…": "agent output, unmodified" } }{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32002, "message": "agent not found: nonexistent-agent" } }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
| 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 |
{
"agent_id": "echo-agent-01",
"name": "Echo Agent",
"url": "http://localhost:18080/",
"skills": [{ "id": "echo", "name": "Echo", "tags": ["test", "echo"] }]
}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.
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.
| 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 |
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
The connector returns standard JSON-RPC 2.0 base codes plus four A2A-specific codes. See 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
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
A2aConfig is configured in Connectors.A2A.*. The connector is enabled by default — set CONNECTORSA2_A_ENABLE=false to disable it. See Configuration and the shared HTTP server 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
Prometheus metrics are exposed on port 8080 at /metrics. See 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.
curl http://localhost:8080/metrics 2>/dev/null | grep kubemq_a2a_Was this page helpful?