<div align="center" id="readme">
<img src="docs/asset/sdforge.png" alt="SDForge Logo" width="200" height="200">
[](https://crates.io/crates/sdforge) [](https://docs.rs/sdforge) [](LICENSE) [](https://github.com/Kirky-X/sdforge/actions) [](https://www.rust-lang.org)
**SDForge** is a Rust-based declarative SDK framework that uses procedural macros to automatically generate multi-protocol service interfaces (HTTP + MCP) from unified function annotations. The key innovation is compile-time protocol selection via Cargo featuresβunused protocols produce zero compiled code.
</div>
## π Table of Contents
<style>
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 20px;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
transition: background-color 0.3s;
z-index: 1000;
}
.back-to-top:hover {
background-color: #0056b3;
}
.doc-nav {
padding: 10px 0;
margin-top: 20px;
border-top: 1px solid #e1e4e8;
}
</style>
- [β¨ Features](#features)
- [π¦ Installation](#installation)
- [π Quick Start](#quick-start)
- [βοΈ Feature System](#feature-system)
- [π‘ Usage Examples](#usage-examples)
- [π Module Prefixes](#module-prefixes)
- [π’ Version Management](#version-management)
- [β οΈ Error Handling](#error-handling)
- [π€οΈ Path Parameters](#path-parameters)
- [π¨ Building and Testing](#building-and-testing)
- [π Documentation](#documentation)
- [π Security Configuration](#security-configuration)
- [β‘ Performance Optimization](#performance-optimization)
- [π OpenAPI Auto-Generation](#openapi-generation)
- [π MCP 2026-07-28 Migration Guide](#mcp-migration)
- [π Production Deployment](#deployment-guide)
- [π Troubleshooting](#troubleshooting)
- [π€ Contributing](#contributing)
- [π License](#license)
- [π Project Structure](#-project-structure)
- [π Links](#-links)
---
## <span id="features">β¨ Features</span>
<div style="border-radius:8px; padding:16px; border:1px solid #E2E8F0;">
- **π― Unified Interface Definition** - Single macro configuration for both HTTP and MCP protocols
- **β‘ Compile-Time Protocol Selection** - Feature-gated code generation with zero runtime overhead for unused protocols
- **π Type Safety** - Compile-time validation of interface definitions
- **π Multi-Protocol Support** - HTTP (Axum), MCP, gRPC, WebSocket, SSE streaming
- **π§© Modular Design** - Feature-based architecture allows selecting only needed functionality
- **π‘οΈ Security Features** - Built-in authentication, rate limiting, and request validation
- **πΎ Caching** - In-memory caching (oxcache 0.3.2), no external database required
- **π§ Configuration Management** - Self-contained TOML configuration (no external config center)
- **π Versioning** - Built-in API version management
</div>
### π Phase 1 Architecture Improvements (v0.1.0)
Recent architectural enhancements include:
- **π Unified Registration System** - Eliminated 95+ lines of duplicate code across HTTP, MCP, WebSocket, and gRPC modules using trait-based abstraction and procedural macros
- **βοΈ Modular Configuration Management** - Refactored configuration into dedicated modules (app, cache, security) with centralized defaults and Builder pattern support
- **π Enhanced Security Module** - API Key versioning, LRU caching, key rotation with audit logging, and comprehensive security headers configuration
- **πΎ Advanced Caching** - Pattern-based cache invalidation, key normalization, batch operations, and statistics tracking
---
## <span id="installation">π¦ Installation</span>
<div style="border-radius:8px; padding:16px; border:1px solid #E2E8F0;">
Add SDForge to your `Cargo.toml`:
```toml
[dependencies]
sdforge = { version = "0.3", features = ["http"] }
```
</div>
---
## <span id="quick-start">π Quick Start</span>
<div style="border-radius:8px; padding:16px; border:1px solid #E2E8F0;">
Define your API with a single macro:
```rust
use sdforge::prelude::*;
#[service_api(
name = "get_user",
version = "v1",
path = "/users/:id",
method = "GET",
tool_name = "get_user",
description = "Get a user by ID"
)]
async fn get_user(id: u64) -> Result<User, ApiError> {
Ok(User { id, name: "Test".into() })
}
#[tokio::main]
async fn main() {
let app = sdforge::http::build();
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
```
</div>
---
## <span id="feature-system">βοΈ Feature System</span>
SDForge uses Cargo features for compile-time protocol selection and feature composition.
### π§ Core Features
| `http` | HTTP server (Axum 0.8.8) | axum, tower, tower-http |
| `mcp` | MCP protocol (rmcp 0.16, 2026-07-28 spec) | rmcp |
| `streaming` | SSE streaming support | tokio-stream, futures-util |
| `timestamp` | Auto-add timestamp to responses | chrono |
| `logging` | Structured request logging | chrono, tokio |
| `security` | Security features (auth, rate limiting) | http, cache, uuid, hmac, sha2, chrono, tokio, secrets, zeroize, subtle, once_cell, argon2, password-hash, rand, regex, oxcache/memory, bincode, hex, base64 |
| `websocket` | WebSocket support | tokio-tungstenite, axum-extra |
| `grpc` | gRPC support | tonic, prost |
| `cache` | Caching support | dep:http, oxcache/memory, async-trait |
| `openapi` | Automatic OpenAPI 3.1 spec generation | utoipa, http |
| `simd-json` | SIMD-accelerated JSON serialization | simd-json |
| `hex` | Hexadecimal encoding utility | hex |
| `full` | All runtime features (excludes simd-json/hex tooling) | - |
### π Feature Dependencies
- `default`: [`http`]
- `mcp`: Independent protocol (depends on external http crate for stateless HTTP header parsing, not sdforge http feature)
- `streaming`: Requires `http`
- `timestamp`: No dependencies
- `logging`: No dependencies
- `security`: Requires `http`, `cache`
- `websocket`: Requires `http`, `streaming`
- `grpc`: Requires `http`
- `cache`: Independent (uses http crate types, not sdforge http feature)
- `openapi`: Requires `http`
---
## π‘ Usage Examples
### π HTTP Only
For traditional REST APIs:
```toml
[dependencies]
sdforge = { version = "0.3", features = ["http"] }
```
### π€ MCP Only
For AI tool integration:
```toml
[dependencies]
sdforge = { version = "0.3", features = ["mcp"] }
```
### π Both Protocols
Exposure via both HTTP and MCP from the same code:
```toml
[dependencies]
sdforge = { version = "0.3", features = ["http", "mcp"] }
```
### π― Full Features
All capabilities enabled:
```toml
[dependencies]
sdforge = { version = "0.3", features = ["full"] }
```
---
## <span id="module-prefixes">π Module Prefixes</span>
Group related APIs with module prefixes for better organization:
```rust
#[service_module(prefix = "/auth")]
mod auth_api {
use super::*;
#[service_api(
name = "login",
version = "v1",
path = "/login",
method = "POST"
)]
async fn login(credentials: Credentials) -> Result<Token, ApiError> {
// Implementation
Ok(Token::new())
}
#[service_api(
name = "logout",
version = "v1",
path = "/logout",
method = "POST"
)]
async fn logout() -> Result<(), ApiError> {
// Implementation
Ok(())
}
}
```
This creates the endpoints:
- `/auth/api/v1/login`
- `/auth/api/v1/logout`
---
## <span id="version-management">π’ Version Management</span>
Support multiple API versions simultaneously:
```rust
#[service_api(
name = "get_user",
version = "v1",
path = "/users/:id",
method = "GET",
tool_name = "get_user_v1"
)]
async fn get_user_v1(id: u64) -> Result<UserV1, ApiError> {
Ok(UserV1 { id, name: "John Doe".into() })
}
#[service_api(
name = "get_user",
version = "v2",
path = "/users/:id",
method = "GET",
tool_name = "get_user_v2"
)]
async fn get_user_v2(id: u64) -> Result<UserV2, ApiError> {
Ok(UserV2 { id, first_name: "John".into(), last_name: "Doe".into() })
}
```
This creates versioned endpoints:
- `/api/v1/users/:id` β `get_user_v1`
- `/api/v2/users/:id` β `get_user_v2`
---
## <span id="error-handling">β οΈ Error Handling</span>
Define custom error types and convert them to `ServiceError`:
```rust
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MyError {
#[error("Resource not found: {resource}")]
NotFound { resource: String },
#[error("Validation failed: {field}")]
ValidationError { field: String },
#[error("Unauthorized access")]
Unauthorized,
}
impl From<MyError> for ServiceError {
fn from(err: MyError) -> Self {
match err {
MyError::NotFound { resource } => ServiceError::with_details(
"NOT_FOUND",
format!("Resource not found: {}", resource),
serde_json::json!({ "resource": resource }),
404,
),
MyError::ValidationError { field } => ServiceError::with_details(
"VALIDATION_ERROR",
format!("Validation failed for field: {}", field),
serde_json::json!({ "field": field }),
400,
),
MyError::Unauthorized => ServiceError::new(
"UNAUTHORIZED",
"Authentication required",
401,
),
}
}
}
```
---
## <span id="path-parameters">π€οΈ Path Parameters</span>
Extract path parameters using Rust naming conventions. The macro automatically maps path segments to function parameters:
```rust
#[service_api(
name = "get_user",
version = "v1",
path = "/users/:id",
method = "GET"
)]
async fn get_user(id: u64) -> Result<User, ApiError> {
// `id` is automatically extracted from `/users/:id`
Ok(User { id, name: "John".into() })
}
#[service_api(
name = "get_comment",
version = "v1",
path = "/posts/:post_id/comments/:comment_id",
method = "GET"
)]
async fn get_comment(
post_id: u64,
comment_id: u64
) -> Result<Comment, ApiError> {
// Both parameters are extracted from the path
Ok(Comment { post_id, comment_id, text: "Test".into() })
}
```
### πΉ Multiple Path Parameters
For nested resources:
```rust
#[service_api(
name = "get_nested_resource",
version = "v1",
path = "/orgs/:org_id/projects/:project_id/tasks/:task_id",
method = "GET"
)]
async fn get_task(
org_id: u64,
project_id: u64,
task_id: u64
) -> Result<Task, ApiError> {
Ok(Task { org_id, project_id, task_id, title: "Task".into() })
}
```
---
## <span id="building-and-testing">π¨ Building and Testing</span>
### π§ Build Commands
```bash
# Build with HTTP only
cargo build --features http
# Build with MCP only
cargo build --features mcp
# Build with all features
cargo build --features full
# Build with custom feature set
cargo build --features "http,cache,security"
# Release build
cargo build --release --features http
```
### π§ͺ Test Commands
```bash
# Run tests with HTTP
cargo test --features http
# Run tests with MCP
cargo test --features mcp
# Run tests with both protocols
cargo test --features "http,mcp"
# Run tests with all features
cargo test --features full
# Run specific test
cargo test test_get_user --features http
# Run tests with output
cargo test --features http -- --nocapture
# Run tests in release mode
cargo test --release --features http
```
### β¨ Formatting and Linting
```bash
# Format code
cargo fmt
# Check formatting
cargo fmt --check
# Run Clippy
cargo clippy --all-features
# Run Clippy with all targets
cargo clippy --all-features --all-targets
```
---
## <span id="documentation">π Documentation</span>
- [π API Documentation](https://docs.rs/sdforge)
- [π‘ Examples](./examples/)
---
## <span id="contributing">π€ Contributing</span>
We welcome contributions! Please submit pull requests with clear descriptions of your changes.
### π οΈ Development Setup
```bash
# Clone the repository
git clone https://github.com/Kirky-X/sdforge.git
cd sdforge
# Install pre-commit hooks
./scripts/install-pre-commit.sh
# Install development tools
cargo install cargo-watch cargo-edit
# Run tests
cargo test --all-features
```
### π Code Style
- Format code with `cargo fmt` before committing
- Run `cargo clippy --all-features` to check for issues
- Follow the existing code style and patterns
- Add tests for new features
- Update documentation as needed
---
## <span id="license">π License</span>
Licensed under the MIT License:
- [MIT License](LICENSE) or http://opensource.org/licenses/MIT
---
## <span id="project-structure">π Project Structure</span>
```
sdforge/
βββ src/ # Main framework crate
β βββ core/ # Core types and error handling
β βββ http/ # HTTP protocol implementation
β βββ mcp/ # MCP protocol implementation
β βββ security/ # Security features
β βββ cache/ # Caching implementation
β βββ websocket/ # WebSocket support
β βββ grpc/ # gRPC support
β βββ streaming/ # SSE streaming support
β βββ config/ # Configuration management
β βββ lib.rs # Library entry point
βββ macros/ # Procedural macros crate
β βββ src/
β βββ Cargo.toml
βββ docs/ # Documentation
βββ .github/ # GitHub workflows
βββ scripts/ # Build and utility scripts
```
---
## <span id="links">π Links</span>
- **π Repository**: https://github.com/Kirky-X/sdforge
- **π Documentation**: https://docs.rs/sdforge
- **π Issues**: https://github.com/Kirky-X/sdforge/issues
- **π¬ Discussions**: https://github.com/Kirky-X/sdforge/discussions
---
## <span id="security-configuration">π Security Configuration</span>
SDForge provides comprehensive security features out of the box. Here's how to configure them:
### π‘οΈ Authentication Setup
```rust
use sdforge::prelude::*;
#[service_api(
name = "secure_endpoint",
version = "v1",
path = "/secure",
method = "GET",
auth_required = true
)]
async fn secure_endpoint(
auth_context: AuthContext
) -> Result<String, ApiError> {
// Only authenticated users can access this
Ok(format!("Hello, {}!", auth_context.user_id().unwrap_or("Anonymous")))
}
```
### β‘ Rate Limiting
```toml
# config.toml
[rate_limit]
enabled = true
requests_per_minute = 60
burst_size = 10
```
### π API Key Authentication
```rust
use sdforge::security::{ApiKeyAuth, auth_middleware};
let app = Router::new()
.route("/api/*path", get(handler))
.layer(auth_middleware(ApiKeyAuth::new("your-secret-key")));
```
### β οΈ Security Defaults (v0.3.0+)
> **Note**: v0.3.0 tightened security defaults. Please check during migration:
> - **JWT secret minimum length**: `MIN_SECRET_LENGTH=32`. Secrets shorter than 32 characters are rejected with an error
> - **ServerConfig default host**: Changed from `"0.0.0.0"` (fail-open) to `"127.0.0.1"` (fail-safe loopback). Production deployments must explicitly configure host
> - **CORS validation tightened**: `"http://"` (scheme only, no host) is now rejected
---
## <span id="performance-optimization">β‘ Performance Optimization</span>
### π Caching Configuration
```toml
# config.toml
[cache]
enabled = true
default_ttl_secs = 600
max_items = 5000
track_stats = true
```
### π Memory Management
```rust
use sdforge::config::CacheConfig;
let cache_config = CacheConfig {
enabled: true,
default_ttl_secs: 600,
max_items: 5000,
track_stats: true,
};
```
### βοΈ Connection Pooling
```rust
use sdforge::config::AppConfig;
use sdforge::http::build_with_config;
let config = AppConfig::default();
let app = build_with_config(&config)?;
```
---
## <span id="openapi-generation">π OpenAPI Auto-Generation</span>
SDForge v0.2.0 introduces automatic OpenAPI 3.1 specification generation based on [utoipa 5.5](https://crates.io/crates/utoipa). When the `openapi` feature is enabled, each `#[service_api]` macro registers an `OpenApiRouteInfo` at compile time via `inventory`. At runtime, calling `generate_openapi_spec()` collects all routes and generates a complete specification.
### π§ Enabling
```toml
[dependencies]
sdforge = { version = "0.3", features = ["http", "openapi"] }
```
### π Basic Usage
```rust
use sdforge::openapi::generate_openapi_spec;
// Collect all routes registered via #[service_api] and generate the OpenAPI specification
let spec = generate_openapi_spec();
// Serialize to JSON to write to a file or return to the client
let json = serde_json::to_string_pretty(&spec).unwrap();
println!("{json}");
```
### π¨ Custom Metadata
Use the `OpenApiBuilder` chainable calls to customize the `info` section (title, version, description). Routes are always collected from the global `inventory` registry:
```rust
use sdforge::openapi::OpenApiBuilder;
let spec = OpenApiBuilder::new()
.title("My Service")
.version("2.0.0")
.description("User-facing API for the billing domain")
.build();
```
### π Macro Integration
When the `openapi` feature is enabled, `#[service_api]` automatically generates registration codeβno manual maintenance required:
```rust
#[service_api(
name = "get_user",
version = "v1",
path = "/users/:id",
method = "GET",
description = "Get a user by ID"
)]
async fn get_user(id: u64) -> Result<User, ApiError> { /* ... */ }
```
The above code automatically submits an `OpenApiRouteInfo { path: "/users/{id}", method: "GET", ... }` to the global registry at compile time. `generate_openapi_spec()` will include it in the generated specification.
> **Note**: When the `openapi` feature is not enabled, the macro does not generate any utoipa-related codeβzero runtime overhead.
---
## <span id="mcp-migration">π MCP 2026-07-28 Migration Guide</span>
v0.2.0 fully migrates the MCP implementation from `mcp-sdk 0.0.3` to [`rmcp 0.16`](https://crates.io/crates/rmcp), adapting to the MCP 2026-07-28 specification. This migration is a **BREAKING** change.
### β οΈ BREAKING Changes
| `mcp-sdk = "0.0.3"` dependency | `rmcp = "0.16"` dependency |
| `initialize` handshake flow | Removed, replaced with `server/discover` endpoint |
| Stateful sessions (`StatefulServerHandler`) | Stateless adapter layer (`StatelessServerHandler`) |
| `register_mcp(&mut Server)` signature | `register_mcp(&mut dyn McpToolRegistry)` |
### π οΈ Stateless Adapter Layer
`StatelessServerHandler` implements the `rmcp::ServerHandler` trait. None of its methods depend on session state, adapting to the stateless protocol model of the 2026-07-28 specification:
```rust
use sdforge::mcp::stateless::StatelessServerHandler;
let handler = StatelessServerHandler::new();
// Mount to HTTP routes via rmcp's axum integration
```
### π¨ HTTP Header Protocol
The stateless protocol passes methods and tool names through HTTP headers, parsed by `parse_mcp_headers`:
```rust
use sdforge::mcp::headers::parse_mcp_headers;
// Client requests must carry:
// Mcp-Method: tools/call
// Mcp-Name: get_user
let info = parse_mcp_headers(&headers)?;
```
Missing headers return `400 Bad Request`, consistent with the 2026-07-28 specification.
### π Multi Round-Trip Requests (MRTR)
New MRTR support is added. Tools can suspend execution via `InputRequiredResult` and wait for the client to provide additional input. It is automatically canceled after a 300-second timeout:
```rust
use sdforge::mcp::mrtr::MrtrSessionManager;
let manager = MrtrSessionManager::new();
let result = manager.create_session("session-1", "get_user")?;
// The client later resumes execution via session_id
```
### πΎ Cache Semantics
The `cache_semantics` module handles the `ttlMs` and `cacheScope` fields, supporting both `global` and `request` cache scopes. It integrates with oxcache to implement tool result caching.
### π Migration Steps
1. Replace the `mcp-sdk` dependency in `Cargo.toml` with `rmcp` (`features = ["server"]`)
2. Change the `register_mcp(&mut Server)` call to `register_mcp(&mut dyn McpToolRegistry)`
3. Remove the `initialize` handshake-related code and use the `server/discover` endpoint instead
4. If you need MRTR or cache semantics, import the corresponding modules
> For the complete migration example, see `examples/src/mcp/migration_2026.rs`.
---
## <span id="deployment-guide">π Production Deployment</span>
### π³ Docker Deployment
```dockerfile
FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release --features full
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/sdforge /usr/local/bin/
EXPOSE 3000
CMD ["sdforge", "serve", "--port", "3000"]
```
### βΈοΈ Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: sdforge-api
spec:
replicas: 3
selector:
matchLabels:
app: sdforge-api
template:
metadata:
labels:
app: sdforge-api
spec:
containers:
- name: sdforge
image: sdforge:latest
ports:
- containerPort: 3000
env:
- name: FEATURES
value: "full"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
```
### π§ Environment Configuration
```bash
# Production environment variables
export RUST_LOG=info
export SD_FORGE_PORT=3000
export SD_FORGE_HOST=0.0.0.0
export SD_FORGE_CONFIG_PATH=/etc/sdforge/config.toml
export SD_FORGE_FEATURES=full
```
---
## <span id="troubleshooting">π Troubleshooting</span>
### π Common Issues
#### **Compilation Errors**
```bash
# Error: Feature not found
# Solution: Check available features
# Enable specific features
cargo build --features "http,security,cache"
```
#### **Runtime Issues**
```bash
# Check logs with tracing
RUST_LOG=debug cargo run --features logging
# Common port conflicts
# Solution: Change port or kill existing process
lsof -i :3000
kill -9 <PID>
```
#### **Performance Issues**
```bash
# Profile with cargo-flamegraph
cargo install flamegraph
cargo flamegraph --bin sdforge --features full
# Memory usage analysis
valgrind --tool=massif target/release/sdforge
```
### π Health Check Endpoint
```rust
#[service_api(
name = "health_check",
version = "v1",
path = "/health",
method = "GET"
)]
async fn health_check() -> Result<HealthStatus, ApiError> {
Ok(HealthStatus {
status: "healthy".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
uptime: get_uptime(),
})
}
```
### π Getting Help
- π [Documentation](https://docs.rs/sdforge)
- π [Issue Tracker](https://github.com/Kirky-X/sdforge/issues)
- π¬ [Discussions](https://github.com/Kirky-X/sdforge/discussions)
- π§ [Support Email](mailto:support@sdforge.dev)
---
<div align="center">
**Built with β€οΈ using Rust**
</div>
---
<div align="center">
</div>
<style>
.back-to-top {
position: fixed;
bottom: 20px;
right: 20px;
padding: 10px 20px;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
transition: background-color 0.3s;
z-index: 1000;
}
.back-to-top:hover {
background-color: #0056b3;
}
</style>