Skip to content

About

Ephemeral, privileged MCP relays that let Claude Code administer home-lab appliances (Synology NAS, ASUS/Merlin router) over Tailscale — token auth, forensic audit, gated root exec.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

mage-hands

Always-up, privileged MCP relays that let Claude Code administer home-lab appliances it can't (or shouldn't) run an agent on directly. The model runs on your Mac; each appliance runs only a tiny relay that executes structured requests locally and returns results.

The project is two layers:

Layer What it is Lives in
Core (mage_hands_core) Reusable, pip-installable relay framework: token auth, forensic audit, the lease gate, the gated run() tool, and the read path policy. The security machinery lives here once, so every appliance inherits it. common/
Appliances Thin servers that pick an executor (how to run on the target) and register target-specific tools. synology-hands administers a Synology NAS (privileged container + nsenter). synology-hands/

Heads-up: the relay is always up. Read-only inspection tools always work. Anything that can change the box (run, read_file, restarts, firewall changes) needs a time-limited lease that Michael grants out-of-band with a YubiKey touch (nas-unlock <appliance> on the Mac); a locked call returns a structured refusal telling Claude to ask for it. The relay is root on the target and is not sandboxed; safety comes from network isolation (loopback bind + tailnet-only tailscale serve), a bearer token, the lease gate, dry-run/replay gating on raw execution, and a forensic audit log. (The host's own WAN exposure is separate; the internet_exposure tool surfaces it.)

How it works

┌──────────────┐  https://<nas>.<tailnet>.ts.net/mcp   ┌─────────────────────────────┐
│ Claude (Mac) │ ─── Tailscale Serve (TLS, ACL'd) ───► │ appliance (e.g. Synology)   │
│  MCP client  │ ◄── structured JSON ─────────────────│  Tailscale + Serve :443     │
└──────────────┘  Authorization: Bearer <token>        │   ↓ proxy 127.0.0.1:8787    │
                  (Serve injects Tailscale-User-*)      │  relay container (always up)│
                                                        │   privileged, pid:host, /:/  │
                                                        │   FastMCP /mcp → nsenter →  │
                                                        │   host (docker, smartctl…)  │
                                                        └─────────────────────────────┘
  1. The relay runs in a privileged container and drives the host via nsenter -t 1 — so it uses the host's own toolchain (docker, smartctl, syno*), no docker-socket mount.
  2. tailscale serve terminates HTTPS on the tailnet (never the public internet) and proxies to the relay bound on loopback only.
  3. Your Mac connects to it as a normal remote MCP server; tools appear as mcp__<name>__*.

See ARCHITECTURE.md for the request lifecycle, security model, tool tiers, and audit schema; AGENTS.md for a contributor/agent guide; lessons.md for deployment lessons and Synology gotchas.

Quick start

Already deployed and just want to use it? → docs/getting-started.md (the tools load in any Claude session; unlock with nas-unlock when a change is needed).

Deploying to a new appliance? → docs/deploy.md (end-to-end runbook).

Prerequisites

  • An appliance that runs Docker/Container Manager (x86 Synology DSM 7.2+ for synology-hands).
  • Tailscale on both the appliance and your Mac, with MagicDNS + HTTPS enabled on the tailnet.
  • Claude Code on your Mac.

Tool tiers

The relay exposes capabilities in three tiers (see ARCHITECTURE.md):

Tier Nature Examples Gating
A inspection (read-only) system_info, disk_usage, storage_health, list_containers, container_logs, internet_exposure, performance, pending_updates, firewall_status, firewall_rules, firewall_diagnose none: always open
A, gated file read read_file lease + allow/deny policy
B controlled mutation restart_container, restart_service, firewall_enable, firewall_disable, firewall_reload, firewall_set_rules, pending_updates(check=true) lease; typed args; firewall changes are lock-out-guarded
C raw root exec run(command, exec_token) lease + dry-run → one-time replay token + catastrophic-pattern denylist

Repository layout

mage-hands/
├── common/                       # mage_hands_core — the reusable relay framework
│   ├── pyproject.toml
│   └── mage_hands_core/
│       ├── server.py             # build_server(): FastMCP + auth + audit + lifespan
│       ├── auth.py               # StaticTokenVerifier (real 401 at the transport layer)
│       ├── audit.py              # forensic JSONL log + identity allowlist + lease gate
│       ├── lease.py              # the lease file: read / validate / slide, fail closed
│       ├── exec.py               # Runners (Shell/Nsenter) + gated run() (token + timeouts)
│       ├── denylist.py           # catastrophic-command regexes for run()
│       ├── policy.py             # PathPolicy + policied read_file
│       ├── meminfo.py            # memory_stats(): MemAvailable, emulated on DSM's 3.10 kernel
│       └── config.py             # env-driven Config
├── synology-hands/               # appliance #1: Synology NAS
│   ├── server.py                 # Tier A/B tools + lease tiers; registers read_file + run()
│   ├── firewall.py               # DSM firewall audit + lock-out-guarded changes
│   ├── Dockerfile  compose.yaml  .env.example
│   ├── scripts/                  # mage-hands-lease · install-host · smoke-test.py · tailscale/plex/watchtower-update · ups-healthcheck
│   └── README.md
├── net-monitor/                  # standalone (NOT an MCP relay): always-on internet connectivity logger on kappa
│   ├── compose.yaml  monitor.sh  summary.sh
│   └── README.md
├── docs/
│   ├── getting-started.md        # use a deployed relay from a fresh Claude session
│   ├── deploy.md                 # deploy/operate a new appliance
│   ├── maintenance.md            # update Tailscale, scheduled tasks, troubleshooting
│   └── lease-gate-design.md      # the approved lease-gate design
├── ARCHITECTURE.md  AGENTS.md  CLAUDE.md  lessons.md
├── CONTRIBUTING.md               # how to contribute + security-first ground rules
├── LICENSE                       # Apache-2.0
└── README.md

License

MIT © Michael Timpe.

About

Ephemeral, privileged MCP relays that let Claude Code administer home-lab appliances (Synology NAS, ASUS/Merlin router) over Tailscale — token auth, forensic audit, gated root exec.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages