Skip to main content

MCP gateway overview

Horizon's hosted gateway is the half of the product Byte8 runs. It terminates the Model Context Protocol on the Streamable HTTP transport, authenticates AI agents, and translates MCP tool calls into REST calls into your Magento store using the per-store API key.

Endpoint

POST https://horizon.byte8.io/mcp
Content-Type: application/json
X-Horizon-Key: <store-api-key>

Streamable HTTP is the modern MCP transport — single endpoint, streaming responses, no long-lived WebSocket. Most MCP clients (Claude Desktop, Anthropic SDK, agent frameworks) support it natively.

The gateway is implemented in Rust on top of rmcp (version 1.1+, with the transport-streamable-http-server feature).

Authentication

Every MCP request must carry an X-Horizon-Key header. The key:

  • Is a per-store secret, not a per-user one.
  • Is generated by the dashboard on store creation or rotation.
  • Is shown to you exactly once — there's no "reveal" later.
  • Is hashed at rest. A leak means rotate, not panic-revoke-all.

If the key is missing, malformed, or unknown, the gateway returns 401 before the MCP layer even sees the request — so a bad key never appears as a tool error, just a transport failure on the agent side.

See API keys for rotation patterns.

Per-tenant isolation

Every tool invocation is scoped to a single store:

  1. The X-Horizon-Key header identifies which stores row in byte8_horizon is being addressed.
  2. The user_id on that row is the tenant boundary — all subsequent queries (usage logs, rate-limit check, etc.) filter by that user_id.
  3. The gateway calls only the Magento at stores.base_url, with stores.api_key (a different key, used for gateway→Magento auth).

There's no shared catalog data between tenants — even product names with the same SKU in two merchants' stores stay completely separate.

What the gateway does on a tool call

Walk through search_products step-by-step:

  1. MCP request lands at /mcp. The rmcp server deserialises the JSON-RPC envelope.
  2. Auth middleware reads X-Horizon-Key, looks up the store, attaches the store record and user_id to the request context.
  3. Rate-limit check — if the store's plan has a monthly quota and that's exhausted in the Trial tier, the call is rejected here. For paid tiers, the call proceeds and the overage is metered.
  4. Tool dispatcher routes the call to the search_products handler in mcp/tools.rs.
  5. MagentoClient issues GET https://<base_url>/horizon/api/products?... with the store-side X-Horizon-Key.
  6. Response shaping — the Magento JSON is mapped to the MCP ProductSummary type with denormalised fields.
  7. Background logging — a usage_logs row is written asynchronously (failures don't block the response).

End-to-end p50 is typically under 200ms for catalog calls on a warm Magento.

Rate limiting

The gateway implements a RateLimiter keyed on (store_id, current_month). Limits are read from plan_limits in byte8_core and applied per plan tier:

TierBehaviour at quota
TrialHard 429 (no card on file)
Starter / Pro / Agency / EnterpriseSoft cap — request continues, overage counted in usage_logs for end-of-month Stripe billing

We never 429 a paid tier — visibility is what merchants pay for, so the gateway prefers to serve and bill than block.

What the gateway is not

  • ❌ A model host. Horizon doesn't run Claude / GPT / open-weights — agents bring their own model.
  • ❌ An embeddings or vector store. Catalog search is currently full-text against Magento's indexed fields.
  • ❌ A storefront cache layer. We do small TTL caches on hot reads, but the source of truth is always your Magento.
  • ❌ A scraper. The Magento extension explicitly publishes the data; we don't crawl your site.

Next