field manual

Roundhouse User Guide

On this page

Everything you need to install, operate, and administer Roundhouse — from docker compose up to a fleet of deployed MCP servers.

Screenshots in this guide are shown in dark theme. A light-theme set lives under docs/screenshots/light/. See docs/capture/README.md for how to re-capture.

Getting started#

Roundhouse runs entirely on your own hardware. The only requirements are Docker and Docker Compose — there is no cloud dependency, no external service to sign up for, and no telemetry.

Install#

bash
git clone https://github.com/Karmatek-Consulting-LLC/roundhouse.git
cd roundhouse
cp .env.example .env
docker compose up -d

When the API logs print Application startup complete, the platform is live at http://localhost:3080.

To stop the platform:

bash
docker compose down        # preserve database + spec files
docker compose down -v     # wipe everything

Sign in#

Roundhouse uses email/password sign-in. The first admin is created from the ADMIN_EMAIL and ADMIN_PASSWORD environment variables when the API boots (defaults: [email protected] / admin — change them in .env before first boot, or from the Users page after).

Login
fig. 01 — login

Connecting clients#

Every deployed server gets a stable URL like http://localhost:3080/s/my-server/mcp. Traefik routes MCP clients straight to the spawned container — the platform never proxies MCP traffic on the hot path.

Callers authenticate with a bearer token minted on the server's Auth tab (see Auth below).

Claude Desktop#

Add to claude_desktop_config.json:

json
{
  "mcpServers": {
    "my-server": {
      "url": "http://localhost:3080/s/my-server/mcp",
      "headers": {
        "Authorization": "Bearer <token from the server's Auth panel>"
      }
    }
  }
}

Claude Code#

bash
claude mcp add my-server \
  --url    http://localhost:3080/s/my-server/mcp \
  --header "Authorization: Bearer <token>"

Any other MCP client that speaks streamable HTTP works the same way: point it at the server URL and pass the token in the Authorization header.

Dashboard#

The dashboard is the home view after sign-in. It surfaces fleet-wide health (running/stopped/errored counts), recent call volume, the busiest servers, and a rolling activity timeline.

Dashboard
fig. 02 — dashboard

Servers list#

The full inventory of MCP servers you can see. Each row shows the server's deploy mode (structured or code), live status, recent call count, and quick actions.

Servers list
fig. 03 — servers list

Creating a server#

The Create button opens a dialog with a tab for each way a server can be authored.

Structured#

The default: an empty spec-managed server. Primitives, packages, env vars, and middleware are managed through the UI; Roundhouse owns the Dockerfile.

Create — Structured
fig. 04 — create — structured

Code-first#

You supply a complete server.py (FastMCP). Roundhouse still owns the Dockerfile and platform middleware, but the primitive surface is hidden in favour of a full source editor.

Create — Code-first
fig. 05 — create — code-first

From Git#

Clone a repo that declares its dependencies in roundhouse.json. The imported server registers as not_deployed — Roundhouse seeds the environment variables declared by the manifest, populates pip and apt packages, and waits for an operator to fill in secrets before the first deploy.

Create — From Git
fig. 06 — create — from git

Import#

Paste an exported spec JSON (from another Roundhouse instance, or from POST /api/servers/{name}/export) to clone a server's configuration verbatim.

Create — Import
fig. 07 — create — import

The server editor#

Selecting a server opens the two-pane editor. The left rail is the server's table of contents — primitives, configuration, and operational tabs. The right pane is whichever section you've selected.

Overview#

The overview is the editor's home base: description, replicas, resource limits, and the lifecycle controls (Start, Stop, Redeploy, Delete, plus Update from Git for repos imported via from-git).

Editor — overview
fig. 08 — editor — overview

Primitives#

Structured servers expose tools, resources, resource templates, and prompts. Each primitive is edited in-place: parameters, body, and optional middleware overrides (rate limit, max concurrency).

Tool#

Editor — primitive (tool)
fig. 09 — editor — primitive (tool)

Resource#

Editor — primitive (resource)
fig. 10 — editor — primitive (resource)

Prompt#

Editor — primitive (prompt)
fig. 11 — editor — primitive (prompt)

Adding a new primitive#

Editor — new primitive
fig. 12 — editor — new primitive

Imports and globals#

Any free-form Python imports or module-level globals that should appear in the generated server.py.

Editor — imports
fig. 13 — editor — imports

PyPI packages#

Pip dependencies. They are pinned into the generated Dockerfile and installed at build time.

Editor — packages (pip)
fig. 14 — editor — packages (pip)

APT packages#

OS-level packages installed via apt-get in the build. Use for native toolchains a Python wheel needs.

Editor — apt packages
fig. 15 — editor — apt packages

Environment variables#

Per-server env vars (with optional encrypted-at-rest secrets) and global imports. Changes save instantly; the redeploy banner surfaces because the new value only takes effect on the next container restart.

Editor — env vars
fig. 16 — editor — env vars

Auth (server tokens)#

Bearer tokens that callers present in the Authorization header. Per-token scopes can lock individual primitives down to specific token holders.

Editor — auth
fig. 17 — editor — auth

Assets#

Arbitrary files that get baked into the image under /app/assets/. Useful for prompt templates, large JSON fixtures, or any read-only data your tools reference at runtime.

Editor — assets
fig. 18 — editor — assets

Usage#

Per-primitive call counts, p50/p95/p99 latency, error rate, and the busiest client tokens. Sampled in-process by the platform middleware and surfaced without an extra metrics backend — no Prometheus, no Grafana, no add-on agent. Drilling into a server's usage tab is how you find latency regressions or the one tool that's getting hammered.

Editor — usage (dispatch)
fig. 19 — editor — usage (dispatch)

A busier server (crew-scheduling, which carries the heaviest call volume in the demo fleet) makes the chart variety obvious:

Editor — usage (crew-scheduling)
fig. 20 — editor — usage (crew-scheduling)

Logs#

Streams stdout/stderr from the server container via Server-Sent Events.

For spec-based servers, the Level dropdown writes the LOG_LEVEL environment variable on the spec; the platform middleware reads it and configures stdlib logging accordingly. At DEBUG, the middleware also emits a start record (with arguments) for every tool/resource/prompt call. Failed calls promote the end-of-call record to WARNING. The dropdown is hidden for code-mode servers, since they own their own logging surface.

Editor — logs
fig. 21 — editor — logs

Source (code mode)#

For servers in code mode, the editor replaces the primitive surface with a full server.py editor (CodeMirror, Python syntax highlighting). The platform still controls the Dockerfile.

Editor — source (code mode)
fig. 22 — editor — source (code mode)

Code-mode servers still expose every operational tab — env vars, logs, usage — so the operator surface is unchanged.

Editor — code-mode env
fig. 23 — editor — code-mode env

Stopped server#

A stopped server keeps its spec on disk; restarting it reuses the cached image without a rebuild.

Editor — stopped
fig. 24 — editor — stopped

Platform administration#

Roundhouse ships with a small set of platform admin views.

Platform settings#

Host configuration: external hostname, Docker registry credentials, custom CA bundle for outbound TLS, and platform-wide env defaults that get imported into spec-based servers.

Platform settings
fig. 25 — platform settings

Users#

User accounts with role assignment. The seeded demo includes a handful of operators alongside the platform admin; superadmins can reset passwords and revoke access from this page.

Users
fig. 26 — users

Teams#

Teams group users into shared-access bundles. A team can own one or more servers, and any team member inherits access to those servers without needing per-server token grants. The seeded demo creates three railroad departments — Operations, Maintenance of Way, and Revenue Service — each with its own member roster.

Teams
fig. 27 — teams

Audit log#

Every state-mutating action — server create/delete, env-var change, token rotation — is recorded with the acting user, target, and a structured detail payload.

Audit log
fig. 28 — audit log

Configuration reference#

Everything reads from environment variables — see .env.example for the full list. The knobs you'll actually touch:

VariablePurpose
APP_KEYbase64:<32 random bytes> — encrypts runtime tokens at rest. Generate with printf 'base64:%s' "$(openssl rand -base64 32)".
MCP_BASE_URLThe URL clients see for spawned servers. Set this when deploying past localhost.
MCP_DOCKER_HOST/var/run/docker.sock (default) or tcp://socket-proxy:2375 for hardened Swarm setups.
MAX_MCP_SERVER_REPLICASPer-server replica cap (Swarm only).
ADMIN_EMAIL / ADMIN_PASSWORDFirst-boot seed user. Ignored once a user exists.

Behind a corporate TLS-inspecting proxy#

If your network MITMs outbound TLS during image builds, drop the proxy's CA bundle at api/docker/corp-ca.crt before running docker compose build. The Dockerfiles pick it up automatically; the file is gitignored so it can't be committed by accident. In any other environment, leave it absent and the build skips that step.

Deployment modes#

ModeFileBest for
Local / single-hostdocker-compose.ymlTrying it out, small teams, hosting on one box. Single Docker daemon, Traefik on the same socket.
Docker Swarmdocker-stack.ymlMulti-node, scoped socket proxies. Designed to sit behind a cluster ingress that terminates TLS.

Both modes are fully self-contained — well suited to air-gapped and restricted networks where outbound connectivity and cloud integrations aren't an option.