omv-mcp
An MCP server that lets an AI assistant manage anOpenMediaVault NAS — check disks and filesystems, inspect shares and users, readS.M.A.R.T. health, restart services, apply configuration changes, run background jobs andtail their output.
It connects over plain SSH and drives OMV's own omv-rpc CLI, which is the exact sameRPC layer the web interface uses. Nothing gets installed on the NAS and no extra port isopened.
MCP client --stdio--> omv_mcp.py --ssh--> NAS --> omv-rpc --> OMV RPC layer
(your PC) (your PC) (the same layer as the web UI)
No dependencies. The server is pure standard library — including its MCP protocollayer — so there is no virtualenv to create, nothing to pip install, and it runs on anyPython 3.9 or newer.
Why only six tools
OMV exposes roughly 50 RPC services with several hundred methods between them, and everyplugin adds more. Rather than wrapping each one in a hand-written tool, omv-mcp exposes asmall generic set: discover the services, discover a service's methods, call amethod. The assistant explores the API the same way a developer would.
The practical benefit: any plugin you install later — Docker/Compose, ZFS, K8s, whatever —works immediately, with no update to this server.
Requirements
| NAS | OpenMediaVault 7 or 8, reachable over SSH |
| Your machine | Python 3.9 or newer. That includes the Python already on macOS and most Linux systems; on Windows, install it from python.org or the Microsoft Store. |
| Access | An SSH key that can log in without a password, and a user who may run omv-rpc |
Developed and verified against OpenMediaVault 8.5.6-1 (Synchrony) on Debian 13.OMV 7 uses the same RPC layer and is expected to work; reports welcome.
Install as a Claude Desktop extension (recommended)
The extension gives you a settings panel — NAS address, SSH user, key, read-only mode —so nothing has to be edited in any file.
- Download
omv-mcp-<version>.mcpbfrom thelatest release. - Double-click it, or drag it onto the Claude Desktop window. (Also available underSettings → Extensions → Advanced settings → Install Extension…)
- Fill in at least the NAS hostname or IP address, then enable the extension.
| Setting | Default | What it does |
|---|---|---|
| NAS hostname or IP address | — | Required. An IP, or a host from your ~/.ssh/config. |
| SSH username | root |
The account used to log in |
| SSH port | 22 |
Change only for a non-standard port |
| SSH private key | (empty) | Optional; empty uses your ssh-agent and ~/.ssh/config |
| OpenMediaVault user | admin |
The OMV login the RPC runs as — not the SSH user |
| Run commands with sudo | off | Turn on when the SSH user is not root |
| Read-only mode | on | Refuses anything that is not a read operation |
| Allow arbitrary shell commands | off | Adds the unrestricted omv_shell tool |
| Command timeout | 60s |
Per-command limit |
Read-only mode is on by default. Turn it off once you are comfortable letting Claudechange things.
You still need working SSH key access to the NAS — see SSH setup below.
Building the bundle yourself
git clone https://github.com/mbgroen/omv-mcp.git
cd omv-mcp
python3 scripts/build_mcpb.py
The .mcpb lands in dist/. It is an ordinary zip archive with a manifest.json at theroot, so the build script needs nothing but the standard library — no Node.js, no mcpbCLI.
Install manually
Useful for Claude Code, for other MCP clients, or if you would rather not use anextension.
git clone https://github.com/mbgroen/omv-mcp.git
There is nothing to install. Point your client at omv_mcp.py with your system Python.
Claude Code:
claude mcp add openmediavault -e OMV_SSH_HOST=192.168.1.100 -e OMV_READONLY=1 -- python3 "$PWD/omv_mcp.py"
Claude Desktop, in ~/Library/Application Support/Claude/claude_desktop_config.json(macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"openmediavault": {
"command": "python3",
"args": ["/absolute/path/to/omv-mcp/omv_mcp.py"],
"env": {
"OMV_SSH_HOST": "192.168.1.100",
"OMV_SSH_USER": "root",
"OMV_RPC_USER": "admin",
"OMV_READONLY": "1"
}
}
}
}
Use absolute paths, not ~. Restart the client completely afterwards — closing thewindow is not enough for Claude Desktop.
Environment variables
The extension sets these for you; this table is for manual setups.
| Variable | Default | Meaning |
|---|---|---|
OMV_SSH_HOST |
(empty) | Hostname or IP of the NAS. Empty means run commands locally — use that if you run this server on the NAS. |
OMV_SSH_USER |
root |
SSH user |
OMV_SSH_PORT |
22 |
SSH port |
OMV_SSH_KEY |
(empty) | Path to a private key. Leave empty to use your ssh-agent and ~/.ssh/config. |
OMV_RPC_USER |
admin |
The OMV user the RPC call runs as |
OMV_SUDO |
0 |
1 prefixes every command with sudo, for non-root SSH users |
OMV_READONLY |
0 |
1 refuses anything that does not look like a read method, and disables omv_shell |
OMV_ALLOW_SHELL |
1 |
0 removes the omv_shell tool entirely |
OMV_TIMEOUT |
60 |
Per-command timeout in seconds |
Booleans accept 1/true/yes/on and their opposites.
SSH setup
The server runs ssh with BatchMode=yes, so password logins will not work. This isdeliberate: an MCP server has no way to prompt you for a password. Use a key.
ssh-keygen -t ed25519 -C "omv-mcp" # skip if you already have a key
ssh-copy-id [email protected] # use your own NAS address
ssh [email protected] 'omv-rpc -u admin System getInformation'
If that last command prints JSON, the server will work.
Root login refused? OMV disables SSH root login by default. Either enable it underServices → SSH → Permit root login, or use your own account and turn on sudo — thataccount needs to be in the sudo group.
Because ~/.ssh/config is honoured, you can keep the details there instead:
Host nas
HostName 192.168.1.100
User root
IdentityFile ~/.ssh/id_ed25519
and then use nas as the hostname.
Verify
Ask your assistant "Is my OMV connection working?". It should call omv_connection_infoand report your OMV version.
Tools
| Tool | Purpose |
|---|---|
omv_connection_info |
Show the active configuration and test connectivity |
omv_list_services |
List all RPC services, including those added by plugins |
omv_list_methods |
List the methods of one service |
omv_call |
Call an RPC method — the main tool |
omv_wait_for_task |
Collect the output of a background job |
omv_shell |
Run an arbitrary shell command on the NAS |
omv_call
The workhorse. Takes a service, a method, and optionally a parameter dict:
omv_call("System", "getInformation")
omv_call("FileSystemMgmt", "enumerateMountedFilesystems", {"includeRoot": True})
omv_call("ShareMgmt", "enumerateSharedFolders")
omv_call("Smart", "getListBg", {"start": 0, "limit": -1})
A few services to know about:
| Service | What it covers |
|---|---|
System |
System information, time settings, reboot and shutdown |
FileSystemMgmt, DiskMgmt, FsTab |
Disks, filesystems, mount points |
ShareMgmt |
Shared folders and their permissions |
UserMgmt |
Users and groups |
Smart |
S.M.A.R.T. health and scheduled tests |
Services |
Status of the service daemons |
SMB, NFS, FTP, Rsync |
The individual file-sharing services |
Config |
Pending configuration changes and applying them |
Exec |
Background job control |
Rather than memorising these, let the assistant call omv_list_services andomv_list_methods — that always reflects your actual installation.
Background jobs
Heavier methods return {"filename": "..."} and keep running on the NAS. Pass thatfilename to omv_wait_for_task, which polls until the job finishes and returns theaccumulated output:
result = omv_call("Apt", "upgrade") # -> {"filename": "/tmp/bgstatus..."}
omv_wait_for_task(result["filename"], max_seconds=600)
omv_shell
An escape hatch for everything outside the RPC layer — journalctl, docker ps,smartctl, package state. Prefer omv_call for anything OMV manages itself, so OMV'sconfiguration database stays in sync with the system. Disabled by default in theextension.
Security
This server can do anything you can do in the OMV web interface, and — if you enable theshell tool — run arbitrary commands as root. That is the point of it, but be clear-eyedabout what it means: the only thing standing between a mistaken suggestion and a wipedfilesystem is the tool confirmation dialog in your MCP client. Read what it says beforeapproving.
Sensible precautions:
- Keep read-only mode on until you have a feel for what the assistant does with it.
- Leave the shell tool off unless you actually need it.
- Use a dedicated SSH key for this server rather than your everyday key.
- Consider a non-root user with sudo and a narrowed sudoers rule.
- Keep it on your LAN. There is no authentication in this server itself; its securityboundary is your SSH configuration.
Read-only mode uses a prefix heuristic — a method is allowed if its name starts withget, enumerate, list, is, has, read, query, find, exists, count orcheck. It is deliberately conservative and will occasionally block a harmless method.It is a guard rail, not a security boundary: it cannot stop a read method that happens tohave side effects.
Service and method names are validated against ^[A-Za-z0-9_]+$ and every valueinterpolated into a shell command is passed through shlex.quote, so parameters cannotbreak out into the shell.
How it works
Discovery
An RPC service's name is not its file name — it is whatever the PHP getName() methodreturns. So omv_list_services greps the sources in/usr/share/openmediavault/engined/rpc/ and reads the names out. The result is cached forthe lifetime of the process; restart the server after installing a plugin.
Three details about OMV's sources that this parser handles, and which are easy to getwrong if you write your own:
- Quoting is inconsistent. OMV's PHP mixes
'and"freely. On OMV 8.5, six of the52 service names and about a third of allregisterMethod()calls use single quotes.Matching only double quotes silently loses them. - Some service names are lowercase —
kernelandomvextras, for instance — andomv-rpcis case sensitive. Use names exactly asomv_list_servicesreturns them. - One file can hold several RPC classes.
notification.incdefines bothNotificationandEmailNotification, so methods are scoped to the class block theyappear in rather than to the whole file.
Errors are cleaned up too: a failed omv-rpc call writes a JSON blob to stderr containinga full PHP stack trace, and only the message field is surfaced.
The MCP layer
mcp_stdio.py implements the protocol directly: newline-delimited JSON-RPC 2.0 overstdio, initialize with version negotiation, tools/list with input schemas derived fromeach function's signature and docstring, and tools/call.
That is a deliberate choice rather than an exercise. The official MCP Python SDK dependson pydantic, which ships compiled binaries — and the MCPB documentation is explicit thatyou cannot portably bundle compiled dependencies.Implementing the handful of methods a tools-only server needs keeps the extension a single26 KB file that works on macOS, Windows and Linux alike, with no runtime to install and noPython version floor beyond 3.9.
Tests
No dependencies, nothing to install:
python3 -m unittest discover -s tests -t tests -v
Or with pytest, if you prefer its output:
pip install pytest && pytest
The fixtures under tests/fixtures/ are real output captured from an OpenMediaVault8.5.6-1 system (grep dumps of the RPC sources, a successful RPC response and an errorresponse), with host-identifying values replaced. The tests therefore assert against whata NAS actually returns rather than against an idealised sample.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Extension will not start | No python3 on PATH (python on Windows). Check with python3 --version. |
| Server does not appear in the client | JSON syntax error, or a relative path in a manual config |
Permission denied (publickey) |
SSH key not installed, or root login refused by the NAS |
No RPC services found |
The SSH user cannot read /usr/share/openmediavault/engined/rpc |
command not found: omv-rpc |
/usr/sbin is not in the SSH user's PATH — turn on sudo |
Command exceeded 60s |
Raise the timeout, or use omv_wait_for_task for long jobs |
| A method is refused as "not a read method" | Read-only mode is on |
| A newly installed plugin is invisible | Restart the MCP server; the service list is cached |
Claude Desktop writes per-server logs to~/Library/Logs/Claude/mcp-server-openmediavault.log on macOS.
Contributing
Issues and pull requests are welcome — particularly reports from OMV 7, from plugins whoseRPC sources are laid out unusually, and from setups where discovery finds fewer servicesthan the web UI offers.
Please run the test suite before opening a PR. If you are fixing a parsing issue, add afixture captured from the real system alongside it; that is how the existing tests arebuilt.
License
MIT — see LICENSE.
This project is not affiliated with or endorsed by the OpenMediaVault project.