The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the VPS Guardian listing page.
VPS Guardian is a secure Model Context Protocol server for AI agents that work with Linux VPSs. It replaces an unrestricted “run this command and paste the result” loop with named, structured and safety-checked operations.
An agent can inspect a workload, collect bounded diagnostics, preview the impact of a change, and request an exact confirmation for a mutation. The server never exposes a general-purpose shell tool.
Explore the project: capabilities · agent workflows · security model · tool catalogue · release notes
VPS Guardian has two parts:
The English-language panel runs on your computer, not on the VPS or this project's website. Its main screen manages agent permissions: pause/resume MCP calls, cap the safety mode, allow individual tools and set project roots. A collapsed server overview provides optional read-only monitoring. The npm package remains the MCP launcher; the panel comes with the Python package.
With uv installed on your computer:
Without uv, install in a local virtual environment. Windows PowerShell:
Linux/macOS:
The browser opens automatically. Enter the VPS address, SSH user, port and path to your local key, not its contents. Leave the key field empty to use ssh-agent; load encrypted keys into the agent beforehand. Advanced settings accept the Guardian executable path on the VPS and an optional local known_hosts file. Access management requires Guardian 0.30.0+ on the VPS; Projects and Limits require 0.31.0+, Operations requires 0.32.0+, with the adjacent vps-guardian-access executable from the same installation. Older compatible installations can show monitoring only, with an upgrade warning. No server-side web service is installed.
To upgrade an existing pip installation on the VPS:
Upgrade any other Guardian environments used by your agents, then reconnect all agents once so they start the policy-aware server. Subsequent permission changes affect new calls in those sessions without a restart. In-flight operations are not cancelled, and cached client tool lists may still show disabled tools; calls to those tools are rejected.
Use Apply permissions to save a shared per-SSH-user policy on the VPS at ~/.local/share/vps-guardian-access/policy.json. Connection settings stay in local RAM, but the access policy persists across panel/server restarts. Reload policy fetches the current server values; concurrent edits are rejected rather than overwriting another operator's changes. No policy means the existing launch settings remain in force. Invalid or unsafe stored policies fail closed and pause normal MCP access; get_safety_status remains available for recovery. Unsafe file ownership, permissions or symlinks require manual repair by the operator.
controlled cannot elevate a client launched as read-only. Confirmation tokens are the existing same-caller mechanism, not independent human approval.get_safety_status cannot be disabled. Read-only mode can still create bookkeeping records; use tool permissions when you also need to block those entry points.VPS_GUARDIAN_PROJECT_ROOTS, or replace it with up to 16 absolute VPS directory paths, one per line. An empty custom list blocks project workspaces. Filesystem roots and symlink roots are rejected. This controls project tools, not the separate fixed configuration-file directory whitelist.The operator helper is not registered as an ordinary agent tool. Tools and the three read resources enforce access restrictions on the server. Policies apply to upgraded Guardian processes under the same SSH user, not to individually authenticated agent identities. An agent with independent root SSH access, the same account's shell, or permission to change Guardian's code can bypass this MCP boundary. For stronger separation use a restricted dedicated OS account; do not treat the panel as a sandbox or independent approval service.
The Access, Projects and Limits sections require the current Python package on your computer. Projects and Limits additionally require Guardian 0.31.0+ on the VPS. A 0.30.0 server can still manage Access, but cannot enforce these new limits. Upgrade every server environment used by your agents and reconnect once; changing saved limits afterwards does not require another restart.
In Projects, click Find projects for a bounded metadata scan inside the current roots (at most 1,000 entries, 100 directories, 50 projects and a cooperative two-second deadline). Select a project or enter an absolute VPS path and click Inspect metadata. The panel shows top-level file/directory metadata, OS readability and policy decisions for common project tools, without reading source, invoking Git or executing project code. Links and sensitive/hidden names are excluded. Missing projects can be outside the roots, lack recognized markers or exceed the scan budget. When roots are inherited, the helper's launch defaults may differ from an agent's environment; use explicit managed roots for a shared boundary.
Use as the only project root only prepares a draft in Access. Review and click Apply permissions to replace the current roots with that single project. Browsing alone never grants or changes agent access. Policy allowance is not a guarantee that an agent's launch mode, roots or OS permissions permit an operation.
In Limits, choose Auto, Small VPS, Standard or Custom, then Apply limits. Values persist in the same private operator directory as limits.json, separately from policy.json. Revision checks reject concurrent edits. Auto and Standard currently request the same normal budgets; both retain automatic host guards. Small VPS requests smaller reads/searches, one-file patches and no Test Capsules. Custom accepts only the displayed integer ranges; it cannot disable guards or exceed hard ceilings. Reload live limits fetches a fresh server snapshot; it asks before discarding a local draft.
The table distinguishes Requested values from Effective on VPS values. Orange effective values have been reduced by host protection. The applied settings, not an unsaved draft, determine effective limits. Available memory below 512 MiB or one logical CPU reduces several budgets; below 384 MiB capsules are disabled; below 256 MiB additional critical-load restrictions apply. These are cooperative per-operation budgets, not a global CPU/RAM quota or OS sandbox. They do not cancel already running work. Every upgraded process under the same SSH user reads shared settings for subsequent operations. Invalid/unsafe limits use conservative Small VPS defaults and show an error; unsafe ownership/permissions or links may need manual repair.
Editable budgets cover project-file reads (up to 300,000 bytes when host guards permit), MCP tool/resource JSON, HTTP diagnostic bodies, directory entries, journal lines, code-search file/input budgets, project-patch file/staged-text budgets, configuration ChangeSet file/text budgets, Agent Job check concurrency (0 disables checks) and Test Capsules (0 disables, 1 permits). Existing stricter operation-specific bounds still apply. Capsule RAM/CPU/timeout/output bounds remain fixed, not editable. Resource response limits apply to each JSON document; MCP transport encoding and text/structured duplication add overhead. Large fields are omitted with response_truncated and omitted_fields, retaining small status/IDs/tokens where possible. A mutation has already returned: do not repeat it just to obtain omitted output. Request a narrower read or status instead.
Cached project patches recheck current roots and resource limits before preview, staging, testing and applying. A revoked or over-budget candidate must be replaced with an allowed, smaller patch. Configuration ChangeSets also recheck live limits before applying.
Operations requires Guardian 0.32.1+ on the VPS and on your computer. The English panel shows the latest 25 project patches/ChangeSets and up to 25 Agent Jobs, with search, state filters, file fingerprints and a bounded event timeline. Click a record to inspect metadata. Refresh is manual or every 30 seconds while the tab is open; it uses the separate short-lived operator helper, including while agent access is paused. This view does not execute, approve, cancel or retry changes. There is no additional always-on VPS worker.
Agents use list_operations(limit=10) and get_operation(operation_id="...") to retrieve the same metadata. next_before is an opaque cursor for older draft/history pages; pass it back as before. Job lists are separately bounded latest snapshots, not part of that cursor. Normal MCP tool permissions still apply; the human operator's helper is separate. History is per SSH user, not per individual agent or current project root: everyone granted these history tools under that account can see operation metadata.
Staged project patches and configuration ChangeSets now persist in private SQLite storage at ~/.local/share/vps-guardian-access/operations/. A draft survives MCP/panel reconnects for 24 hours, with the existing 24-patch/32-ChangeSet active caps. Active drafts are never silently evicted; storage-full errors require completing an allowed draft or waiting for expiry. Payloads are bounded to 9 MB each and 32 MB total; the SQLite database is capped at 48 MiB. Completed/expired source payloads are removed and their metadata retained for at most 30 days / 200 records. Retention is enforced on requests, not by a background cleanup daemon. Active candidate/original source can contain secrets: files use private permissions, not encryption, and must be protected with the SSH account and disk backups. Panel/history replies never include source, raw command output or confirmation tokens. Secret redaction is best-effort; avoid secrets in titles and paths.
After reconnecting, preview the same staged ID again to obtain a new session-local confirmation token, then apply with the existing tool. Current mode, roots, budgets and live-file fingerprints are rechecked; persistence does not grant access or approve a write. A claimed apply/check has a 120-second lease. If it outlives that lease without a stored outcome, history marks it uncertain, drops the candidate and never replays it. Inspect live files/services before preparing a replacement. This history is not a transactional filesystem journal or an automatic rollback guarantee.
New Agent Jobs use the same fixed private operation directory, independently of VPS_GUARDIAN_STATE_DIR; their existing leases, job TTL and 100-record cap remain. Their database is capped at 8 MiB. Upgrade note: previous releases kept jobs in the old state directory and drafts only in RAM. Old job databases remain untouched and are not automatically imported into shared storage; finish important old jobs before upgrading. Old in-memory drafts cannot be recovered after the old process exits. Upgrade all server environments used by agents and reconnect once.
SSH host-key verification is mandatory. Verify the fingerprint independently and connect once with ordinary SSH before using the panel. The dashboard does not accept unknown keys or use custom SSH config/aliases, ProxyCommand or jump hosts: enter a directly reachable IP/hostname. A changed host key must be investigated, not bypassed.
Connection settings and snapshots stay in memory; the panel never uploads or reads private-key contents. One MCP connection samples metrics every 30 seconds; manual refresh is limited to once per 10 seconds. Unavailable metrics are shown as unavailable, not zero. No failed systemd units is not a guarantee all applications are healthy. If the connection fails, retained metrics are marked stale.
Keep the printed local link private: its fragment is a per-launch operator access token. The token stays in tab memory and is removed from the address bar; after reloading, reopen the full printed link. Ctrl+C in the launch terminal stops the panel and its connection. --no-browser only prints the link; --port 8765 selects a loopback port. Do not reverse-proxy or expose the panel publicly. Loopback/token checks do not protect against malware running as your local user.
Run once on the VPS. This installs the published, pinned release:
For development from source instead:
If the server runs as a non-root user, grant only the required read access. Docker and journal features gracefully report as unavailable when that access is absent.
Log out and back in after changing groups.
| Mode | Use it when | Result |
|---|---|---|
read-only | Inspecting or diagnosing | Default. Guarded server mutations are blocked; bookkeeping tools may still save records. |
controlled | Assisted administration | Recommended. Each exact change needs a short-lived, single-use confirmation token. |
unrestricted | A separately protected automation environment | Changes run immediately. Avoid on a general-purpose agent. |
Start with read-only; use controlled once the connection is verified.
First verify the VPS fingerprint independently and make a normal SSH connection once. That stores the host key in ~/.ssh/known_hosts (or %USERPROFILE%\.ssh\known_hosts on Windows). The launcher requires host-key verification by default.
Use this configuration for JSON-based MCP clients:
Every item in args is a separate argument. Do not join --host with its value or paste the entire command into one form field.
Open Settings → MCP servers → Add server, choose STDIO, then enter:
| Field | Value |
|---|---|
| Name | vps-guardian |
| Command | npx |
| Environment variables | Leave empty |
| Working directory | Leave empty/default |
Add these arguments as separate rows, in order:
Save, restart the client, then use /mcp to confirm that vps-guardian is connected.
Claude Code
A non-root SSH user — replace root after --user. Do not add passwordless sudo just for the MCP; grant the minimum group permissions needed.
A non-standard port — add separate arguments:
A different server location — add:
Host key verification failed — do not disable verification. Check the VPS fingerprint through a trusted channel and correct known_hosts. Use --known-hosts <path> for a dedicated file. --accept-new-host-key is only for an intentional first-time bootstrap.
Ask the agent: “Check CPU and RAM load on my server.” A correct setup returns structured VPS data rather than a shell command for you to run.
To upgrade the VPS server, install the matching version and restart the client connection:
Then replace @0.25.1 with @X.Y.Z in the client configuration. For source installations, fetch the tag, inspect local changes, check out the tag, and reinstall with .venv/bin/pip install -e ..
VPS Guardian is built around a few workflows instead of a long, unstructured command list:
For smaller agent context, --tool-profile core exposes the everyday tools (including Agent Jobs); omit the flag or choose full for the complete catalogue. The launcher passes this profile to the server over SSH. Both profiles support compact JSON tool results, while new workload and log summaries return short answers by default. The profile takes effect when the MCP connection starts.
For a large project file, ask the agent to use get_project_symbols, then read_project_file_range around the relevant lines. A single range call returns at most 100 KB and includes a SHA-256 fingerprint. A subsequent stage_project_line_edit sends only changed lines and still uses the existing preview, confirmation, conflict check and backup flow. get_workload_brief, summarize_service_logs and get_server_event_delta provide compact operational context without background polling.
Agent Jobs: Create a job with 1-8 allowlisted checks, such as service_status and service_logs for target bot.service. Call advance_agent_job once per check. The job, bounded results, and progress survive MCP reconnects; get_agent_job(after_revision=...) returns only new results, and another authorized MCP client can continue by job ID. On Linux, advance_agent_job(background=true) starts just one detached read-only check that can finish after disconnection; it requires at least 512 MB available memory. After checking the exact service, an agent may propose one restart_service recovery. execute_agent_job_recovery uses the existing read-only/controlled/unrestricted safety mode; in controlled mode review its one-time confirmation and call again with the token. Then call verify_agent_job_recovery and record the conclusion. A possibly executed restart is never retried automatically after a disconnect. Jobs do not run an AI model, always-on worker, arbitrary shell commands, or automatic rollback on the VPS; a service restart cannot be undone. Existing reversible change tools retain their own backup and rollback rules.
Test Capsules: After begin_project_patch and stage_project_line_edit (or stage_project_file_change), call get_test_capsule_status, then test_project_patch(patch_id, check="auto"). In controlled mode, confirm this code-executing check with its own one-time token. auto syntax-checks staged Python or JavaScript files; python_unittest and npm_test explicitly run project tests. If it passes, call preview_project_patch to inspect the diff and obtain the separate apply confirmation, then promote_tested_project_patch with that token. A failed or edited candidate cannot be promoted through this tool. The existing apply_project_patch remains available for projects without Docker and does not claim a capsule test.
Capsules require a local Linux Docker daemon, an already-downloaded image (python:3.12-alpine or node:20-alpine by default) and at least 384 MiB available RAM. An operator may choose an already-local image with project dependencies via VPS_GUARDIAN_CAPSULE_PYTHON_IMAGE or VPS_GUARDIAN_CAPSULE_NODE_IMAGE. Guardian never pulls images or installs dependencies automatically. It copies at most 250 files / 8 MiB, omits common credential files and dependency directories, and allows one check at a time for 30 seconds. The container gets no network, host environment or live-project mount; CPU, RAM, processes and temporary storage are capped. Tests needing network, writable source files or missing dependencies will fail. Source files may still contain hard-coded secrets, so remove those before testing; Docker isolation reduces risk but is not a guarantee against malicious code or kernel vulnerabilities.
Environment Doctor: Ask the agent to call inspect_project_environment(project_path, service_name="bot.service"), then diagnose_project_dependencies for a focused problem report. It reads pyvenv.cfg, Python .dist-info/METADATA, static pyproject.toml/requirements.txt declarations, and direct npm dependencies against a v2/v3 package-lock.json. If both .venv and venv exist, supply an explicit authorized environment_path. include_dev=true includes npm development dependencies. Reuse after_fingerprint to receive only an unchanged reply when the relevant report has not changed. A dependency name is a distribution name, not necessarily its Python import name.
plan_environment_repair explains the next steps without installing packages or restarting anything. plan_capsule_environment prepares bounded direct dependency pins and runtime metadata for a reviewed local Capsule image; it does not build or download that image, verify its contents, or produce a complete transitive lockfile. Python versions come from venv metadata; running service evidence is limited to a Linux systemd MainPID. Stopped units, wrappers, containers, system Python without an authorized venv, inherited/legacy/editable packages, dynamic declarations, requirement directives, URLs, extras and unsupported npm locks may need separate review. Metadata is not proof that an import works, and Node runtime/engine compatibility is not probed. Reads are capped at 2 MB per report, 1,000 directory entries and 100 direct declarations per ecosystem; incomplete scans never report a clean result. No project interpreter, installer, npm script, package index or background worker is started.
Code Navigator: Start with get_project_import_map(project_path, relative_path="app/payments.py"), or use get_project_task_context(project_path, relative_path="app/payments.py", symbol_name="charge") to get a definition, short use-site fragments and related test candidates in one bounded answer. find_project_references distinguishes import-alias candidates from weaker name-only matches. assess_project_change follows reverse imports for up to three hops; test files are selected by import relationships and naming conventions, not measured coverage. For a class method use its exact qualified name, such as Gateway.refund; get_project_symbols helps choose it. All four tools accept after_fingerprint for compact unchanged replies; changing query arguments changes the fingerprint. No scan results are cached, so an unchanged reply saves output tokens but still requires a fresh scan.
No setup is needed beyond configured project roots. Navigator supports UTF-8 Python sources and bounded ASCII symbol names, including relative imports and common src/ layouts. Snippets are limited to one line; literals (including f-strings/template strings) and comments are masked. File, module and identifier names remain visible; use existing range reads for exact source bodies. This is not a runtime call graph, complete dependency analysis or proof that tests cover a change: alias shadowing, ambiguous modules, dynamic imports/reflection, instance types and non-Python/generated sources remain uncertain. Hidden, sensitive and dependency paths are excluded. Unreadable files, unsafe paths, syntax errors and exhausted budgets are reported as partial scans.
Scans are on demand, with no background indexer or disk cache: at most 150 Python files (60 on constrained hosts), 128 KB per file, 2 MB total (750 KB constrained), 1,000 directory entries, eight directory levels and a five-second cooperative scan deadline. AST nodes and extracted facts are capped; files with more than 200 import aliases are skipped. Below 96 MiB available memory, no scan starts. Only one scan per MCP process runs at a time. Separate MCP processes do not share this lock. Reference/import reports return at most 50 entries; task context defaults to 6,000 characters and can be capped between 2,000 and 8,000.
Examples of native MCP tools:
| Request | Example tool | Result |
|---|---|---|
| “Why is the API slow?” | diagnose_workload | Bounded health, logs, OOM and kernel evidence. |
| “What will a restart affect?” | get_change_impact | A read-only dependency and impact report. |
| “Hand this incident to another agent.” | handoff_agent_session | Secret-redacted context and outcome tracking. |
| “Split this audit between agents.” | create_agent_task | Prioritized work with dependencies and expiring ownership. |
| “Check this service, then let another agent continue.” | create_agent_job | Durable, bounded checks with delta results and gated recovery. |
| “Deploy this Nginx change safely.” | plan_config_deployment | Validated diff, backup, confirmation and rollback path. |
See the complete capability guide and full tool catalogue on the project site.
controlled mode uses parameter-bound, single-use confirmation tokens. This is not independent human approval: the caller receives the token and can repeat the operation. Client approval or a separately enforced operator policy is required for that guarantee. An agent with unrestricted SSH access can bypass MCP restrictions.On Linux, hardened project/config reads refuse symlink components and special files. Atomic writes pin the parent directory, refuse unsafe backup paths, use private unique backups and preserve normal ownership/permissions without setuid/setgid bits. Existing private state directories must be owned by the service user with 0700, state/audit files with 0600; unsafe paths fail closed rather than being silently chmodded. Audit files stop accepting writes at 4 MiB each (primary/fallback), and reads inspect at most a 256 KiB tail; an operator must archive/reset full logs. The fallback audit filename is scoped to the effective UID. Python syntax checks use bounded source snapshots with an isolated interpreter, skip oversized files and never claim full success for incomplete scans. These protections do not make root execution or a shared SSH key an isolation boundary.
Details: security model · agent operating guide
@murzirius/vps-guardian-mcpvps-guardian-mcpio.github.murzirius/vps-guardian-mcpPlease report security issues privately rather than publishing exploit details in a public issue.
MIT © 2026 murzirius.