FortiManager Operations — the manager plane
MCP Server
- Server:
fortinet-mcp (NetClaw-authored, spec 080 / roadmap R3)
- Command:
$FORTINET_MCP_CMD
- Transport: stdio
- Requires:
FORTIMANAGER_HOST, FORTIMANAGER_API_TOKEN
- Mode: read-only by default; installs require two gates (below)
v2.0.0: this skill previously declared FORTIMANAGER_MCP_CMD pointing at
jmpijll/fortimanager-mcp, which was never vendored, never registered and not
installable — the skill was a claim with no server behind it. It is now backed by
fortinet-mcp, and the command variable changed to FORTINET_MCP_CMD because one
server serves all three Fortinet planes.
The distinction this skill exists to protect
FortiManager holds INTENT. It does not know what a device is actually doing.
| Question |
Plane |
Skill |
| "What policy is supposed to apply here?" |
manager |
this skill |
| "What is the box actually running? Is the tunnel up?" |
device |
fortigate-ops |
| "Has anything ever matched this rule?" |
analyzer |
fortianalyzer-ops |
| "Run a raw FortiOS CLI command" |
CLI |
multivendor-raw-cli (spec 076) |
A policy package and a FortiGate's running config legitimately diverge between
installs. That gap is where drift, unauthorised change and failed installs live —
use fgt_compare_with_manager to surface it rather than assuming they agree.
Tools (8 read-only + 2 write)
| Tool |
What it answers |
fmg_list_adoms |
Which ADOMs exist. The ADOM scopes everything else |
fmg_list_devices |
Managed FortiGates, connection and sync status |
fmg_list_policy_packages |
Packages in an ADOM and their install targets |
fmg_get_policy_package |
Ordered rules: position, action, enabled state |
fmg_search_rules |
Rules matching a source, destination, service or object |
fmg_resolve_object |
Object/group → members, resolved recursively |
fmg_get_revisions |
Revision history — rollback context |
fmg_preview_install |
What an install would change. No gate required |
fmg_check_change_record |
Is a ServiceNow CR approved? Read-only |
fmg_install_package |
Production change. Two gates, see below |
Every response carries its plane and scope
{ "plane": "manager", "scope": {"adom": "root", "package": "Corp"},
"source": "...", "outcome": "ok", "data": {...}, "notes": [] }
outcome distinguishes results that look alike: ok, empty_result,
plane_unreachable, auth_expired, auth_missing, scope_indeterminate, and the
three separate write refusals. An expired session is auth_expired, never "no
policies exist" — that would be a silent, plausible, wrong answer.
Workflow: policy package audit
fmg_list_adoms → pick the ADOM. A package name is unique only within one.
fmg_list_policy_packages → find the package and its install targets.
fmg_get_policy_package → ordered rules. Note position: shadowing is positional.
fmg_resolve_object on every group a rule references. A rule reported only by
object name is not an audit — "allow GRP_CORP to GRP_DMZ" says nothing about
which addresses that permits.
fmg_get_revisions → rollback context before proposing any change.
- Feed the rules to
fwrule-analyzer for overlap, shadowing and conflict analysis.
Workflow: is intent matching reality?
fmg_get_policy_package — the intent.
fgt_compare_with_manager (in fortigate-ops) — the divergence.
only_in_device entries are candidate out-of-band changes: someone edited
the firewall directly. This is the single most valuable finding here.
only_in_manager usually means the package has not been installed since those
rules were added — check fmg_list_devices sync status.
Writes: two gates, and neither substitutes for the other
fmg_install_package pushes policy to production firewalls — the highest
blast-radius action available here.
| Condition |
Outcome |
FORTINET_ALLOW_WRITES not set |
refused_read_only |
No approved_by |
refused_no_approval |
| No approved ServiceNow CR (non-lab) |
refused_no_change_record |
| Both present |
proceeds: revision identified → install → verify |
Human approval and a ServiceNow change record are different gates. A CR does
not imply a human said yes; a human saying yes does not imply change control
approved it. Lab devices waive the CR gate only — never the approval gate — and
a device that cannot be classified is treated as production.
Always run fmg_preview_install first. It shows what would change and needs no gate.
Integration with other skills
| Skill |
How they compose |
fortigate-ops |
Device-plane state; fgt_compare_with_manager for drift |
fortianalyzer-ops |
Whether a rule has actually matched traffic |
fwrule-analyzer |
Feed retrieved policy to its FortiOS parser for overlap/shadowing |
servicenow-change-workflow |
Supplies the CR that satisfies gate 2 of fmg_install_package |
multivendor-raw-cli |
Raw FortiOS CLI (spec 076) — a different plane, not a substitute |
gait-session-tracking |
Every operation here is GAIT-audited automatically |
Important rules
- Treat package install as production change execution — baseline, verify, rollback.
- Always name the ADOM. A package without one is ambiguous.
- Resolve objects before drawing conclusions.
- Never present manager intent as observed device state.
- An empty result is not an error, and
auth_expired is not "no data".
1---
2name: fortimanager-ops
3description: "FortiManager policy operations — ADOM inventory, policy package review, recursive object resolution, revision history, install preview, and gated package install. Use when auditing FortiGate firewall policy at the MANAGER level (intent), reviewing ADOM policy packages, or planning a package install with rollback context."
4version: 2.0.0
5license: Apache-2.0
6tags: [fortinet, fortimanager, firewall, policy, adom, security, multi-vendor]
7user-invocable: true
8metadata:
9 { "openclaw": { "requires": { "bins": ["python3"], "env": ["FORTINET_MCP_CMD", "FORTIMANAGER_HOST", "FORTIMANAGER_API_TOKEN"] } } }
10---
11
12# FortiManager Operations — the manager plane
13
14## MCP Server
15
16- **Server**: `fortinet-mcp` (NetClaw-authored, spec 080 / roadmap R3)
17- **Command**: `$FORTINET_MCP_CMD`
18- **Transport**: stdio
19- **Requires**: `FORTIMANAGER_HOST`, `FORTIMANAGER_API_TOKEN`
20- **Mode**: read-only by default; installs require **two** gates (below)
21
22> **v2.0.0**: this skill previously declared `FORTIMANAGER_MCP_CMD` pointing at
23> `jmpijll/fortimanager-mcp`, which was never vendored, never registered and not
24> installable — the skill was a claim with no server behind it. It is now backed by
25> `fortinet-mcp`, and the command variable changed to `FORTINET_MCP_CMD` because one
26> server serves all three Fortinet planes.
27
28## The distinction this skill exists to protect
29
30**FortiManager holds INTENT. It does not know what a device is actually doing.**
31
32| Question | Plane | Skill |
33|---|---|---|
34| "What policy is *supposed* to apply here?" | manager | **this skill** |
35| "What is the box *actually* running? Is the tunnel up?" | device | `fortigate-ops` |
36| "Has anything ever *matched* this rule?" | analyzer | `fortianalyzer-ops` |
37| "Run a raw FortiOS CLI command" | CLI | `multivendor-raw-cli` (spec 076) |
38
39A policy package and a FortiGate's running config legitimately diverge between
40installs. That gap is where drift, unauthorised change and failed installs live —
41use `fgt_compare_with_manager` to surface it rather than assuming they agree.
42
43## Tools (8 read-only + 2 write)
44
45| Tool | What it answers |
46|---|---|
47| `fmg_list_adoms` | Which ADOMs exist. The ADOM scopes everything else |
48| `fmg_list_devices` | Managed FortiGates, connection and sync status |
49| `fmg_list_policy_packages` | Packages in an ADOM and their install targets |
50| `fmg_get_policy_package` | Ordered rules: position, action, enabled state |
51| `fmg_search_rules` | Rules matching a source, destination, service or object |
52| `fmg_resolve_object` | Object/group → members, **resolved recursively** |
53| `fmg_get_revisions` | Revision history — rollback context |
54| `fmg_preview_install` | What an install *would* change. **No gate required** |
55| `fmg_check_change_record` | Is a ServiceNow CR approved? Read-only |
56| `fmg_install_package` | **Production change.** Two gates, see below |
57
58## Every response carries its plane and scope
59
60```jsonc
61{ "plane": "manager", "scope": {"adom": "root", "package": "Corp"},
62 "source": "...", "outcome": "ok", "data": {...}, "notes": [] }
63```
64
65`outcome` distinguishes results that look alike: `ok`, `empty_result`,
66`plane_unreachable`, `auth_expired`, `auth_missing`, `scope_indeterminate`, and the
67three separate write refusals. **An expired session is `auth_expired`, never "no
68policies exist"** — that would be a silent, plausible, wrong answer.
69
70## Workflow: policy package audit
71
721. `fmg_list_adoms` → pick the ADOM. A package name is unique only within one.
732. `fmg_list_policy_packages` → find the package and its install targets.
743. `fmg_get_policy_package` → ordered rules. Note **position**: shadowing is positional.
754. `fmg_resolve_object` on every group a rule references. **A rule reported only by
76 object name is not an audit** — "allow GRP_CORP to GRP_DMZ" says nothing about
77 which addresses that permits.
785. `fmg_get_revisions` → rollback context before proposing any change.
796. Feed the rules to `fwrule-analyzer` for overlap, shadowing and conflict analysis.
80
81## Workflow: is intent matching reality?
82
831. `fmg_get_policy_package` — the intent.
842. `fgt_compare_with_manager` (in `fortigate-ops`) — the divergence.
853. `only_in_device` entries are candidate **out-of-band changes**: someone edited
86 the firewall directly. This is the single most valuable finding here.
874. `only_in_manager` usually means the package has not been installed since those
88 rules were added — check `fmg_list_devices` sync status.
89
90## Writes: two gates, and neither substitutes for the other
91
92`fmg_install_package` pushes policy to production firewalls — the highest
93blast-radius action available here.
94
95| Condition | Outcome |
96|---|---|
97| `FORTINET_ALLOW_WRITES` not set | `refused_read_only` |
98| No `approved_by` | `refused_no_approval` |
99| No approved ServiceNow CR (non-lab) | `refused_no_change_record` |
100| Both present | proceeds: revision identified → install → verify |
101
102**Human approval and a ServiceNow change record are different gates.** A CR does
103not imply a human said yes; a human saying yes does not imply change control
104approved it. Lab devices waive the CR gate **only** — never the approval gate — and
105a device that cannot be classified is treated as **production**.
106
107Always run `fmg_preview_install` first. It shows what would change and needs no gate.
108
109## Integration with other skills
110
111| Skill | How they compose |
112|---|---|
113| `fortigate-ops` | Device-plane state; `fgt_compare_with_manager` for drift |
114| `fortianalyzer-ops` | Whether a rule has actually matched traffic |
115| `fwrule-analyzer` | Feed retrieved policy to its FortiOS parser for overlap/shadowing |
116| `servicenow-change-workflow` | Supplies the CR that satisfies gate 2 of `fmg_install_package` |
117| `multivendor-raw-cli` | Raw FortiOS CLI (spec 076) — a different plane, not a substitute |
118| `gait-session-tracking` | Every operation here is GAIT-audited automatically |
119
120## Important rules
121
122- **Treat package install as production change execution** — baseline, verify, rollback.
123- **Always name the ADOM.** A package without one is ambiguous.
124- **Resolve objects before drawing conclusions.**
125- **Never present manager intent as observed device state.**
126- **An empty result is not an error**, and `auth_expired` is not "no data".