Skip to main content
Glama
truenas-mcp

truenas-mcp

truenas-mcp

MCP server for TrueNAS SCALE 25.10+ and the Docker containers running on it. Runs as a container on the NAS and serves MCP over Streamable HTTP. Talks to TrueNAS via the JSON-RPC 2.0 WebSocket API and to Docker via the local socket.

Why this exists: The official truenas/truenas-mcp binary uses the legacy DDP protocol, which was removed in TrueNAS 25.10. This server targets the new wss://host/api/current JSON-RPC 2.0 endpoint exclusively.

Read-only by default. All TrueNAS tools are read-only. Docker write tools (start/stop/restart, update/rollback, image pull/prune) exist but are only registered when DOCKER_WRITE_TOOLS=true.

Upgrading from 1.x? The npm package @profanter-dev/truenas-mcp (stdio, npx) is deprecated. 2.x ships only as the container image ghcr.io/profanter-dev/truenas-mcp — see Migrating from 1.x.


Requirements

  • TrueNAS SCALE 25.10 or later, with Docker (e.g. stacks managed by Dockge)

  • A TrueNAS API key (generate in System → API Keys)


Related MCP server: truenas-aiops

Deploy

Deploy as a Dockge stack (or plain docker compose) on the NAS. A full example with Traefik labels is in compose.example.yml:

services:
  truenas-mcp:
    image: ghcr.io/profanter-dev/truenas-mcp:latest
    restart: unless-stopped
    environment:
      TRUENAS_HOST: 192.168.1.29:444
      TRUENAS_API_KEY: ${TRUENAS_API_KEY}
      TRUENAS_INSECURE: "true"
      MCP_AUTH_TOKEN: ${MCP_AUTH_TOKEN}
      STACKS_DIR: /mnt/apps/stacks
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /proc:/host/proc:ro   # optional: host processes for process_list
      - /mnt/apps/stacks:/mnt/apps/stacks:ro   # optional: compose files for stack_list/stack_compose (same path as on the host)
    group_add:
      - ${DOCKER_GID}   # stat -c %g /var/run/docker.sock
    read_only: true
    cap_drop: [ALL]
    security_opt: [no-new-privileges:true]

The image runs as the unprivileged node user; group_add gives it access to the Docker socket. Generate the bearer token with openssl rand -hex 32.

GET /healthz (unauthenticated) reports { truenas, docker, version } and backs the image's HEALTHCHECK.


Configuration

Variable

Required

Default

Description

TRUENAS_HOST

✓

host:port of the TrueNAS web UI, e.g. 192.168.1.29:444

TRUENAS_API_KEY

✓

TrueNAS API key

TRUENAS_INSECURE

false

true to skip TLS certificate verification (self-signed certs)

MCP_AUTH_TOKEN

✓

Bearer token clients must send to /mcp

PORT

3000

HTTP port

DOCKER_SOCKET

/var/run/docker.sock

Container tools are disabled if the socket is missing

DOCKER_WRITE_TOOLS

false

true registers the Docker write tools (see below)

HOST_PROC

/host/proc

Where the host's /proc is mounted read-only; enables host-wide process_list

STACKS_DIR

Directory holding the compose stacks (e.g. Dockge/Dockhand /mnt/apps/stacks), mounted read-only at the same path as on the host so the paths in compose labels resolve; enables stack_list / stack_compose


Claude Code setup

claude mcp add --transport http truenas https://truenas-mcp.example.com/mcp \
  --header "Authorization: Bearer <MCP_AUTH_TOKEN>"

Or on the LAN without Traefik, publish the port (ports: ["3000:3000"]) and use http://<nas-ip>:3000/mcp.


Tools

All entities follow a consistent list / details pattern. TrueNAS timestamps are returned in the NAS's configured timezone with their UTC offset (e.g. 2026-10-25T02:00:10+01:00), so they read as local time and stay unambiguous across DST changes.

Storage

Tool

Description

pool_list

All ZFS pools — health, status, usable capacity (used + available)

pool_details

Single pool: health, capacity, last scrub

disk_list

All disks — model, type, pool assignment, current temperature, ZFS error counts

disk_details

Single disk: serial, size, current temperature and 7-day min/max/avg, vdev assignment, ZFS errors

dataset_list

All ZFS datasets with used/available space; optionally filter by pool

path_permissions

Owner, mode and ACL of a path under /mnt as the NAS sees it, plus every parent directory (traversal); optionally a user's UID and groups to compare against

dataset_details

Full properties for a single dataset: compression, dedup, quota, and more

snapshot_list

ZFS snapshots ordered newest-first; filter by dataset, name substring (name_contains) or creation date (since), configurable limit, boot-pool excluded by default

snapshot_details

Single snapshot: size, referenced, compression ratio, clones

TrueNAS 25.10 removed the SMART test API, so SMART results are not available; disk health shows up as alerts (alert_list) and ZFS error counts.

Shares

Tool

Description

share_list

All SMB and NFS shares — path, enabled state, comment

share_details

Full config for a single SMB or NFS share

Services

Tool

Description

service_list

All TrueNAS services with running state and boot-enable flag

service_details

Single service state plus service-specific config (e.g. SSH port and auth settings, SMB workgroup, NFS settings). Private/public key material is stripped.

System

Tool

Description

system_info

Hostname, version, CPU, memory, uptime, load average (1/5/15 min), timezone

metrics_history

History of a Reports graph (cpu, cputemp, memory, load, disk/disktemp per disk, interface, ARC, …) over a time range: min/mean/max with peak time, a downsampled series, and reboots with their downtime window (detected from uptime)

Apps

Tool

Description

app_list

All installed TrueNAS catalog apps with state, version, and update availability

app_details

Single app: state, version, portals, active workloads, notes

app_logs

Recent log output for a TrueNAS catalog app

Note: These tools cover apps installed via the TrueNAS Apps UI (catalog apps). Docker Compose stacks managed by external tools such as Dockge are not visible through the TrueNAS WebSocket API — use the container tools below instead.

Docker containers

Tool

Description

container_list

All Docker containers (running and stopped) — name, image, state, health

container_details

Single container: image, state, ports, mounts, networks, labels, and runtime settings (devices/GPUs, shared network_mode, restart policy, privileges, capabilities, limits, DNS). Secret env vars are redacted.

container_logs

Recent log output for a container; configurable line count (default 100)

stack_list

All compose stacks: each declared service with its container(s) and state, plus missing_containers (declared but no container exists) and orphan_containers (container whose service is no longer declared). Needs STACKS_DIR.

stack_compose

A stack's compose file(s) with secret values redacted and ${VAR} references kept; for .env only the variable names. Needs STACKS_DIR.

Registered when the Docker socket is mounted.

Updates

Tool

Description

update_check

For every container, compares the digest of the image it runs with the registry's current digest for its tag. Read-only.

Diagnostics

Tool

Description

container_stats

Live usage per running container, sorted by CPU: CPU % (100 = one core), memory working set / page cache / limit, PIDs, network and disk IO rates and totals

process_list

Top processes by current CPU or memory, each attributed to its container or host; CPU summed per container; host summary with CPU breakdown (user/system/iowait/steal), load, memory incl. ZFS ARC, swap, pressure stall info, uninterruptible/zombie counts. Command lines are redacted.

process_list needs the host's /proc mounted read-only (/proc:/host/proc:ro). Without it, it falls back to per-container ps output (container processes only, lifetime-average CPU).

Docker write tools (opt-in: DOCKER_WRITE_TOOLS=true)

Tool

Description

container_start

Start a stopped container

container_stop

Stop a container (optional graceful timeout)

container_restart

Restart a container (optional graceful timeout)

image_pull

Pull the latest version of a container's image without recreating it

container_update

Update one container or all running ones: pull, recreate with the same configuration, verify health (see below)

container_rollback

Swap back to the old container kept by a failed update (or keep_old). Deletes nothing.

image_prune

Delete dangling images, or with all=true every unused image

Stop/restart/update refuse to act on the MCP server's own container. Write tools carry MCP destructiveHint annotations so clients can ask for confirmation.

How container_update works

Containers are recreated from their own configuration via the Docker API — no compose files needed, and Dockge still sees them as part of their stack:

  1. Pull the tag; stop if the image is unchanged (unless force).

  2. Build the new container from the old one's config, dropping values inherited from the old image (ENV, labels, CMD, …) so the new image's defaults apply. Auto-assigned MAC addresses and hostnames are not copied; anonymous volumes are re-attached by name.

  3. Stop dependents sharing its network (network_mode: service:X), stop it, rename it to <name>-old-<timestamp>, create and start the new one.

  4. Wait until it is healthy (healthcheck) or stays up for 10 s (no healthcheck).

  5. Recreate the dependents against the new container, then delete the old containers.

Rollback is only automatic when the new version never ran — Docker refused to create or start it (port in use, missing mount, …). If it started and then crashed, turned unhealthy or timed out, it may already have migrated data, so it is left untouched (not even stopped) and the old container is kept stopped as <name>-old-<timestamp>. Inspect its logs (included in the result) and fix forward, or call container_rollback deliberately.

Jobs & Alerts

Tool

Description

job_list

Unique job types with last-run status and timestamp (cron jobs shown with human-readable description)

job_history

Full run history for a specific job, including each run's arguments (secrets masked; for mail.send the email subject, recipients and body); requires description (from job_list) and limit. TrueNAS keeps jobs in memory, so history starts at the last boot.

alert_list

Active alerts sorted by severity: CRITICAL → ERROR → WARNING → NOTICE → INFO. Cleared alerts are gone — use job_history for mail.send to read alert emails.

Scheduled & data-protection tasks

Tool

Description

snapshot_task_list

Periodic snapshot tasks — dataset, recursive/exclude, naming schema, retention, cron schedule, enabled, last run

replication_task_list

Replication tasks — direction, transport, source/target, linked snapshot tasks and naming schemas, schedule, retention, last run. SSH credentials reduced to their name; encryption keys never returned.

cronjob_list

Cron jobs — description, command (credentials redacted), user, cron schedule, enabled

audit_query

Audit log, newest first, filterable by service, event (e.g. SHUTDOWN, REBOOT, AUTHENTICATION), method, username and time range (since/before). Survives reboots, so it shows who shut the NAS down and from where. Needs an API key whose user has the SYSTEM_AUDIT_READ role (e.g. Read-only Administrator).


Security

Access to the Docker socket is root-equivalent on the host, and mounting it :ro does not change that — it only protects the socket file, not the API behind it. This server is therefore the security boundary:

  • src/docker-client.ts checks every request against a fixed allowlist of method + path (read endpoints, start/stop/restart, image pull/prune, and the create/rename/delete steps of an update). Query strings are built internally; there is no generic passthrough, exec, build, volume or network access.

  • /containers/create is only reached through the update path, whose body is always derived from an existing container's inspect output — tool arguments can't supply container configuration, mounts or privileges. Containers are never deleted with their volumes.

  • The /proc mount is read-only; process command lines are redacted for passwords, tokens and URL credentials.

  • The stacks mount is read-only. Only compose files and .env are read, only after their real path is confirmed to be inside STACKS_DIR (no tool argument is used as a path); .env values are never returned, and secret-looking compose values are redacted.

  • /mcp requires the bearer token; comparison is constant-time.

  • Run the container read-only, without capabilities, as non-root (see the compose example), and keep it behind TLS (e.g. Traefik) if it is reachable beyond the LAN.


Development

cp .env.example .env   # fill in values
yarn install
yarn dev               # tsx src/index.ts, serves on :3000
docker build --build-arg APP_VERSION=dev -t truenas-mcp:dev .

Releases: pushing a v* tag builds and publishes ghcr.io/profanter-dev/truenas-mcp (X.Y.Z, X.Y, X, latest) and creates a GitHub release.


Migrating from 1.x

  1. Deploy the container as above (the docker-socket-proxy stack is no longer needed).

  2. Replace the stdio registration:

    claude mcp remove truenas
    claude mcp add --transport http truenas https://truenas-mcp.example.com/mcp \
      --header "Authorization: Bearer <MCP_AUTH_TOKEN>"
  3. DOCKER_PROXY_URL / DOCKER_PROXY_USER / DOCKER_PROXY_PASS are gone; the socket mount replaces them.


Protocol details

  • WebSocket URL: wss://<host>/api/current

  • Auth: auth.login_with_api_key — called once on connect; never reconnects per-call

  • Rate limit: TrueNAS enforces 20 auth attempts per 60 s; exceeding triggers a 10-minute lockout

  • The server maintains a single persistent connection, shared by all MCP clients, with exponential-backoff reconnection on unexpected disconnects

Related MCP Connectors

Related MCP Servers