ADR-0008: MCP primary, HTTP secondary¶
- Status: Accepted
- Date: 2026-04-12
Context¶
Trails serves capabilities to two audiences: agents and humans. Transport options:
- HTTP/REST primary, MCP secondary. Rails-era default. Agents speak MCP via a shim; humans hit REST.
- MCP primary, HTTP secondary. Agents are the design-target audience; humans get HTTP as a convenience.
- Dual-primary. Both transports peers; canonical spec split evenly.
- Protocol-agnostic core. No transport is primary; all are adapters.
The distinction matters because primary means: when the canonical capability manifest changes and a field can't be projected to a transport, that transport degrades, not the canonical form. Choosing which transport shapes the framework's natural DX.
Decision¶
MCP is the primary transport; HTTP is secondary.
- MCP server is auto-mounted by default; MCP is the protocol the framework's conventions optimize for.
- HTTP (FastAPI) adapter is auto-mounted but can be disabled per-app.
- OpenAPI is generated from capability descriptors for legacy HTTP clients.
- When a capability descriptor field can't be expressed in MCP, the field is projected away in the MCP
tools/listresponse — but the canonical JSON-LD descriptor remains at/.well-known/capabilities(see ADR-0005). - Content negotiation on the HTTP side:
Accept: text/markdownfor humans,application/ld+jsonfor semantic clients,application/jsonfor plain JSON.
Consequences¶
Positive¶
- Matches the thesis. Agents are the primary consumer; the framework should treat them as first-class.
- Aligns with emerging standards. MCP is becoming the de facto agent-to-tool protocol.
- Cleaner identity story. MCP servers can present consistent DID-based identity without HTTP-session baggage.
- Natural bi-modal rendering. Humans get HTML/Markdown; agents get JSON-LD — both driven from the same template.
Negative¶
- Most production tooling (reverse proxies, WAFs, rate limiters, LB) assumes HTTP. Mitigated by HTTP always being available; MCP rides over HTTP/SSE anyway.
- MCP ecosystem is young. Spec still evolving. Mitigated by tracking MCP releases and keeping projection layer thin.
- Some deployments may want MCP off entirely. Mitigated by
[transport.mcp] enabled = falseconfig.
Non-consequences¶
- HTTP is not deprecated or second-class in implementation; it's just not where the canonical shape lives.
- Authentication works identically on both transports (biscuit + DID).
Revisit conditions¶
- If MCP is superseded or fragments into incompatible variants, re-evaluate. The protocol-agnostic core design (ADR-0005) makes this manageable — we'd add a new projection, not rewrite.
- If the user base shifts strongly toward HTTP-only semantic services, we can re-balance primary/secondary without breaking apps.