AI agents are genuinely useful for infrastructure work: asking “which guests are over-committed?”, “what fired in the SIEM overnight?” or “which devices have not checked in?” in plain English is a lovely way to operate a lab. The problem is the obvious one. The easy way to wire an agent into your infrastructure is to give it a shell and a token, and that is exactly how you end up with a very polite, very fast outage.
So I built my own Model Context Protocol servers, with safety as the first requirement and convenience as the second.
The four servers
- Proxmox: inventory, status and resource usage across the cluster.
- Wazuh: alerts, agent health and rule metadata from the SIEM.
- Tailscale: which devices exist, their state and their access tags.
- Jira: read and update tickets so findings land where the work is tracked.
Each is a small, separate server. They do not share credentials, and an agent only gets the ones a task actually needs.
Rule one: tools are the permission boundary
An MCP server decides what an agent can do by what tools it exposes. That is the whole security model, so I treat the tool list the way I would treat a firewall ruleset: default deny, add deliberately.
There is no “run this command” tool. There is no generic API passthrough. Every capability is a named, narrow function with a typed input schema:
{
"name": "list_guests",
"description": "List virtual machines and containers with status and resource usage. Read-only.",
"inputSchema": {
"type": "object",
"properties": {
"status": { "type": "string", "enum": ["running", "stopped", "all"] }
},
"additionalProperties": false
}
}
Strict schemas, no free-form strings where an enum will do, and additionalProperties: false so the model cannot smuggle in parameters I did not plan for.
Rule two: read-mostly
The large majority of the tools are read-only, and that is where the value is. Questions are safe. Actions are where risk lives, so actions get a higher bar:
- They are separate tools, clearly named, never a flag on a read tool.
- They are narrow: “restart this one guest”, not “execute this call”.
- Anything destructive is either absent or requires an explicit confirmation step from a human.
- The credential behind a server is scoped to what its tools need, not to what the platform allows.
If a server is compromised, or the model is confused, the worst case is bounded by that credential. Defence in depth means the tool design is not the only thing between an agent and trouble.
Rule three: tool output is untrusted input
This is the one people forget. A SIEM alert contains text from the network. A ticket contains text from whoever wrote it. When that text flows back into the model, it can contain instructions, and a model can follow them.
So I treat everything returned by a tool as data, never as a command. Outputs are trimmed to what is needed, free-form fields are clearly delimited, and the high-risk action tools stay out of any session that is reading untrusted content. The agent that summarises alerts is not the agent that can restart things.
Rule four: leave a trail
Every tool call is logged with what was asked and what was returned, and those logs go to the same monitoring stack as everything else. If an agent does something surprising, I want to reconstruct exactly what happened.
What it feels like
Mostly, it feels like having a very fast colleague who can read every dashboard at once but is not allowed near the production switch. “Summarise last night’s high-severity alerts and open a ticket for anything new” is a real and satisfying sentence now, and the thing that makes it comfortable is that I know what the agent cannot do.
If you are building your own, start with the tool list, make it smaller than you think it should be, and widen it only when you can say exactly why. Capability is easy to add later. Trust is not.