Upstream is unsafe to point at a NAS that hosts anything of consequence: - system_reboot is registered with an empty input schema and a handler that calls system.reboot immediately, so a model can take the host down in one unconfirmed tool call. The README's claim of dry-run on 'all write operations' is not accurate: dry-run is opt-in per call, and ExecuteWithDryRun() falls through to real execution when the argument is omitted. - get_app_config returns app.config verbatim, putting database passwords, encryption keys and API tokens into the model's context and into any transcript that persists it. -read-only is a fail-closed allowlist: 31 non-mutating tools are served, the other 21 are refused, and anything not explicitly reviewed -- including tools upstream adds later -- is refused by default. A denylist would silently admit the next system_reboot. Refused tools are hidden from tools/list and rejected at dispatch. Redaction is unconditional, read-write mode included. A credential has no business reaching the model, and relying on the operator to field-filter is not a control. Tests assert the mutating-tool list against the live registry, so an upstream rename breaks the build instead of quietly widening the boundary.
15 KiB
TrueNAS MCP Server
This is a fork of truenas/truenas-mcp, adding a hard
-read-onlymode and unconditional secret redaction. See Why this fork exists. Upstream remains the canonical project; this tracks it and carries a small safety patch.
⚠️ Research Preview This project is in active development and released as a research preview. APIs and features may change. Not recommended for production use.
A Model Context Protocol (MCP) server for TrueNAS that enables AI models to interact with the TrueNAS API using natural language queries.
Why this fork exists
Two problems make upstream unsafe to point at a NAS that hosts anything you care about.
1. system_reboot has no confirmation. It is registered with an empty input schema — no
dry_run, no argument of any kind — and its handler calls system.reboot immediately. A model can
reboot the host in one unconfirmed tool call. Upstream's README claims "Dry-Run Mode — Preview
changes before execution for all write operations." That is not accurate: dry-run is opt-in per
call, and ExecuteWithDryRun() falls straight through to real execution when the argument is
absent. It is a hint, not a boundary.
2. get_app_config returns credentials verbatim. It calls app.config and returns the app's
entire configuration map — database passwords, encryption keys, Redis passwords, API tokens — into
the model's context, and from there into a transcript that persists indefinitely.
This fork adds:
-read-only— a fail-closed allowlist. The server serves 31 non-mutating tools and refuses the other 21, including anything upstream adds later that we have not reviewed. A denylist of known-bad names would silently admit the nextsystem_reboot; an allowlist cannot. Refused tools are hidden fromtools/listand rejected at dispatch, so naming one directly does not work.- Unconditional secret redaction — every tool response is walked and credential-shaped fields are masked, in read-write mode too. There is no legitimate reason for a password to reach the model, and "the operator remembered to field-filter" is not a control.
truenas-mcp --truenas-url 192.168.1.10 --api-key "$TRUENAS_API_KEY" --read-only
Both behaviours are covered by tests in tools/readonly_test.go, which
assert against the live upstream registry — so if upstream renames or removes a tool, the test fails
loudly rather than quietly widening the boundary.
Table of Contents
- Features
- Architecture
- Building
- Installation
- Command-Line Options
- Connection Details
- Security
- Example Usage
- Advanced Features
- Development
Features
TrueNAS MCP provides comprehensive management capabilities through natural language:
Core Categories
- 📊 Monitoring - System info, health, alerts, performance metrics
- 💾 Storage - Pools, datasets, snapshots, shares (SMB/NFS)
- 🖥️ Virtualization - VM management and status
- 🔐 Directory Services - Active Directory, LDAP, FreeIPA integration and health monitoring
- 📈 Capacity Planning - Utilization analysis and trend projections
- 🔄 Maintenance - System updates, boot environments, pool scrubs
- 📦 Applications - Catalog search, guided installation with storage setup, app management and upgrades
- ⚙️ Tasks - Long-running operation tracking
Key Capabilities
- Intelligent Filtering & Sorting - Query datasets, snapshots, VMs with smart filters
- Read-Only Mode -
-read-onlyserves only non-mutating tools and refuses the rest (fail-closed) - Secret Redaction - credential-shaped fields are masked in every tool response
- Dry-Run Mode - opt-in per call via
"dry_run": true, and supported by some write operations. It is not a safety boundary: a call that omits the argument executes for real, andsystem_rebootaccepts no arguments at all. Use-read-onlyif you need a boundary. - Task Tracking - Automatic progress monitoring for updates, upgrades, and scrubs
- Safety Checks - Built-in validation prevents dangerous operations
- Natural Language - Ask questions in plain English, get actionable insights
📖 View complete feature list →
Architecture
Single native binary that runs on your desktop and connects directly to TrueNAS:
┌──────────────────┐
│ Claude Desktop │
└────────┬─────────┘
│ stdio (JSON-RPC)
┌────────▼───────────────────┐
│ truenas-mcp │ (Your Desktop)
│ - stdio interface │
│ - Tool registry │
│ - WebSocket client │
└────────┬───────────────────┘
│ Secure WebSocket (wss://)
│ + TrueNAS API key auth
┌────────▼──────────────────┐
│ TrueNAS Middleware │
│ - WebSocket HTTPS endpoint│
│ - Port 443 (wss) │
└───────────────────────────┘
Key Benefits:
- ✅ No deployment to TrueNAS required
- ✅ Runs entirely on your desktop
- ✅ Secure WebSocket connection (wss://) to TrueNAS middleware
- ✅ Self-signed certificate support (works with TrueNAS defaults)
- ✅ Cross-platform support (macOS, Linux, Windows)
- ✅ Simple configuration with hostname or full WebSocket URL
- ✅ API key protection (requires encrypted connections)
Building
# Download dependencies
go mod download
# Build for local platform
make build
# Build for all platforms (macOS, Linux, Windows)
make build-all
Installation
Step 1: Download or Build Binary
Choose the appropriate binary for your platform:
macOS (Apple Silicon):
sudo cp truenas-mcp-darwin-arm64 /usr/local/bin/truenas-mcp
sudo chmod +x /usr/local/bin/truenas-mcp
macOS (Intel):
sudo cp truenas-mcp-darwin-amd64 /usr/local/bin/truenas-mcp
sudo chmod +x /usr/local/bin/truenas-mcp
Linux:
sudo cp truenas-mcp-linux-amd64 /usr/local/bin/truenas-mcp
sudo chmod +x /usr/local/bin/truenas-mcp
Windows:
copy truenas-mcp-windows-amd64.exe C:\Windows\System32\truenas-mcp.exe
Step 2: Get TrueNAS API Key
- Log into your TrueNAS web interface
- Go to System Settings → API Keys
- Click Add to create a new API key
- Give it a name (e.g., "Claude Desktop MCP")
- Make sure it has appropriate permissions (admin recommended)
- Copy the API key - you'll need it for configuration
Step 3: Configure Your MCP Client
Claude Desktop
Edit your Claude Desktop configuration file:
macOS:
vi ~/Library/Application\ Support/Claude/claude_desktop_config.json
Linux:
vi ~/.config/Claude/claude_desktop_config.json
Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add the TrueNAS MCP server configuration:
{
"mcpServers": {
"truenas": {
"command": "truenas-mcp",
"args": [
"--truenas-url", "truenas.local",
"--api-key", "your-api-key-here"
]
}
}
}
Configuration options:
Option 1: Hostname (automatically uses wss://):
"args": [
"--truenas-url", "192.168.0.31",
"--api-key", "your-api-key-here"
]
Option 2: Using environment variables:
{
"mcpServers": {
"truenas": {
"command": "truenas-mcp",
"env": {
"TRUENAS_URL": "192.168.0.31",
"TRUENAS_API_KEY": "your-api-key-here"
}
}
}
}
Claude Code
Claude Code uses the claude mcp command to configure MCP servers:
Add TrueNAS MCP server:
claude mcp add truenas -- truenas-mcp --truenas-url 192.168.0.31 --api-key your-api-key-here
Verify the configuration:
claude mcp list
claude mcp get truenas
Manage the server:
# Remove the server
claude mcp remove truenas
# Re-add with updated configuration
claude mcp add truenas -- truenas-mcp --truenas-url 192.168.0.31 --api-key new-api-key
Step 4: Restart Your MCP Client
Claude Desktop: Quit Claude Desktop completely and restart it.
Claude Code: The MCP server will be loaded automatically when you use Claude Code.
Step 5: Verify the Connection
You should now be able to ask TrueNAS questions:
- "What version of TrueNAS is running?"
- "Show me all storage pools and their health"
- "List all datasets"
- "What shares are configured?"
- "Show me system metrics for the past hour"
Command-Line Options
Flags
--truenas-url- TrueNAS hostname (required, or useTRUENAS_URLenv var)- Examples:
truenas.localor192.168.0.31(automatically useswss://on port 443) - ⚠️ Note:
ws://(unencrypted) is not allowed - TrueNAS will revoke API keys used over unencrypted connections
- Examples:
--api-key- TrueNAS API key for authentication (required, or useTRUENAS_API_KEYenv var)--insecure- Skip TLS verification (not needed - self-signed certs accepted by default)--debug- Enable debug logging--version- Print version and exit
Examples
# Basic usage
./truenas-mcp --truenas-url 192.168.0.31 --api-key your-api-key
# Using environment variables
export TRUENAS_URL=192.168.0.31
export TRUENAS_API_KEY=your-api-key
./truenas-mcp
# With debug logging
./truenas-mcp --truenas-url 192.168.0.31 --api-key your-api-key --debug
Connection Details
How It Works
The binary connects directly to TrueNAS middleware's WebSocket endpoint:
- Uses secure WebSocket (wss://): Connects to
wss://your-truenas:443/websocket - Self-signed certs accepted: Works with TrueNAS default self-signed certificates
- Authenticates via API key: Uses
auth.login_with_api_keymethod
⚠️ Security Requirement
IMPORTANT: TrueNAS requires encrypted connections (wss://) for API key authentication. Using unencrypted ws:// will cause your API key to be revoked as a security measure. This binary defaults to wss:// to protect your credentials.
Troubleshooting
Connection Issues:
- Verify TrueNAS is accessible from your machine
- Check firewall allows port 443 (wss)
- Verify API key is valid and has admin permissions
Authentication Failures:
- Generate a new API key in TrueNAS System Settings → API Keys
- Ensure the key has appropriate permissions
- Check that the key wasn't accidentally truncated when copying
Security
- Authentication: TrueNAS API key required for all operations
- TLS/SSL: Only supports wss:// (encrypted) - ws:// is rejected for security
- Self-signed certificates: Accepted by default (common for TrueNAS)
- Network: Client-only (no listening ports, all connections outbound)
- API Key Storage: Recommend using environment variables instead of command-line args
Security Best Practices
- Always use secure WebSocket (wss://) - enforced by default, ws:// is rejected
- Generate dedicated API key for MCP use only
- Use environment variables for API keys in Claude Desktop config
- Restrict API key permissions to minimum required
- Rotate API keys periodically
Example Usage
Once connected via an MCP client, ask questions in natural language:
Quick Start Examples
Monitoring:
- "What version of TrueNAS is running?"
- "Are there any system alerts?"
- "Show me CPU and memory usage over the past day"
Storage:
- "Show me all storage pools and their health status"
- "What are the top 10 datasets using the most space?"
- "List snapshots for the tank/shares/data dataset"
Maintenance:
- "Check if there are any TrueNAS system updates available"
- "What's the scrub status of my pools?"
- "Show me boot environments that are safe to delete"
Management:
- "Create a new dataset for file sharing"
- "Set up a weekly scrub schedule for tank on Sunday at 2am"
- "Upgrade the plex app to the latest version"
💬 View complete example queries →
Advanced Features
MCP Tasks for Long-Running Operations
The server implements MCP Tasks specification for operations that take time to complete (like app upgrades):
How it works:
- Write operations (like
upgrade_app) return atask_idinstead of blocking - Tasks are automatically tracked in the background
- Use
tasks_getwith the task ID to check progress - Tasks update automatically - no manual polling needed
Example:
User: "Upgrade the plex app"
→ Returns: {"task_id": "abc-123", "status": "working", ...}
User: "Check task abc-123"
→ Returns: {"status": "completed", "result": {...}}
Task States:
working- Operation in progresscompleted- Operation finished successfullyfailed- Operation encountered an errorcancelled- Operation was cancelled
Dry-Run Mode for Write Operations
Write operations support previewing changes before execution:
How to use:
Add "dry_run": true to any write operation to preview what would happen without making changes.
Example:
Tool: upgrade_app
Args: {"app_name": "plex", "dry_run": true}
Returns:
{
"tool": "upgrade_app",
"current_state": {
"name": "plex",
"version": "1.32.5.7349",
"state": "RUNNING"
},
"planned_actions": [
{
"step": 1,
"description": "Stop application containers",
"operation": "stop",
"target": "plex"
},
{
"step": 2,
"description": "Upgrade from 1.32.5.7349 to latest",
"operation": "upgrade",
"target": "plex"
},
{
"step": 3,
"description": "Start application with new version",
"operation": "start",
"target": "plex"
}
],
"warnings": [],
"estimated_time": {
"min_seconds": 30,
"max_seconds": 300,
"note": "Time varies based on image size and network speed"
}
}
Benefits:
- See exactly what will change before committing
- Understand prerequisites and warnings
- Get time estimates for operations
- Build confidence before making changes
Development
# Run linters
make lint
# Run tests
make test
# Clean build artifacts
make clean