# Shared HTTP Server (/connectors/concepts/shared-http-server)



Three built-in gateways run on **one shared HTTP server** inside kubemq-server:
CloudEvents (documented here under Connectors) plus the AI gateways
[A2A and MCP](/aiway) (documented under Aiway). There is no separate port or
process per gateway: they register their routes on the same server, pass through the
same middleware chain, and inherit the same auth, CORS, and TLS configuration.

## One server, many connectors [#one-server-many-connectors]

When kubemq-server starts, each enabled connector registers its routes on the shared
HTTP server, and the server is started once. Requests to `/a2a/*`, `/mcp`, and `/ce/*`
all land on the same listener and flow through the same middleware pipeline before
reaching the connector that owns the route.

<Mermaid
  chart="`
graph LR
CALLER[&#x22;HTTP / JSON-RPC<br/>CloudEvents client&#x22;]
REC[&#x22;Recovery&#x22;]
GATE[&#x22;Traffic Gate&#x22;]
OTEL[&#x22;OTel Tracing&#x22;]
CORS[&#x22;CORS&#x22;]
AUTH[&#x22;Auth&#x22;]
BODY[&#x22;Body Limit&#x22;]
LOG[&#x22;Logging&#x22;]
ROUTE[&#x22;Connector route<br/>/a2a · /mcp · /ce&#x22;]

CALLER --> REC --> GATE --> OTEL --> CORS --> AUTH --> BODY --> LOG --> ROUTE

class CALLER client
class REC,GATE,OTEL,CORS,AUTH,BODY,LOG,ROUTE broker
`"
/>

*A request passes through the middleware chain in order before reaching a connector route.*

## Port 9090 [#port-9090]

The shared HTTP server listens on **port 9090**. The port is configured by
`Connectors.Http.Port`; if it is left unset, it &#x2A;*inherits from `Rest.Port`** (which
defaults to `9090`). A warning is logged if both are set to different values.

Connector metrics are exposed separately on **port 8080** (`/metrics`), alongside the
internal dashboard API. See [Observability](/connectors/concepts/observability) for the
metrics surface and the AI dashboard.

Health and readiness probes are public and unauthenticated:

```bash
curl http://localhost:9090/ping
curl http://localhost:9090/health
curl http://localhost:9090/ready
```

## The middleware chain [#the-middleware-chain]

Every request flows through the same middleware stack, applied outermost-first:

| Order | Middleware       | Purpose                                                                         |
| ----- | ---------------- | ------------------------------------------------------------------------------- |
| 1     | **Recovery**     | Catches panics so one bad request cannot crash the server.                      |
| 2     | **Traffic Gate** | Rejects requests with HTTP `503` while the broker is not ready.                 |
| 3     | **OTel Tracing** | OpenTelemetry instrumentation (`kubemq-http`), when telemetry is enabled.       |
| 4     | **CORS**         | Configurable cross-origin policy via `HttpConfig.Cors`.                         |
| 5     | **Auth**         | Extracts a JWT Bearer token and sets claims; anonymous claims when auth is off. |
| 6     | **Body Limit**   | Caps request body size (default `100M`).                                        |
| 7     | **Logging**      | Debug-level request start/end logging.                                          |

The **traffic gate** is wired to the broker's readiness: the server automatically
starts accepting traffic when the broker becomes ready and rejects it (with `503`)
when it is not. This is why a connector can be enabled yet briefly return `503` during
startup.

Auth and TLS/mTLS are shared across all connectors and documented once in
[Auth & security](/connectors/reference/auth-and-security).

### SSE and request timeouts [#sse-and-request-timeouts]

The server's `WriteTimeout` is set to `0` so long-lived **Server-Sent Events** streams
(A2A `message/stream`, CloudEvents SSE subscriptions) stay open indefinitely. Non-SSE
route groups instead apply a per-route `TimeoutMiddleware` — default **60 seconds**,
returning HTTP `504 Gateway Timeout` if the deadline is exceeded before the response is
committed.

For requests that proxy to a downstream agent, a `GatewayTimeoutBuffer` of **10
seconds** is added on top of the caller-specified timeout, so the gateway does not time
out before the agent it is waiting on.

## Enable model: on by default [#enable-model-on-by-default]

All three gateways are **enabled by default**. Start kubemq-server and `/a2a/*`,
`/mcp`, and `/ce/*` are live — there is **no flag to turn them on**. (The A2A and MCP
gateways are documented under [Aiway](/aiway); their enable vars are listed here
because they share this server's enable model.) To turn one off, set its enable env var
to `false`:

| Connector   | Disable with                  |
| ----------- | ----------------------------- |
| A2A         | `CONNECTORSA2_A_ENABLE=false` |
| MCP         | `CONNECTORSMCP_ENABLE=false`  |
| CloudEvents | `CONNECTORSCE_ENABLE=false`   |

For example, to run with MCP disabled:

<RunKubeMQ variant="disable" ports="[50000]" env="{ CONNECTORSMCP_ENABLE: 'false' }" />

<Callout type="warn">
  **The enable var names are irregular by design.** Environment variables are derived
  from the dotted config keys (`Connectors.A2A.Enable`, `Connectors.MCP.Enable`,
  `Connectors.CE.Enable`) by a snake-casing transform that splits on letter-case
  boundaries, strips dots, and uppercases. The boundaries fall in unexpected places — so
  A2A becomes `CONNECTORSA2_A_ENABLE` (the `2`→`A` boundary inserts an underscore), while
  MCP and CE join into `CONNECTORSMCP_ENABLE` and `CONNECTORSCE_ENABLE` with **no**
  underscore before the connector name. Use these exact names; never invent a
  `=true&#x60; flag to enable a connector, and note that **`CONNECTORS_CE_ENABLE` (with an
  underscore) does not work** — the live binding is `CONNECTORSCE_ENABLE`.
</Callout>

This differs from older KubeMQ behavior, where these gateways were off by default and
opted in. That framing is stale: today they ship on.

## Reserved channel prefix [#reserved-channel-prefix]

The shared server reserves the &#x2A;*`_AGENTS_.`** channel prefix for internal agent
platform subjects (agent request/reply, SSE stream relays, and registry replication).
Any user operation targeting a channel that begins with `_AGENTS_.` is **rejected** by
`IsReservedChannel`. Pick channel names outside this prefix for your own queues,
events, commands, and queries.

## Related [#related]

<Cards>
  <Card title="Auth & security" href="/connectors/reference/auth-and-security" description="JWT Bearer auth, CORS, origin validation, and TLS/mTLS for every connector." />

  <Card title="Observability" href="/connectors/concepts/observability" description="Prometheus metrics on port 8080, OTel tracing, and the AI dashboard." />

  <Card title="Connectors overview" href="/connectors" description="What connectors are and how the gateways fit together." />

  <Card title="Aiway" href="/aiway" description="The AI Agents Fabric — where the A2A and MCP gateways are documented." />
</Cards>
