# slave
A Laravel-inspired Rust framework for **MCP-first applications**.
Every business operation in a slave app is an
[MCP](https://modelcontextprotocol.io) tool served over Streamable HTTP, so AI
agents and your own web apps use the same, single API. The `slave` CLI
scaffolds the backend (users, roles, OpenID Connect sign-in, Turso Cloud database,
migrations), adds server-rendered web apps (MiniJinja, Tera, Handlebars or
Liquid templates), and routes them by domain or path through a gateway, in the spirit of `laravel new`, `php artisan make:*`
and `Route::domain()`.
```
browser / AI agent
│
▼
gateway (Pingora, optional) ── config/apps.toml: route by domain and/or path
├──► MCP backend /mcp: auth_*, user_*, role_*, your tools … ← owns the database
└──► apps/storefront theme, server-rendered templates (calls MCP tools)
```
## Why
- **One interface.** No REST layer next to MCP: the tools an AI agent calls are
the same ones your dashboards call.
- **Batteries included, security delegated.** Users and roles, sign-in
through any OpenID Connect provider (no passwords stored, ID tokens
verified on every call), migrations with rollback, tests that drive `/mcp`
end to end with a built-in test identity provider.
- **Pick a template language per app.** Server-rendered pages (MiniJinja,
Tera, Handlebars or Liquid) for a storefront theme or an admin area; apps
reach the backend only through MCP.
- **Laravel ergonomics.** Generators, optional domain and path routing,
`route:list`, `slave build` then one `slave serve` for everything.
- **Agent-ready projects.** Every new project ships an `AGENTS.md` (and a
`CLAUDE.md` that imports it) teaching coding agents the architecture, the
rules and recipes for adding features.
## Install
```sh
cargo install slave
```
Requirements: Rust 1.85+. Optional: [CMake](https://cmake.org) once you
install the gateway (Pingora compiles zlib-ng: `brew install cmake` /
`apt install cmake`).
## Quick start
```sh
slave new shop && cd shop
slave migrate # users and roles (set OIDC_* and TURSO_* in .env first)
slave make:app storefront --domain "{toko}.localhost" # theme per store (installs the gateway)
slave build # first build takes a while; later ones seconds
slave serve
```
Without `--domain`/`--path`, template apps are served directly on their own
ports and the gateway (and its Pingora build) is never added.
```
MCP backend http://127.0.0.1:3000/mcp
Gateway http://127.0.0.1:8000
http://mcp.localhost:8000 → MCP backend
http://<toko>.localhost:8000 → storefront
```
The first user to sign in through your OpenID Connect provider becomes
`admin`. MCP clients send the provider's ID token, for example Claude Code:
```sh
claude mcp add --transport http shop http://mcp.localhost:8000/mcp
```
(with `--header "Authorization: Bearer $ID_TOKEN"`), or sign in through one
of the apps.
## Commands
| `slave new <name> [--gateway]` | Create a project: MCP backend, auth, users, roles, migrations, `AGENTS.md` |
| `slave build [<component>...] [--release]` | Compile everything, or only `backend`, `gateway` or named apps, e.g. only what changed in CI (`--list` shows them) |
| `slave serve [--release]` | Run what was built: backend, gateway and apps; never compiles |
| `slave install:gateway` | Add the optional domain/path gateway |
| `slave make:tools <Name>Tools [--module m]` | MCP tools for a module (`src/modules/<m>/tools.rs`) |
| `slave make:model <Name> [--module m]` | A model in `src/modules/<m>/models/` |
| `slave make:migration <name>` | A timestamped migration (`create_*_table` gets a CREATE TABLE template) |
| `slave make:app <name> [--domain <host>] [--path </prefix>] [--stack minijinja\|tera\|handlebars\|liquid]` | A web app in `apps/<name>`; direct on its port, or routed by the gateway |
| `slave migrate`, `migrate:status`, `migrate:rollback` | Run, inspect and roll back migrations |
| `slave route:list`, `route:match <host>[/path]` | Inspect domain and path routing |
## Routing
`config/apps.toml` maps requests to apps, like Laravel's `Route::domain()` and
`Route::prefix()`:
```toml
[[domain]]
host = "admin.localhost" # by domain
app = "dashboard"
port = 4000
[[domain]]
host = "{toko}.localhost" # {toko} reaches the app as X-Slave-Param-Toko
app = "storefront"
port = 4001
[[domain]]
path = "/console" # by path, on any host (add `host` to restrict)
app = "console"
port = 4002
```
The most specific entry wins regardless of order: the longest path prefix,
then the most specific host. Unknown routes get 404, a down app gets 502, and
spoofed `X-Slave-*` / `X-Forwarded-*` headers are dropped.
## Crates
| [`slave`](https://crates.io/crates/slave) | This CLI and the project templates |
| [`slave-framework`](https://crates.io/crates/slave-framework) | Runtime used by generated apps: server lifecycle, config, logging, Turso database and migrations, password hashing and tokens |
| [`slave-gateway`](https://crates.io/crates/slave-gateway) | The domain/path routing gateway, built on [Pingora](https://github.com/cloudflare/pingora) |
## Repository layout
```
src/ the slave CLI (commands, generators)
templates/ files written by `slave new` and `slave make:*`
framework/ slave-framework
gateway/ slave-gateway
tests/ end-to-end tests of the CLI and generated projects
```
Run the tests with `cargo test --workspace`; `cargo test -p slave --test cli
-- --ignored` also builds a generated project and runs its own test suite.
## License
MIT