MCP: Giving AI Agents the Keys to the (Carefully Segmented) Kingdom

mcpjuniperpfsensearubasynologyclaude-code

The Model Context Protocol (MCP) is Anthropic’s answer to the question: how do you give an AI agent structured, safe access to real systems without either hardcoding a thousand tool definitions or letting it loose with unrestricted API access?

The answer is a standardised client-server protocol where each “MCP server” exposes a set of typed tools, the AI client calls those tools through a defined interface, and you get auditability, composability, and the ability to revoke access by stopping a process.

The lab has four. One for each piece of major infrastructure.

Why Custom MCP Servers

Pre-built MCP servers exist for many common services (GitHub, Slack, various databases). Pre-built servers for a Juniper EX3300 running JunOS 21.x, a pfSense firewall with a custom REST API, an ArubaOS 8.x wireless controller, and a Synology NAS do not exist in any form that was useful without significant modification. So: custom servers.

All four use stdio transport rather than SSE (Server-Sent Events). This is a constitution-level requirement: SSE has reliability issues in Docker environments and behind reverse proxies, while stdio transport is a simple pipe that works reliably anywhere Python runs. Each server is a Python process that reads JSON-RPC requests from stdin and writes responses to stdout.

Juniper MCP: Network Switch Management

The Juniper MCP server uses junos-eznc — PyEZ, Juniper’s Python library for NETCONF-based device management. It provides eleven tools:

  • execute_junos_command — Run arbitrary CLI commands (show interfaces, show route, etc.)
  • get_junos_config — Retrieve full device configuration
  • junos_config_diff — Compare configuration versions (rollback comparison)
  • gather_device_facts — Collect device model, software version, uptime
  • get_router_list — List configured devices from devices.json
  • load_and_commit_config — Apply configuration changes (set/text/XML formats)
  • render_and_apply_j2_template — Apply Jinja2 configuration templates
  • add_device / reload_devices — Manage the device list at runtime

The devices.json config maps friendly names to connection parameters:

{
  "core-switch": {
    "ip": "172.16.100.254",
    "port": 22,
    "username": "<ai-service-account>",
    "auth": { "type": "ssh_key", "private_key_path": "~/.ssh/id_ed25519" }
  }
}

SSH key authentication only. No passwords for network device access — that’s a requirement, not a preference. The network device service account is a named account in Authentik that has Juniper CLI access but no write permissions by default. Load-and-commit operations require explicit tool invocation with a configuration payload, which means the agent has to deliberately construct and submit a config change — it can’t accidentally commit one as a side effect of a query.

pfSense MCP: Firewall Management

The pfSense server uses FastMCP and pfSense’s REST API. pfSense-CE with the API plugin exposes firewall rules, DHCP leases, interfaces, routing tables, and more via authenticated REST calls.

Key capabilities:

  • Query firewall rules by interface and direction
  • View DHCP leases and correlate them with Nmap discovery data
  • Check routing table entries (useful for debugging the Tailscale VPN routes)
  • View interface statistics and state

The start script pulls the pfSense API key from VaultWarden at runtime:

PFSENSE_API_KEY=$(vault-get-secret.sh "<pfsense-secret-entry>" password)
export PFSENSE_URL="https://pfsense.sdx.local"
export AUTH_METHOD="api_key"
exec /usr/bin/python3 -m src.main

Aruba MCP: Wireless Controller

The Aruba server interacts with ArubaOS 8.x via its REST API. The scope is deliberately read-only — show commands only. Making configuration changes to the wireless controller via an AI agent is a well-intentioned idea right up until it causes a broadcast storm, at which point it becomes a very good reason for spending a Saturday with a console cable.

Available operations:

  • List access points and their status (up/down, clients connected, channel utilisation)
  • Show associated client devices (MAC, IP, SSID, signal strength)
  • Execute arbitrary show commands (output parsed and returned as text)
  • View controller health and system statistics

In practice, the Aruba MCP is most useful for answering “why is WiFi slow in the back bedroom” type queries — Claude can check which AP the device is associated with, what channel it’s on, and whether there’s interference, without anyone having to log into the controller web UI.

Synology MCP: NAS Management

The Synology server is the most functionally broad, covering five API surface areas:

  • FileStation — Browse files, create/delete directories, move files
  • DownloadStation — Manage downloads (add, pause, resume, status)
  • NFS — View and manage NFS mounts and exports
  • Health — Storage pool status, volume health, SMART data
  • User Management — List and manage NAS user accounts

The health endpoint is the one used most frequently: a quick query confirms that all storage pools are healthy, all disks are within normal temperature ranges, and no SMART errors are accumulating. The alternative is logging into the Synology web UI, which requires remembering the URL, the credentials, and whether you’re on the right VLAN.

Claude Code Integration

All four servers are configured in F:\Projects\.claude\settings.json:

{
  "mcpServers": {
    "juniper": {
      "command": "wsl",
      "args": ["-d", "Ubuntu", "--", "bash", "-c",
               "cd ~/mcp-servers/juniper && python3 jmcp.py -f devices.json -t stdio"],
      "type": "stdio"
    },
    "pfsense": {
      "command": "wsl",
      "args": ["-d", "Ubuntu", "--", "bash", "-lc",
               "~/mcp-servers/start-pfsense.sh"],
      "type": "stdio"
    }
  }
}

From Windows, Claude Code spawns WSL2 processes to launch the servers. From WSL2-native contexts (LangGraph agents running inside WSL2), the servers are launched directly without the wsl wrapper.

The Use Case: Conversational Infrastructure Management

The before state: checking firewall rules required logging into pfSense, navigating to the firewall rules page, filtering by interface, and reading a table of rules.

The after state:

“Claude, show me all firewall rules that allow traffic from the VM VLAN to the infrastructure VLAN.”

Claude calls the pfSense MCP, retrieves the relevant rules, and presents them in a readable format with a brief explanation of what each rule permits. The same query that took four browser navigation steps now takes one conversational turn.

More usefully: when debugging why a new VM couldn’t reach the Synology NAS, Claude was able to simultaneously query the pfSense firewall rules, the Juniper switch port configuration, and the Synology NFS exports — correlating three separate data sources in a single response. That kind of multi-source correlation is where conversational infrastructure management earns its keep.

The servers have been running stably since May 8, 2026. The Juniper MCP in particular has become a first-line tool for switch configuration verification — it’s faster to ask Claude to diff the running config against a saved baseline than to SSH in and run the comparison manually.