added gitea, n8n and invokeai MCP servers, plus docs/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
code
2026-07-14 20:47:29 +00:00
parent 06fcf6bab7
commit bcd7e012ba
9 changed files with 375 additions and 1 deletions
+14
View File
@@ -0,0 +1,14 @@
# claudaris docs
Claudaris (klaw-DAR-iss) is a movable Docker container for running
[Claude Code](https://code.claude.com/docs) in isolation, built on Arch Linux.
Your login, dotfiles, and MCP configuration live on the host, so the image can
be rebuilt at any time (always pulling the latest Claude Code release) without
losing anything.
- **[Getting started](getting-started.md)** — prerequisites, first-time setup,
and daily use.
- **[The `claudaris` executable](claudaris.md)** — every subcommand, what it
does, and the files and mounts behind it.
- **[MCP servers](mcp.md)** — the Gitea, n8n, and InvokeAI MCP servers baked
into the image, and how to point them at your instances.
+96
View File
@@ -0,0 +1,96 @@
# The `claudaris` executable
`./claudaris` is a single bash script at the repo root that wraps every
Docker operation. It `cd`s to its own directory first, so it can be invoked
from anywhere. Run it with no arguments (or `help`) for usage.
```
Usage: ./claudaris <command>
Commands:
config, configure Interactive wizard to write .env with your settings
build Build the container image (always --no-cache --pull)
start Start (or create) the container
connect Start the container if needed, then attach a tmux session
remove, stop, rm Stop and remove the container
help Show this message
```
All commands source `.env` if present (gitignored, written by `config`),
falling back to `NAME=claudaris` and the defaults listed below.
## `config` (alias: `configure`)
Interactive wizard: prompts for `NAME`, `DATA_DIR`, and `WORKSPACE_DIR`,
showing the current/default value for each (enter keeps it), then writes all
three to `.env`. Safe to re-run any time. See
[Getting started](getting-started.md#1-configure) for what each variable
means.
## `build`
```bash
sudo ./claudaris build
```
Runs `docker build --no-cache --pull -t "$NAME" .`. Always uncached so a
rebuild picks up the latest Arch packages, the latest Claude Code release
(installed straight into `/root` in the image), and the latest
[MCP server](mcp.md) releases.
## `start`
Creates and starts the container — or just `docker start`s it if a container
named `$NAME` already exists (in that case none of the mount setup below is
re-evaluated; use `remove` first to pick up mount changes).
On the host side it first seeds, without overwriting anything that already
exists:
- `$DATA_DIR/home/.bashrc` and `$DATA_DIR/home/bash_aliases` — copied from
`files/bashrc` and `files/bash_aliases`.
- `$DATA_DIR/claude/credentials.json` and `$DATA_DIR/claude/claude.json`
created empty. Seeding them as files matters: if Docker had to create the
mount targets itself it would make directories, breaking the login mounts.
Then it runs the container with:
| Mount / flag | Purpose |
| --- | --- |
| `--name "$NAME" --hostname "$NAME"` | Prompt reads `root@$NAME` instead of a random container ID. |
| `$DATA_DIR/home/.bashrc``/root/.bashrc` | Per-host shell config, editable without a rebuild. |
| `$DATA_DIR/home/bash_aliases``/opt/dotfiles/bash_aliases` | Per-host aliases and [MCP env vars](mcp.md); sourced by `.bashrc`. |
| `$DATA_DIR/claude/credentials.json``/root/.claude/.credentials.json` | Claude Code OAuth token. |
| `$DATA_DIR/claude/claude.json``/root/.claude.json` | Claude Code account/onboarding state and user-scope MCP config. |
| `$WORKSPACE_DIR``/projects` | Your project workspace. |
| `$DATA_DIR/ssh``/root/.ssh` (read-only, only if the host dir exists) | Opt-in SSH access to other machines. |
The login files are individual file mounts rather than a directory volume on
purpose: Claude Code saves them atomically (write a temp file, then
`rename()` over the target), and `rename()` onto a symlink replaces the
symlink instead of writing through it — which would silently break
persistence. A bind mount doesn't have that failure mode.
## `connect`
```bash
sudo ./claudaris connect
```
Runs `docker start "$NAME"` (a no-op if already running) and then
`docker exec -it "$NAME" tmux`. The in-container `tmux` is a wrapper that
attaches to the `main` session if it exists and creates it otherwise, so
`connect` always lands you in the same session.
The explicit `docker start` matters because the container stops itself: the
entrypoint creates the `main` tmux session and exits once that session ends
(tmux defaults — a window closes when its shell exits, the session closes
with its last window). There's no `--restart` policy, so after a Ctrl+D the
container sits stopped until `connect` (or `start`) brings it back.
## `remove` (aliases: `stop`, `rm`)
Stops (ignoring errors if already stopped) and removes the container, so the
next `start` recreates it fresh — needed after a `build` to actually run the
new image, or after changing mounts. Nothing under `$DATA_DIR` or
`$WORKSPACE_DIR` is touched.
+89
View File
@@ -0,0 +1,89 @@
# Getting started
Claudaris runs Claude Code inside an Arch Linux container, with your project
workspace bind-mounted in and your login persisted on the host. Everything is
driven by the [`./claudaris` executable](claudaris.md) at the repo root.
## Prerequisites
- Docker installed and running on the host.
- Root (or equivalent Docker) access — the examples below use `sudo`.
- A Claude account to log in with on first run.
## 1. Configure
From the repo root, run the interactive wizard and answer its prompts
(press enter to keep a default):
```bash
./claudaris config
```
It asks for three values and writes them to `.env` (gitignored — every user
of a shared checkout keeps their own):
| Variable | Default | Meaning |
| --------------- | ---------------------- | -------------------------------------------------------------- |
| `NAME` | `claudaris` | Image *and* container name. Pick something unique to you (e.g. `chris-claude`) if several people run containers from the same repo checkout. |
| `DATA_DIR` | `/data/$NAME` | Host directory for everything that must survive rebuilds: dotfiles, Claude login, optional SSH keys. |
| `WORKSPACE_DIR` | `/home/$USER/projects` | Host directory mounted as `/projects` inside the container — this is where Claude Code works. |
Re-run `config` any time to change these.
## 2. Build the image
```bash
sudo ./claudaris build
```
The build always runs with `--no-cache --pull`, so every rebuild picks up the
latest Arch packages and the latest Claude Code release. Claude Code is
installed into the image itself (not a volume) — rebuilding is how you
upgrade it.
## 3. Start and connect
```bash
sudo ./claudaris start
sudo ./claudaris connect
```
`start` creates the container (seeding host-side copies of the dotfiles and
login files on first run); `connect` attaches you to its tmux session. You
land in a shell at `/projects` as `root@$NAME`.
On first connect, run `claude` and log in. The two files that carry the login
(`~/.claude/.credentials.json` and `~/.claude.json`) are bind-mounted from
`$DATA_DIR/claude/` on the host, so you stay logged in across container
rebuilds and re-creations.
## 4. Daily use
- **Attach:** `sudo ./claudaris connect` — also restarts the container if it
stopped. The container exits on its own when the tmux session ends (last
shell exits via Ctrl+D, `exit`, or a crash); there is no Docker restart
policy, so `connect` bringing it back up is the normal flow.
- **Detach without stopping anything:** standard tmux detach
(`Ctrl+b d`).
- **Upgrade Claude Code / packages:** `sudo ./claudaris remove`, then
`sudo ./claudaris build` and `sudo ./claudaris start`. Login, dotfiles, and
MCP registration all survive this.
## Customizing your shell
`~/.bashrc` and the aliases file are bind-mounted single files living at
`$DATA_DIR/home/.bashrc` and `$DATA_DIR/home/bash_aliases` on the host. Edit
them there (or inside the container) and the changes persist across rebuilds —
no image change needed. They're seeded from `files/bashrc` and
`files/bash_aliases` the first time `start` runs and never overwritten after
that.
This is also where you configure the [MCP servers](mcp.md): the bottom of
`bash_aliases` has commented `export` lines for the Gitea/n8n/InvokeAI URLs
and tokens.
## Optional: SSH access to other machines
To let the container SSH out, create `$DATA_DIR/ssh` on the host and populate
it with keys/config *before* running `start`. If the directory exists, it's
bind-mounted read-only at `/root/.ssh`. It's off by default.
+78
View File
@@ -0,0 +1,78 @@
# MCP servers
The image ships three [MCP](https://modelcontextprotocol.io/) servers for
Claude Code, all speaking stdio:
| Server | Name in `/mcp` | What it gives Claude | Source |
| --- | --- | --- | --- |
| Gitea | `gitea` | Repos, issues, PRs, releases on your Gitea instance | [gitea/gitea-mcp](https://gitea.com/gitea/gitea-mcp) (official, latest release binary) |
| n8n | `n8n` | n8n node documentation, plus workflow creation/management when the API env vars are set | [czlonkowski/n8n-mcp](https://github.com/czlonkowski/n8n-mcp) (npm) |
| InvokeAI | `invokeai` | Text-to-image, img2img, and upscaling against your InvokeAI instance | [coinstax/invokeai-mcp-server](https://github.com/coinstax/invokeai-mcp-server) (PyPI) |
## How install and registration are split
The Dockerfile only *installs* the servers (`/usr/local/bin/n8n-mcp`,
`/usr/local/bin/gitea-mcp`, and a `uv` tool venv for InvokeAI).
*Registration* happens in `files/entrypoint.sh` on every container start,
via `claude mcp add --scope user`.
It has to work that way: user-scope MCP config lives in `~/.claude.json`,
which [`claudaris start`](claudaris.md#start) bind-mounts from the host — so
anything registered at build time would be shadowed by the mounted file. The
runtime registration writes into the mounted file instead, which also means
it persists across rebuilds like the login does.
Registration is guarded per server: a server that's already present is left
alone, so hand edits to its config survive restarts. The flip side is that a
server deleted with `claude mcp remove` comes back on the next container
start — to change one permanently, edit its entry rather than removing it.
## Pointing the servers at your instances
The servers read their URLs and tokens from the environment Claude Code runs
in (Claude Code passes its environment through to stdio MCP servers). Set
them in your host-persisted aliases file, `$DATA_DIR/home/bash_aliases`,
where a commented template already exists at the bottom:
```bash
export GITEA_HOST='https://gitea.example.com'
export GITEA_ACCESS_TOKEN='...'
export N8N_API_URL='https://n8n.example.com' # optional: enables workflow management tools
export N8N_API_KEY='...'
export INVOKEAI_BASE_URL='http://invokeai.example.com:9090'
```
Then start a new shell (or `source ~/.bashrc`) and launch `claude`. Because
the file lives on the host, tokens never end up in the image or the repo. If
your `bash_aliases` was seeded before the MCP servers existed, copy the
template block from `files/bash_aliases`.
Notes:
- **URLs must be reachable from inside the container.** `127.0.0.1` means
the container itself, not the host. Use the host's LAN address or service
hostname.
- **Gitea:** create the token in Gitea under *Settings → Applications →
Generate New Token*.
- **n8n:** without `N8N_API_URL`/`N8N_API_KEY` the server still works as a
node-documentation reference; the API pair unlocks creating and managing
workflows. Create the key in n8n under *Settings → n8n API*.
- **InvokeAI:** defaults to `http://127.0.0.1:9090` if unset. Upstream
hardcodes that URL; the Dockerfile patches the package so
`INVOKEAI_BASE_URL` is honored (and the build fails loudly if a future
upstream release breaks the patch). Registration uses
`python -m invokeai_mcp_server` because the package's console script is
broken upstream.
## Checking it works
Inside the container:
```bash
claude mcp list # all three should show as configured
```
Or in a Claude Code session, run `/mcp` to see connection status, and try
something like *"list my Gitea repos"*. A server whose env vars are unset
will show as failed/erroring until you set them — the other servers are
unaffected.