Palo Alto Firewall
A Model Context Protocol server enabling your AI client (Claude, Gemini, Codex …) to
query and modify a Palo Alto Networks PAN-OS firewall via the XML API.
Every write stages to the candidate configuration; nothing goes live until
commit_config. Ships with a PANOS_ENABLE_WRITE kill-switch
for read-only deployments.
What This Server Does
Palo Alto Firewall exposes a curated set of tools that let an
MCP-capable assistant inspect and change a Palo Alto Networks PAN-OS
firewall over the XML API. Read tools pull system info, configs, policies,
objects, and logs. Write tools stage edits, address objects, groups, services,
tags, and security rules, to the firewall's candidate
configuration. Nothing reaches production until the commit_config
tool runs.
Unlike the read-only servers in this directory, this one is intentionally
read/write. The commit lifecycle is explicit: stage your changes, run
validate_commit to dry-run them, then commit_config to
push live, or discard_changes to roll the candidate back. Pair it
with a non-committing admin role on the firewall and you can let the assistant
propose changes that a human approves before they ship.
If you only want monitoring, set PANOS_ENABLE_WRITE=no and every
write/commit tool refuses, the server behaves read-only regardless of the
admin role's capabilities.
What to Ask, in Plain English
Real questions you can put to your AI client once this server is wired in. The model figures out which tools to call, you describe the outcome. Because this server can also write, the prompts split into two groups: reading (always safe) and writing (stages to the candidate config; you commit explicitly).
Reading (always safe):
- "Show me all security rules that allow inbound RDP from any zone."
- "List address objects that aren't referenced by any rule."
- "What's currently staged in the candidate config but not yet committed?"
- "Pull the last 50 threat logs and group them by source country."
- "Run
show interface alland tell me which links are down."
Writing & committing (each write stages to the candidate config; nothing reaches production until commit_config):
- "Create address object
web-srv-01for 10.1.1.10/32 with description 'production web 01'." - "Add a security rule
allow-webfromtrusttountrustfor that object, action allow." - "Validate the candidate config, list any errors or warnings before I commit."
- "Commit with the description 'add web-srv-01 web rule'."
- "Actually, discard those staged changes, I want to start over."
Before You Start
- Docker Desktop with the MCP Toolkit extension, or Podman 4.x+ on
$PATH. Pick one; the step-by-step for each is below. - Palo Alto Networks firewall running PAN-OS 10.1 or newer.
- Pre-generated PAN-OS API key, created via the firewall's
type=keygenendpoint (Step 0 below). - A scoped admin role bound to the API user, grant only the XML API actions and the objects/rulebases you want the assistant to touch. Strongly consider a role that cannot commit, leaving
commit_configas a human-only path. See Write Safety. - An MCP-capable client, Claude Desktop, Claude Code, Codex, Gemini CLI, or any client speaking MCP over stdio.
31 Tools Across Read, Write, and Commit
<show> operational commands.ip-netmask, ip-range, fqdn), address groups, service objects, and tags.move_security_rule (top / bottom / before / after).set_config / edit_config / delete_config for anything not covered by a named tool. Element must parse as well-formed XML.validate_commit for a dry-run, commit_config to push live, discard_changes to revert the candidate.Build & Configure
Two supported runtimes, pick the one you already run. Both build the same image
and keep your firewall credentials out of the AI client's config: in the
runtime's secret store (recommended) or a local .env file you
control. Generate the API key first (it's identical for either path), then
expand your runtime below.
$ curl -X POST 'https://<firewall-host>/api/?type=keygen' \ --data-urlencode 'user=<admin-username>' \ --data-urlencode 'password=<admin-password>'
Copy the <key> value from the response. Don't paste it into a chat, it goes into a runtime secret (or your local .env) in the steps below. If your firewall uses a self-signed cert, pin it once with openssl s_client and use --cacert rather than -k.
Docker Docker Desktop · MCP Gateway ›
Clone the repository. The Docker backend lives at the repo root and ships a custom-catalog.yaml alongside the Dockerfile.
$ git clone https://github.com/rosarion97/palo_alto-mcp-server-public.git $ cd palo_alto-mcp-server-public
The image tag must match the image: field in custom-catalog.yaml (paloalto-mcp-server:latest).
$ docker build -t paloalto-mcp-server .
Two ways to hand the container its credentials. Option A keeps your key in Docker's secret store and is recommended. The PANOS_ENABLE_WRITE flag controls whether write/commit tools are active, set it to "no" to deploy this server as read-only.
$ docker mcp secret set PANOS_HOST="firewall.example.com" $ docker mcp secret set PANOS_API_KEY="LUFRPT1xxxxxxxxxxxxxxxxxxxxxxxxxx==" $ docker mcp secret set PANOS_VERIFY_SSL="yes" $ docker mcp secret set PANOS_VSYS="vsys1" $ docker mcp secret set PANOS_ENABLE_WRITE="yes" # "no" for read-only
With Option A, continue through Steps 4–7 below.
chmod 600 .env, never commit it. This
path skips the gateway: skip Steps 4–6, point your client at
docker run --env-file (below), then jump to Step 7.
$ cp .env.example .env $ chmod 600 .env # then edit: PANOS_HOST / PANOS_API_KEY / PANOS_VERIFY_SSL / PANOS_VSYS / PANOS_ENABLE_WRITE
{
"mcpServers": {
"paloalto-firewall": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"--env-file", "/absolute/path/to/.env",
"paloalto-mcp-server:latest"
]
}
}
}
Steps 4–6 are the Option A (secret store) path. Used Option B? Skip to Step 7.
$ mkdir -p ~/.docker/mcp/catalogs $ cp custom-catalog.yaml ~/.docker/mcp/catalogs/custom.yaml
Add the entry under the single top-level registry: key in ~/.docker/mcp/registry.yaml. Don't overwrite the file if it already exists.
registry:
paloalto-firewall:
catalog: custom
enabled: true
Add the gateway block to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json). The gateway spawns the firewall image on demand, reads your catalog and registry, and resolves your PANOS_* secrets at request time. Replace <your-username> with your macOS username (run whoami to check):
{
"mcpServers": {
"mcp-toolkit-gateway": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "/var/run/docker.sock:/var/run/docker.sock",
"-v", "/Users/<your-username>/.docker/mcp:/mcp",
"-v", "/Users/<your-username>/Library/Caches/docker-secrets-engine/engine.sock:/root/.cache/docker-secrets-engine/engine.sock",
"docker/mcp-gateway:latest",
"--catalog=/mcp/catalogs/custom.yaml",
"--registry=/mcp/registry.yaml",
"--transport=stdio"
]
}
}
}
All three bind-mounts are required: the Docker socket lets the gateway spawn the server container; ~/.docker/mcp is where it reads your catalog and registry; the docker-secrets-engine socket is the resolver Docker Desktop exposes for the secret store, without it your PANOS_* URLs resolve to empty strings and the server never starts. On Linux Docker Desktop the path is ~/.docker/desktop/secrets-engine/engine.sock; find it with find ~ -name engine.sock.
claude mcp add -s user mcp-toolkit-gateway -- docker run …; Codex reads the same shape from ~/.codex/config.toml as [mcp_servers.mcp-toolkit-gateway]. Any MCP-capable client, Google's Gemini included, reads an mcpServers block from its own settings file; only the location changes, the entry is identical.
Then quit and reopen the client. claude_desktop_config.json never contains the API key, the gateway resolves it from Docker's secret store at request time. As a shortcut, docker mcp client connect claude-desktop (or MCP Toolkit → Clients in Docker Desktop) writes a similar block automatically; the explicit JSON above survives Docker Desktop updates that may rewrite the auto-managed entry.
$ docker mcp server list # paloalto-firewall → enabled $ docker mcp tools list
Podman Rootless · stdio ›
Clone the repo and change into the podman/ directory, which holds the Containerfile and server files.
$ git clone https://github.com/rosarion97/palo_alto-mcp-server-public.git $ cd palo_alto-mcp-server-public/podman
The machine is a small VM that runs your containers. Linux users skip this step.
$ podman machine init $ podman machine start
$ podman build -t paloalto-mcp-server:latest .
Two ways to hand the container its credentials. Option A keeps your key in Podman's encrypted secret store and is recommended; Option B writes a plaintext .env. Five values to set: PANOS_HOST, PANOS_API_KEY, PANOS_VERIFY_SSL, PANOS_VSYS, and PANOS_ENABLE_WRITE.
$ printf '%s' 'firewall.example.com' | podman secret create PANOS_HOST - $ printf '%s' 'LUFRPT1xxxxxxxxxxxxxxxxxxxxxxxxxx==' | podman secret create PANOS_API_KEY - $ printf '%s' 'yes' | podman secret create PANOS_VERIFY_SSL - $ printf '%s' 'vsys1' | podman secret create PANOS_VSYS - $ printf '%s' 'yes' | podman secret create PANOS_ENABLE_WRITE - # "no" for read-only
printf '%s' (no trailing newline) avoids a stray newline that can trigger "Could not connect" or "Write operations are disabled" errors.
chmod 600 .env,
never commit it.
$ cp .env.example .env $ chmod 600 .env # then edit with your values
Add the entry matching your Step 4 choice.
{
"mcpServers": {
"paloalto-firewall": {
"command": "podman",
"args": [
"run", "-i", "--rm",
"--secret", "PANOS_HOST,type=env",
"--secret", "PANOS_API_KEY,type=env",
"--secret", "PANOS_VERIFY_SSL,type=env",
"--secret", "PANOS_VSYS,type=env",
"--secret", "PANOS_ENABLE_WRITE,type=env",
"paloalto-mcp-server:latest"
]
}
}
}
{
"mcpServers": {
"paloalto-firewall": {
"command": "podman",
"args": [
"run", "-i", "--rm",
"--env-file", "/absolute/path/to/.env",
"paloalto-mcp-server:latest"
]
}
}
}
mcpServers block from its own settings.json
, Google's Gemini included. Drop this same entry into that client's
settings.json and the firewall tools appear there too. Only the
file's name and location change; the server entry is identical.
If podman isn't on the client's PATH (common for macOS GUI apps), replace "podman" with its absolute path, run which podman to find it.
Run the same command your client will run, it should start and wait on stdin for JSON-RPC. Press Ctrl+C to exit.
$ podman run --rm -i \
--secret PANOS_HOST,type=env \
--secret PANOS_API_KEY,type=env \
--secret PANOS_VERIFY_SSL,type=env \
--secret PANOS_VSYS,type=env \
--secret PANOS_ENABLE_WRITE,type=env \
paloalto-mcp-server:latest
Six Safeguards Between the LLM and Production
This server can change live firewall configuration. The model is honest about what's at stake; the server has six layers of defense built in:
- Staging by default. Every write tool targets the candidate config (
action=set/edit/delete/move). Changes are not live untilcommit_configruns.discard_changesrolls back the candidate to the running config. - Dry-run validation.
validate_commitruns a full commit validation and surfaces errors / warnings before you commit. - Write kill-switch. Set
PANOS_ENABLE_WRITE=noand every write / commit tool refuses; the server behaves read-only. Flip it toyesonly when you intend to make changes. - Input validation. Object names are restricted to
[A-Za-z0-9_.\- ]; ports, tag colors, and address types are validated against patterns / enums; free-text fields (descriptions, comments, DAG filters) are XML-escaped before being embedded, blocking attribute-quote breakouts and XML injection. - Well-formed-only raw edits.
set_config/edit_configrequire the XPath to start with/configand the element to parse as valid XML before anything is sent. - RBAC is the backstop. Scope the API key's admin role to exactly what the model should touch. Consider a role that can stage and validate but cannot commit, leaving
commit_configas a human-only path. The firewall rejects anything the role forbids.
validate_commit in your workflow, and prefer a non-committing role
plus PANOS_ENABLE_WRITE=no until you're confident.
Use At Your Own Risk
PANOS_ENABLE_WRITE=no on any deployment that should stay observational.
Use at your own risk. Not affiliated with or endorsed by Palo Alto Networks.