C1 Identity & least privilege
Moderate 0.50 / 1.00
In its default local setup the server runs as whoever launched it and, with no policy file, the authorization middleware allows every tool call on every host. Remote calls use the operator's own SSH identity (default keys or agent) against any host name the model supplies, and local commands inherit the server's full environment. The project does ship a real, deterministic authorization layer: a YAML policy that maps tool, host and token claims to deny, local, or SSH with a specific per-rule key and user, and denies anything unmatched. It is opt-in, so it can only lift this criterion to the off-by-default ceiling.
C2 Approval gates
Moderate 0.65 / 1.00
As a tool server, the host owns the approval prompt, so this rates what the server tells the host. In the default 'fixed' toolset every tool is genuinely read-only and annotated readOnlyHint, the server refuses calls to tools outside the configured toolset, and nothing it exposes changes state, so a wrongly approved call can't change anything. The opt-in run_script toolset is weaker: the 'read-only' run_script tool trusts a flag the model sets (checked by an LLM gatekeeper), run_script_with_confirmation executes immediately on the server side; approval enforcement on the app-only script path is also not complete.
C3 Tool & action scoping
Moderate 0.55 / 1.00
Tools are narrow and well built in shape: each runs a fixed command as an argument list (no shell locally, shell-quoted over SSH), with typed parameters, numeric bounds, regex-validated PCP names, and a resolved-path allowlist for read_log_file. But path checking is shallow: read_file and the listing tools accept any absolute path without '..', so the model can read any file the user can read (SSH keys, cloud credentials), which makes the log allowlist moot. The host argument is any string with no allowlist, and other argument validation is not strict.
C4 Code-execution isolation
Minimal 0.20 / 1.00
The default read-only toolset never runs model-written code: every command is a fixed argument list. Code execution exists only in the opt-in run_script toolset, which is therefore what this criterion rates. There, scripts flagged read-only by the model run under sudo systemd-run with a read-only filesystem and no network, but scripts flagged as modifying run as root with only PrivateTmp and NoNewPrivileges; the wrapper is also not a complete boundary. Because the score reflects that opt-in path, it says little about the default install, which has no execution surface.
C5 Untrusted input blast radius
Minimal 0.30 / 1.00
The server feeds the model content that others can write: journal entries, log files, service output, process command lines and arbitrary file contents. Some tools return structured models (log entries with their unit or path), but read_file and several status tools return plain text with no marker that it is untrusted. In the default mode a hijacked model can read local secrets such as SSH keys and then leak them through the host name argument, which triggers DNS lookups and SSH connection attempts to any name it chooses. No tool can change state, so the worst case is data exfiltration, not destruction.
C6 Memory, context & configuration integrity
N/A · full credit 1.00 / 1.00
The server keeps no memory, conversation store or retrieval index, and reads no instruction or configuration files from a working directory. Settings come only from LINUX_MCP_* environment variables and command-line flags (pydantic-settings with no .env file), and the optional policy file comes from an explicit operator-supplied path. Validated scripts are held in memory only for the life of the process.
C7 Third-party extensions
N/A · full credit 1.00 / 1.00
The server loads no third-party code at runtime: no plugin system, no MCP client, no package installs on the model's behalf, and no model files. The MCP App HTML/JS is read from the package's own resources. A _vendor directory lets downstream packagers bundle dependencies at build time, which is ordinary supply chain rather than runtime extension loading.
C8 Secrets & sensitive-data protection
Minimal 0.38 / 1.00
The few secrets the server holds (SSH key passphrase, OAuth client secrets) are SecretStr values, there is no telemetry, and tool-call logging redacts any parameter whose name looks sensitive. But nothing stops secrets reaching the model: read_file returns any file the user can read, including private SSH keys and cloud credential files, and local commands inherit the server's full environment, including gatekeeper API keys when that toolset is enabled.
C9 Audit & traceability
Moderate 0.50 / 1.00
Every one of the server's 30 tools is wrapped by a logging decorator that writes a structured record (tool, sanitized arguments, host, status, duration, timestamp) to rotating text and JSON files under ~/.local/share, and remote commands are logged with their exact command line and exit code. Local commands are logged only at debug level, and in the default local mode there is no user identity and authorization decisions are logged only at debug. The files are ordinary user-writable files kept for ten days.
C10 Limits & kill switch
Moderate 0.55 / 1.00
Every command the server runs, locally or over SSH, has a 30-second default timeout; file reads are capped at 1 MiB, log and journal reads at 10,000 lines, and listings at depth one. There are no rate or concurrency limits, some outputs (process list, service list) are unbounded, and a local timeout kills only the direct child process, not any processes it spawned.