Install any skill in seconds. Free to start, no credit card required.
Get Started Free →FortiAnalyzer log operations — policy-filtered traffic log query within a bounded time window, offset pagination, per-policy activity checks, and logging-device inventory. Use when asking whether traffic actually matched a firewall rule, investigating what hit a policy, or determining whether a rule is genuinely unused.
.claude/skills/automateyournetwork-fortianalyzer-ops/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 16% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -5% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 50% | 0% |
fortinet-mcp (NetClaw-authored, spec 080 / roadmap R3)$FORTINET_MCP_CMD/jsonrpc (the same dialect FortiManager speaks)FORTIANALYZER_HOST, FORTIANALYZER_API_TOKEN (FortiAnalyzer 7.2.2+ for token auth)> ### "No logs matched" is NOT "this rule is unused."
This skill exists to answer "is this rule dead?" — and that question is dangerously easy to answer wrongly. An empty result can mean:
Reporting any of those as "unused" would license someone to delete a live firewall rule. So an empty result returns the explicit outcome no_logs_in_window, never ok and never an error, with a message saying what it does and does not prove.
This is the same error class as spec 078's "no advisories ≠ not vulnerable" and spec 079's "no probes found ≠ outage", and it gets the same treatment: a separate, named outcome that cannot be silently collapsed.
| Question | Plane | Skill | |---|---|---| | "Has anything actually matched this rule?" | analyzer | this skill | | "What policy is intended?" | manager | fortimanager-ops | | "What is the box running right now?" | device | fortigate-ops |
The manager knows a rule exists. Only the analyzer knows whether anyone ever matched it. A configured rule is not a used rule.
| Tool | What it answers | |---|---| | faz_query_logs | Traffic logs matching a filter within a bounded window | | faz_fetch_more | Next page, re-run at an offset | | faz_policy_activity | Did anything match policy N in this window? | | faz_list_devices | Which devices forward logs here — check this first |
If you supply no window, the tools apply the last 24 hours and say so in scope.window_start / scope.window_end and in notes. An unbounded log query against a busy analyzer is slow, expensive, and produces a result nobody can interpret because they do not know what period it covers.
Every response echoes the window actually queried, not the one requested.
faz_fetch_more re-runs the search at a new offset. It does not reuse FortiAnalyzer's search task id (tid), because those are single-use and expire — treating one as a durable cursor produces silent truncation, where you believe you have all the results and you have some of them.
faz_list_devices — confirm the device that owns the rule actually forwardslogs here. If it does not, stop: an empty result would mean nothing.
faz_policy_activity with the policy id and an explicit, generous window.ok with sessions_matched > 0 → the rule is live. Do not remove it.no_logs_in_window → nothing matched in that window. Not proof of disuse.the period, logging enabled on the rule itself.
fortimanager-ops, which enforces the two write gates.
faz_query_logs with filter_expr (e.g. policyid=12) and a bounded window.faz_fetch_more using the returned next_offset while has_more.fmg_resolve_object to check whetherthe traffic matches what the rule intended to permit.
jsonc{ "plane": "analyzer", "scope": {"adom": "root", "window_start": "...", "window_end": "..."}, "outcome": "no_logs_in_window", "message": "... This is NOT evidence the rule is unused." }
The window is part of the scope, not an optional detail. A log result without its window is uninterpretable, so a response that cannot state one is an error.
| Skill | How they compose | |---|---| | fortimanager-ops | Find the rule (intent), then come here for whether it was used | | fortigate-ops | Device state; use together to separate "not configured" from "not used" | | fwrule-analyzer | Shadowed rules found there + zero activity here = strong removal case | | servicenow-change-workflow | Rule removal is a production change — CR required | | gait-session-tracking | Every query here is GAIT-audited automatically |
faz_list_devices before trusting silence.Other measured skills in the registry, with their headline benchmark lift.