Adapters and Extensibility
Relevant source files
The following files were used as context for generating this wiki page:
Adapters in shunt serve as the protocol boundary between the gateway and various LLM providers. They implement a unified interface to handle upstream communication, credential injection, and protocol translation, allowing the gateway to support diverse backends like OpenAI, Anthropic, and Cursor while maintaining compatibility with the Anthropic Messages API for clients.
The system is designed for high extensibility, primarily through table-driven configuration. New providers and models can often be added via config entries in [[routes]] or [providers.*] without code changes. For genuinely new protocols, the architecture provides a standard Adapter trait that isolates provider-specific logic from the core request pipeline.
Sources: ARCHITECTURE.md:12-19, ARCHITECTURE.md:37-43, ARCHITECTURE.md:108-112
Adapter Architecture
The Adapter trait is the core seam for extensibility. It ensures that the request pipeline remains decoupled from the specifics of how an upstream provider communicates. Routing logic determines which adapter to use based on the model ID, but the adapter itself handles the execution.
The Adapter Trait
Every provider implementation must conform to the Adapter trait. An adapter receives a Route and the current AppState (a snapshot of config and auth).
The diagram shows the relationship between the base Adapter trait and its concrete implementations.
Sources: ARCHITECTURE.md:37-45, src/adapters/mod.rs
Dispatch Workflow
The request flow starts in proxy.rs, where the incoming request is buffered and routed. The proxy::forward function dispatches the request to the appropriate adapter based on the AdapterKind.
The flow diagram illustrates how a request progresses from arrival to adapter-specific dispatch.
Sources: src/proxy.rs:11-20, ARCHITECTURE.md:47-52
Core Adapter Types
The system includes several built-in adapter types that cover most common LLM protocols.
| Adapter Kind | Protocol Target | Key Features |
|---|---|---|
anthropic | Anthropic Messages API | Passthrough, Claude OAuth support, custom base_url support. |
responses | OpenAI Responses API | Anthropic ⇄ OpenAI translation, streaming support, WebSocket v2 for Codex. |
cursor | Cursor ConnectRPC | Protobuf/SSE bridge for Cursor-specific protocol. |
Sources: ARCHITECTURE.md:120-132, blueprints/provider/_generic.md:14-22
Anthropic Adapter
This is the default passthrough adapter. It can be used for any backend that is already Anthropic-compatible (e.g., DeepSeek, Kimi, MiniMax) by simply configuring a custom base_url. No new code is required for these providers.
Sources: ARCHITECTURE.md:134-136, blueprints/provider/_generic.md:16
Responses Adapter
Handles translation between the Anthropic Messages format used by the client and the OpenAI Responses format. It is used for OpenAI, xAI, and ChatGPT/Codex. It supports a specialized WebSocket v2 transport for the Codex backend.
Sources: ARCHITECTURE.md:63-68, ARCHITECTURE.md:123-128
Extensibility Mechanisms
Extensibility in shunt follows a "config-first" philosophy.
Table-Driven Configuration
Most new capabilities are added by updating shunt.toml rather than Rust code. A new model or provider can be defined by specifying:
kind: The adapter type to use.base_url: The endpoint for the provider.auth: Credentials and authentication mode (e.g.,api_key,OAuth).
Sources: ARCHITECTURE.md:108-112, blueprints/provider/_generic.md:28-33
Protocol Integration Blueprint
When a provider does not fit existing protocols, a new adapter must be implemented. The process is guided by a formal blueprint:
- Establish Contract: Research request/response schemas and streaming frames.
- Decide on Code: Verify if
anthropic,responses, orcursorkinds can be reused with config. - Design Integration: Map the protocol to Anthropic primitives.
- Implement Tests: Add focused integration tests for streaming, errors, and auth.
Sources: blueprints/provider/_generic.md:7-40, src/blueprints.rs:37-55
Invariants and Constraints
To maintain gateway integrity, all adapters must adhere to specific architectural invariants:
- Preserve Streaming: Upstream SSE responses must never be buffered unless the client explicitly requested non-streaming output.
- Error Reshaping: All gateway-owned errors must be reshaped into the Anthropic error envelope (
{"type":"error", "error": {...}}). - Config Snapshotting: The configuration is snapshotted once per request; a mid-request reload does not affect in-flight requests.
- Unmapped Traffic: Requests that do not match a specific route must pass through to the
default_provider(typically Anthropic) unchanged.
Sources: ARCHITECTURE.md:140-155, AGENTS.md:52-58
Extensibility Summary
The adapter system provides a modular foundation for supporting a wide range of LLMs. By combining a standardized trait for core protocol logic with a flexible, table-driven configuration system, shunt allows for rapid integration of new models. The emphasis on preserving streaming and consistent error handling ensures that the gateway remains transparent to Claude Code and other client applications.
Sources: ARCHITECTURE.md:12-19, blueprints/provider/_generic.md:42-55