ADR-088: MCP surface — tool shape, resource shape, auth, decision-module binding
Context
ADR-077 D4 committed Spectral to exposing the decision contract through MCP (Model Context Protocol) as a first-class equivalent to the HTTP surface. The decision request/response shapes are identical across protocols (per ADR-077 D2 + D3); the auth model uses the same signed JWTs (per ADR-077 D4 + ADR-039 + ADR-087). The implementation specifics — tool/resource shape, auth integration with MCP transports, binding to underlying decision modules — are what this ADR pins.
This ADR pins those specifics for v0.
Decision
D1 — Single decide tool; action is a parameter
Spectral exposes one primary MCP tool for the decision contract. The tool’s input schema mirrors the HTTP POST /api/decide request body shape (per ADR-077 D2):
// tool name: "spectral.decide"{ "input_schema": { "org_id": "string (required)", "domain": "string (required)", "action": "string (required)", "context": "object (required; supplied attributes per the action's context schema)", "world_model_version": "integer (optional; pins to a specific version)", "correlation_id": "string (optional; passthrough)" }, "output_schema": { "status": "string (GREEN | GREEN-SKIP | YELLOW | RED)", "work_frame": "object (binding contract per ADR-077 D3)", "decision_metadata": "object (audit substrate per ADR-077 D3)" }}A single tool matches ADR-077 D2’s body-based routing — the action is a parameter, not a URL discriminator. Agent framework authors learn one tool surface; tool registration scales with neither customer count nor action count. Action validity per the authenticated (org_id, domain) is checked at request entry; an action not authorized for the caller fails per ADR-006 D3 + RFC 9457 envelope.
D2 — spectral.list_actions tool for discoverability
A secondary tool provides explicit action enumeration for agent frameworks that prefer to discover capabilities programmatically:
// tool name: "spectral.list_actions"{ "input_schema": { "domain": "string (optional; filter by domain)" }, "output_schema": { "actions": [ { "action": "string", "domain": "string", "description": "string", "context_schema": "object (the action's supplied-attribute schema)", "active_world_model_version": "integer" } ] }}The tool returns actions available to the authenticated caller’s (org, domain) scope per their JWT claims (per ADR-086 D3 + ADR-087 D1). This rides the action discoverability commitment from ADR-077 D5 (OpenAPI-style direction); the HTTP-side discoverability endpoint lands separately in the next ADR (B3). The two surfaces — MCP list_actions and the HTTP discoverability endpoint — share the same underlying introspection logic.
D3 — No MCP resources at v0; tools only
MCP supports two primary primitives — tools (callable functions) and resources (readable artifacts the agent can fetch). v0 exposes tools only; no MCP resources are defined.
Read-only inspection (world models, System Cards, audit records, decision history) lives on the customer dashboard per the customer dashboard Codex page — that is the customer-visible inspection surface. Agent frameworks that need to introspect world-model state at decision time use spectral.list_actions (D2) for action-level discovery; deeper introspection (rule corpus, configuration, version history) is not exposed via MCP at v0.
MCP resources are reservable post-release if agent-framework customers need resource-shaped access. The deferral is forward-compatible — adding resources later does not require restructuring the v0 tool surface.
D4 — Auth: Bearer JWT via ADR-039 + ADR-087 substrate
Spectral’s MCP server uses HTTP transport (with Server-Sent Events for streaming responses where appropriate). Auth follows the standard HTTP Bearer-token pattern: clients present a signed JWT in the Authorization: Bearer <token> header per ADR-039. The same JWKS-local validation logic (per ADR-039 D4a) applies; the same per-request mirror check (per ADR-039 D4b + ADR-087 D4) applies; the same act-claim delegation semantics (per ADR-087 D2) apply identically across HTTP and MCP entries.
No new auth infrastructure. The MCP transport receives the bearer token at connection time; the server validates it, extracts (org_id, domain_id, scopes, act?), and uses the extracted identity for tool-call authorization.
Other MCP transports (stdio for local agent integrations, WebSocket) are reservable; v0 commits HTTP transport with Bearer JWT as the primary integration shape.
D5 — Binding: thin protocol adapter inside api
The MCP server is implemented as a thin protocol adapter inside the existing api deployable (per ADR-076 D1 + Session 3d.2 — api hosts decision-module routes). MCP tool invocations convert to the same internal call path that HTTP POST /api/decide uses; no separate mcp-server deployable.
The shared code path means uniform application of:
- Rate limiting per ADR-084 D1 (per-org + per-key buckets apply to MCP entries identically to HTTP).
- Per-tenant observability per ADR-084 D2 (MCP entry dimensions surface in the same metrics).
- Module integrity per ADR-080 (hash check + approval check at load).
- Sandbox per ADR-083 (Layers 1–3 apply identically).
- Module store consistency per ADR-085 (cache + event-driven invalidation shared).
- Audit chain per ADR-077 D3 + ADR-080 D4 (decision records include the entry protocol — HTTP vs MCP — as a dimension).
MCP server endpoint URL: illustrative POST /api/mcp (or equivalent — final path is design space per the ADR-077 D-note on placeholder URLs). HTTP transport selection (plain HTTP request/response vs SSE for streaming) is implementation tuning, not architecturally committed shape; for spectral.decide the request/response is synchronous (decisions are deterministic and return promptly per ADR-083 + ADR-077), so plain HTTP suffices.
Alternatives considered
One tool per action per (org, domain) (dynamic tool registration). Rejected. MCP servers typically register tools statically at session start; dynamic tool sets per session would force the server to enumerate actions and register tools on every connection. The single-tool-with-parameter approach (D1) is simpler operationally and matches the HTTP body-routing shape.
MCP resources at v0 (world models, System Cards, audit records as readable resources). Rejected for v0. The dashboard covers inspection; agent frameworks at v0 do not have a demonstrated need for resource-shaped introspection beyond what spectral.list_actions (D2) provides. Reservable for post-release.
Custom MCP auth handshake (not Bearer JWT). Rejected. Reusing the existing JWT substrate (ADR-039 + ADR-087) eliminates a new auth mechanism. MCP’s HTTP transport accommodates Bearer headers natively.
Separate mcp-server deployable. Rejected. Adds a deployable without buying isolation that matters at v0 (MCP and HTTP entries share rate limits, audit chain, and decision-module load anyway). Thin protocol adapter inside api is the lower-overhead path; matches the Session 3d.2 conclusion that /api/decide is just another route on api.
Stdio transport at v0 (local agent integrations via stdio). Rejected for v0. HTTP transport covers server-hosted agent frameworks (the primary customer integration shape); stdio is more relevant for local-CLI integrations that aren’t a v0 priority. Stdio is reservable.
Embed MCP-server-tool definitions in a separate registry service. Rejected. The tool shape is small and slow-changing; in-code definition inside api is sufficient.
Consequences
- A thin MCP protocol adapter lands inside
api(alongside the existing HTTP routes). Implementation work is a Phase 4 epic; the architectural commitment is the single-tool + Bearer-JWT + shared-code-path shape. - MCP entries inherit every cross-cutting concern that HTTP entries get: rate limits (ADR-084), audit chain (ADR-077 D3 + ADR-080 D4), tenant identity (ADR-086 + ADR-087), integrity check (ADR-080), execution sandbox (ADR-083), module-store consistency (ADR-085). No MCP-specific bypass paths exist.
spectral.decideandspectral.list_actionsare the v0 tool surface. Tool-shape evolution (additional tools, additional parameters) is feature work that ships without requiring an ADR amendment unless it breaks the shared-code-path model from D5.- The audit chain captures the entry protocol (HTTP vs MCP) on each decision so operators can analyze customer integration patterns by protocol over time.
- MCP resources, stdio transport, and tool-set evolution beyond the v0 shape are reservable as post-release expansion. The v0 surface is intentionally minimal.
- The MCP surface specifics are settled by this ADR.
- The HTTP-side action discoverability endpoint (B3) lands separately and complements
spectral.list_actions(D2 here); the underlying introspection logic is shared.