Current section
Files
Jump to
Current section
Files
priv/release-notes/0.2.4.md
# Transport Module Refactoring Summary
## Overview
This document summarizes the refactoring of the erlmcp transport modules (`erlmcp_transport_tcp.erl` and `erlmcp_transport_http.erl`) to follow Erlang/OTP best practices and idioms.
## Transport Behavior Definition
Created `erlmcp_transport.erl` as a proper behavior module:
```erlang
-callback init(Opts :: transport_opts()) ->
{ok, State :: transport_state()} |
{error, Reason :: term()}.
-callback send(State :: transport_state(), Data :: iodata()) ->
ok | {error, Reason :: term()}.
-callback close(State :: transport_state()) -> ok.
```
## TCP Transport Refactoring
### Architecture Changes
- **Full gen_server implementation** replacing the basic process model
- **State machine for connection lifecycle** (disconnected → connecting → connected)
- **Supervised process** with proper OTP compliance
### Key Improvements
#### 1. Connection Management
```erlang
-type state() :: #{
socket := gen_tcp:socket() | undefined,
owner := pid(),
host := inet:hostname() | inet:ip_address(),
port := inet:port_number(),
connected := boolean(),
reconnect_timer := reference() | undefined,
reconnect_attempts := non_neg_integer()
}
```
- Automatic reconnection with exponential backoff
- Configurable maximum retry attempts
- Connection state tracking
- Graceful disconnect handling
#### 2. Message Handling
- **Message buffering** for partial messages
- **Line-based protocol** with proper framing
- **Efficient message extraction** from buffer
- **Binary handling** for performance
#### 3. Configuration Options
```erlang
-type tcp_opts() :: #{
host := inet:hostname() | inet:ip_address(),
port := inet:port_number(),
connect_timeout => timeout(),
keepalive => boolean(),
nodelay => boolean(),
buffer_size => pos_integer(),
max_reconnect_attempts => pos_integer() | infinity
}
```
#### 4. Error Recovery
- Exponential backoff with jitter: `min(1000 * 2^attempts + jitter, 60000)`
- Automatic reconnection on connection loss
- Owner process monitoring
- Detailed error logging
### Code Quality Improvements
- Complete type specifications for Dialyzer
- Proper error tuples: `{error, {tcp_send_failed, Reason}}`
- Structured logging with context
- Clean separation of concerns
## HTTP Transport Refactoring
### Architecture Changes
- **Asynchronous request handling** with httpc
- **Connection pooling** to limit concurrent requests
- **Request queue management** for backpressure
### Key Improvements
#### 1. Request Management
```erlang
-type state() :: #{
pending_requests := #{reference() => {pid(), term()}},
message_queue := queue:queue(binary()),
pool_size := pos_integer(),
active_requests := non_neg_integer()
}
```
- Non-blocking async requests
- Request queuing when pool is full
- Request correlation and tracking
- Concurrent request limiting
#### 2. Retry Logic
- **Automatic retry** for failed requests
- **Smart retry decisions** based on error type:
- HTTP 5xx errors → retry
- HTTP 429 (rate limit) → retry
- Connection failures → retry
- Client errors (4xx) → don't retry
- **Exponential backoff** with configurable delays
#### 3. Configuration Options
```erlang
-type http_opts() :: #{
url := binary() | string(),
method => get | post,
headers => [{string(), string()}],
timeout => timeout(),
connect_timeout => timeout(),
max_retries => non_neg_integer(),
retry_delay => pos_integer(),
ssl_options => [ssl:tls_client_option()],
pool_size => pos_integer()
}
```
#### 4. Response Processing
- Content-type validation
- Proper error categorization
- Binary response handling
- Header normalization
### Code Quality Improvements
- Comprehensive type specifications
- Application startup management (inets, ssl)
- Header merging with defaults
- Proper cleanup of pending requests
## Common Patterns Applied
### 1. Process Design
- **gen_server behavior** for both transports
- **Process monitoring** of owner
- **Supervised processes** with proper cleanup
- **Trap exits** for graceful shutdown
### 2. Error Handling
```erlang
%% Descriptive error tuples
{error, not_connected}
{error, {tcp_send_failed, closed}}
{error, {http_error, 503, "Service Unavailable"}}
{error, {request_failed, timeout}}
```
### 3. State Management
- **Immutable state updates** in all callbacks
- **Maps for state** instead of records (more flexible)
- **Clear state transitions** with logging
- **No global state** - everything in process state
### 4. Configuration
- **Rich options** with sensible defaults
- **Runtime reconfiguration** support
- **Type-safe configuration** handling
- **Backward compatibility** maintained
### 5. Observability
- **Structured logging** at appropriate levels
- **Connection state notifications** to owner
- **Error context** in log messages
- **Metrics-friendly** design
## Performance Optimizations
### TCP Transport
- Binary protocol handling
- Configurable buffer sizes
- TCP_NODELAY option support
- Efficient message extraction
### HTTP Transport
- Connection pooling
- Async request processing
- Request batching via queue
- Minimal blocking operations
## Testing Considerations
The refactored modules are more testable:
- Clear separation of pure and side-effecting functions
- Mockable transport behavior
- Observable state via gen_server calls
- Deterministic error handling
## Migration Guide
### For TCP Transport Users
```erlang
%% Old
{tcp, Host, Port, Owner}
%% New
{tcp, #{host => Host, port => Port, owner => Owner}}
```
### For HTTP Transport Users
```erlang
%% Old
{http, URL}
%% New
{http, #{url => URL, owner => Owner}}
```
### Behavior Changes
1. Both transports now monitor the owner process
2. TCP automatically reconnects on failure
3. HTTP retries failed requests
4. Both provide richer error information
## Benefits
1. **Reliability**: Automatic recovery from transient failures
2. **Performance**: Better resource utilization and concurrency
3. **Maintainability**: Clear code structure and documentation
4. **Debuggability**: Comprehensive logging and error messages
5. **Flexibility**: Rich configuration options
6. **Type Safety**: Complete specifications for Dialyzer
## Conclusion
The refactored transport modules now follow Erlang/OTP best practices, providing robust, production-ready network communication for the MCP SDK. They handle edge cases properly, recover from failures gracefully, and provide good observability through structured logging.