mcp-vpsobs
Provides visibility into Docker containers: lists allowlisted containers with status and ports, and tails container logs via docker_logs.
Provides nginx site information and access log analysis: enumerates configured sites with root/proxy_pass, and computes request statistics, status histograms, top paths, and user agents from access logs.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-vpsobsShow me recent nginx errors and current disk usage"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-vpsobs
An AI agent that is troubleshooting a production incident wants to look at
logs, check whether a service is up, and see how full a disk is. The usual
way to give it that is to hand it a shell: SSH credentials, or a "run this
command" tool with a string it can pass to subprocess. That solves the
immediate problem and creates a much bigger one -- the agent (and anything
that can make it say the wrong thing, from a poisoned log line to a
compromised MCP client) now has full read/write access to a host that keeps
other people's systems running.
mcp-vpsobs is a Model Context Protocol server that gives an agent eyes on
a single Linux host without giving it hands. It does not accept a command
string. It does not have a write, restart, kill or exec path anywhere in the
code. Every unit, container, log and directory it can see is named ahead of
time in a YAML allowlist; anything not named is denied before a subprocess
is even started. Every byte it returns has already passed through a
redaction pass that strips bearer tokens, password= assignments,
provider-shaped API keys and e-mail addresses.
Why not just give the agent SSH
The honest argument for a narrower surface:
The blast radius of a mistake is bounded. If the agent (or the model behind it, or a prompt-injected log line it read) decides to do something destructive, there is no
rm, nosystemctl restart, nodocker execfor it to reach for. The collectors in this repo only ever callbackend.runwith a fixed, read-only argv orbackend.read_file; there is no code path that writes to the host.The allowlist is explicit and inspectable. A YAML file is the entire answer to "what can this agent see on my host", instead of "whatever the SSH key's permissions and the model's judgement allow at any given moment."
Denial happens before any host access.
Config.check_unit,check_container,site_log_pathandresolve_directoryraiseDeniedErrorbefore a collector builds an argv or opens a file --tests/test_attack.pyproves this with a backend fixture that fails the test if it is ever called.Secrets get a chance to not leak. Every line handed back to the agent passes through
redact().
What this design does not protect against, stated plainly rather than hand-waved:
Resource exhaustion by a well-behaved-looking client. Each call has a timeout and a byte cap, but nothing here rate-limits repeated calls. An agent that calls
journal_tailin a tight loop can still generate load proportional to how fast it can issue MCP requests. There is no token-bucket or per-minute quota in this codebase.Novel secret shapes. The redaction patterns are shaped (bearer tokens,
key=valueassignments, known provider key formats, e-mails, optionally private IPv4) rather than a generic entropy detector, on purpose -- a blanket "redact anything that looks random" rule would also eat git SHAs, UUIDs and request IDs and make the logs useless. A secret in a format none of the patterns anticipate will pass through unredacted.An operator who allowlists too much. If you put
/indirectoriesor a unit that happens to log credentials inunits, this server will faithfully show an agent everything inside it. The allowlist moves the trust decision to the config file; it does not make the decision for you.Vulnerabilities in the tools it shells out to. This server is only as read-only as
systemctl,journalctl,docker,nginxandopensslare. A bug in one of those binaries, or in themcpSDK itself, is outside what an allowlist can fix.Audit logging. There is no record kept of which tool was called with which arguments. Everything here is visibility of the host, not visibility into how the agent used this server. If you need "who asked what and when," it is not implemented -- add logging in front of the transport before relying on this in a setting where that matters.
Related MCP server: Linux MCP Server
Architecture
config.yaml --> Config (allowlist + limits)
|
v
collectors/* --uses--> Backend (Protocol)
| |
v +-- SubprocessBackend (real: argv-only, shell=False)
redact() / redact_lines() +-- FakeBackend (tests: canned argv -> CommandResult)
|
v
server.py (FastMCP resources + tools, stdio)Allowlist first.
Config(insrc/vpsobs/config.py, not modified by this change) is the only place that knows what is visible. A collector cannot "forget" a check because there is nothing for it to check except by askingConfig.Backend injection. Every collector takes a
Backend--run(argv, timeout, max_bytes)orread_file(path, max_bytes, tail)-- so tests run against aFakeBackendfed canned command output. No test in this repo touches a realsystemctl,docker,journalctl,nginxoropenssl.Redaction is a pipeline, not a suggestion. Anything that reaches an agent goes through
redact()/redact_lines()on the way out.
Resources
URI | Contents |
| uptime, load average, memory, disk usage of |
| allowlisted systemd units with active/sub/load state |
| allowlisted docker containers (name, image, status, ports) |
| nginx |
Tools
Tool | Signature |
|
|
|
|
|
|
|
|
|
|
|
|
Every tool result is a JSON string. Failures (denied, not configured,
timeout, backend error) come back as {"error": ..., "message": ..., "details": {...}} in the same shape, rather than as a different kind of MCP
protocol error -- see "Design notes" below.
Configuration
Copy config.example.yaml to config.yaml and edit it:
units-- systemd unit names visible tohost://servicesandjournal_tail.containers-- docker container names visible tohost://containersanddocker_logs.sites--site label -> access log path, used bynginx_access_stats.directories-- absolute directory rootsdisk_hogsmay inspect.certs-- certificate file pathscert_expiryreports on.redaction.redact_private_ipv4-- also scrub RFC1918/loopback addresses (off by default).limits.timeout_seconds/limits.max_output_bytes/limits.max_lines-- the hard ceiling every collector runs under.nginx_config_command-- override ifnginx -Tis not the right command for your setup (default["nginx", "-T"]).
Design notes
Places where the spec left a decision open and the simpler option was taken:
MCP SDK surface: built on
mcp.server.fastmcp.FastMCP(decorator-based resources/tools, stdio transport) rather than the low-levelmcp.server.ServerAPI, for less boilerplate per resource/tool.Error reporting:
VpsobsErrorsubclasses are caught inserver.pyand serialized as{"error": ...}JSON text in the normal tool/resource result, rather than raised as MCP protocol-level errors. One response shape for both success and failure is simpler for a caller to parse.host://containersfiltering: shows only containers already on thecontainersallowlist, rather than showing every running container annotated with an allowlisted/not flag. Fewer things for an agent to see that it can't act on, and no leaking of the existence of containers the operator never chose to expose.port_mapand the unit allowlist:port_mapdoes not cross-reference listening sockets againstconfig.units. It reports whateverssreports; nothing is denied either way, so filtering would only be cosmetic. Notess -tulpnneeds to run as root (or with equivalent capabilities) to populate the process/pid column at all -- without that,process/pidcome backnull.host://sitesdirective scoping: the nginx config parser tracksserver { ... }blocks (including nestedlocationblocks) forserver_name,rootandproxy_pass, but does not model nginx's directive-inheritance rules (e.g. arootset on the surroundinghttpblock applying to a server that declares none itself).Config file location:
--configdefaults to./config.yamlin the current working directory; there is no/etc/vpsobs/config.yamlfallback. Simpler to reason about, and the caller (Claude Desktop config,claude mcp add, systemd unit, etc.) already has to specify a working directory or an absolute--configpath either way.No audit logging: confirmed absent by reading
server.pyand every collector -- no tool call is logged anywhere beyond whatever themcplibrary itself does. Stated explicitly in "Why not just give the agent SSH" above rather than left implicit.journal_tail/docker_logsgrep: implemented as a plain Python substring filter applied to lines already returned byjournalctl/docker logs, never passed to a subprocess or treated as a regex -- there is no way for agrepvalue to become a shell metacharacter problem because it never reaches an argv.
Running it
pip install -e .
cp config.example.yaml config.yaml # then edit the allowlist for your host
mcp-vpsobs --config config.yamlThe server speaks MCP over stdio. To point Claude Desktop or claude mcp add at it:
claude mcp add vpsobs -- mcp-vpsobs --config /path/to/config.yamlor, in Claude Desktop's claude_desktop_config.json:
{
"mcpServers": {
"vpsobs": {
"command": "mcp-vpsobs",
"args": ["--config", "/path/to/config.yaml"]
}
}
}Testing
pip install -e ".[dev]"
pytest -qEvery test runs against a fixture backend (tests/conftest.py's
FakeBackend) or a pure string-parsing helper -- none of them shell out to a
real systemctl, journalctl, docker, nginx or openssl, so the suite
passes in CI (see .github/workflows/ci.yml) with none of those installed.
As of this commit the suite defines 72 test functions across 13 files
(grep -roh "def test_" tests/ | wc -l); parametrized cases push the number
pytest actually collects higher than that -- run pytest -q for the exact,
current count.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables read-only Linux system diagnostics and troubleshooting on RHEL-based systems, including system info, services, processes, logs, network, and storage analysis. Supports both local and remote SSH execution across multiple hosts.Last updated20MIT
- AlicenseBqualityBmaintenanceAn MCP server for read-only Linux system administration and diagnostics on RHEL-based systems via SSH. It enables users to troubleshoot remote hosts by accessing system information, services, logs, and network configurations through natural language.Last updated19272Apache 2.0
- Alicense-qualityDmaintenanceThis MCP server provides read-only Linux system diagnostics tools for inspecting system information, processes, and log snapshots. It enables AI models to analyze Linux system health, troubleshoot issues, and review security through workflow prompts and HTTP transport with API key authentication.Last updatedMIT
- Alicense-qualityDmaintenanceEnables interaction with Linux system operations via MCP, including CPU, memory, processes, storage, filesystem, hardware, network, monitoring, and logs.Last updated9MIT
Related MCP Connectors
Operate your own Linux servers from your LLM. Requires the SentinelX agent installed per host.
Remote MCP for Android CLI agent build gate, structured receipts, audit logs, and reviewer-ready evi
Remote MCP for A2A dependency inspector MCP, structured receipts, audit logs, and reviewer-ready evi
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/0mandrock1/mcp-vpsobs'
If you have feedback or need assistance with the MCP directory API, please join our Discord server