FortiManager MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| LOG_LEVEL | No | Logging level (e.g., DEBUG, INFO, WARNING). | INFO |
| DEFAULT_ADOM | No | Default Administrative Domain (ADOM) for operations. | root |
| FMG_TOOL_MODE | No | Tool loading mode: 'full' or 'dynamic'. | full |
| MCP_AUTH_TOKEN | No | Bearer token for HTTP authentication (optional). | |
| MCP_SERVER_HOST | No | Bind address for HTTP mode. | 127.0.0.1 |
| MCP_SERVER_MODE | No | MCP transport mode: 'http', 'stdio', or 'auto'. | auto |
| MCP_SERVER_PORT | No | Port for HTTP mode. | 8000 |
| FMG_POLICY_SAFETY | No | Policy safety mode: 'strict', 'warn', 'disabled'. | strict |
| FMG_SCRIPT_SAFETY | No | Script safety mode: 'strict', 'disabled'. | strict |
| FORTIMANAGER_HOST | Yes | FortiManager hostname or IP address. | |
| MCP_ALLOWED_HOSTS | No | List of allowed Host headers (JSON array string). | |
| FORTIMANAGER_TIMEOUT | No | Request timeout in seconds. | 30 |
| FORTIMANAGER_PASSWORD | No | Password for authentication. | |
| FORTIMANAGER_USERNAME | No | Username for authentication (alternative to API token). | |
| FORTIMANAGER_API_TOKEN | No | API token for authentication (recommended). | |
| FORTIMANAGER_VERIFY_SSL | No | Enable SSL verification for FortiManager connection. | true |
| FORTIMANAGER_MAX_RETRIES | No | Maximum number of retry attempts. | 3 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| health_checkA | Check FortiManager MCP server health and connection status. |
| create_device_interfaceA | Create a VLAN subinterface in a device's device DB. Reads back with Args: device: Managed device name name: New interface name (e.g. "vlan15") parent: Physical/aggregate parent interface (e.g. "internal") vlanid: VLAN id (1-4094) ip: Interface address, CIDR ("192.0.2.254/24") or "address netmask" allowaccess: Administrative access to allow (e.g. ["ping", "https"]) role: Interface role: lan, wan, dmz or undefined alias: Short alias shown in the GUI vdom: VDOM the interface belongs to (default "root") description: Interface description Returns: Creation result |
| update_device_interfaceA | Update fields on a device-DB interface. Only provided fields change. Args: device: Managed device name name: Interface name to update ip: New address, CIDR or "address netmask" form allowaccess: Replacement administrative-access list role: Interface role: lan, wan, dmz or undefined alias: Short alias shown in the GUI description: Interface description vlanid: New VLAN id (1-4094) Returns: Update result |
| delete_device_interfaceC | Delete an interface from a device's device DB. Args: device: Managed device name name: Interface name to delete Returns: Deletion result |
| list_device_dhcp_serversA | List DHCP servers configured in a device's device DB. Args: device: Managed device name vdom: VDOM to read (default "root") Returns: dict with device, vdom, count and dhcp_servers (raw device-DB objects, keyed by numeric id) |
| create_device_dhcp_serverA | Create a DHCP server scope in a device's device DB. Args: device: Managed device name interface: Interface the scope serves (e.g. "vlan15") start_ip: First address of the lease range end_ip: Last address of the lease range netmask: Scope netmask (e.g. "255.255.255.0") default_gateway: Gateway handed to clients (defaults to the interface address if omitted, per FortiOS behavior) dns_servers: Up to three DNS servers to hand out; omitted means the device's system DNS is used (dns-service default) lease_time: Lease time in seconds vdom: VDOM the scope lives in (default "root") Returns: Creation result including the new scope id |
| create_device_snifferA | Create a firewall sniffer definition on a managed device. This targets the FortiGate's own persistent Args: device: Managed device name interface: Interface name traffic sniffing takes place on host: Hosts to filter for (e.g. "1.1.1.1", "2.2.2.0/24") port: Ports to sniff (e.g. "80", "1-1024") protocol: IANA protocol number as a string (e.g. "6" for TCP) vlan: VLAN(s) to sniff capture_ipv6: Include IPv6 packets (default: False) capture_non_ip: Include non-IP packets (default: False) status: "enable" or "disable" (default: "enable") logtraffic: "all", "utm", or "disable" (optional) sniffer_id: Explicit sniffer ID (0-9999); omit to let FortiOS assign the next available one vdom: VDOM the sniffer lives in (default "root") Returns: Creation result including the new sniffer id |
| create_device_on_demand_snifferA | Create a bounded on-demand packet sniffer on a managed device. Distinct FortiOS object from create_device_sniffer's How to actually trigger the capture and retrieve packets once this definition exists is not part of FortiOS's Configuration API (this tool only stages the definition, config-CRUD like every other tool in this module) -- that would be a separate monitor-API or CLI mechanism, out of scope here. Args: device: Managed device name name: On-demand sniffer name interface: Interface name traffic sniffing takes place on max_packet_count: Maximum packets to capture (default: 4000) hosts: IPv4/IPv6 hosts to filter for ports: Ports to filter for (each 1-65535) protocols: IANA protocol numbers to filter for (each 0-255) capture_non_ip: Include non-IP packets (default: False) vdom: VDOM the sniffer lives in (default "root") Returns: Creation result |
| update_device_dhcp_serverA | Update a device-DB DHCP server scope by id. Only provided fields change. The lease range is replaced as a whole, so start_ip and end_ip must be given together. Args: device: Managed device name dhcp_server_id: Numeric scope id (see list_device_dhcp_servers) start_ip: New first address of the lease range end_ip: New last address of the lease range netmask: New scope netmask default_gateway: New gateway handed to clients dns_servers: Up to three DNS servers; replaces the served set lease_time: New lease time in seconds vdom: VDOM the scope lives in (default "root") Returns: Update result |
| delete_device_dhcp_serverA | Delete a DHCP server scope from a device's device DB. Args: device: Managed device name dhcp_server_id: Numeric scope id (see list_device_dhcp_servers) vdom: VDOM the scope lives in (default "root") Returns: Deletion result |
| list_device_vapsB | List wireless VAPs (SSIDs) in a device's device DB. Args: device: Managed device name vdom: VDOM to read (default "root") Returns: dict with device, vdom, count and vaps (passphrases stripped) |
| create_device_vapA | Create a wireless VAP (SSID) in a device's device DB. The VAP becomes broadcastable once added to a FortiAP profile (assign_vap_to_wtp_profile). Args:
device: Managed device name
name: VAP object name
ssid: SSID to broadcast
security: Security mode, a FortiOS Returns: Creation result with any echoed passphrase stripped |
| delete_device_vapA | Delete a wireless VAP from a device's device DB. Args: device: Managed device name name: VAP object name to delete vdom: VDOM the VAP lives in (default "root") Returns: Deletion result |
| assign_vap_to_wtp_profileA | Add a VAP (SSID) to a FortiAP profile's radios so it gets broadcast. Reads the profile, appends the VAP to each selected radio's manual VAP list, and switches that radio's vap-all mode to "manual" (the mode that honors an explicit list). VAPs already present are left in place. Args: device: Managed device name profile: FortiAP (WTP) profile name vap: VAP object name to broadcast radios: Radios to add the SSID to, from 1-3 (default [1, 2]) vdom: VDOM the profile lives in (default "root") Returns: Update result listing the radios changed |
| list_device_wtp_profilesB | List FortiAP (WTP) profiles in a device's device DB. Args: device: Managed device name vdom: VDOM to read (default "root") Returns: dict with device, vdom, count and profiles (credential fields stripped -- see _WTP_PROFILE_SECRET_FIELDS) |
| get_device_wtp_profileA | Get a FortiAP (WTP) profile from a device's device DB, radios included. Args: device: Managed device name profile: FortiAP (WTP) profile name vdom: VDOM the profile lives in (default "root") Returns: dict with device, vdom and the stored profile object (credential fields stripped -- see _WTP_PROFILE_SECRET_FIELDS), or a not_found error if the profile does not exist |
| update_device_wtp_profile_radioA | Update a single radio's channel and/or channel width on a FortiAP profile. This is a read-modify-write, like assign_vap_to_wtp_profile: the profile is read first and every other field already on the target radio (band, power, vap-all, vaps, mode, ...) is carried through unchanged, minus a couple of read-only bookkeeping keys the API injects on GET but rejects on write (see _WTP_RADIO_READONLY_KEYS). FortiManager replaces a nested radio object rather than merging into it, so writing back only channel/channel-bonding would reset the rest to defaults on the next install_device_settings. Args: device: Managed device name profile: FortiAP (WTP) profile name radio: Radio to update, 1-3 channel: Channel(s) to pin the radio to. For radio 1/2 (2.4/5GHz) this is a single-element list, e.g. [36], to pin one channel. For radio 3 (6GHz) at a bonding width above 20MHz, FortiOS represents the wide channel as the full list of its member 20MHz sub-channels rather than a single label -- pass either the single conventional wide-channel label (e.g. [15] for 160MHz channel 15) and it is expanded to the correct member list automatically, or the explicit member list itself (e.g. [1, 5, 9, 13, 17, 21, 25, 29]). channel_bonding: Channel width/bonding, one of WTP_PROFILE_CHANNEL_BONDING_MODES ("20MHz", "40MHz", "80MHz", "160MHz") vdom: VDOM the profile lives in (default "root") Returns: Update result |
| list_device_wtpsA | List managed FortiAPs registered in a device's device DB. Args: device: Managed device name (the wireless controller / FortiGate) vdom: VDOM to read (default "root") Returns: dict with device, vdom, count and wtps (device-DB objects keyed by wtp-id/serial, credential fields stripped -- see _WTP_SECRET_FIELDS) |
| get_device_wtpA | Get one managed FortiAP by wtp-id (serial number). Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) vdom: VDOM the AP is registered in (default "root") Returns: dict with device, vdom and the stored wtp object (credential fields stripped -- see _WTP_SECRET_FIELDS), or a not_found error if it does not exist |
| create_device_wtpA | Register a managed FortiAP in a device's device DB. This is the wireless-controller.wtp table: the mapping from a physical AP's serial number to the profile it runs. A wtp-profile that exists in the device DB but is not referenced by any wtp entry is pruned by FortiManager on install, so registering the AP here is required before a profile-only setup survives install_device_settings. Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) wtp_profile: FortiAP (WTP) profile name to assign name: Friendly display name admin: Authorization state: "discovered" (awaiting authorization), "enable" (authorized) or "disable"; default "discovered" location: Free-text location string comment: Free-text comment vdom: VDOM to register the AP in (default "root") Returns: Creation result |
| update_device_wtpA | Update fields on a device-DB managed FortiAP. Only provided fields change. Repointing wtp_profile drops a reference from the old profile. FortiManager prunes a wtp-profile that no wtp entry references on install (see create_device_wtp), so moving the last AP off a profile loses that profile on the next install_device_settings. Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) wtp_profile: New FortiAP (WTP) profile name to assign name: New friendly display name admin: New authorization state: "discovered", "enable" or "disable" location: New free-text location string comment: New free-text comment vdom: VDOM the AP is registered in (default "root") Returns: Update result |
| delete_device_wtpA | Delete a managed FortiAP registration from a device's device DB. This drops the AP's reference to its wtp-profile. FortiManager prunes a wtp-profile that no wtp entry references on install (see create_device_wtp), so deleting the last AP on a profile loses that profile on the next install_device_settings. Args: device: Managed device name wtp_id: FortiAP serial number (the wtp-id / table mkey) vdom: VDOM the AP is registered in (default "root") Returns: Deletion result |
| create_device_groupA | Create a device group in FortiManager's device manager database. Device groups organize managed devices (and other groups) for bulk operations like policy installation. This is a dvmdb object -- creation does not touch any live device. Args: adom: ADOM name where the group will be created name: Group name (dvmdb group.name, master key) os_type: Device platform family the group targets (default: "unknown"). One of: unknown, fos, fsw, foc, fml, faz, fwb, fch, fct, log, fmg, fsa, fdd, fac, fpx, fna, ffw, fsr, fad, fdc, fap, fxt, fts, fai, fwc, fis, fed, fpa, fca, ftc, fss, fra, sim, fdt. description: Optional group description Returns: dict: Create result with keys: - status: "success" or "error" - group: Created group name/adom - message: Status or error message Example: >>> result = await create_device_group( ... adom="root", ... name="Branch-Firewalls", ... os_type="fos", ... description="All branch FortiGates", ... ) |
| delete_device_groupA | Delete a device group from FortiManager. Removes the group object itself. Member devices are not affected -- only their group membership is removed. WARNING: This operation cannot be undone. Args: adom: ADOM name where the group is located name: Group name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await delete_device_group("root", "Branch-Firewalls") |
| add_device_to_groupA | Add a single device to a device group. Non-destructive: existing group members are preserved. Args: adom: ADOM name group: Target device group name device: Device name to add vdom: Device VDOM to add (default: "root") Returns: dict: Add result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await add_device_to_group("root", "Branch-Firewalls", "FGT-Branch1") |
| remove_device_from_groupA | Remove a single device from a device group. Args: adom: ADOM name group: Device group name device: Device name to remove vdom: Device VDOM to remove (default: "root") Returns: dict: Remove result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await remove_device_from_group("root", "Branch-Firewalls", "FGT-Branch1") |
| add_devices_to_group_bulkA | Add multiple devices to a device group in one call. Non-destructive: existing group members are preserved. All devices are
added on the same VDOM; call Args: adom: ADOM name group: Target device group name devices: List of device names to add vdom: VDOM to add each device on (default: "root") Returns: dict: Add result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of memberships actually changed: FortiManager reports no per-member result, so a device that does not exist or is already a member is indistinguishable from one that was added. - message: Status or error message Example: >>> result = await add_devices_to_group_bulk( ... "root", "Branch-Firewalls", ["FGT-Site1", "FGT-Site2"] ... ) |
| remove_devices_from_group_bulkA | Remove multiple devices from a device group in one call. Args: adom: ADOM name group: Device group name devices: List of device names to remove vdom: VDOM the devices were added on (default: "root") Returns: dict: Remove result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of memberships actually changed: FortiManager's DELETE on a non-member is a lenient no-op that still reports success, so a device that was never in the group is indistinguishable from one that was removed. - message: Status or error message Example: >>> result = await remove_devices_from_group_bulk( ... "root", "Branch-Firewalls", ["FGT-Site1", "FGT-Site2"] ... ) |
| add_group_to_groupA | Nest a device group inside another device group. A group member has no VDOM (unlike a device member), which is what distinguishes a nested-group entry from a device entry in the dvmdb group membership list. Args: adom: ADOM name parent_group: Group that will contain child_group child_group: Group to nest inside parent_group Returns: dict: Add result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await add_group_to_group("root", "All-Branches", "Branch-Firewalls") |
| remove_group_from_groupB | Remove a nested group from its parent device group. Args: adom: ADOM name parent_group: Group that currently contains child_group child_group: Nested group to remove from parent_group Returns: dict: Remove result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await remove_group_from_group("root", "All-Branches", "Branch-Firewalls") |
| list_device_vdomsA | List VDOMs for a specific device. Virtual Domains (VDOMs) are independent virtual instances within a FortiGate device, each with its own configuration. Args: device: Device name adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") Returns: dict: VDOM list with keys: - status: "success" or "error" - count: Number of VDOMs - vdoms: List of VDOM objects with name, status, etc. - message: Error message if failed Example: >>> result = await list_device_vdoms("FGT-HQ", "root") >>> for vdom in result['vdoms']: ... print(f"VDOM: {vdom['name']}") |
| get_device_statusA | Get device status including connection and config sync status. Returns status information for all devices or a specific device:
Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") device: Specific device name (optional, returns all if not specified) Returns: dict: Device status with keys: - status: "success" or "error" - count: Number of devices - devices: List of device status objects - message: Error message if failed Example: >>> # Get all device status >>> result = await get_device_status("root") >>> for dev in result['devices']: ... print(f"{dev['name']}: {dev['conn_status_str']}") |
| search_devicesA | Search for devices with filters. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by device name (partial match) platform_filter: Filter by platform type (e.g., "FortiGate-VM") os_version_filter: Filter by OS version as major[.minor[.patch]] (e.g., "7", "7.4" or "7.4.12"; a leading "v" is accepted) connection_status: Filter by status ("up" or "down") Returns: dict: Search results with keys: - status: "success" or "error" - count: Number of matching devices - devices: List of matching device objects - message: Error message if failed Example: >>> # Find all FortiGate VMs >>> result = await search_devices(platform_filter="FortiGate-VM") |
| add_deviceA | Add a new device to FortiManager. Registers a device with FortiManager for central management. Can add either a real device (with IP) or a model device (with serial number). Args: adom: ADOM name where device will be added name: Device display name ip: Device IP address (for real device connection) serial_number: Device serial number (for model device or validation) admin_user: Admin username for device connection admin_pass: Admin password for device connection description: Device description platform: Platform type (default: "FortiGate-VM64") mgmt_mode: Management mode - "fmg" (FortiManager only), or "fmgfaz" (both) flags: Additional flags like ["create_task"] Returns: dict: Add result with keys: - status: "success" or "error" - device: Added device information - task_id: Task ID if run as background task - message: Error message if failed Example: >>> # Add a FortiGate with IP >>> result = await add_device( ... adom="root", ... name="FGT-Branch1", ... ip="192.168.1.1", ... admin_user="admin", ... admin_pass="password123" ... ) |
| add_model_deviceA | Add a model device for offline provisioning. Model devices allow pre-configuring policies and settings before the actual device connects to FortiManager. Useful for zero-touch provisioning workflows. Args: adom: ADOM name where device will be added name: Device display name serial_number: Device serial number (e.g., "FGVM020000123456") platform: Platform type (default: "FortiGate-VM64") os_version: FortiOS version (default: "7.0") description: Device description Returns: dict: Add result with keys: - status: "success" or "error" - device: Added device information - message: Status or error message Example: >>> result = await add_model_device( ... adom="root", ... name="FGT-NewBranch", ... serial_number="FGVM02TM12345678", ... platform="FortiGate-60F", ... os_version="7.4" ... ) |
| delete_deviceA | Delete a device from FortiManager. Removes a device registration. Does not affect the actual device or its configuration - only removes it from FortiManager management. WARNING: This operation cannot be undone. Args: adom: ADOM name where device is located device: Device name to delete flags: Additional flags like ["create_task"] Returns: dict: Delete result with keys: - status: "success" or "error" - task_id: Task ID if run as background task - message: Status or error message Example: >>> result = await delete_device("root", "FGT-OldBranch") >>> if result['status'] == 'success': ... print("Device removed from FortiManager") |
| add_devices_bulkA | Add multiple devices to FortiManager in bulk. Registers multiple devices at once for efficiency. Args: adom: ADOM name where devices will be added devices: List of device configurations. Each device dict can contain: - name: Device display name (required) - ip: Device IP address - sn: Serial number - adm_usr: Admin username - adm_pass: Admin password - desc: Description - platform_str: Platform type - os_ver: OS version flags: Additional flags like ["create_task"] Returns: dict: Bulk add result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of devices actually added: the add runs as a task, so per-device results only exist once it completes. - task_id: Task ID if run as background task - message: Error message if failed Example: >>> devices = [ ... {"name": "FGT-Site1", "ip": "10.0.1.1", "adm_usr": "admin", "adm_pass": "pass1"}, ... {"name": "FGT-Site2", "ip": "10.0.2.1", "adm_usr": "admin", "adm_pass": "pass2"}, ... ] >>> result = await add_devices_bulk("root", devices) |
| delete_devices_bulkA | Delete multiple devices from FortiManager in bulk. Removes multiple device registrations at once. WARNING: This operation cannot be undone. Args: adom: ADOM name where devices are located devices: List of device names to delete flags: Additional flags like ["create_task"] Returns: dict: Bulk delete result with keys: - status: "success" or "error" - requested_count: Number of devices in the request. NOT a count of devices actually deleted: the delete runs as a task, so per-device results only exist once it completes. - task_id: Task ID if run as background task - message: Error message if failed Example: >>> result = await delete_devices_bulk("root", ["FGT-Old1", "FGT-Old2"]) |
| update_deviceA | Update device properties. Modify device metadata like description and location. Args: adom: ADOM name device: Device name description: New device description latitude: GPS latitude for device location longitude: GPS longitude for device location Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await update_device( ... adom="root", ... device="FGT-HQ", ... description="Main headquarters firewall", ... latitude=37.7749, ... longitude=-122.4194 ... ) |
| reload_device_listA | Reload the device list from FortiManager database. Forces FortiManager to refresh its device cache. Useful after direct database changes or if device list appears stale. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") Returns: dict: Reload result with keys: - status: "success" or "error" - message: Status or error message |
| get_device_realtime_statusA | Get real-time status from a managed device. Queries the actual device through FortiManager proxy to get current system status including CPU, memory, and uptime. Args: adom: ADOM name device: Device name Returns: dict: Real-time status with keys: - status: "success" or "error" - data: Device status from FortiGate API - message: Error message if failed Example: >>> result = await get_device_realtime_status("root", "FGT-HQ") >>> print(f"Uptime: {result['data'].get('uptime')}") |
| get_device_interfacesA | Get interface information from a managed device. Queries the actual device through FortiManager proxy to get current interface configuration and status. Args: adom: ADOM name device: Device name Returns: dict: Interface information with keys: - status: "success" or "error" - data: Interface list from FortiGate API - message: Error message if failed Example: >>> result = await get_device_interfaces("root", "FGT-HQ") >>> for iface in result['data']: ... print(f"{iface['name']}: {iface.get('ip')}") |
| get_device_interface_configA | Read a device's interface CONFIG objects from FortiManager's device DB. Unlike Args:
device: Managed device name (e.g. "FGT-HQ"). Defaults to DEFAULT_DEVICE
when omitted; required if that is not configured.
vlanids: Optional list of VLAN ids to filter on (matches Returns: dict with keys: - device - interfaces: raw device-DB interface config (list or dict), credential fields stripped -- see _INTERFACE_SECRET_FIELDS - summary: condensed per-interface list (name/alias/vlanid/ip/...) - interface_count: number of summarized interfaces - error / error_code: on failure |
| get_device_client_locationA | Locate a client on a device (Asset Identity Center): which FortiAP / FortiSwitch + VLAN it is connected through. Proxies the device's detected-device inventory
( Filter to one client by Args: adom: ADOM name (defaults to DEFAULT_ADOM / "root" when omitted) device: Managed device name (e.g. "FGT-HQ"). Defaults to DEFAULT_DEVICE when omitted; required if that is not configured. ip: Client IPv4 address to locate (exact match) mac: Client MAC address to locate (case-insensitive) hostname: Client hostname to locate (case-insensitive substring) Returns: dict with keys: - adom, device - query: the filter used (or None for full inventory) - match_count - clients: summarized detected-client records (ap/switch/port/vlan/...) - error / error_code: on failure |
| get_firmware_upgrade_pathA | Preview the multi-step firmware upgrade path to a target version. Non-destructive: internally forces flags="f_preview" so FortiManager returns the intermediate versions it would step the device through without starting an upgrade. Some FortiOS upgrades cannot jump directly to the target release and must pass through intermediate builds first; this shows that path before you call upgrade_device_firmware. Args: device: Managed device name target_version: Target firmware version, e.g. "7.2.9-b1688" adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") vdom: VDOM to scope the request to (optional) Returns: dict: Upgrade path with keys: - status: "success" or "error" - upgrade_path: List of version strings FMG would step through - message: Error message if failed Example: >>> result = await get_firmware_upgrade_path("FGT-HQ", "7.2.9-b1688") >>> print(result["upgrade_path"]) ['6.0.8-303', '6.2.4-1112', '7.2.9-1688'] |
| list_available_firmwareA | List firmware versions available for a platform. Combines versions on FortiGuard (FDS) servers with any versions imported by a FortiManager administrator -- this is the device-reported catalog of what CAN be installed, not just what's already downloaded (see list_firmware_images for that). Args: platform: Platform string, e.g. "FortiGate-VM64-KVM" (omit to get firmware for all platforms) product: Product code, e.g. "FGT", "FMG" (optional) Returns: dict: Firmware catalog with keys: - status: "success" or "error" - version_list: List of {platform, product, versions: [...]} - message: Error message if failed Example: >>> result = await list_available_firmware(platform="FortiGate-VM64-KVM") >>> for entry in result["version_list"]: ... print(entry["platform"], len(entry["versions"])) |
| list_firmware_imagesA | List firmware image files stored on the FortiManager's local disk. These are the files already imported by an administrator and/or downloaded from FortiGuard -- a subset of what list_available_firmware reports, scoped to what FortiManager could push without first fetching it from FortiGuard. Args: system: Product code to filter by, e.g. "FMG", "FGT" (optional; FortiManager returns every stored image when omitted or when there's no match for the given code) Returns: dict: Local image list with keys: - status: "success" or "error" - image_list: List of {platform, product, version, build, date, image_path, image_size} - message: Error message if failed |
| upgrade_device_firmwareA | Trigger a firmware upgrade on a managed device. Device-affecting: this reboots the device onto the new firmware. Asynchronous -- FortiManager runs the upgrade as a background task (create_task="enable" is always sent) and this returns immediately with a task_id. Poll it with get_task/wait_for_task rather than assuming success from this call's return. Strongly recommended to call get_firmware_upgrade_path first: FMG may need to step the device through intermediate versions to reach the target, and this tool upgrades straight to target_version without walking that path itself. Safety: governed by FMG_FIRMWARE_SAFETY (default "strict"), same pattern as trigger_fmg_restore -- refused unless confirm=True is also passed. A firmware upgrade reboots a real managed device onto new code; there is no dry-run and no content to screen, so the gate is a plain explicit confirmation. This is unrelated to spawn_guarded below, which only bounds concurrency, not whether the call is allowed to happen at all. Set FMG_FIRMWARE_SAFETY=disabled to remove the gate. Args: device: Managed device name target_version: Target firmware version, e.g. "7.2.9-b1688" adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") vdom: VDOM to scope the request to (optional) flags: One upgrade flag (optional): "f_boot_alt_partition" (boot from alternate partition after upgrade), "f_skip_retrieve" (don't retrieve device config after upgrade), "f_skip_multi_steps" (skip the multi-step upgrade process), "f_skip_fortiguard_img" (let the device download firmware from FortiGuard itself) schedule_time: Schedule the upgrade for a future time instead of running it immediately (optional; FMG-native time format) confirm: Must be True to proceed under FMG_FIRMWARE_SAFETY=strict (default). Ignored when FMG_FIRMWARE_SAFETY=disabled. Returns: dict: Trigger result with keys: - status: "success" or "error" - task_id: Task ID for monitoring (if successful) - message: Status or error message Example: >>> path = await get_firmware_upgrade_path("FGT-HQ", "7.2.9-b1688") >>> result = await upgrade_device_firmware( ... "FGT-HQ", "7.2.9-b1688", confirm=True ... ) >>> if result["status"] == "success": ... await wait_for_task(result["task_id"], timeout=1800) |
| get_firmware_upgrade_reportA | Get the firmware upgrade report for a device under a named profile. Reports per-step upgrade results (health checks, package status, timestamps) for an upgrade run tracked under profile_name. The How-To Guide separately lists a "get upgrade history" question but marks it TBD with no confirmed distinct endpoint -- it speculates the same URL this wraps, so no separate history tool is provided; this is the one confirmed endpoint for both purposes. profile_name must be a firmware upgrade profile FortiManager already knows about (e.g. one created through the GUI's Firmware Upgrade wizard, or the auto-created Firmware Template "key" a /um/image/upgrade/ext-style upgrade returns -- not something this module generates on its own). Args: device: Managed device name profile_name: Firmware upgrade profile name to report on adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") Returns: dict: Upgrade report with keys: - status: "success" or "error" - report: Raw report data (nested per-device task/step details) - message: Error message if failed |
| trigger_fmg_backupA | Trigger a FortiManager system backup to a remote server. Starts an asynchronous backup of the FortiManager's own configuration to an external FTP/SCP/SFTP/TFTP server. This is a FortiManager-itself operation -- it does not touch any managed device. Use wait_for_task() or get_task() (system_tools.py) to monitor completion; once done, the backup file is on the remote server at the given filename. Args: service: Transfer protocol - "ftp", "scp", "sftp", or "tftp" server: Remote server hostname or IP address filename: Destination path and file name on the remote server (e.g. "tmp/fmg_backup.dat") username: Login username for the remote server (required for ftp/ scp/sftp; optional) userpasswd: Login password for the remote server (optional) port: Remote server port (optional; protocol default used if unset) passwd: Password to encrypt the backup file with (optional; FMG 7.0.11+/7.2.5+/7.4.2+ no longer allow an unencrypted backup) Returns: dict: Backup result with keys: - status: "success" or "error" - task_id: Task ID for monitoring (if returned) - message: Status or error message Example: >>> result = await trigger_fmg_backup( ... service="ftp", ... server="10.210.35.207", ... filename="tmp/fmg_backup.dat", ... username="tiger", ... userpasswd="fortinet", ... passwd="fortinet", ... ) >>> if result["status"] == "success": ... await wait_for_task(result["task_id"]) |
| trigger_fmg_restoreA | Restore the FortiManager system from a backup on a remote server. Restores the FortiManager's own configuration from a backup file previously uploaded to an external FTP/SCP/SFTP/TFTP server (see trigger_fmg_backup). This is a disruptive, FortiManager-itself operation -- it does not touch any managed device, but it does REPLACE FMG's ENTIRE configuration and will interrupt the FMG service. Safety: governed by FMG_RESTORE_SAFETY (default "strict"). Under strict, this call is refused unless confirm=True is also passed -- unlike install_package (gated by a verified preview) or firewall policy writes (gated by content screening), there is no dry-run or content check possible for a restore, so the gate is a plain explicit confirmation instead. Set FMG_RESTORE_SAFETY=disabled to remove the gate entirely. Args: service: Transfer protocol - "ftp", "scp", "sftp", or "tftp" server: Remote server hostname or IP address filename: Path and file name of the backup on the remote server username: Login username for the remote server (optional) userpasswd: Login password for the remote server (optional) port: Remote server port (optional; protocol default used if unset) passwd: Password to decrypt the backup file, if it was encrypted (optional) confirm: Must be True to proceed under FMG_RESTORE_SAFETY=strict (default). Ignored when FMG_RESTORE_SAFETY=disabled. Returns: dict: Restore result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await trigger_fmg_restore( ... service="ftp", ... server="10.210.35.207", ... filename="tmp/fmg_backup.dat", ... username="tiger", ... userpasswd="fortinet", ... confirm=True, ... ) |
| list_packet_capturesA | List existing FortiManager packet capture definitions. Returns the packet capture (sniffer) definitions configured on the FortiManager itself, for traffic originating or destined to the FMG. These are configuration records, not live capture results -- use get_packet_capture_status() for running/packet-count state. Returns: dict: Capture list with keys: - status: "success" or "error" - count: Number of capture definitions - captures: List of capture objects (id, interface, host, port, protocol, vlan, ipv6, non-ip, max-packet-count) - message: Error message if failed Example: >>> result = await list_packet_captures() >>> for cap in result["captures"]: ... print(f"{cap['id']}: {cap['interface']}") |
| add_packet_captureA | Add a new FortiManager packet capture definition. Creates a packet capture (sniffer) definition for traffic originating or destined to the FortiManager itself. The definition is created but not started -- call start_packet_capture() with the returned capture_id to begin capturing. Args: interface: Interface to capture on (e.g. "port1") host: Filter by host address (optional, matches any host if unset) port: Filter by port or port range as a string, e.g. "80" or "1-1024" (optional) protocol: Filter by IP protocol number as a string, e.g. "6" for TCP (optional) vlan: Filter by VLAN ID as a string (optional) capture_ipv6: Include IPv6 traffic (default: False) capture_non_ip: Include non-IP traffic (default: False) max_packet_count: Maximum number of packets to capture (default: 4000, matching the FMG default) Returns: dict: Create result with keys: - status: "success" or "error" - capture_id: ID of the created capture definition (FMG auto-assigns this) - message: Status or error message Example: >>> result = await add_packet_capture(interface="port8", max_packet_count=300) >>> if result["status"] == "success": ... await start_packet_capture(result["capture_id"]) |
| start_packet_captureB | Start a packet capture from an existing definition. Args: capture_id: ID of an existing packet capture definition (see list_packet_captures() or add_packet_capture()) Returns: dict: Start result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await start_packet_capture(1) |
| stop_packet_captureB | Stop a running packet capture. Args: capture_id: ID of an existing packet capture definition Returns: dict: Stop result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await stop_packet_capture(1) |
| get_packet_capture_statusA | Get the running/packet-count status of packet captures. FortiManager reports capture progress for all definitions in one call; when capture_id is given this filters the response down to that one definition. Args: capture_id: ID of a specific capture definition to filter to (optional; returns all captures' status if unset) Returns: dict: Status result with keys: - status: "success" or "error" - count: Number of capture statuses returned - captures: List of {id, max_packets, packets, running} objects - message: Error message if failed Example: >>> result = await get_packet_capture_status(capture_id=2) >>> print(result["captures"][0]["running"]) # 1 if still running |
| get_fmg_licenseA | Get the FortiManager license/contract status. Returns the FortiManager's own license contract details (support entitlements, expiry dates, serial number) -- not managed-device licensing. Returns: dict: License result with keys: - status: "success" or "error" - license: License data (contract list, count) - message: Error message if failed Example: >>> result = await get_fmg_license() >>> print(result["license"]["contract"][0]["serial"]) |
| delete_taskA | Delete a task record from FortiManager's task list. Deletes a completed task's record, or cancels/stops a still-running one (per the How-To guide: "Deleting a task could be used to delete a completed task or cancelling/stopping a running task."). Complements list_tasks/get_task/wait_for_task in system_tools.py. Args: task_id: Task ID number Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await delete_task(11111) |
| list_addressesA | List firewall address objects in an ADOM. Address objects define network entities (hosts, subnets, FQDNs) used in firewall policies. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) type_filter: Filter by type ("ipmask", "fqdn", "iprange", "wildcard") Returns: dict: Address list with keys: - status: "success" or "error" - count: Number of addresses - addresses: List of address objects - message: Error message if failed Example: >>> # List all addresses >>> result = await list_addresses("root") |
| get_addressA | Get detailed information about a firewall address object. Args: adom: ADOM name name: Address object name Returns: dict: Address details with keys: - status: "success" or "error" - address: Full address configuration - message: Error message if failed |
| create_address_subnetA | Create a subnet/network address object. Args: adom: ADOM name name: Address object name subnet: IP/netmask (e.g., "10.0.0.0/24" or "10.0.0.0 255.255.255.0") comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message Example: >>> result = await create_address_subnet( ... adom="root", ... name="LAN-Subnet", ... subnet="192.168.1.0/24", ... comment="Local network" ... ) |
| create_address_hostA | Create a host address object (single IP). Args: adom: ADOM name name: Address object name ip: Host IP address (e.g., "10.0.0.100") comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message Example: >>> result = await create_address_host( ... adom="root", ... name="WebServer", ... ip="192.168.1.100", ... comment="Production web server" ... ) |
| create_address_fqdnA | Create an FQDN (Fully Qualified Domain Name) address object. FQDN addresses are resolved dynamically by FortiGate. Args: adom: ADOM name name: Address object name fqdn: Domain name (e.g., "www.example.com") comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message Example: >>> result = await create_address_fqdn( ... adom="root", ... name="Google-DNS", ... fqdn="dns.google.com" ... ) |
| create_address_rangeA | Create an IP range address object. Args: adom: ADOM name name: Address object name start_ip: Start IP address end_ip: End IP address comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created address name - message: Status or error message Example: >>> result = await create_address_range( ... adom="root", ... name="DHCP-Pool", ... start_ip="192.168.1.100", ... end_ip="192.168.1.200" ... ) |
| update_addressA | Update an existing address object. Only specified fields will be updated. Args: adom: ADOM name name: Current address name new_name: New name (optional) subnet: New subnet for ipmask type (optional) fqdn: New FQDN for fqdn type (optional) comment: New comment (optional) Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_addressA | Delete a firewall address object. WARNING: This will fail if the address is in use by policies or groups. Args: adom: ADOM name name: Address object name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_address_groupsA | List firewall address groups in an ADOM. Address groups contain multiple address objects for easier policy management. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Group list with keys: - status: "success" or "error" - count: Number of groups - groups: List of address group objects - message: Error message if failed |
| get_address_groupB | Get detailed information about an address group. Args: adom: ADOM name name: Address group name Returns: dict: Group details with keys: - status: "success" or "error" - group: Full group configuration including members - message: Error message if failed |
| create_address_groupA | Create an address group. Args: adom: ADOM name name: Group name members: List of address object names to include comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created group name - message: Status or error message Example: >>> result = await create_address_group( ... adom="root", ... name="Web-Servers", ... members=["WebServer1", "WebServer2", "WebServer3"], ... comment="Production web servers" ... ) |
| update_address_groupB | Update an address group. Args: adom: ADOM name name: Group name members: New member list (replaces existing) comment: New comment Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_address_groupA | Delete an address group. WARNING: This will fail if the group is in use by policies. Args: adom: ADOM name name: Group name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_servicesA | List custom service objects in an ADOM. Service objects define network protocols and ports used in policies. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) protocol_filter: Filter by protocol ("TCP/UDP/SCTP", "ICMP", "IP") Returns: dict: Service list with keys: - status: "success" or "error" - count: Number of services - services: List of service objects - message: Error message if failed |
| get_serviceC | Get detailed information about a service object. Args: adom: ADOM name name: Service object name Returns: dict: Service details with keys: - status: "success" or "error" - service: Full service configuration - message: Error message if failed |
| create_service_tcp_udpA | Create a TCP/UDP/SCTP/UDP-Lite service object. Args: adom: ADOM name name: Service object name tcp_portrange: TCP port range (e.g., "80", "8080-8090", "80 443 8080") udp_portrange: UDP port range (e.g., "53", "500-502") sctp_portrange: SCTP port range (e.g., "3868", "2905-2907") udplite_portrange: UDP-Lite port range (e.g., "1234") comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created service name - message: Status or error message Example: >>> # Create HTTP/HTTPS service >>> result = await create_service_tcp_udp( ... adom="root", ... name="Custom-Web", ... tcp_portrange="80 443 8080", ... comment="Custom web ports" ... ) |
| create_service_icmpA | Create an ICMP service object. Args: adom: ADOM name name: Service object name icmp_type: ICMP type (0-255, optional for all types) icmp_code: ICMP code (0-255, optional) comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created service name - message: Status or error message Example: >>> # Create ping service (ICMP echo request) >>> result = await create_service_icmp( ... adom="root", ... name="Custom-Ping", ... icmp_type=8, ... comment="ICMP Echo Request" ... ) |
| update_serviceB | Update a service object. Args: adom: ADOM name name: Service name tcp_portrange: New TCP port range udp_portrange: New UDP port range comment: New comment Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_serviceA | Delete a service object. WARNING: This will fail if the service is in use by policies. Args: adom: ADOM name name: Service name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_service_groupsA | List service groups in an ADOM. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Group list with keys: - status: "success" or "error" - count: Number of groups - groups: List of service group objects - message: Error message if failed |
| get_service_groupA | Get detailed information about a service group. Args: adom: ADOM name name: Service group name Returns: dict: Group details with keys: - status: "success" or "error" - group: Full group configuration including members - message: Error message if failed |
| create_service_groupA | Create a service group. Args: adom: ADOM name name: Group name members: List of service names to include comment: Optional comment Returns: dict: Create result with keys: - status: "success" or "error" - name: Created group name - message: Status or error message Example: >>> result = await create_service_group( ... adom="root", ... name="Web-Services", ... members=["HTTP", "HTTPS", "DNS"], ... comment="Common web services" ... ) |
| update_service_groupB | Update a service group. Args: adom: ADOM name name: Group name members: New member list (replaces existing) comment: New comment Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_service_groupA | Delete a service group. WARNING: This will fail if the group is in use by policies. Args: adom: ADOM name name: Group name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| search_objectsA | Search across all firewall objects by name. Searches addresses, address groups, services, and service groups for objects matching the search term. Args: adom: ADOM name search_term: Search term (partial match) Returns: dict: Search results with keys: - status: "success" or "error" - addresses: Matching address objects - address_groups: Matching address groups - services: Matching service objects - service_groups: Matching service groups - total_count: Total matches - message: Error message if failed Example: >>> result = await search_objects("root", "web") |
| find_object_usageA | Find everywhere an ADOM object is referenced (where-used). Runs FortiManager's cache-daemon where-used search: policies, groups, and other objects that reference the given object. This is a usage-graph lookup, distinct from object_tools.search_objects (which matches objects BY NAME, not by reference). Args: adom: ADOM name (use "global" to search a Global ADOM object) object_name: Name of the object to trace usage for object_type: One of "address", "address_group", "service", "service_group" (default: "address") timeout: Maximum seconds to wait for the search to complete (default: 60, capped at 300) poll_interval: Seconds between progress checks (default: 2, clamped to 1-30) Returns: dict: Usage result with keys: - status: "success" or "error" - object_name: The object searched for - object_type: The object type searched - total_matches: Number of referencing locations - used_in: List of where-used entries, each with the referencing object's root ADOM/package and the matching attr/mapping_name/mkey detail FortiManager returned - message: Error message if failed Example: >>> result = await find_object_usage("root", "WebServer") >>> for entry in result["used_in"]: ... print(entry["root"], entry["data"]) |
| find_duplicate_objectsA | Find objects with identical content configured under different names. Runs FortiManager's "find duplicates" option against every object of the given type in the ADOM. FortiManager compares the requested fields across all objects of that type and adds a "duplicate entries" list to each object that shares its content with at least one other object -- the field itself, not something computed client-side. Args: adom: ADOM name object_type: One of "address", "address_group", "service", "service_group" (default: "address") fields: Object fields to compare for duplication (e.g. ["name", "type", "subnet"] for addresses). Omit to let FortiManager use its default field set for the type. Returns: dict: Duplicate scan result with keys: - status: "success" or "error" - object_type: The object type scanned - objects: Full per-object list FortiManager returned (each entry has a "duplicate entries" list when it has a match) - duplicate_count: Number of objects that have at least one duplicate entry - message: Error message if failed Example: >>> result = await find_duplicate_objects("root", object_type="address") >>> for obj in result["objects"]: ... if obj.get("duplicate entries"): ... print(obj["name"], "duplicates:", obj["duplicate entries"]) |
| policy_lookupA | Simulate a firewall policy lookup for a traffic 5-tuple against a managed device. Proxies FortiGate's Args:
adom: ADOM name
device: Managed device name (e.g. "FGT-HQ"). Accepts the
"device[vdom]" bracket suffix used elsewhere in this codebase
to target a specific VDOM.
dest: Destination IPv4 address to test (e.g. "8.8.8.8")
protocol: IP protocol number to test -- 6=TCP, 17=UDP, 1=ICMP,
47=GRE, 50=ESP, 51=AH, 58=ICMPv6, 132=SCTP.
Sent to the device as both the Returns:
dict: Lookup result with keys:
- status: "success" or "error"
- adom, device
- package_hint: the caller-supplied Example: >>> result = await policy_lookup( ... adom="root", ... device="branch1_fgt", ... srcintf="vl_lan", ... protocol=6, ... dest="8.8.8.8", ... ) >>> result["matched"] False |
| create_packageA | Create a new policy package. Policy packages contain firewall policies, security profiles, and other configurations to be deployed to managed devices. Args: adom: ADOM name name: Package name ngfw_mode: NGFW mode - "profile-based" (default) or "policy-based" central_nat: Enable central NAT (default: False) Returns: dict: Create result with keys: - status: "success" or "error" - package: Created package name - message: Status or error message Example: >>> result = await create_package( ... adom="root", ... name="Branch-Policy", ... ngfw_mode="profile-based" ... ) |
| delete_packageA | Delete a policy package. WARNING: This will delete the package and all its policies. This operation cannot be undone. Args: adom: ADOM name package: Package name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| clone_packageA | Clone a policy package. Creates a copy of an existing package with all its policies. Args: adom: ADOM name package: Source package name new_name: Name for the cloned package Returns: dict: Clone result with keys: - status: "success" or "error" - package: New package name - message: Status or error message Example: >>> result = await clone_package( ... adom="root", ... package="default", ... new_name="default-copy" ... ) |
| assign_packageA | Assign a policy package to devices. Associates a policy package with specific devices/VDOMs. The package can then be installed to these devices. Args: adom: ADOM name package: Package name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...] Returns: dict: Assignment result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await assign_package( ... adom="root", ... package="Branch-Policy", ... devices=[ ... {"name": "FGT-Branch1", "vdom": "root"}, ... {"name": "FGT-Branch2", "vdom": "root"} ... ] ... ) |
| list_firewall_policiesA | List firewall policies in a policy package. Args: adom: ADOM name package: Policy package name fields: Specific fields to return (optional) limit: Maximum number of policies to return (optional) offset: Starting position for pagination (default: 0) Returns: dict: Policy list with keys: - status: "success" or "error" - count: Number of policies returned - total: Total number of policies in package - policies: List of policy objects - message: Error message if failed Example: >>> # Get all policies >>> result = await list_firewall_policies("root", "default") |
| get_firewall_policyA | Get detailed information about a specific firewall policy. Args: adom: ADOM name package: Policy package name policyid: Policy ID number Returns: dict: Policy details with keys: - status: "success" or "error" - policy: Full policy configuration - message: Error message if failed Example: >>> result = await get_firewall_policy("root", "default", 1) >>> print(f"Policy name: {result['policy']['name']}") |
| create_firewall_policyA | Create a new firewall policy. Creates a firewall policy in the specified policy package. The policy won't be active until the package is installed to devices. Args:
adom: ADOM name
package: Policy package name
name: Policy name
srcintf: Source interfaces (e.g., ["internal"])
dstintf: Destination interfaces (e.g., ["wan1"])
srcaddr: Source addresses (e.g., ["all"])
dstaddr: Destination addresses (e.g., ["all"])
service: Services (e.g., ["ALL", "HTTP", "HTTPS"])
action: Policy action - "accept" or "deny" (default: "accept")
schedule: Schedule object name (default: "always")
nat: Enable NAT (default: False)
logtraffic: Log mode - "all", "utm", or "disable" (default: "utm")
status: Policy status - "enable" or "disable" (default: "enable")
comments: Policy comments (optional)
policyid: Specific policy ID (optional, auto-assigned if not set)
srcaddr_negate: Match all sources EXCEPT srcaddr (optional; omitted
from the payload when not set, leaving the FortiManager default)
dstaddr_negate: Match all destinations EXCEPT dstaddr (optional)
service_negate: Match all services EXCEPT service (optional)
utm_status: Enable/disable security profile inspection on this policy
(optional). Required (True) for any of the profile fields below
to actually apply.
av_profile: Antivirus profile name, e.g. "default" (optional)
ips_sensor: IPS sensor name, e.g. "default" (optional)
webfilter_profile: Web filter profile name (optional)
dnsfilter_profile: DNS filter profile name (optional)
application_list: Application control list name (optional)
file_filter_profile: File filter profile name (optional)
ssl_ssh_profile: SSL/SSH inspection profile name, e.g.
"certificate-inspection" (optional)
profile_protocol_options: Protocol options profile name (optional).
Mutually exclusive with profile_group -- it is itself a member
of the FortiOS Returns: dict: Create result with keys: - status: "success" or "error" - policyid: Created policy ID - message: Status or error message Example: >>> result = await create_firewall_policy( ... adom="root", ... package="default", ... name="Allow-Web-Traffic", ... srcintf=["internal"], ... dstintf=["wan1"], ... srcaddr=["LAN-Subnet"], ... dstaddr=["all"], ... service=["HTTP", "HTTPS"], ... action="accept", ... nat=True, ... utm_status=True, ... av_profile="default", ... ips_sensor="default", ... ) |
| update_firewall_policyA | Update an existing firewall policy. Only the specified fields will be updated; other fields remain unchanged. Args:
adom: ADOM name
package: Policy package name
policyid: Policy ID to update
name: New policy name (optional)
srcintf: New source interfaces (optional)
dstintf: New destination interfaces (optional)
srcaddr: New source addresses (optional)
dstaddr: New destination addresses (optional)
service: New services (optional)
action: New action - "accept" or "deny" (optional)
schedule: New schedule (optional)
nat: Enable/disable NAT (optional)
logtraffic: New log mode (optional)
status: New status (optional)
comments: New comments (optional)
global_label: Policy section label (optional)
global_label_color: Policy section color ID 0-31 (optional)
srcaddr_negate: Match all sources EXCEPT srcaddr (optional; None
leaves the field untouched, like every other optional field)
dstaddr_negate: Match all destinations EXCEPT dstaddr (optional)
service_negate: Match all services EXCEPT service (optional)
utm_status: Enable/disable security profile inspection (optional)
av_profile: Antivirus profile name (optional)
ips_sensor: IPS sensor name (optional)
webfilter_profile: Web filter profile name (optional)
dnsfilter_profile: DNS filter profile name (optional)
application_list: Application control list name (optional)
file_filter_profile: File filter profile name (optional)
ssl_ssh_profile: SSL/SSH inspection profile name (optional)
profile_protocol_options: Protocol options profile name (optional).
Mutually exclusive with profile_group in the same call -- it is
itself a member of the FortiOS Note:
Sending any individual security-profile field (av_profile,
ssl_ssh_profile, profile_protocol_options, etc.) on a policy that is
currently in group mode flips Returns: dict: Update result with keys: - status: "success" or "error" - policyid: Updated policy ID - message: Status or error message Example: >>> # Disable a policy >>> result = await update_firewall_policy( ... adom="root", ... package="default", ... policyid=10, ... status="disable" ... ) |
| delete_firewall_policyA | Delete a firewall policy. WARNING: This operation cannot be undone. Args: adom: ADOM name package: Policy package name policyid: Policy ID to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| delete_firewall_policies_bulkA | Delete multiple firewall policies at once. WARNING: This operation cannot be undone. Args: adom: ADOM name package: Policy package name policyids: List of policy IDs to delete Returns: dict: Delete result with keys: - status: "success", "partial", or "error" - deleted_count: Number of policies deleted - deleted: Policy IDs that were deleted - failed: Per-item failures [{"policyid", "message", "error_code"}, ...] - message: Status or error message Example: >>> result = await delete_firewall_policies_bulk( ... adom="root", ... package="default", ... policyids=[5, 6, 7, 8] ... ) |
| move_firewall_policyA | Move a firewall policy to a new position. Reorders a policy relative to another policy in the rule list. Policy order determines evaluation priority. Args: adom: ADOM name package: Policy package name policyid: Policy ID to move target_policyid: Reference policy ID position: Where to place - "before" or "after" (default: "before") Returns: dict: Move result with keys: - status: "success" or "error" - message: Status or error message Example: >>> # Move policy 10 before policy 5 >>> result = await move_firewall_policy( ... adom="root", ... package="default", ... policyid=10, ... target_policyid=5, ... position="before" ... ) |
| search_firewall_policiesA | Search firewall policies with filters. Args: adom: ADOM name package: Policy package name name_filter: Filter by policy name (partial match) srcaddr_filter: Filter by source address (partial match) dstaddr_filter: Filter by destination address (partial match) service_filter: Filter by service (partial match) action_filter: Filter by action ("accept" or "deny") status_filter: Filter by status ("enable" or "disable") Returns: dict: Search results with keys: - status: "success" or "error" - count: Number of matching policies - policies: List of matching policy objects - message: Error message if failed Example: >>> # Find all deny policies >>> result = await search_firewall_policies( ... adom="root", ... package="default", ... action_filter="deny" ... ) |
| get_policy_servicesA | Get services configured on a firewall policy with optional group resolution. Retrieves the service list from a firewall policy and optionally resolves each service/group into its detailed definition (ports, protocols, group members). Useful for policy hardening workflows where you need to compare actual traffic against configured services. Args: adom: ADOM name package: Policy package name policy_id: Policy ID number resolve: If True, resolve each service to its definition including port ranges and group members (default: True) Returns: dict: Service information with keys: - status: "success" or "error" - policy_id: The policy ID queried - policy_name: Name of the policy - service_names: List of raw service names from the policy - services: Resolved service details (if resolve=True) - message: Error message if failed Example: >>> # Get resolved services for policy 10 >>> result = await get_policy_services("root", "default", 10) |
| preview_installA | Preview installation changes before applying. Shows what configuration changes would be made to devices without actually installing the package. Use this to verify changes before deployment. Args: adom: ADOM name package: Policy package name (optional, preview device settings if None) devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...] Returns: dict: Preview result with keys: - status: "success" or "error" - task_id: Task ID for retrieving preview results - message: Status or error message Example: >>> # Start preview >>> result = await preview_install( ... adom="root", ... package="default", ... devices=[{"name": "FGT-HQ", "vdom": "root"}] ... ) >>> # Wait for preview to complete, then get results >>> if result["status"] == "success": ... from fortimanager_mcp.tools.system_tools import wait_for_task ... await wait_for_task(result["task_id"]) ... preview = await get_preview_result(...) |
| get_preview_resultA | Get the result of an installation preview. Call this after preview_install() task completes to see the detailed configuration changes. Args: adom: ADOM name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...] Returns: dict: Preview results with keys: - status: "success" or "error" - preview: Preview data with configuration changes - message: Error message if failed |
| list_local_in_policiesA | List IPv4 local-in policies in a policy package. Local-in policies control access to the FortiGate's own management plane (GUI, SSH, ping, API, SNMP, etc.), not through-traffic. Args: adom: ADOM name package: Policy package name fields: Specific fields to return (optional) limit: Maximum number of policies to return (optional) offset: Starting position for pagination (default: 0) Returns: dict: Policy list with keys: - status: "success" or "error" - count: Number of policies returned - total: Total number of local-in policies in package - policies: List of local-in policy objects - message: Error message if failed Example: >>> result = await list_local_in_policies("root", "default") |
| get_local_in_policyB | Get detailed information about a specific IPv4 local-in policy. Args: adom: ADOM name package: Policy package name policyid: Local-in policy ID number Returns: dict: Policy details with keys: - status: "success" or "error" - policy: Full local-in policy configuration - message: Error message if failed Example: >>> result = await get_local_in_policy("root", "default", 1) |
| create_local_in_policyA | Create a new IPv4 local-in policy. Local-in policies control traffic destined to the FortiGate's own
management plane (GUI, SSH, ping, API, SNMP, etc.) -- a distinct object
space from the regular firewall/policy table (create_firewall_policy),
which governs traffic passing through the device. There is no
srcintf/dstintf pair here: Args: adom: ADOM name package: Policy package name intf: Incoming interface(s) this policy applies to, e.g. ["port1"] srcaddr: Source addresses this policy matches (e.g. ["Admin-Subnet"]) dstaddr: Destination addresses on the FortiGate itself (e.g. ["all"]) service: Services this policy matches, e.g. ["HTTPS", "SSH"] action: Policy action - "accept" or "deny" (default: "deny", the FortiOS field default -- unlike create_firewall_policy this defaults closed since it governs management-plane access) schedule: Schedule object name(s) (default: ["always"] if omitted) status: Policy status - "enable" or "disable" (default: "enable") logtraffic: Local-in traffic logging - "enable" or "disable" (optional) comments: Policy comments (optional) policyid: Specific policy ID (optional, auto-assigned if not set) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) ha_mgmt_intf_only: Restrict this policy to the HA management interface only (optional). IPv4-only field -- there is no equivalent on create_local_in_policy6. virtual_patch: Enable/disable virtual patching on this policy (optional) Returns: dict: Create result with keys: - status: "success" or "error" - policyid: Created policy ID - message: Status or error message Example: >>> result = await create_local_in_policy( ... adom="root", ... package="default", ... intf=["port1"], ... srcaddr=["Admin-Subnet"], ... dstaddr=["all"], ... service=["HTTPS", "SSH"], ... action="accept", ... ) |
| update_local_in_policyA | Update an existing IPv4 local-in policy. Only the specified fields will be updated; other fields remain unchanged. Args: adom: ADOM name package: Policy package name policyid: Local-in policy ID to update action: New action - "accept" or "deny" (optional) intf: New incoming interface(s) (optional) srcaddr: New source addresses (optional) dstaddr: New destination addresses (optional) service: New services (optional) schedule: New schedule (optional) status: New status - "enable" or "disable" (optional) logtraffic: New local-in traffic logging setting (optional) comments: New comments (optional) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) ha_mgmt_intf_only: Restrict this policy to the HA management interface only (optional). IPv4-only field. virtual_patch: Enable/disable virtual patching on this policy (optional) Returns: dict: Update result with keys: - status: "success" or "error" - policyid: Updated policy ID - message: Status or error message Example: >>> # Disable a local-in policy >>> result = await update_local_in_policy( ... adom="root", ... package="default", ... policyid=1, ... status="disable" ... ) |
| delete_local_in_policyB | Delete an IPv4 local-in policy. WARNING: This operation cannot be undone. Args: adom: ADOM name package: Policy package name policyid: Local-in policy ID to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_local_in_policies6A | List IPv6 local-in policies in a policy package. Local-in policies control access to the FortiGate's own management plane (GUI, SSH, ping, API, SNMP, etc.) over IPv6, not through-traffic. Args: adom: ADOM name package: Policy package name fields: Specific fields to return (optional) limit: Maximum number of policies to return (optional) offset: Starting position for pagination (default: 0) Returns: dict: Policy list with keys: - status: "success" or "error" - count: Number of policies returned - total: Total number of local-in policies6 in package - policies: List of local-in policy6 objects - message: Error message if failed Example: >>> result = await list_local_in_policies6("root", "default") |
| get_local_in_policy6B | Get detailed information about a specific IPv6 local-in policy. Args: adom: ADOM name package: Policy package name policyid: Local-in policy6 ID number Returns: dict: Policy details with keys: - status: "success" or "error" - policy: Full local-in policy6 configuration - message: Error message if failed Example: >>> result = await get_local_in_policy6("root", "default", 1) |
| create_local_in_policy6A | Create a new IPv6 local-in policy. IPv6 counterpart of create_local_in_policy -- see that tool's docstring
for the local-in-policy concept. Args: adom: ADOM name package: Policy package name intf: Incoming interface(s) this policy applies to, e.g. ["port1"] srcaddr: Source IPv6 addresses this policy matches dstaddr: Destination IPv6 addresses on the FortiGate itself (e.g. ["all"]) service: Services this policy matches, e.g. ["HTTPS", "SSH"] action: Policy action - "accept" or "deny" (default: "deny", the FortiOS field default) schedule: Schedule object name(s) (default: ["always"] if omitted) status: Policy status - "enable" or "disable" (default: "enable") logtraffic: Local-in traffic logging - "enable" or "disable" (optional) comments: Policy comments (optional) policyid: Specific policy ID (optional, auto-assigned if not set) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) virtual_patch: Enable/disable virtual patching on this policy (optional) Returns: dict: Create result with keys: - status: "success" or "error" - policyid: Created policy ID - message: Status or error message Example: >>> result = await create_local_in_policy6( ... adom="root", ... package="default", ... intf=["port1"], ... srcaddr=["Admin-Subnet-v6"], ... dstaddr=["all"], ... service=["HTTPS", "SSH"], ... action="accept", ... ) |
| update_local_in_policy6A | Update an existing IPv6 local-in policy. Only the specified fields will be updated; other fields remain unchanged. Args: adom: ADOM name package: Policy package name policyid: Local-in policy6 ID to update action: New action - "accept" or "deny" (optional) intf: New incoming interface(s) (optional) srcaddr: New source IPv6 addresses (optional) dstaddr: New destination IPv6 addresses (optional) service: New services (optional) schedule: New schedule (optional) status: New status - "enable" or "disable" (optional) logtraffic: New local-in traffic logging setting (optional) comments: New comments (optional) srcaddr_negate: Match all sources EXCEPT srcaddr (optional) dstaddr_negate: Match all destinations EXCEPT dstaddr (optional) service_negate: Match all services EXCEPT service (optional) virtual_patch: Enable/disable virtual patching on this policy (optional) Returns: dict: Update result with keys: - status: "success" or "error" - policyid: Updated policy ID - message: Status or error message Example: >>> result = await update_local_in_policy6( ... adom="root", ... package="default", ... policyid=1, ... status="disable" ... ) |
| delete_local_in_policy6A | Delete an IPv6 local-in policy. WARNING: This operation cannot be undone. Args: adom: ADOM name package: Policy package name policyid: Local-in policy6 ID to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_device_revisionsA | List the device DB revision history for a managed device. Every install, auto-update, retrieve, or revert creates a new device DB
revision. Note: Requires the device to have at least one device DB revision on record (i.e. it has been installed to or retrieved from at least once). A freshly added device that has never gone through either will make FortiManager return a generic internal-server error here rather than an empty list -- that is FMG's own handling of an empty revision table, not a sign this tool or the underlying API is broken. Args: device: Managed device name Returns: dict: status, device, base_ver, count, revisions (each entry has revision, comments, instime/instusr, modtime/modusr, status, tag) Example: >>> result = await list_device_revisions("hub2") >>> for rev in result["revisions"]: ... print(rev["revision"], rev["comments"]) |
| get_device_revisionA | Check out one device DB revision's stored configuration text. Note: Requires the device to have at least one device DB revision on record -- see the note on list_device_revisions. Args: device: Managed device name revision: Revision number (see list_device_revisions), or -1 (default) for the latest revision Returns: dict: status, device, revision, content (device DB config text with known secret-bearing directives redacted -- see redact_config_text_secrets. Structured dict-field masking elsewhere in this codebase can't see inside this multi-line CLI text, so this applies a separate directive-name-based redaction.) Example: >>> result = await get_device_revision("hub2", revision=8) >>> print(result["content"]) |
| diff_device_revisionA | Diff a past device DB revision against the CURRENT device DB. FortiManager has no native diff endpoint for device DB revisions (unlike ADOM DB revisions -- see diff_adom_revision). This instead fetches both the past revision's config text (checkout) and the current device DB export, then computes a client-side unified line diff. Read-only. Note: The checkout half of this requires the device to have at least one device DB revision on record -- see the note on list_device_revisions. Args: device: Managed device name revision: Revision number to compare against current (see list_device_revisions) Returns: dict: status, device, revision, changed (bool), diff (unified diff text, empty string if unchanged; both sides are redacted of known secret-bearing directives before diffing -- see redact_config_text_secrets -- so a line whose only change is its secret value will not show as changed) Example: >>> result = await diff_device_revision("hub2", revision=7) >>> if result["changed"]: ... print(result["diff"]) |
| revert_device_revisionA | Revert a device's device DB to a past revision. This rewrites FortiManager's stored device DB copy for Safety: governed by FMG_REVERT_SAFETY (default "strict"), same pattern as revert_adom_revision -- refused unless confirm=True is also passed. Set FMG_REVERT_SAFETY=disabled to remove it. Note:
Requires the device to have at least one device DB revision on
record -- see the note on list_device_revisions. Not live-tested
(reverting a device's config is a real, consequential write); the
request shape matches the FNDN-documented, non-deprecated
Args: device: Managed device name revision: Revision number to revert to (see list_device_revisions) confirm: Must be True to proceed under FMG_REVERT_SAFETY=strict (default). Ignored when FMG_REVERT_SAFETY=disabled. Returns: dict: status, device, revision, message |
| list_adom_revisionsB | List the ADOM DB revision history for an ADOM. Args: adom: ADOM name fields: Specific fields to return (optional, returns all if omitted) Returns: dict: status, adom, count, revisions (each entry has version, name, desc, created_by, created_time, locked) Example: >>> result = await list_adom_revisions("demo") >>> for rev in result["revisions"]: ... print(rev["version"], rev["desc"]) |
| get_adom_revisionA | Get one ADOM DB revision's metadata. Args:
adom: ADOM name
revision: Revision number (the Returns: dict: status, adom, revision (name, desc, created_by, created_time, locked, version) |
| diff_adom_revisionA | Diff a past ADOM DB revision against the CURRENT live ADOM. Starts a FortiManager cache-diff job (the revision as source, the live ADOM as destination), polls it to completion, and returns the summary. Read-only -- nothing is written to the ADOM. Args: adom: ADOM name revision: Revision number to compare against current (see list_adom_revisions) timeout: Maximum wait for the diff job in seconds (default 60, capped at 300) poll_interval: Seconds between poll attempts (default 2, capped at 30) Returns: dict: status, adom, revision, obj (object-level change summary, keyed by category), pkg (per-package change summary) |
| revert_adom_revisionA | Revert the live ADOM DB to a past revision. FortiManager has no dedicated revert-ADOM-revision call. The documented
mechanism (How-To 013, "How to revert an ADOM Revision?") is to
Safety: governed by FMG_REVERT_SAFETY (default "strict"), same pattern as trigger_fmg_restore -- refused unless confirm=True is also passed. A clone-revert has no dry-run or content-screening equivalent (unlike the firewall-policy revert, which is gated by content instead): it replaces the entire live ADOM DB in one call, so the gate is a plain explicit confirmation. Set FMG_REVERT_SAFETY=disabled to remove it. Args:
adom: ADOM name
revision: Revision number to revert to (the Returns: dict: status, adom, reverted_from, new_revision, message |
| list_policy_revisionsA | List the change log for a policy package's firewall policies. Args: adom: ADOM name package: Policy package name policyid: Limit to one policy's change history (optional; all policies in the package if omitted) Returns: dict: status, adom, package, policyid, count, changes -- each entry has act (1=created, 2=deleted, 3=modified), key (the policyid), config (a JSON-encoded policy snapshot at that change -- pass straight to revert_firewall_policy), timestamp, user Example: >>> result = await list_policy_revisions("demo", "ppkg_001", policyid=14) >>> first_change = result["changes"][0] >>> await revert_firewall_policy("demo", "ppkg_001", first_change["config"]) |
| diff_policy_packageA | Diff a policy package's objects at a past ADOM revision against its CURRENT live state. Package-level changes are tracked as part of the ADOM DB revision history, not separately, so this reuses the same cache-diff job as diff_adom_revision, scoped to one package's summary view. Read-only. Args: adom: ADOM name package: Policy package name revision: ADOM revision number to compare against current (see list_adom_revisions) timeout: Maximum wait for the diff job in seconds (default 60, capped at 300) poll_interval: Seconds between poll attempts (default 2, capped at 30) Returns: dict: status, adom, package, revision, summary (change summary for this package only, keyed by category -- e.g. 181 = firewall policy) |
| revert_firewall_policyA | Restore a firewall policy to a past change-log snapshot. FortiManager has no dedicated revert-firewall-policy endpoint. The
documented mechanism (How-To 008, "How to revert a firewall policy from
a past changes?") is to capture a past change's Args: adom: ADOM name package: Policy package name config: A snapshot from list_policy_revisions' "config" field -- either the JSON string as returned, or the already-decoded dict. Must include "policyid". Any "oid" key is stripped automatically (FortiManager rejects the write if it is present). revision_note: Optional note recorded against this change Returns: dict: status, adom, package, policyid, message Example: >>> revisions = await list_policy_revisions("demo", "ppkg_001", policyid=14) >>> await revert_firewall_policy( ... "demo", "ppkg_001", revisions["changes"][0]["config"], ... revision_note="Revert from create time", ... ) |
| list_scriptsA | List CLI scripts in an ADOM. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") script_type: Filter by type (cli, tcl, cligrp, tclgrp, jinja) target: Filter by target (device_database, remote_device, adom_database) limit: Maximum number of scripts to return Returns: List of scripts with name, type, target, and description |
| get_scriptA | Get details of a specific CLI script. Args: adom: ADOM name name: Script name Returns: Script details including content |
| create_scriptB | Create a new CLI script. Args: adom: ADOM name name: Script name content: Script content (CLI commands) script_type: Script type - cli, tcl, cligrp, tclgrp, jinja (default: cli) target: Execution target (default: device_database) - device_database: Device Database - adom_database: Policy Package or ADOM Database - remote_device: Remote FortiGate Directly (via CLI) description: Script description Returns: Created script details |
| update_scriptA | Update an existing CLI script. Args: adom: ADOM name name: Script name to update content: New script content description: New description script_type: New script type target: New target Returns: Updated script details |
| delete_scriptB | Delete a CLI script. Args: adom: ADOM name name: Script name to delete Returns: Deletion result |
| execute_script_on_deviceA | Execute a CLI script on a specific device. The script must have target=remote_device to run directly on the device. Script execution creates a task that should be monitored for completion. IMPORTANT: Keep the session alive while script executes on remote devices. Args: adom: ADOM name script: Script name to execute device: Target device name Returns: Task ID for monitoring execution progress |
| execute_script_on_devicesB | Execute a CLI script on multiple devices. Args: adom: ADOM name script: Script name to execute devices: List of device names Returns: Task ID for monitoring execution progress |
| execute_script_on_device_groupA | Execute a CLI script on a device group. Args: adom: ADOM name script: Script name to execute group: Device group name Returns: Task ID for monitoring execution progress |
| execute_script_on_packageA | Execute a CLI script against a policy package or ADOM database. The script must have target=adom_database. This modifies the package/ADOM DB, not a live device. Args: adom: ADOM name script: Script name to execute package: Policy package name Returns: Task ID for monitoring execution progress |
| get_script_log_latestB | Get the latest script execution log. Args: adom: ADOM name device: Optional device name to filter logs Returns: Latest script execution log with content and execution time |
| get_script_log_summaryA | Get script execution log summary. Lists all script executions with log IDs for detailed retrieval. Args: adom: ADOM name device: Optional device name to filter logs Returns: List of script execution summaries with log_id, script_name, exec_time |
| get_script_log_outputA | Get specific script execution output by log ID. Log ID can be derived from task ID:
Args: adom: ADOM name log_id: Log ID from execution task or log summary device: Optional device name (required for device-specific logs) Returns: Script execution output content |
| list_sdwan_templatesA | List SD-WAN templates in an ADOM. SD-WAN templates define WAN interface configurations, performance SLAs, and traffic steering rules for SD-WAN deployments. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number of templates to return Returns: List of SD-WAN templates with name, type, and assigned devices |
| get_sdwan_templateA | Get details of a specific SD-WAN template. Args: adom: ADOM name name: SD-WAN template name Returns: SD-WAN template details including interfaces, SLAs, and rules |
| get_device_sdwanA | Get a managed device's SD-WAN configuration from FortiManager's device DB. Unlike Args: device: Managed device name (e.g. "myfw01") vdom: VDOM name (default: "root") Returns: dict with keys: - device, vdom - sdwan: raw system/sdwan config object - summary: condensed members/zones/health-checks/service rules - error / error_code: on failure |
| get_device_sdwan_monitorA | Read a device's LIVE SD-WAN Monitor status through the FortiManager proxy. Unlike
Use it to answer "how are the uplinks doing right now / which member is breaching SLA", which the config read cannot answer. Args: adom: ADOM name (defaults to DEFAULT_ADOM / "root" when omitted) device: Managed device name (e.g. "FGT-HQ"). Defaults to DEFAULT_DEVICE when omitted; required if that is not configured. Returns: dict with keys: - adom, device - members: raw virtual-wan/members proxy response - health_check: raw virtual-wan/health-check proxy response - summary: condensed member links/bandwidth + per-member SLA - error / error_code: on failure |
| resolve_datasourceA | Resolve the objects a config attribute is allowed to reference. Generic config-DB introspection built on FortiManager's Only Args: url: A cdb config path, e.g. "pm/config/adom/root/obj/system/sdwan" attr: The attribute whose referenceable objects to resolve, e.g. "internet-service-name" or "service" adom: ADOM name (informational; default: DEFAULT_ADOM env var or "root") Returns: dict with keys: - url, attr, adom - datasource: raw list/dict of referenceable objects - error / error_code: on failure |
| create_sdwan_templateB | Create a new SD-WAN template. Creates an empty SD-WAN template that can be configured with interfaces, SLAs, and routing rules. Args: adom: ADOM name name: SD-WAN template name description: Optional description Returns: Created SD-WAN template |
| delete_sdwan_templateB | Delete an SD-WAN template. Note: Template must not be assigned to any devices before deletion. Args: adom: ADOM name name: SD-WAN template name Returns: Deletion result |
| assign_sdwan_templateC | Assign an SD-WAN template to a device. Args: adom: ADOM name template: SD-WAN template name device: Device name to assign vdom: VDOM name (default: root) Returns: Assignment result |
| assign_sdwan_template_bulkA | Assign an SD-WAN template to multiple devices. Args: adom: ADOM name template: SD-WAN template name devices: List of devices [{"name": "dev1", "vdom": "root"}, ...] Returns: dict: Assignment result with keys: - success: True - requested_count: Number of devices in the request. NOT a count of assignments actually made: FortiManager reports no per-device result for this scope-member ADD, and the client raises on any non-zero code, so everything reaching this line is a code 0 that says nothing about individual devices (same shape as #78's device-group bulk tools). - result: Raw client result |
| unassign_sdwan_templateB | Unassign an SD-WAN template from a device. Args: adom: ADOM name template: SD-WAN template name device: Device name to unassign vdom: VDOM name (default: root) Returns: Unassignment result |
| list_ips_sensorsA | List IPS sensors in an ADOM. IPS sensors are collections of intrusion-prevention signature filters and overrides, referenced by firewall policies for the "IPS" security profile. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Sensor list with keys: - status: "success" or "error" - count: Number of sensors - sensors: List of IPS sensor objects - message: Error message if failed |
| get_ips_sensorB | Get detailed information about an IPS sensor. Args: adom: ADOM name name: IPS sensor name Returns: dict: Sensor details with keys: - status: "success" or "error" - sensor: Full sensor configuration, including its "entries" list of signature filters/overrides - message: Error message if failed |
| create_ips_sensorA | Create an IPS sensor. The sensor is created empty (no signature filters/overrides). Use add_ips_sensor_signature_override to populate it afterward. Args: adom: ADOM name name: Sensor name comment: Optional comment block_malicious_url: Enable/disable malicious URL blocking ("enable" or "disable") scan_botnet_connections: Botnet-server connection handling ("disable", "block", or "monitor") extended_log: Enable/disable extended logging ("enable" or "disable") Returns: dict: Create result with keys: - status: "success" or "error" - name: Created sensor name - message: Status or error message Example: >>> result = await create_ips_sensor( ... adom="root", ... name="Custom-IPS", ... scan_botnet_connections="block", ... comment="Baseline IPS sensor", ... ) |
| update_ips_sensorA | Update an IPS sensor's top-level settings. Only specified fields will be updated. To change signature filters/overrides use add_ips_sensor_signature_override / remove_ips_sensor_signature_override instead. Args: adom: ADOM name name: Sensor name comment: New comment block_malicious_url: Enable/disable malicious URL blocking scan_botnet_connections: Botnet-server connection handling ("disable", "block", or "monitor") extended_log: Enable/disable extended logging Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_ips_sensorA | Delete an IPS sensor. WARNING: This will fail if the sensor is in use by policies. Args: adom: ADOM name name: Sensor name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_ips_sensor_signature_overridesA | List the signature-filter/override entries of an IPS sensor. Each entry is either a broad filter (matched by severity/protocol/OS/...) or a specific signature override (matched by an explicit "rule" list of signature names). Use the returned "id" values with remove_ips_sensor_signature_override. Args: adom: ADOM name sensor: IPS sensor name Returns: dict: Entry list with keys: - status: "success" or "error" - count: Number of entries - entries: List of entry objects - message: Error message if failed |
| add_ips_sensor_signature_overrideA | Add a signature filter/override entry to an IPS sensor. FortiManager exposes an IPS sensor's "entries" list as its own addressable nested collection (its own add/get/set/update/delete/move endpoints under .../sensor/{sensor}/entries), so this calls that sub-resource's ADD endpoint directly rather than reading the whole sensor, splicing the list in Python, and writing it back -- unlike update_device_wtp_profile_radio's device-DB radio field, which has no such sub-resource and must be read-modify-written as a whole object. Pass Args: adom: ADOM name sensor: IPS sensor name (must already exist -- see create_ips_sensor) rule: Specific signature names to override (optional; omit for a broad filter entry) entry_id: Explicit entry ID (0-4294967295). If omitted, FortiManager assigns the next available ID. status: Status of the entry ("disable", "enable", or "default"; default: "enable") action: Action for matched traffic ("pass", "block", "reset", or "default"; default: "default") log: Enable/disable logging for this entry ("enable" or "disable") log_packet: Enable/disable packet logging for this entry ("enable" or "disable") severity: Signature severities to match (e.g. ["critical", "high"]) default_action: Broad-filter action filter ("block", "pass", or "all") default_status: Broad-filter status filter ("disable", "enable", or "all") Returns: dict: Add result with keys: - status: "success" or "error" - sensor: Sensor name - message: Status or error message Example: >>> # Override a specific signature to block+log >>> result = await add_ips_sensor_signature_override( ... adom="root", ... sensor="Custom-IPS", ... rule=["trojan.generic"], ... status="enable", ... action="block", ... log="enable", ... ) |
| remove_ips_sensor_signature_overrideA | Remove a signature filter/override entry from an IPS sensor. Use list_ips_sensor_signature_overrides to find the entry's "id" first if it is not already known. Args: adom: ADOM name sensor: IPS sensor name entry_id: ID of the entry to remove (as returned in its "id" field) Returns: dict: Remove result with keys: - status: "success" or "error" - message: Status or error message |
| list_ssl_ssh_profilesA | List SSL/SSH inspection profiles in an ADOM. SSL/SSH inspection profiles control how a FortiGate decrypts and inspects TLS/SSH traffic, referenced by firewall policies for the "SSL/SSH Inspection" security profile. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of SSL/SSH profile objects - message: Error message if failed |
| get_ssl_ssh_profileA | Get detailed information about an SSL/SSH inspection profile. Args: adom: ADOM name name: SSL/SSH profile name Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full profile configuration - message: Error message if failed |
| create_ssl_ssh_profileB | Create an SSL/SSH inspection profile. Args: adom: ADOM name name: Profile name comment: Optional comment ssl_inspection: Generic SSL inspection level ("disable", "certificate-inspection", or "deep-inspection") https_inspection: HTTPS inspection level ("disable", "certificate-inspection", or "deep-inspection") caname: CA certificate(s) used for SSL inspection (datasource names) untrusted_caname: Untrusted CA certificate(s) (datasource names) server_cert: Certificate(s) used to replace the server certificate (datasource names; only used when server_cert_mode="replace") server_cert_mode: Re-sign or replace the server's certificate ("re-sign" or "replace") mapi_over_https: Enable/disable inspection of MAPI over HTTPS rpc_over_https: Enable/disable inspection of RPC over HTTPS Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message Example: >>> result = await create_ssl_ssh_profile( ... adom="root", ... name="Custom-Deep-Inspection", ... ssl_inspection="deep-inspection", ... https_inspection="deep-inspection", ... server_cert_mode="re-sign", ... comment="Deep inspection for outbound web traffic", ... ) |
| update_ssl_ssh_profileA | Update an SSL/SSH inspection profile. Only specified fields will be updated. Args: adom: ADOM name name: Profile name comment: New comment ssl_inspection: Generic SSL inspection level ("disable", "certificate-inspection", or "deep-inspection") https_inspection: HTTPS inspection level ("disable", "certificate-inspection", or "deep-inspection") caname: New CA certificate(s) (datasource names, replaces existing) untrusted_caname: New untrusted CA certificate(s) (replaces existing) server_cert: New server certificate(s) (replaces existing) server_cert_mode: Re-sign or replace the server's certificate mapi_over_https: Enable/disable inspection of MAPI over HTTPS rpc_over_https: Enable/disable inspection of RPC over HTTPS Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_ssl_ssh_profileA | Delete an SSL/SSH inspection profile. WARNING: This will fail if the profile is in use by policies. Args: adom: ADOM name name: Profile name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_dlp_profilesA | List DLP (Data Loss Prevention) profiles in an ADOM. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of DLP profile objects - message: Error message if failed |
| get_dlp_profileB | Get detailed information about a DLP profile. Args: adom: ADOM name name: DLP profile name Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full profile configuration - message: Error message if failed |
| create_dlp_profileA | Create a DLP (Data Loss Prevention) profile. The profile is created without rules. Rule management (config dlp profile / rule) is out of scope for this tool; add rules via the FortiManager GUI/CLI or a follow-up tool once this profile exists. Args: adom: ADOM name name: Profile name comment: Optional comment feature_set: Flow or proxy-based inspection ("flow" or "proxy") dlp_log: Enable/disable DLP logging ("enable" or "disable") extended_log: Enable/disable extended logging ("enable" or "disable") Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message Example: >>> result = await create_dlp_profile( ... adom="root", ... name="Custom-DLP", ... feature_set="flow", ... dlp_log="enable", ... ) |
| update_dlp_profileB | Update a DLP profile's top-level settings. Only specified fields will be updated. Args: adom: ADOM name name: Profile name comment: New comment feature_set: Flow or proxy-based inspection ("flow" or "proxy") dlp_log: Enable/disable DLP logging extended_log: Enable/disable extended logging Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_dlp_profileA | Delete a DLP profile. WARNING: This will fail if the profile is in use by policies. Args: adom: ADOM name name: Profile name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_waf_profilesA | List WAF (Web Application Firewall) profiles in an ADOM. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of WAF profile objects - message: Error message if failed |
| get_waf_profileB | Get detailed information about a WAF profile. Args: adom: ADOM name name: WAF profile name Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full profile configuration - message: Error message if failed |
| create_waf_profileA | Create a WAF (Web Application Firewall) profile. The profile is created with FortiManager/FortiOS defaults for its nested signature/constraint/method/url-access sections. Fine-grained control of those sections is out of scope for this tool; adjust them via the FortiManager GUI/CLI or a follow-up tool once this profile exists. Args: adom: ADOM name name: Profile name comment: Optional comment external: Enable/disable external HTTP inspection ("enable" or "disable") extended_log: Enable/disable extended logging ("enable" or "disable") Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message Example: >>> result = await create_waf_profile( ... adom="root", ... name="Custom-WAF", ... extended_log="enable", ... comment="Baseline WAF profile", ... ) |
| update_waf_profileA | Update a WAF profile's top-level settings. Only specified fields will be updated. Args: adom: ADOM name name: Profile name comment: New comment external: Enable/disable external HTTP inspection extended_log: Enable/disable extended logging Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_waf_profileA | Delete a WAF profile. WARNING: This will fail if the profile is in use by policies. Args: adom: ADOM name name: Profile name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_antivirus_profilesA | List antivirus profiles in an ADOM. Antivirus profiles define virus/malware scanning behavior per protocol (HTTP, FTP, SMTP, etc.) and are applied to firewall policies. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of antivirus profile objects - message: Error message if failed |
| get_antivirus_profileB | Get detailed information about an antivirus profile. Args: adom: ADOM name name: Antivirus profile name Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full antivirus profile configuration - message: Error message if failed |
| create_antivirus_profileA | Create an antivirus profile. Args: adom: ADOM name name: Profile name comment: Optional comment feature_set: Flow/proxy feature set - "proxy" or "flow" (optional) scan_mode: Scan mode - "default" or "legacy" (optional) av_virus_log: Enable/disable AntiVirus logging - "enable" or "disable" (optional) Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message Example: >>> result = await create_antivirus_profile( ... adom="root", ... name="strict-av", ... feature_set="flow", ... av_virus_log="enable", ... ) |
| update_antivirus_profileA | Update an existing antivirus profile. Only specified fields will be updated. Args: adom: ADOM name name: Profile name comment: New comment (optional) feature_set: New flow/proxy feature set - "proxy" or "flow" (optional) scan_mode: New scan mode - "default" or "legacy" (optional) av_virus_log: New AntiVirus logging setting - "enable" or "disable" (optional) Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_antivirus_profileB | Delete an antivirus profile. WARNING: This will fail if the profile is in use by firewall policies. Args: adom: ADOM name name: Profile name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_webfilter_profilesA | List web filter profiles in an ADOM. Web filter profiles define URL category rating actions, FortiGuard web filtering behavior, and related web-content controls. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of web filter profile objects - message: Error message if failed |
| get_webfilter_profileB | Get detailed information about a web filter profile. Args: adom: ADOM name name: Web filter profile name Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full web filter profile configuration - message: Error message if failed |
| create_webfilter_profileB | Create a web filter profile. Args: adom: ADOM name name: Profile name comment: Optional comment feature_set: Flow/proxy feature set - "proxy" or "flow" (optional) log_all_url: Enable/disable logging all URLs visited - "enable" or "disable" (optional) web_content_log: Enable/disable logging blocked web content - "enable" or "disable" (optional) Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message Example: >>> result = await create_webfilter_profile( ... adom="root", ... name="strict-web", ... feature_set="flow", ... log_all_url="enable", ... ) |
| update_webfilter_profileA | Update an existing web filter profile. Only specified fields will be updated. Args: adom: ADOM name name: Profile name comment: New comment (optional) feature_set: New flow/proxy feature set - "proxy" or "flow" (optional) log_all_url: New "log all URLs" setting - "enable" or "disable" (optional) web_content_log: New blocked-content logging setting - "enable" or "disable" (optional) Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_webfilter_profileB | Delete a web filter profile. WARNING: This will fail if the profile is in use by firewall policies. Args: adom: ADOM name name: Profile name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_dnsfilter_profilesB | List DNS filter profiles in an ADOM. DNS filter profiles control FortiGuard DNS category rating actions, botnet C&C domain blocking, and DNS-level safe search. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: Profile list with keys: - status: "success" or "error" - count: Number of profiles - profiles: List of DNS filter profile objects - message: Error message if failed |
| get_dnsfilter_profileB | Get detailed information about a DNS filter profile. Args: adom: ADOM name name: DNS filter profile name Returns: dict: Profile details with keys: - status: "success" or "error" - profile: Full DNS filter profile configuration - message: Error message if failed |
| create_dnsfilter_profileB | Create a DNS filter profile. Args: adom: ADOM name name: Profile name comment: Optional comment block_action: Action for blocked domains - "block", "redirect", or "block-sevrfail" (optional) block_botnet: Enable/disable blocking botnet C&C DNS lookups - "enable" or "disable" (optional) safe_search: Enable/disable Google/Bing/YouTube/etc. safe search - "enable" or "disable" (optional) Returns: dict: Create result with keys: - status: "success" or "error" - name: Created profile name - message: Status or error message Example: >>> result = await create_dnsfilter_profile( ... adom="root", ... name="strict-dns", ... block_botnet="enable", ... safe_search="enable", ... ) |
| update_dnsfilter_profileA | Update an existing DNS filter profile. Only specified fields will be updated. Args: adom: ADOM name name: Profile name comment: New comment (optional) block_action: New blocked-domain action - "block", "redirect", or "block-sevrfail" (optional) block_botnet: New botnet C&C blocking setting - "enable" or "disable" (optional) safe_search: New safe search setting - "enable" or "disable" (optional) Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_dnsfilter_profileB | Delete a DNS filter profile. WARNING: This will fail if the profile is in use by firewall policies. Args: adom: ADOM name name: Profile name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| list_application_listsA | List application-control lists in an ADOM. Application control lists define pass/block/monitor actions for application signatures and categories, applied to firewall policies. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") name_filter: Filter by name (partial match) Returns: dict: List result with keys: - status: "success" or "error" - count: Number of application-control lists - lists: List of application-control list objects - message: Error message if failed |
| get_application_listB | Get detailed information about an application-control list. Args: adom: ADOM name name: Application-control list name Returns: dict: List details with keys: - status: "success" or "error" - list: Full application-control list configuration - message: Error message if failed |
| create_application_listB | Create an application-control list. Args: adom: ADOM name name: List name comment: Optional comment deep_app_inspection: Enable/disable deep application inspection - "enable" or "disable" (optional) unknown_application_action: Pass or block traffic from unknown applications - "pass" or "block" (optional) other_application_action: Action for other applications - "pass" or "block" (optional) Returns: dict: Create result with keys: - status: "success" or "error" - name: Created list name - message: Status or error message Example: >>> result = await create_application_list( ... adom="root", ... name="strict-appctrl", ... unknown_application_action="block", ... other_application_action="block", ... ) |
| update_application_listA | Update an existing application-control list. Only specified fields will be updated. Args: adom: ADOM name name: List name comment: New comment (optional) deep_app_inspection: New deep-inspection setting - "enable" or "disable" (optional) unknown_application_action: New unknown-application action - "pass" or "block" (optional) other_application_action: New other-application action - "pass" or "block" (optional) Returns: dict: Update result with keys: - status: "success" or "error" - message: Status or error message |
| delete_application_listA | Delete an application-control list. WARNING: This will fail if the list is in use by firewall policies. Args: adom: ADOM name name: List name to delete Returns: dict: Delete result with keys: - status: "success" or "error" - message: Status or error message |
| get_system_statusA | Get FortiManager system status and version information. Returns comprehensive system status including:
Returns: dict: System status with keys: - status: "success" or "error" - data: System status information - message: Error message if failed Example: >>> result = await get_system_status() >>> print(f"Version: {result['data']['Version']}") >>> print(f"Hostname: {result['data']['Hostname']}") |
| get_ha_statusA | Get FortiManager High Availability (HA) health. Returns what FortiManager reports at Returns: dict: HA status with keys: - status: "success" or "error" - data: The HA status FortiManager returned - message: Error message if failed Example: >>> result = await get_ha_status() >>> print(result["data"].get("HA Health Status")) |
| list_adomsA | List all Administrative Domains (ADOMs) in FortiManager. ADOMs partition FortiManager into separate management domains, each with its own devices, policies, and configurations. Args: fields: Specific fields to return (optional, returns all if not specified) Returns: dict: ADOM list with keys: - status: "success" or "error" - count: Number of ADOMs - adoms: List of ADOM objects with name, desc, state, etc. - message: Error message if failed Example: >>> result = await list_adoms() >>> for adom in result["adoms"]: ... print(f"{adom['name']}: {adom.get('desc', 'No description')}") |
| get_adomA | Get detailed information about a specific ADOM. Args: name: ADOM name (e.g., "root", "customer-a") include_details: Include sub-objects (default: False) Returns: dict: ADOM details with keys: - status: "success" or "error" - adom: ADOM object with full configuration - message: Error message if failed Example: >>> result = await get_adom("root") >>> print(f"State: {result['adom']['state']}") |
| list_devicesA | List all managed devices in an ADOM. FortiManager manages FortiGate and other Fortinet devices. This lists all devices registered in the specified ADOM. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") fields: Specific fields to return (optional) Returns: dict: Device list with keys: - status: "success" or "error" - count: Number of devices - devices: List of device objects with name, ip, os_ver, etc. - message: Error message if failed Example: >>> result = await list_devices("root") >>> for device in result["devices"]: ... print(f"{device['name']}: {device.get('ip', 'N/A')}") |
| get_deviceA | Get detailed information about a specific managed device. Args: name: Device name adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") include_details: Include sub-objects like VDOMs (default: False) Returns: dict: Device details with keys: - status: "success" or "error" - device: Device object with full configuration - message: Error message if failed Example: >>> result = await get_device("FGT-HQ", "root") >>> print(f"Version: {result['device']['os_ver']}") >>> print(f"Platform: {result['device']['platform_str']}") |
| list_device_groupsA | List all device groups in an ADOM. Device groups organize managed devices for bulk operations like policy installation or configuration deployment. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") Returns: dict: Device groups with keys: - status: "success" or "error" - count: Number of groups - groups: List of device group objects - message: Error message if failed |
| list_tasksA | List all tasks in FortiManager. Tasks represent background operations like policy installation, device provisioning, and other long-running processes. Args: filter_state: Filter by task state (optional): - "pending": Not started - "running": Currently executing - "done": Completed - "error": Failed - "cancelling": Being cancelled - "cancelled": Cancelled Returns: dict: Task list with keys: - status: "success" or "error" - count: Number of tasks - tasks: List of task objects with id, state, progress, etc. - message: Error message if failed Example: >>> # Get all tasks >>> result = await list_tasks() |
| get_taskA | Get detailed status of a specific task. Args: task_id: Task ID number include_details: Include task line details (default: False) Returns: dict: Task details with keys: - status: "success" or "error" - task: Task object with id, state, progress, result, etc. - lines: Task line details (if include_details=True) - message: Error message if failed Example: >>> result = await get_task(12345) >>> print(f"State: {result['task']['state']}") >>> print(f"Progress: {result['task'].get('percent', 0)}%") |
| wait_for_taskA | Wait for a task to complete. Polls the task status until it completes or times out. Useful for waiting on installation or provisioning operations. Args: task_id: Task ID number timeout: Maximum wait time in seconds (default: 300, capped at 3600) poll_interval: Seconds between status checks (default: 5) Returns: dict: Final task status with keys: - status: "success" or "error" - task: Final task object - completed: Whether task completed (vs timeout) - message: Status or error message Example: >>> # Wait for policy installation >>> result = await wait_for_task(12345, timeout=600) >>> if result['completed']: ... print("Installation finished!") |
| list_packagesA | List all policy packages in an ADOM. Policy packages contain firewall policies, security profiles, and other configurations to be installed on managed devices. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") Returns: dict: Package list with keys: - status: "success" or "error" - count: Number of packages - packages: List of package objects - message: Error message if failed Example: >>> result = await list_packages("root") >>> for pkg in result["packages"]: ... print(f"{pkg['name']}: {pkg.get('type', 'policy')}") |
| get_packageA | Get detailed information about a policy package. Args: name: Package name adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") include_details: Include policies and settings (default: False) Returns: dict: Package details with keys: - status: "success" or "error" - package: Package object - message: Error message if failed |
| install_packageA | Install a policy package to managed devices. Deploys firewall policies and configurations from a policy package to the specified devices. This is an asynchronous operation - use wait_for_task() to monitor completion. By default (FMG_INSTALL_SAFETY=strict) a real install requires a verified preview first: run preview_install for the same ADOM, package, and devices, wait for its task to finish, then install. Each preview authorizes one install, and only while the package is unchanged — if the package was edited after the preview, the install is refused (preview_stale) and a fresh preview is required. Args: adom: ADOM name package: Policy package name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...] preview: If True, only preview changes without applying (default: False) Returns: dict: Installation result with keys: - status: "success" or "error" - task_id: Task ID for monitoring (if successful) - message: Status or error message Example: >>> # Install to single device >>> result = await install_package( ... adom="root", ... package="default", ... devices=[{"name": "FGT-HQ", "vdom": "root"}] ... ) >>> if result["status"] == "success": ... await wait_for_task(result["task_id"]) |
| install_device_settingsA | Install device settings only (without policy package). Deploys device-level configurations like interfaces, DNS, NTP without reinstalling the full policy package. Args: adom: ADOM name devices: Target devices [{"name": "FGT1", "vdom": "root"}, ...] Returns: dict: Installation result with keys: - status: "success" or "error" - task_id: Task ID for monitoring - message: Status or error message |
| lock_adomA | Lock an ADOM for editing (workspace mode). In workspace mode, ADOMs must be locked before making changes. This prevents conflicts from multiple administrators editing the same ADOM simultaneously. Args: adom: ADOM name to lock Returns: dict: Lock result with keys: - status: "success" or "error" - message: Status or error message Example: >>> result = await lock_adom("root") >>> if result["status"] == "success": ... # Make changes... ... await commit_adom("root") ... await unlock_adom("root") |
| unlock_adomA | Unlock an ADOM (workspace mode). Release the lock on an ADOM. Changes should be committed before unlocking to persist them. Args: adom: ADOM name to unlock Returns: dict: Unlock result with keys: - status: "success" or "error" - message: Status or error message |
| commit_adomA | Commit changes to an ADOM (workspace mode). Saves all pending changes made to the ADOM. Must be called before unlocking to persist changes. Args: adom: ADOM name to commit Returns: dict: Commit result with keys: - status: "success" or "error" - message: Status or error message |
| list_templatesA | List all provisioning templates in an ADOM. Returns all template types: IPsec, BGP, system, etc. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number of templates to return Returns: List of templates with name, type, and settings |
| get_templateB | Get details of a specific provisioning template. Args: adom: ADOM name name: Template name Returns: Template details including settings and scope |
| list_system_templatesA | List system templates (device profiles) in an ADOM. System templates configure device settings like DNS, NTP, logging, etc. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number to return Returns: List of system templates with name, type, and assigned devices |
| get_system_templateB | Get details of a specific system template. Args: adom: ADOM name name: System template name Returns: System template details including widgets and assigned devices |
| assign_system_templateB | Assign a system template to a device. Args: adom: ADOM name template: System template name device: Device name to assign vdom: VDOM name (default: root) Returns: Assignment result |
| assign_system_template_bulkA | Assign a system template to multiple devices. Args: adom: ADOM name template: System template name devices: List of devices [{"name": "dev1", "vdom": "root"}, ...] Returns: dict: Assignment result with keys: - success: True - requested_count: Number of devices in the request. NOT a count of assignments actually made: FortiManager reports no per-device result for this scope-member ADD, and the client raises on any non-zero code, so everything reaching this line is a code 0 that says nothing about individual devices (same shape as #78's device-group bulk tools). - result: Raw client result |
| unassign_system_templateA | Unassign a system template from a device. Args: adom: ADOM name template: System template name device: Device name to unassign vdom: VDOM name (default: root) Returns: Unassignment result |
| list_cli_template_groupsA | List CLI template groups in an ADOM. CLI template groups contain CLI commands to be executed on devices. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number to return Returns: List of CLI template groups |
| get_cli_template_groupA | Get details of a CLI template group. Args: adom: ADOM name name: CLI template group name Returns: CLI template group details |
| create_cli_template_groupB | Create a new CLI template group. Args: adom: ADOM name name: CLI template group name description: Optional description Returns: Created CLI template group |
| delete_cli_template_groupB | Delete a CLI template group. Args: adom: ADOM name name: CLI template group name Returns: Deletion result |
| list_template_groupsA | List template groups in an ADOM. Template groups combine multiple templates (system, CLI, SD-WAN, etc.) into a single package for device assignment. Args: adom: ADOM name (default: from DEFAULT_ADOM env var, or "root") limit: Maximum number to return Returns: List of template groups |
| get_template_groupB | Get details of a template group. Args: adom: ADOM name name: Template group name Returns: Template group details including member templates |
| assign_template_groupC | Assign a template group to a device. Args: adom: ADOM name template_group: Template group name device: Device name vdom: VDOM name (default: root) Returns: Assignment result |
| validate_templateA | Validate a template group for a device. Checks that all metadata variables are resolved for the device. Args: adom: ADOM name template_group: Template group name device: Target device name vdom: VDOM name (default: root) Returns: Task ID for monitoring validation progress |
| list_device_ipsec_phase1_interfacesA | List IPsec phase1-interface (remote gateway) definitions in a device's device DB. Args: device: Managed device name vdom: VDOM to read (default "root") Returns: dict with device, vdom, count and interfaces (credential fields stripped -- see _PHASE1_SECRET_FIELDS) |
| get_device_ipsec_phase1_interfaceA | Get one IPsec phase1-interface (remote gateway) from a device's device DB. Args: device: Managed device name name: Phase1-interface (remote gateway) name vdom: VDOM it lives in (default "root") Returns: dict with device, vdom and the stored object (credential fields stripped -- see _PHASE1_SECRET_FIELDS), or a not_found error |
| create_device_ipsec_phase1_interfaceA | Create an IPsec phase1-interface (remote gateway / IKE SA) in a device's device DB. Route-based (interface-mode) tunnel only -- this always writes an
Args: device: Managed device name name: New phase1-interface (remote gateway) name interface: Local physical/aggregate/loopback/VLAN outgoing interface type: Remote gateway type: "static", "dynamic" or "ddns"; default "static" remote_gw: Remote gateway's external IPv4 address. Required when type is "static" -- a static gateway with no address cannot connect psksecret: Pre-shared key for PSK authentication, 6-128 characters ike_version: IKE protocol version, "1" or "2" mode: IKEv1 ID protection mode: "main" or "aggressive" (ignored on IKEv2) peertype: Peer ID acceptance: "any", "one", "dialup", "peer" or "peergrp". Defaults to "any" when type is "static" and no value is given -- live-verified against FMG 7.6.7: a static PSK gateway created without peertype is rejected with "peer invalid value", so this tool supplies the working default rather than surfacing that error to the caller proposal: Phase1 encryption/authentication proposal list (e.g. ["aes256-sha256"]). Values are passed through to FortiManager largely unvalidated -- see module docstring dhgrp: Phase1 Diffie-Hellman group list (e.g. ["14", "21"]). Same pass-through as proposal nattraversal: NAT traversal: "disable", "enable" or "forced" dpd: Dead Peer Detection mode: "disable", "on-idle" or "on-demand" keylife: Phase1 key life in seconds comments: Free-text comment vdom: VDOM the gateway lives in (default "root") Returns: Creation result with any echoed credential stripped |
| update_device_ipsec_phase1_interfaceA | Update fields on a device-DB IPsec phase1-interface. Only provided fields change. Args: device: Managed device name name: Phase1-interface (remote gateway) name to update interface: New local outgoing interface type: New remote gateway type: "static", "dynamic" or "ddns" remote_gw: New remote gateway IPv4 address psksecret: New pre-shared key, 6-128 characters ike_version: New IKE protocol version, "1" or "2" mode: New IKEv1 ID protection mode: "main" or "aggressive" peertype: New peer ID acceptance mode proposal: Replacement phase1 proposal list dhgrp: Replacement phase1 DH group list nattraversal: New NAT traversal setting dpd: New Dead Peer Detection mode keylife: New phase1 key life in seconds comments: New free-text comment vdom: VDOM the gateway lives in (default "root") Returns: Update result with any echoed credential stripped |
| delete_device_ipsec_phase1_interfaceA | Delete an IPsec phase1-interface from a device's device DB. Any phase2-interface still pointing at this phase1name should be deleted first (delete_device_ipsec_phase2_interface) -- FortiManager rejects deleting a phase1-interface a phase2-interface still references. Args: device: Managed device name name: Phase1-interface (remote gateway) name to delete vdom: VDOM it lives in (default "root") Returns: Deletion result |
| list_device_ipsec_phase2_interfacesA | List IPsec phase2-interface (tunnel/selector) definitions in a device's device DB. Args: device: Managed device name vdom: VDOM to read (default "root") Returns: dict with device, vdom, count and interfaces |
| get_device_ipsec_phase2_interfaceA | Get one IPsec phase2-interface (tunnel/selector) from a device's device DB. Args: device: Managed device name name: Phase2-interface (tunnel) name vdom: VDOM it lives in (default "root") Returns: dict with device, vdom and the stored object, or a not_found error |
| create_device_ipsec_phase2_interfaceA | Create an IPsec phase2-interface (tunnel/traffic selector) in a device's device DB. Args: device: Managed device name name: New phase2-interface (tunnel) name phase1name: Parent phase1-interface (remote gateway) name this tunnel is negotiated under src_subnet: Local proxy ID subnet, CIDR ("192.168.1.0/24") or "address netmask" form. Omitted means the FortiOS default (0.0.0.0/0, the whole local network) dst_subnet: Remote proxy ID subnet, same accepted forms. Omitted means the FortiOS default (0.0.0.0/0) proposal: Phase2 encryption/authentication proposal list (e.g. ["aes256-sha256"]). Pass-through, see module docstring dhgrp: Phase2 PFS Diffie-Hellman group list. Pass-through, see module docstring pfs: Perfect Forward Secrecy: "disable" or "enable" replay: Replay detection: "disable" or "enable" keepalive: Keep-alive: "disable" or "enable" auto_negotiate: IPsec SA auto-negotiation: "disable" or "enable" keylifeseconds: Phase2 key life in seconds (120-172800) comments: Free-text comment vdom: VDOM the tunnel lives in (default "root") Returns: Creation result |
| update_device_ipsec_phase2_interfaceA | Update fields on a device-DB IPsec phase2-interface. Only provided fields change. Args: device: Managed device name name: Phase2-interface (tunnel) name to update phase1name: New parent phase1-interface name src_subnet: New local proxy ID subnet, CIDR or "address netmask" form dst_subnet: New remote proxy ID subnet, same accepted forms proposal: Replacement phase2 proposal list dhgrp: Replacement phase2 DH group list pfs: New Perfect Forward Secrecy setting replay: New replay detection setting keepalive: New keep-alive setting auto_negotiate: New IPsec SA auto-negotiation setting keylifeseconds: New phase2 key life in seconds (120-172800) comments: New free-text comment vdom: VDOM the tunnel lives in (default "root") Returns: Update result |
| delete_device_ipsec_phase2_interfaceB | Delete an IPsec phase2-interface from a device's device DB. Args: device: Managed device name name: Phase2-interface (tunnel) name to delete vdom: VDOM it lives in (default "root") Returns: Deletion result |
| get_device_sslvpn_settingsA | Get the SSL-VPN (Agentless VPN) settings object from a device's device DB. This is a per-vdom singleton -- there is exactly one, so no list/create/ delete tools exist for it, matching the FNDN schema's "object" (not "table") classification. Args: device: Managed device name vdom: VDOM to read (default "root") Returns: dict with device, vdom and the stored settings object |
| update_device_sslvpn_settingsA | Update the SSL-VPN (Agentless VPN) settings object in a device's device DB. Only provided fields change -- see get_device_sslvpn_settings for the current state to base a partial update on. Args: device: Managed device name status: Enable/disable SSL-VPN: "disable" or "enable" port: SSL-VPN access port (1-65535) source_interface: Source interface names of incoming traffic default_portal: Default web portal name (see update_device_sslvpn_web_portal) servercert: Server certificate name to present to clients idle_timeout: Idle timeout in seconds before disconnect auth_timeout: Authentication timeout in seconds (1-259200, 0 = no timeout) dns_server1: Primary DNS server handed to clients dns_server2: Secondary DNS server handed to clients reqclientcert: Require client certificates: "disable" or "enable" https_redirect: Redirect port 80 to the SSL-VPN port: "disable" or "enable" ssl_min_proto_ver: Minimum TLS version: "tls1-0", "tls1-1", "tls1-2" or "tls1-3" vdom: VDOM the settings apply to (default "root") Returns: Update result |
| get_device_sslvpn_web_portalA | Get an SSL-VPN web portal from a device's device DB. Args: device: Managed device name name: Web portal name vdom: VDOM it lives in (default "root") Returns: dict with device, vdom and the stored portal object (bookmark credentials stripped -- see _PORTAL_SECRET_FIELDS), or a not_found error |
| update_device_sslvpn_web_portalA | Update an SSL-VPN web portal in a device's device DB. The portal itself must already exist in the device DB (created via the FortiOS CLI/GUI or a prior install) -- this tool only updates an existing entry, matching the "get/update" scope of this module. Only provided fields change. Args: device: Managed device name name: Web portal name to update tunnel_mode: Enable/disable full-tunnel mode: "disable" or "enable" split_tunneling: Enable/disable split tunneling: "disable" or "enable" ip_pools: Firewall address/address-group names to draw tunnel-mode client IPs from web_mode: Enable/disable web-mode (clientless HTML5) access: "disable" or "enable" dns_server1: Primary DNS server handed to portal clients dns_server2: Secondary DNS server handed to portal clients vdom: VDOM the portal lives in (default "root") Returns: Update result |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 233 tools
The set has many large familes with overlapping boundaries: list_templates/list_system_templates/list_sdwan_templates/list_cli_template_groups/list_template_groups, get_device_interfaces vs get_device_interface_config, device sniffer vs on-demand sniffer vs FortiManager packet capture, and several revision/diff tools. Descriptions usually explain the difference, but with 233 tools an agent will frequently face plausible near-matches.
Names are consistently snake_case and mostly follow a verb_noun pattern (list_*, get_*, create_*, update_*, delete_*). Minor deviations exist where add_* is used instead of create_* for devices/groups/templates, and IPv6/local-in variants introduce suffixes like local_in_policies6, but the overall convention remains readable.
233 tools is far beyond a practical MCP tool surface; even for a broad platform like FortiManager, the set is too large to select from reliably and should be consolidated into fewer domain-level tools. The count alone creates a severe context and misselection burden.
Coverage is extensive across ADOMs, devices, device-DB objects, firewall policies, local-in policies, security profiles, templates, SD-WAN, revisions, scripts, firmware, and tasks. There are still lifecycle gaps, such as limited create/update/delete coverage for some template families and missing single-object getters or deletes for a few device-DB objects, but core workflows are well covered.