Skip to content

rhi-zone/server-less

server-less

Tests Rust License

Write less server code. Server-less is a projection system for Rust. You write an impl block — plain methods with plain types — and server-less projects it onto arbitrary protocols: HTTP, CLI, WebSocket, MCP, gRPC, and more.

You're not writing a server, a CLI app, or an API. You're writing your logic. Server-less handles the rest.

impl UserService {
    pub fn create_user(&self, name: String, email: String) -> Result<User, UserError> {
        // This is just your code. No framework, no protocol awareness.
    }
}

Add attributes to project it:

#[http(prefix = "/api")]  // → POST /api/users, GET /api/users/{id}
#[cli(name = "users")]    // → users create-user --name X --email Y
#[mcp]                    // → MCP tools: create_user, get_user

Each projection is competitive with hand-written code using the protocol's native library (axum, clap, etc.). That's the quality bar, not the pitch. The pitch is: annotate once, project anywhere.

Quick Start

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": {...}}

Available Macros

Runtime Protocol Handlers

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

Schema Generators

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

Specification Generators

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

Error Handling

Macro Purpose Status
#[derive(ServerlessError)] Error code inference + HTTP status mapping ✅ Working

Coordination

Macro Purpose Status
#[serve] Compose multiple protocol routers ✅ Working
#[route] Per-method attribute overrides ✅ Working

Total: 18 macros, 171 passing tests, 0 failures

Features

  • 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>, (), plain T all 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

Installation

[dependencies]
# Get everything (recommended for getting started)
server-less = "0.2"

# Or select specific features
server-less = { version = "0.2", default-features = false, features = ["http", "cli", "mcp"] }

Available Features

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).

Examples

Check out examples/ for working code:

Server-Sent Events (SSE) Streaming

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.

Roadmap

v0.2 — Shipped ✅

  • Runtime protocols: HTTP, CLI, MCP, WebSocket, JSON-RPC, GraphQL
  • Schema generators: gRPC, Cap'n Proto, Thrift, Smithy, Connect
  • Spec generators: OpenRPC, AsyncAPI, JSON Schema, Markdown docs
  • #[derive(ServerlessError)] with per-protocol status mapping
  • Blessed presets: #[server], #[rpc], #[tool], #[program]
  • Mount points for nested subcommand/route composition
  • #[route] / #[response] HTTP overrides
  • #[param(positional)], #[param(short)], #[param(query)]
  • #[server(skip)], #[server(hidden)], #[cli(default)]
  • CLI output: Display default, --json, --jq, --output-schema
  • Iterator / SSE streaming return types
  • 466 passing tests

Up Next

  • WebSocket server-push patterns
  • Middleware / hooks for cross-cutting concerns (auth, rate limiting, logging)
  • IDE code action hints

Later

  • API versioning
  • Multi-language client generation (TypeScript, Python)
  • Production hardening and stability guarantees

Philosophy

Server-less is a projection system, not a framework. The distinction matters:

  • Frameworks own your code. You write handlers in their shape, using their types.
  • Server-less projects your code. You write plain Rust methods. Attributes are semantic metadata — #[param(help = "...")] becomes CLI help text and OpenAPI description and MCP tool input docs simultaneously.

Progressive disclosure. The zero-config case should just work. Complexity appears only when you need it. Don't like how server-less handles something? Drop that one derive and write it by hand — everything else still composes.

Prior art: Serde. #[derive(Serialize)] doesn't compete with hand-written JSON serializers. It's a projection from Rust types onto data formats. Server-less does the same thing, from Rust methods onto protocols.

See docs/design/ for detailed design documents.

Development

nix develop        # Enter dev shell (optional)
cargo build        # Build all crates
cargo test         # Run all tests (329 passing)
cargo clippy       # Lint checks
cargo expand       # Inspect macro expansion

Project Structure

server-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

Documentation

Part of RHI

Server-less is part of the RHI ecosystem - tools for building composable systems.

Contributing

Contributions welcome! Please check:

  1. Run tests: cargo test
  2. Run clippy: cargo clippy --all-targets --all-features -- -D warnings
  3. Format code: cargo fmt --all
  4. Follow conventional commits

See CLAUDE.md for development guidelines.

License

MIT License - see LICENSE for details.

Acknowledgments

Inspired by Serde's model: derive macros as a projection interface, not a straitjacket.

Packages

 
 
 

Contributors

Languages