protocol_transport_core
Protocol Transport Core
Section titled “Protocol Transport Core”Universal transport foundation for multiple protocols
- A2A (Agent-to-Agent)
- MCP (Model Context Protocol)
- Redpanda PubSub
- REST (GET/PUT/POST)
Design Principles
Section titled “Design Principles”- Protocol Agnostic: Transport independent of protocol details
- WASM-Optimized: SpinKube-first design
- Zero-Copy: Minimal serialization overhead
- Composable: Mix and match transports and protocols
Constants
Section titled “Constants”JSONRPC_VERSION
Section titled “JSONRPC_VERSION”const JSONRPC_VERSION: &str = ;JSON-RPC 2.0 version constant
RPC_REQUEST_TIMEOUT
Section titled “RPC_REQUEST_TIMEOUT”const RPC_REQUEST_TIMEOUT: Duration = ;Per-request RPC timeout (wall-clock). Applied to non-streaming calls.
Type Aliases
Section titled “Type Aliases”ProtocolResult
Section titled “ProtocolResult”type ProtocolResult = Result<T, ProtocolError>;TransportResult
Section titled “TransportResult”type TransportResult = Result<T, TransportError>;Result types
JsonRpcId
Section titled “JsonRpcId”type JsonRpcId = serde_json::Value;JSON-RPC 2.0 request ID type
Can be a string, number, or null according to the JSON-RPC 2.0 specification. The ID is used to correlate requests with responses and must be echoed back in the response exactly as received.
Valid ID Types
Section titled “Valid ID Types”- String:
"request-123","abc-def-456" - Number:
1,42,1234567890 - Null:
null(though discouraged for traceability)
ProtocolHandlerFn
Section titled “ProtocolHandlerFn”type ProtocolHandlerFn = Box<dyn Fn + Send + Sync>;Protocol Handler Function - Function-based approach for WASM compatibility
Traits
Section titled “Traits”ProtocolHandler
Section titled “ProtocolHandler”Protocol Handler Trait - Implement for each protocol
Associated Types
type Request—type Response—type Error—
Required / Provided Methods
fn protocol_name(&self) -> &''static strProtocol name (e.g., “A2A”, “MCP”, “REST”)
fn encode_request(&self, request: &<Self as ?>::Request) -> Result<UniversalRequest, <Self as ?>::Error>Serialize protocol request to universal format
fn decode_request(&self, universal: &UniversalRequest) -> Result<<Self as ?>::Request, <Self as ?>::Error>Deserialize universal request to protocol format
fn encode_response(&self, response: &<Self as ?>::Response) -> Result<UniversalResponse, <Self as ?>::Error>Serialize protocol response to universal format
fn decode_response(&self, universal: &UniversalResponse) -> Result<<Self as ?>::Response, <Self as ?>::Error>Deserialize universal response to protocol format
Transport
Section titled “Transport”Transport Trait - HTTP, SSE, etc.
Uses async fn in trait without Send bounds because WASM
targets (Spin SDK) do not produce Send futures.
Required / Provided Methods
async fn send(&self, request: UniversalRequest) -> Result<UniversalResponse, TransportError>async fn health_check(&self) -> Result<(), TransportError>AsyncProtocolHandler
Section titled “AsyncProtocolHandler”Async Protocol Handler - Spin SDK async compatible
Required / Provided Methods
fn protocol_name(&self) -> &''static strProtocol name (e.g., “A2A”, “MCP”, “REST”)
fn handle_request_sync(&self, request: UniversalRequest) -> Result<UniversalResponse, ProtocolError>Handle request synchronously (async operations handled internally)
Structs
Section titled “Structs”ForwardedHeaders
Section titled “ForwardedHeaders”Methods
fn new(headers: HashMap<String, String>) -> Selfas_map
Section titled “as_map”fn as_map(&self) -> &HashMap<String, String>into_map
Section titled “into_map”fn into_map(self) -> HashMap<String, String>ProtocolHeaders
Section titled “ProtocolHeaders”Common Protocol Headers
Fields
| Field | Type | Description |
|---|---|---|
protocol | String | |
version | String | |
correlation_id | String | |
client_agent_id | Option<String> | |
trace_id | Option<String> |
Methods
from_headers
Section titled “from_headers”fn from_headers(headers: &HashMap<String, String>) -> SelfExtract protocol headers from raw headers
to_headers
Section titled “to_headers”fn to_headers(&self) -> HashMap<String, String>Convert to raw headers HashMap
JsonRpcError
Section titled “JsonRpcError”JSON-RPC 2.0 Error Object structure
Represents detailed error information in JSON-RPC 2.0 error responses. Includes a standard error code, human-readable message, and optional additional data for debugging and error handling.
Fields
| Field | Type | Description |
|---|---|---|
code | i64 | Error code (standard or application-defined) |
message | String | Error message |
data | Option<serde_json::Value> | Optional additional error data |
Methods
fn new(code: i64, message: String) -> SelfCreate a new JSON-RPC 2.0 error
with_data
Section titled “with_data”fn with_data(code: i64, message: String, data: serde_json::Value) -> SelfCreate a new JSON-RPC 2.0 error with additional data
JsonRpcNotification
Section titled “JsonRpcNotification”JSON-RPC 2.0 Notification structure
Represents a JSON-RPC 2.0 notification message that does not expect a response. Notifications are “fire-and-forget” messages used for operations where the caller doesn’t need to know the result.
Fields
| Field | Type | Description |
|---|---|---|
jsonrpc | String | JSON-RPC version (must be “2.0”) |
method | String | Method name to invoke |
params | serde_json::Value | Method parameters (optional, defaults to null) |
Methods
fn new(method: String, params: serde_json::Value) -> SelfCreate a new JSON-RPC 2.0 notification
is_valid
Section titled “is_valid”fn is_valid(&self) -> boolCheck if this is a valid JSON-RPC 2.0 notification
JsonRpcRequest
Section titled “JsonRpcRequest”JSON-RPC 2.0 Request structure
Represents a JSON-RPC 2.0 request message that expects a response.
All requests must include the jsonrpc, id, and method fields.
The params field is optional and defaults to null.
Fields
| Field | Type | Description |
|---|---|---|
jsonrpc | String | JSON-RPC version (must be “2.0”) |
id | JsonRpcId | Request ID (string, number, or null) |
method | String | Method name to invoke |
params | serde_json::Value | Method parameters (optional, defaults to null) |
Methods
fn new(id: JsonRpcId, method: String, params: serde_json::Value) -> SelfCreate a new JSON-RPC 2.0 request
is_valid
Section titled “is_valid”fn is_valid(&self) -> boolCheck if this is a valid JSON-RPC 2.0 request
JsonRpcResponse
Section titled “JsonRpcResponse”JSON-RPC 2.0 Response structure
Represents a JSON-RPC 2.0 response message sent back to the client.
A response must contain either a result (for success) or an error
(for failure), but never both. The id field must match the request ID.
Fields
| Field | Type | Description |
|---|---|---|
jsonrpc | String | JSON-RPC version (always “2.0”) |
result | Option<serde_json::Value> | Successful result (mutually exclusive with error) |
error | Option<JsonRpcError> | Error object (mutually exclusive with result) |
id | JsonRpcId | Request ID (copied from request) |
Methods
success
Section titled “success”fn success(id: JsonRpcId, result: serde_json::Value) -> SelfCreate a successful JSON-RPC 2.0 response
fn error(id: JsonRpcId, code: i64, message: String) -> SelfCreate an error JSON-RPC 2.0 response
error_with_data
Section titled “error_with_data”fn error_with_data(id: JsonRpcId, code: i64, message: String, data: serde_json::Value) -> SelfCreate an error JSON-RPC 2.0 response with additional data
is_success
Section titled “is_success”fn is_success(&self) -> boolCheck if this is a success response
is_error
Section titled “is_error”fn is_error(&self) -> boolCheck if this is an error response
IdleTimeoutStream
Section titled “IdleTimeoutStream”Stream wrapper that terminates when no item arrives within idle_timeout.
The timer resets on every yielded item — a stream emitting one token
every 500ms runs indefinitely. A stream that goes silent for
idle_timeout is terminated with None (clean EOF).
Methods
fn new(inner: S, idle_timeout: Duration) -> SelfStreamingPolicy
Section titled “StreamingPolicy”Three-clock streaming timeout configuration.
Unconditional: compiled for both WASM and native targets. On WASM, values are stored for config parity but not enforced until WASI 0.3 streaming support is available.
Fields
| Field | Type | Description |
|---|---|---|
connect_ms | u64 | TCP + TLS handshake timeout. Default: 10s. |
first_byte_ms | u64 | Time until first data chunk arrives (after headers). Default: 45s. |
idle_ms | u64 | Max silence between consecutive chunks — resets on each chunk. Default: 90s. |
Methods
connect_timeout
Section titled “connect_timeout”fn connect_timeout(&self) -> Durationfirst_byte_timeout
Section titled “first_byte_timeout”fn first_byte_timeout(&self) -> Durationidle_timeout
Section titled “idle_timeout”fn idle_timeout(&self) -> DurationUniversalRequest
Section titled “UniversalRequest”Universal Request - Protocol-agnostic request container
Fields
| Field | Type | Description |
|---|---|---|
method | String | |
uri | String | |
headers | HashMap<String, String> | |
body | Vec<u8> | |
protocol | String | |
correlation_id | String |
UniversalResponse
Section titled “UniversalResponse”Universal Response - Protocol-agnostic response container
Fields
| Field | Type | Description |
|---|---|---|
status | u16 | |
headers | HashMap<String, String> | |
body | Vec<u8> | |
protocol | String | |
correlation_id | String |
ProtocolRouter
Section titled “ProtocolRouter”Server Router - Route requests to appropriate protocol handlers (Spin async compatible)
Methods
fn new() -> Selfregister_fn
Section titled “register_fn”fn register_fn<F>(&mut self, protocol: &str, handler: F)where F: Fn + Send + Sync + ?Register protocol handler function
register
Section titled “register”fn register<H>(&mut self, protocol: &str, handler: H)where H: AsyncProtocolHandler + ?Register async protocol handler
route_request
Section titled “route_request”fn route_request(&self, request: UniversalRequest) -> Result<UniversalResponse, ProtocolError>Route request to appropriate handler
list_protocols
Section titled “list_protocols”fn list_protocols(&self) -> Vec<String>List available protocols
ProtocolError
Section titled “ProtocolError”Universal Protocol Error
Variants
| Variant | Description |
|---|---|
UnsupportedProtocol(String) | |
ProtocolNotFound(String) | |
Validation(String) | |
Parsing(String) | |
Transport(TransportError) | |
Serialization(serde_json::Error) | |
Internal(String) |
Methods
internal_error
Section titled “internal_error”fn internal_error(msg: &str) -> SelfCreate internal error
is_retryable
Section titled “is_retryable”fn is_retryable(&self) -> boolCheck if error is retryable
TransportError
Section titled “TransportError”Universal Transport Error
Variants
| Variant | Description |
|---|---|
Network(_) | |
Serialization(String) | |
Timeout(String) | |
Http { ... } | |
Configuration(String) | |
Authentication(String) |
Methods
is_retryable
Section titled “is_retryable”fn is_retryable(&self) -> boolCheck if error is retryable
is_auth_error
Section titled “is_auth_error”fn is_auth_error(&self) -> boolCheck if error is due to authentication
JsonRpcIncoming
Section titled “JsonRpcIncoming”Incoming JSON-RPC message types
Represents all possible incoming JSON-RPC 2.0 messages that can be received
and parsed. The enum uses serde’s untagged feature to automatically detect
the message type based on the presence of the id field.
Message Type Detection
Section titled “Message Type Detection”- Request: Has
idfield - expects a response - Notification: No
idfield - fire-and-forget - Batch: Array of requests/notifications (optional feature)
Variants
| Variant | Description |
|---|---|
Request(JsonRpcRequest) | Standard JSON-RPC 2.0 request with ID |
Notification(JsonRpcNotification) | JSON-RPC 2.0 notification (no ID, no response expected) |
Functions
Section titled “Functions”sanitize_header_map
Section titled “sanitize_header_map”fn sanitize_header_map(headers: &http::HeaderMap) -> ForwardedHeaderssanitize_headers
Section titled “sanitize_headers”fn sanitize_headers(headers: &HashMap<String, String>) -> ForwardedHeadersMacros
Section titled “Macros”create_universal_server!
Section titled “create_universal_server!”SERVER MACRO - Create universal server with protocol registration
The key insight: Spin http_component functions are sync, but can call async operations internally