Skip to content

mcfortigate

Ask a FortiGate a question in plain language and get an answer back. Nothing here can change a configuration.

The obvious way to build this is one tool per FortiOS endpoint returning raw JSON, and that turns out to work badly. A single FortiOS policy object carries more than eighty fields, most of them empty strings, unused IPv6 arrays, and internal UUIDs. Ask a model which policies allow traffic into the DMZ against a raw wrapper and it spends most of its attention on "srcaddr6": [] before it finds anything useful.

So the tools here are shaped around the questions rather than around the endpoints. find_references is five REST calls collapsed into one answer about whether an object is safe to delete — an answer that is deliberately three-valued, because the client library turns a denied read into an empty list and a naive version of this tool reports a heavily-used object as unreferenced. find_device merges the wireless client list, the DHCP lease table, and the ARP table into a single record per device, which requires normalizing MAC casing first because FortiOS reports it differently on each of those three endpoints. That work happens once, here, instead of being rediscovered by every model that asks.

The long version of the argument is worth reading before you decide how much of your own tooling to shape this way.

Terminal window
claude mcp add fortigate \
--env FORTIGATE_HOST=fgt.example.com \
--env FORTIGATE_TOKEN=your-token \
-- uvx mcfortigate

That is the whole setup, once you have a read-only REST API admin on the appliance. Getting started walks through creating one.

Read-only, structurally

Every tool issues GET and nothing else, and says so through the MCP readOnlyHint annotation. There is no create, update, or delete path in the codebase to disable, misconfigure, or talk a model into reaching. How that is enforced.

Configuration and live state are different

A DHCP-assigned default route is in the live routing table and will never be in the static route configuration. The tools keep the two apart on purpose. Why it matters.

FortiOS quirks, already handled

Booleans are the strings "enable" and "disable", so bool() reads every disabled thing as enabled. That one mistake once flipped every route to blackhole. The full catalogue.

Several appliances, short aliases

Set FORTIGATE_TARGETS and the keys become the target argument on every tool. Omit it when only one is configured. Multi-appliance setup.