Write less server code. Composable derive macros for Rust. Write your implementation once, project it into multiple protocols.
Server-less is about minimizing boilerplate while maximizing flexibility:
- Convention over configuration - Sensible defaults that just work
- Composable - Stack multiple macros on the same impl block
- Progressive disclosure - Simple by default, powerful when needed
- Escape hatches - Drop to manual code whenever you want
Write your business logic once, expose it through multiple protocols:
use server_less::prelude::*;
struct UserService { /* ... */ }
#[http(prefix = "/api")]
#[cli(name = "users")]
#[mcp(namespace = "users")]
#[ws(path = "/ws")]
impl UserService {
/// Create a new user
pub async fn create_user(&self, name: String, email: String) -> Result<User, UserError> {
// your implementation
}
/// Get user by ID
pub async fn get_user(&self, id: String) -> Option<User> {
// your implementation
}
/// List all users
pub fn list_users(&self, limit: Option<u32>) -> Vec<User> {
// your implementation
}
}This generates:
| Protocol | Generated | Usage |
|---|---|---|
| HTTP | http_router() |
Axum router with POST /api/users, GET /api/users/{id}, OpenAPI |
| CLI | cli_command() |
Clap commands: users create-user --name X --email Y |
| MCP | mcp_call() |
Model Context Protocol tools: users_create_user, users_get_user |
| WebSocket | ws_router() |
JSON-RPC 2.0: {"method": "create_user", "params": {...}} |
Generate working server implementations:
| Macro | Protocol | Generated Code | Status |
|---|---|---|---|
#[http] |
REST/HTTP | Axum router + OpenAPI spec | ✅ Production Ready |
#[cli] |
Command Line | Clap subcommands | ✅ Production Ready |
#[mcp] |
Model Context Protocol | Tool schemas + dispatch | ✅ Production Ready |
#[ws] |
WebSocket | JSON-RPC 2.0 over WebSocket | ✅ Stable |
#[jsonrpc] |
JSON-RPC | Standalone JSON-RPC handler | ✅ Stable |
#[graphql] |
GraphQL | Schema + resolvers (async-graphql) | ✅ Working* |
*GraphQL has minor type mapping limitations for complex types
Generate IDL/schema files for cross-language services:
| Macro | Protocol | Output | Status |
|---|---|---|---|
#[grpc] |
gRPC | .proto files (Protocol Buffers) |
✅ Working |
#[capnp] |
Cap'n Proto | .capnp schema files |
✅ Working |
#[thrift] |
Apache Thrift | .thrift IDL files |
✅ Working |
#[smithy] |
AWS Smithy | .smithy model files |
✅ Working |
#[connect] |
Connect RPC | Connect protocol schemas | ✅ Working |
Generate API documentation and contracts:
| Macro | Spec Type | Output | Status |
|---|---|---|---|
#[openrpc] |
OpenRPC | JSON-RPC API specification | ✅ Working |
#[asyncapi] |
AsyncAPI | WebSocket/messaging spec | ✅ Working |
#[jsonschema] |
JSON Schema | JSON Schema definitions | ✅ Working |
#[markdown] |
Markdown | Human-readable API docs | ✅ Working |
| Macro | Purpose | Status |
|---|---|---|
#[derive(ServerlessError)] |
Error code inference + HTTP status mapping | ✅ Working |
| Macro | Purpose | Status |
|---|---|---|
#[serve] |
Compose multiple protocol routers | ✅ Working |
#[route] |
Per-method attribute overrides | ✅ Working |
Total: 18 macros, 171 passing tests, 0 failures
- Impl-first design - Write methods once, derive protocol handlers
- Method naming conventions -
create_*→ POST,get_*→ GET,list_*→ collection, etc. - Return type handling -
Result<T,E>,Option<T>,Vec<T>,(), plainTall mapped correctly - Async support - Both sync and async methods work seamlessly
- SSE streaming -
impl Stream<Item=T>for Server-Sent Events (Rust 2024+ use<>) - Error mapping - Automatic HTTP status codes and error responses
- Doc comments -
///comments become API documentation - Parameter extraction - Automatic path/query/body inference
- Feature gated - Only compile what you need
- Zero runtime overhead - Pure compile-time code generation
[dependencies]
# Get everything (recommended for getting started)
server-less = { git = "https://github.com/rhi-zone/server-less" }
# Or select specific features
server-less = { git = "https://github.com/rhi-zone/server-less", default-features = false, features = ["http", "cli", "mcp"] }| Category | Features |
|---|---|
| Runtime protocols | http, cli, mcp, ws, jsonrpc, graphql |
| Schema generators | grpc, capnp, thrift, smithy, connect |
| Spec generators | openrpc, asyncapi, jsonschema, markdown |
| Convenience | full (all features, default) |
Note: ServerlessError derive is always available (zero deps).
Check out examples/ for working code:
- http_service.rs - REST API with Axum + OpenAPI
- cli_service.rs - CLI application with Clap
- user_service.rs - Multi-protocol (HTTP + CLI + MCP + WS)
- ws_service.rs - WebSocket JSON-RPC server
- streaming_service.rs - SSE streaming over HTTP
Server-less supports SSE streaming by returning impl Stream<Item = T>:
use futures::stream::{self, Stream};
#[http]
impl Service {
/// Stream events to the client
pub fn stream_events(&self, count: u64) -> impl Stream<Item = Event> + use<> {
stream::iter((0..count).map(|i| Event { id: i }))
}
}Important: The + use<> syntax is required for Rust 2024 edition when using impl Trait in return position with streaming. This tells the compiler to capture all generic parameters in scope. Without it, you'll get compilation errors about lifetime capture.
// ✅ Correct - Rust 2024
pub fn stream(&self) -> impl Stream<Item = T> + use<> { ... }
// ❌ Error - Missing use<> in Rust 2024
pub fn stream(&self) -> impl Stream<Item = T> { ... }The generated code automatically wraps your stream in SSE format with proper event handling.
- Core runtime protocols (HTTP, CLI, MCP, WebSocket, JSON-RPC, GraphQL)
- Schema generators (gRPC, Cap'n Proto, Thrift, Smithy, Connect)
- Specification generators (OpenRPC, AsyncAPI, JSON Schema, Markdown)
- Error derive macro with HTTP status mapping
- Serve macro for multi-protocol composition
- 187 passing integration tests ✨
- Complete design documentation and tutorials
- Working examples for all major protocols
- GraphQL improvements: Array/object type mapping fixed
- Error handling: Proper Result types in schema validation
- SSE streaming: Server-Sent Events support
- Documentation: Inline docs for all macros with examples
- Attribute customization:
#[route(method="POST", path="/custom", skip, hidden)] - Helpful error messages: All macros provide actionable hints
- Compile-time validation: HTTP path validation, duplicate route detection
- Response customization:
#[response(status = 201)] - Parameter customization:
#[param(query, name = "q")] - WebSocket bidirectional: Server-push patterns
- OpenAPI separation: Extract as standalone macro
- Improved error messages with better span information
- Code action support (IDE integration hints)
- Middleware/hooks pattern for cross-cutting concerns
- Hot reloading support for development
- Schema validation at compile time
- Performance benchmarks vs hand-written code
- API versioning support
- Rate limiting derive macro
- Authentication/authorization hooks
- Request/response transformation layers
- Schema sharing across protocols
- Multi-language client generation (TypeScript, Python)
- API stability guarantees
- Production battle-testing
- Performance optimization
- Long-term support commitment
- Extension ecosystem
Server-less follows an impl-first design approach:
- Write your implementation - Focus on business logic
- Add protocol macros - Derive handlers from methods
- Customize as needed - Progressive disclosure of complexity
- Escape hatch available - Drop to manual code when needed
- Minimize barrier to entry - The simple case should be trivial
- Progressive disclosure - Complexity appears only when you need it
- Gradual refinement - Start simple, incrementally add control
- Not here to judge - Support multiple workflows, don't prescribe
- Silly but proper - Simple things done right (good errors, readable code)
See docs/design/ for detailed design philosophy.
nix develop # Enter dev shell (optional)
cargo build # Build all crates
cargo test # Run all tests (171 passing)
cargo clippy # Lint checks
cargo expand # Inspect macro expansionserver-less/
├── crates/
│ ├── server-less/ # Main crate (re-exports)
│ ├── server-less-macros/ # Proc macro implementations (18 macros, 5,142 LOC)
│ ├── server-less-core/ # Core traits & error types
│ ├── server-less-parse/ # Shared parsing utilities
│ └── server-less-rpc/ # RPC dispatch utilities
└── docs/
├── design/ # Design documents
└── .vitepress/ # Documentation site
- Design Philosophy - Impl-first approach and naming conventions
- Extension Coordination - How macros compose
- Implementation Notes - Technical decisions
- Iteration Log - Evolution and design decisions
- CLAUDE.md - Development guidelines for AI assistants
Server-less is part of the Rhizome ecosystem - tools for building composable systems.
Related projects:
- Lotus - Object store (uses server-less for server setup)
- Spore - Lua runtime with LLM integration
- Hypha - Async runtime primitives
Contributions welcome! Please check:
- Run tests:
cargo test - Run clippy:
cargo clippy --all-targets --all-features -- -D warnings - Format code:
cargo fmt --all - Follow conventional commits
See CLAUDE.md for development guidelines.
MIT License - see LICENSE for details.
Inspired by the composability of Serde and the "just works" experience of Clap.