slave 0.13.0

CLI for slave, a Laravel-inspired Rust framework for MCP-first apps: scaffold an MCP backend with auth, roles, teams and SurrealDB migrations, add server-rendered web apps, and route them by domain or path
slave-0.13.0 is not a library.

slave

A Laravel-inspired Rust framework for MCP-first applications.

Every business operation in a slave app is an MCP 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, teams, passwordless sign-in, SurrealDB with SurrealQL migrations), adds server-rendered web apps (MiniJinja, Tera, Handlebars or Liquid templates), and serves them all from one process routed by domain or path (any app can also run as a process of its own, proxied), in the spirit of laravel new, php artisan make:* and Route::domain().

browser / AI agent
        │
        ▼
runtime (one process) ── 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 in-process)

Why

  • One interface. No REST layer next to MCP: the tools an AI agent calls are the same ones your dashboards call.
  • Batteries included, no passwords. Users and roles, sign-in with a code sent by email or a passkey, server-side sessions that end at once on sign-out, SurrealDB with SurrealQL migrations, and tests that drive /mcp end to end with a throwaway database per test.
  • 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

cargo install slave

Requirements: Rust 1.85+. Deploying to Fly.io needs the Fly CLI.

Quick start

slave new shop && cd shop
surreal start --user root --pass root   # in another terminal, or set SURREAL_* in .env
slave migrate          # create the tables
slave build            # the first build takes a few minutes, later ones seconds
slave serve            # sign-in codes are printed in the log until SMTP_URL is set

Open http://admin.localhost:8000 and sign in with any email; the code is printed in the log until SMTP_URL and MAIL_FROM are set in .env. The first user becomes super admin, and anyone can then add a passkey for quick sign-in. slave doctor checks the setup and says what to fix. Add apps with slave make:app storefront --domain "{toko}.localhost".

Everything runs in one process, the runtime (runtime/). Without --domain/--path, an app is served directly on its own port (still in that process). make:app --separate or app:separate <app> runs an app as a process of its own, proxied by the runtime, to deploy or scale it separately.

  MCP backend   http://127.0.0.1:3000/mcp
  Runtime       http://127.0.0.1:8000  (one process)
    http://mcp.localhost:8000            → MCP backend
    http://<toko>.localhost:8000         → storefront

MCP clients sign in with the auth_email_start and auth_email_verify tools and send the session token they get, for example Claude Code:

claude mcp add --transport http shop http://mcp.localhost:8000/mcp

(with --header "Authorization: Bearer $TOKEN").

Commands

Command Does
slave new <name> Create a project: MCP backend, auth, users, roles, migrations, the runtime, AGENTS.md
slave build [<component>...] [--release] Compile everything, or only backend, runtime or a separate app, e.g. only what changed in CI (--list shows them)
slave serve [--release] Run what was built: the runtime plus each separate app; never compiles
slave doctor Check database, sign-in email, passkeys and build
slave app:separate <app>, app:embed <app> Run an app as a process of its own (proxied by the runtime), or back inside it
slave install:fly [--app <name>] [--region <code>] [--domain <host>] Fly.io files for the single runtime: Dockerfile, fly.toml, config/apps.fly.toml
slave deploy:fly [--secrets <env file>] Create the Fly app if needed, stage secrets, check the required ones, fly deploy (migrations as the release command)
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/ plus a migration defining its table
slave make:migration <name> An empty SurrealQL migration in database/migrations/
slave login [--url], slave logout Sign in to the project's backend (a code by email; needs slave serve running)
slave make:app <name> [--team <slug>] [--domain <host>] [--path </prefix>] [--customer] [--separate] [--stack minijinja|tera|handlebars|liquid] A web app in apps/<name>, owned by one of your teams (requires slave login); direct on its port, or routed by domain/path; in the runtime or separate
slave migrate, migrate:status, migrate:fresh [--force] Apply pending migrations; list them; drop everything and re-apply (asks to confirm)
slave route:list, route:match <host>[/path] Inspect domain and path routing

Database

The backend keeps the platform's own data (users, roles, teams, and what you add) in SurrealDB, on a server it reaches over WebSocket: locally surreal start --user root --pass root, in production SurrealDB Cloud or your own cluster. Set SURREAL_URL, SURREAL_NS, SURREAL_DB, SURREAL_USER, SURREAL_PASS and SURREAL_AUTH_LEVEL in .env, then slave migrate. Migrations are SurrealQL files in database/migrations/, each applied once, in one transaction. Web apps never open the database; they call MCP tools. Tests create a fresh database per test on TEST_SURREAL_URL (default ws://127.0.0.1:8000, root/root) and remove it afterwards.

Deploying to Fly.io

fly auth login
cp .env .env.production                        # then edit it for production
slave install:fly --app tokoku --domain example.com   # optional: deploy:fly runs it if needed
slave deploy:fly --secrets .env.production
fly certs add "*.example.com" --app tokoku     # one per host install:fly prints

install:fly writes a Dockerfile, fly.toml and config/apps.fly.toml (your routes with localhost moved under --domain). deploy:fly creates the app, stages the env file as Fly secrets, refuses to deploy while a required secret is missing, and runs fly deploy; pending migrations run first (release_command). Machines are stateless, so fly scale count 2 works. Environment on Fly:

Variable Required What
SURREAL_URL yes The platform's SurrealDB, e.g. wss://<instance>.surreal.cloud. Migrations run against it on every deploy (release_command)
SURREAL_USER, SURREAL_PASS yes A SurrealDB user; prefer a database user (DEFINE USER ... ON DATABASE, SURREAL_AUTH_LEVEL=database)
SURREAL_NS, SURREAL_DB, SURREAL_AUTH_LEVEL no Namespace and database (default main); where the user is defined (root by default)
SMTP_URL, MAIL_FROM yes Where sign-in codes are sent from, e.g. smtps://user:pass@smtp.resend.com and Shop <no-reply@shop.com>
PASSKEY_RP_ID, PASSKEY_ORIGIN yes The domain passkeys belong to (shared by its subdomains), e.g. shop.com and https://shop.com
<APP>_PUBLIC_URL no Fixed public origin of an app (otherwise taken from the request)
MCP_ALLOWED_HOSTS no Only if MCP clients reach the backend directly instead of through a routed host
APP_NAME, RUST_LOG no Set in fly.toml / the image (info)

Set by the image, do not override: APP_HOST, APP_PORT, GATEWAY_LISTEN (0.0.0.0:8080), SLAVE_ROUTES (config/apps.fly.toml) and SLAVE_TRUSTED_PROXY (1, so apps see https behind Fly's proxy).

Routing

config/apps.toml maps requests to apps, like Laravel's Route::domain() and Route::prefix():

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

Crate Role
slave This CLI and the project templates
slave-framework Runtime used by generated apps: server lifecycle, config, logging, the SurrealDB connection and migrations, passwordless sign-in (email codes, passkeys, sessions), and a test database
slave-gateway Domain/path routing for the runtime: apps in-process, a hyper proxy for separate apps

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