Skip to main content
Glama
amittell

firewalla-mcp-server

Firewalla MCP Server

npm version

A Model Context Protocol (MCP) server that provides real-time access to Firewalla firewall data through 28 read-only tools, plus 11 opt-in write tools, compatible with any MCP client.

This is a community project, not affiliated with Firewalla. It reads your data through the Firewalla MSP API, so it needs an MSP account with API access.

Why Firewalla MCP Server?

Simple Network Security Integration

  • 28 read-only tools for network monitoring and analysis: 23 Direct API Endpoints + 5 Convenience Wrappers

  • 11 write tools, off unless FIREWALLA_ENABLE_WRITE_TOOLS=true, so by default nothing can change your box

  • Advanced Search with query syntax and filters

  • Clean, Verified Architecture with corrected API schemas

Related MCP server: Firewalla MCP Server

Features

  • Real-time Firewall Data: Query security alerts, network flows, and device status

  • Security Analysis: Get insights on threats, blocked attacks, and network anomalies

  • Bandwidth Monitoring: Track top bandwidth consumers and usage patterns

  • Rule Management: View firewall rules; with the write tools, create, pause, resume and delete them

  • Target Lists: View target lists; with the write tools, create, update and delete them

  • Search Tools: Query syntax with filters and logical operators

Client Setup Guides

Client

Quick Start

Full Guide

Claude Desktop

npm i -g firewalla-mcp-server → Configure MCP

Setup Guide

Claude Code

claude mcp add firewalla -e ... -- npx -y firewalla-mcp-server

Setup Guide

VS Code

Built-in MCP (1.102+): servers in .vscode/mcp.json

Setup Guide

Cursor

~/.cursor/mcp.json or a project's .cursor/mcp.json

Setup Guide

Roo Code

The extension was shut down on 2026-05-15; the guide covers existing installs

Setup Guide

Cline

MCP Servers → Configure → cline_mcp_settings.json

Setup Guide

Open WebUI

mcpo, or a native MCP connection over HTTP

Setup Guide

How It Works

Claude Desktop/Code ↔ MCP Server ↔ Firewalla API

The MCP server acts as a bridge between Claude and your Firewalla firewall, translating Claude's requests into Firewalla API calls and returning the results in a format Claude can understand.

Prerequisites

  • Node.js 18+ and npm. On Node 18-22, npm prints an EBADENGINE warning for geoip-lite 2.x, which declares Node 24 for its database update script; the lookups the server uses run on Node 18 and later, and CI tests 18, 20, 22 and 24.

  • Firewalla MSP account with API access

  • Your Firewalla device online and connected

Upgrading to 2.0.0

2.0.0 changes these defaults from 1.5.0. A setup that relied on an old default needs the setting in the last column.

Up to 1.5.0

From 2.0.0

What to set

pause_rule, resume_rule, create_target_list, update_target_list and delete_target_list were always registered

All 11 write tools are off; the server lists 24 read-only tools, and a call to a write tool answers "Unknown tool" and sends nothing

FIREWALLA_ENABLE_WRITE_TOOLS=true

The HTTP transport listened on every interface

It listens on 127.0.0.1. The Docker image sets MCP_HTTP_HOST=0.0.0.0, so a published port reaches it once MCP_HTTP_BEARER_TOKEN is set

MCP_HTTP_HOST=0.0.0.0 outside Docker, with MCP_HTTP_BEARER_TOKEN

Any Host header was served

403 unless it is localhost, 127.0.0.1, [::1] or the MCP_HTTP_HOST address; a wildcard MCP_HTTP_HOST (0.0.0.0, ::, as in the Docker image) adds nothing, so every other name clients use must be listed

the name clients connect by (a compose service name, a LAN address) in MCP_HTTP_ALLOWED_HOSTS

Any Origin was served

403 for a request with an Origin header, which browsers send; MCP clients that send none are not affected

the page's origin in MCP_HTTP_ALLOWED_ORIGINS: scheme, host and port (http://localhost:6274). The setting and the header are compared as URL origins, so letter case and a default port make no difference and a path in the setting is dropped. There is no wildcard: an entry with * in its host stops the server at startup. The opaque origin null is always refused

No token check (MCP_HTTP_BEARER_TOKEN did not exist)

On an address other than loopback, the Docker image's 0.0.0.0 included, the server exits with code 1 at startup unless MCP_HTTP_BEARER_TOKEN is set, and it refuses a token shorter than 16 characters wherever it listens. With the token, a request without Authorization: Bearer <token> gets 401

MCP_HTTP_BEARER_TOKEN (openssl rand -hex 32 makes one) and the same token in every client, or MCP_HTTP_ALLOW_NO_TOKEN=true on a network no untrusted machine can reach, such as a compose network with no published port

Any path starting with MCP_HTTP_PATH was served

Only the configured MCP_HTTP_PATH (default /mcp) and that path with one trailing slash, with or without a query string; with the default, /mcpx and /mcp/x get 404

a client URL that ends in exactly the configured MCP_HTTP_PATH

MCP_TEST_MODE=true started under any NODE_ENV

Refused with NODE_ENV=production, which the Docker image sets

-e NODE_ENV=development with -e MCP_TEST_MODE=true

API_RATE_LIMIT was range-checked and not applied

Each server process starts at most API_RATE_LIMIT requests in any rolling 5 minutes (default 100), counting 429 retries and the retry of a failed read; cache hits do not count. A request that cannot start within 20 s fails and says when capacity returns. A request still waiting when its tool gives up at 30 s is not sent; archive_alarm, mute_alarm and delete_alarm have no such tool deadline. The MSP API allowed 100 requests per fixed 5-minute window on the one token measured; whether that is per token or per account was not measured

when several server processes or other clients use one token, an API_RATE_LIMIT for each so that together they stay at 100 or less: each process counts only its own requests, so lowering one does not cover the others

Other changes a script may notice:

  • With FIREWALLA_BOX_ID set, get_alarm_trends covers that box instead of every box, and makes 31 requests for the default 30d instead of 1. An explicit group still reads the group's series in one request.

  • Tool responses are compact JSON, without the indentation.

  • AND, OR and NOT are translated to the API's grammar, which has none of them: status:blocked AND region:US matched 0 flows up to 1.5.0 and now matches what status:blocked region:US does. A query with no API form is refused as a validation error that suggests runnable queries: an OR between different fields, NOT over an AND, a comma list with a space around a comma, and geographic names the API does not have, such as country: (use region:). Up to 1.5.0 these returned wrong results or none.

  • IDs are no longer trimmed: an ID with leading or trailing whitespace, /, ?, # or % is refused before any request.

  • A streamed get_flow_data chunk gives each flow's time as ts, an ISO string, like a plain page, instead of timestamp.

  • Over HTTP, a request with the session ID of a session the server no longer holds gets 404 instead of 400.

CHANGELOG.md has the full list.

Quick Start

1. Installation

# Install globally
npm install -g firewalla-mcp-server

# Or install locally in your project
npm install firewalla-mcp-server

Option B: Use Docker

Warning: Not for production use – secrets visible in process list

The examples below pass credentials directly in the command line, which exposes them to process listing and shell history. For production use, consider these secure alternatives:

  • Use --env-file with a .env file: docker run --env-file .env ...

  • Set environment variables in your shell before running Docker

  • Use Docker secrets for orchestration environments

Stdio Transport (Default - for Claude Desktop integration):

# Using Docker Hub image (minimal config)
docker run -it --rm \
  -e FIREWALLA_MSP_TOKEN=your_token \
  -e FIREWALLA_MSP_ID=yourdomain.firewalla.net \
  amittell/firewalla-mcp-server

# Or with optional box filter
docker run -it --rm \
  -e FIREWALLA_MSP_TOKEN=your_token \
  -e FIREWALLA_MSP_ID=yourdomain.firewalla.net \
  -e FIREWALLA_BOX_ID=your_box_gid \
  amittell/firewalla-mcp-server

# Or build locally
docker build -t firewalla-mcp-server .
docker run -it --rm \
  -e FIREWALLA_MSP_TOKEN=your_token \
  -e FIREWALLA_MSP_ID=yourdomain.firewalla.net \
  firewalla-mcp-server

# Recommended: Using env file (more secure)
docker run -it --rm --env-file .env amittell/firewalla-mcp-server

HTTP Transport (for standalone Docker containers and external access):

# Make a token and keep the value echo prints: every client sends it
export MCP_HTTP_BEARER_TOKEN="$(openssl rand -hex 32)"
echo "$MCP_HTTP_BEARER_TOKEN"

# Run with HTTP transport on port 3000. -e MCP_HTTP_BEARER_TOKEN with no
# value passes the exported one without putting it on the command line
docker run -d --name firewalla-mcp \
  -p 3000:3000 \
  -e MCP_TRANSPORT=http \
  -e MCP_HTTP_PORT=3000 \
  -e MCP_HTTP_BEARER_TOKEN \
  -e FIREWALLA_MSP_TOKEN=your_token \
  -e FIREWALLA_MSP_ID=yourdomain.firewalla.net \
  amittell/firewalla-mcp-server

# Add FIREWALLA_BOX_ID if you want to filter to a specific box
# -e FIREWALLA_BOX_ID=your_box_gid \

# The server will be accessible at http://localhost:3000/mcp, and clients
# send the header: Authorization: Bearer <the token echo printed>

# Using env file (recommended): besides the credentials, .env sets
# MCP_TRANSPORT=http and MCP_HTTP_BEARER_TOKEN; to add a new token to it:
# echo "MCP_HTTP_BEARER_TOKEN=$(openssl rand -hex 32)" >> .env
docker run -d --name firewalla-mcp \
  -p 3000:3000 \
  --env-file .env \
  amittell/firewalla-mcp-server

# For docker-compose
cat > docker-compose.yml << EOF
version: '3.8'
services:
  firewalla-mcp:
    image: amittell/firewalla-mcp-server
    ports:
      - "3000:3000"
    environment:
      - MCP_TRANSPORT=http
      - MCP_HTTP_PORT=3000
      # The image already listens on every interface of the container
      - MCP_HTTP_HOST=0.0.0.0
      # From the shell or a .env next to this file; openssl rand -hex 32
      - MCP_HTTP_BEARER_TOKEN=\${MCP_HTTP_BEARER_TOKEN}
      # Other containers reach it as http://firewalla-mcp:3000/mcp
      - MCP_HTTP_ALLOWED_HOSTS=firewalla-mcp
      - FIREWALLA_MSP_TOKEN=\${FIREWALLA_MSP_TOKEN}
      - FIREWALLA_MSP_ID=\${FIREWALLA_MSP_ID}
      # Optional: filter to specific box
      # - FIREWALLA_BOX_ID=\${FIREWALLA_BOX_ID}
    restart: unless-stopped
EOF

docker-compose up -d

The image sets MCP_HTTP_HOST=0.0.0.0 so that a published port reaches the server, and on that address the server does not start without MCP_HTTP_BEARER_TOKEN (at least 16 characters; openssl rand -hex 32 makes one): the container exits with code 1, and docker logs shows a refusing to start line that says why. -p 3000:3000 publishes the port on every interface of the Docker host, where anyone on your network can reach it. Publishing it on this machine only (-p 127.0.0.1:3000:3000) still needs the token: another container on the same Docker network can connect to the container's own address, and one that did, with Host: localhost and no token, got 200 when the check was turned off. MCP_HTTP_ALLOW_NO_TOKEN=true turns the check off, for a network no untrusted machine or container can reach, such as a compose network with no published port; the server then prints a warning when it starts. The server answers only requests whose Host header is localhost, 127.0.0.1 or [::1], so a client that connects by another name, such as the host's LAN address or a compose service name, needs that name in MCP_HTTP_ALLOWED_HOSTS. See HTTP transport security.

Option C: Install from source

git clone https://github.com/amittell/firewalla-mcp-server.git
cd firewalla-mcp-server
npm install
npm run build

2. Configuration

Create a .env file with your Firewalla credentials:

# Required
FIREWALLA_MSP_TOKEN=your_msp_access_token_here
FIREWALLA_MSP_ID=yourdomain.firewalla.net

# Optional - filters all queries to a specific box
# FIREWALLA_BOX_ID=your_box_gid_here

# Optional - default box for single-box operations, without filtering queries
# FIREWALLA_DEFAULT_BOX_ID=your_box_gid_here

# Optional - register the 11 write tools (default: off, read-only)
# FIREWALLA_ENABLE_WRITE_TOOLS=true

Getting Your Credentials:

  1. Log into your Firewalla MSP portal at https://yourdomain.firewalla.net

  2. Your MSP ID is the full domain (e.g., company123.firewalla.net)

  3. Generate an access token in API settings

  4. (Optional) Find your Box GID in device settings to filter queries to a specific box, or retrieve available boxes using the get_boxes tool

Box ID is optional. Without FIREWALLA_BOX_ID, queries cover every box on the account. The few operations that act on one box (get_specific_alarm, archive_alarm, mute_alarm, delete_alarm, create_rule, rename_device) take a gid argument, and without one they use FIREWALLA_BOX_ID, then FIREWALLA_DEFAULT_BOX_ID, then the account's only box. On an account with several boxes and none of those set, get_specific_alarm, archive_alarm, mute_alarm and delete_alarm check each box, and create_rule and rename_device refuse and list the boxes.

Test mode. MCP_TEST_MODE=true starts the server without credentials, to check that it starts: it uses a dummy token, API (https://test.firewalla.net) and box instead of your settings, always runs on stdio, and cannot read your Firewalla data. With NODE_ENV=production the server refuses test mode and exits with code 1. The Docker image sets NODE_ENV=production, so pass -e NODE_ENV=development along with -e MCP_TEST_MODE=true.

Transport Configuration

The MCP server supports two transport modes:

Stdio Transport (Default): Standard input/output communication for Claude Desktop and similar MCP clients

MCP_TRANSPORT=stdio

HTTP Transport: HTTP server mode for Docker containers, MCP orchestrators, and external access

MCP_TRANSPORT=http
MCP_HTTP_PORT=3000          # Default: 3000
MCP_HTTP_PATH=/mcp          # Default: /mcp. Other paths get 404
MCP_HTTP_HOST=127.0.0.1     # Address to listen on, no port (::1 or [::1] for IPv6). Default: 127.0.0.1 (0.0.0.0 in the Docker image)
MCP_HTTP_BEARER_TOKEN=      # Clients must send Authorization: Bearer <token>. Required, 16+ characters, unless MCP_HTTP_HOST is loopback
MCP_HTTP_ALLOW_NO_TOKEN=    # "true" starts beyond loopback without a token, for a network no untrusted machine can reach
MCP_HTTP_ALLOWED_HOSTS=     # More Host header names to accept, comma-separated
MCP_HTTP_ALLOWED_ORIGINS=   # Browser origins to accept, comma-separated, e.g. http://localhost:6274

HTTP transport security

Every request can spend your MSP token, so the HTTP server follows the security rules of the MCP transport specification:

  • It listens on 127.0.0.1, this machine only. Set MCP_HTTP_HOST=0.0.0.0 (or one address) to accept other machines, and set MCP_HTTP_BEARER_TOKEN with it: on any address but loopback (127.0.0.0/8, ::1, localhost) the server refuses to start without a token, and exits with code 1 after one line on stderr that says why. MCP_HTTP_ALLOW_NO_TOKEN=true starts it without one, for a network no untrusted machine can reach, such as a compose network with no published port; the server then writes a warning to stderr at startup, whatever LOG_LEVEL says.

  • With MCP_HTTP_BEARER_TOKEN set, a request without Authorization: Bearer <token> gets 401. A token shorter than 16 characters stops startup wherever the server listens; openssl rand -hex 32 makes one 64 characters long.

  • A request whose Host header is not localhost, 127.0.0.1, [::1], the MCP_HTTP_HOST address (unless that is the wildcard 0.0.0.0 or ::, which adds nothing) or a name in MCP_HTTP_ALLOWED_HOSTS gets 403. This stops DNS rebinding, where a web page points its own domain name at your machine.

  • A request with an Origin header, which browsers send, gets 403 unless the origin is in MCP_HTTP_ALLOWED_ORIGINS, compared as URL origins (letter case and a default port make no difference). Non-browser MCP clients send no Origin and are not affected. An allowed origin gets CORS headers on every answer, refusals such as a 401 for a missing token included, so a web page on it can call the server and read why a request was refused.

  • The MCP endpoint is the configured MCP_HTTP_PATH exactly (default /mcp), that path with one trailing slash, and either with a query string. Any other path gets 404, CORS preflight requests included: with the default, /mcpx and /mcp/x do.

  • A request body may be at most 1 MB, and a client has 10 seconds to send the headers and 30 seconds for the whole request. An answer given without reading the body (401, 403, 404, 405, a malformed session ID, a CORS preflight) closes the connection, so a client cannot hold one open by sending the body slowly.

  • A session ends when the client sends DELETE, after MCP_SESSION_IDLE_TIMEOUT_MS without a request (default 30 minutes), or when the server restarts. A request with the ID of a session the server does not hold gets 404, and the transport specification has the client start a new session with a new initialize. A request with no session ID, other than initialize, gets 400.

When to use HTTP transport:

  • Running in Docker containers independently

  • Accessing from MCP orchestrators (e.g., open-webui)

  • Multiple clients need to connect to the same server instance

  • Network-based access to the MCP server

When to use stdio transport:

  • Claude Desktop integration (default)

  • Claude Code CLI integration

  • Single-process MCP client setups

  • Standard MCP client configurations

3. Build and Start

npm run build
npm run mcp:start

4. Connect Claude Desktop

Add this configuration to your Claude Desktop claude_desktop_config.json:

If installed via npm

{
  "mcpServers": {
    "firewalla": {
      "command": "npx",
      "args": ["firewalla-mcp-server"],
      "env": {
        "FIREWALLA_MSP_TOKEN": "your_msp_access_token_here",
        "FIREWALLA_MSP_ID": "yourdomain.firewalla.net",
        "FIREWALLA_BOX_ID": "your_box_gid_here"
      }
    }
  }
}

If using Docker

{
  "mcpServers": {
    "firewalla": {
      "command": "docker",
      "args": ["run", "-i", "--rm", 
        "-e", "FIREWALLA_MSP_TOKEN=your_token",
        "-e", "FIREWALLA_MSP_ID=yourdomain.firewalla.net",
        "-e", "FIREWALLA_BOX_ID=your_box_gid",
        "amittell/firewalla-mcp-server"
      ]
    }
  }
}

If installed from source

{
  "mcpServers": {
    "firewalla": {
      "command": "node",
      "args": ["/full/path/to/firewalla-mcp-server/dist/server.js"],
      "env": {
        "FIREWALLA_MSP_TOKEN": "your_msp_access_token_here",
        "FIREWALLA_MSP_ID": "yourdomain.firewalla.net",
        "FIREWALLA_BOX_ID": "your_box_gid_here"
      }
    }
  }
}

Config file locations:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

5. Next Steps

Usage Examples

Step-by-Step First Use

1. Verify Connection After completing the setup, verify the MCP server is working:

# Start the server
npm run mcp:start

# stdout carries only the MCP protocol; stderr shows JSON log lines, the
# last one with "message":"Firewalla MCP Server running on stdio transport"

2. Test with Claude Open Claude Desktop and try these starter queries:

Basic Health Check:

"Can you check my Firewalla status and show me a summary?"

This uses: the firewalla://summary resource and the get_simple_statistics tool

Security Overview:

"What security alerts do I have? Show me the 5 most recent ones."

This uses: get_active_alarms tool with limit parameter

Practical Workflows

Daily Security Review:

"Give me today's security report. Include:
1. Any new security alerts
2. Top 3 devices using bandwidth
3. Any devices that went offline
4. Status of critical firewall rules"

Investigating Suspicious Activity:

"I noticed unusual traffic. Can you:
1. Show me all security and abnormal upload alarms from the last 4 hours
2. Find any blocked connections to external IPs
3. Check which devices had the most network activity"

Network Troubleshooting:

"A device seems to have connectivity issues. Can you:
1. Check if device 192.168.1.100 is online
2. Show its recent network flows
3. See if any rules are blocking its traffic"

Bandwidth Investigation:

"Our internet is slow. Help me find the cause:
1. Show top 10 bandwidth users in the last hour
2. Look for any devices with unusual upload/download patterns
3. Check for any streaming or video traffic"

Advanced Search Examples

Find Specific Threats:

search for: security activity alarms from IP range 10.0.0.* in the last 24 hours

Uses: search_alarms with query: "type:1 AND device.ip:10.0.0. AND ts:>=<unix time 24 hours ago>"*

Analyze Rule Effectiveness:

"Show me firewall rules that blocked the most connections this week"

Uses: get_network_rules + search_flows for blocked traffic analysis

Device Behavior Analysis:

"Find all devices that were online yesterday but are offline now"

Uses: get_offline_devices, which lists offline devices most recently seen first, with last_seen; search_devices takes no time fields

Troubleshooting Common Issues

Connection Problems: If you get authentication errors:

  1. Verify your .env file has correct credentials

  2. Check your MSP token hasn't expired

  3. Confirm your Box ID is the full GID format

Empty Results: If queries return no data:

  1. Check your Firewalla is online and reporting

  2. Verify the time range isn't too narrow

  3. Try broader search terms first

Performance Issues: If responses are slow:

  1. Reduce the limit parameter in queries

  2. Use more specific time ranges

  3. Check your network connection to the MSP API

Available Tools (28 read-only, 11 opt-in write tools)

Core Tools

  • Security: Get alarms, analyze threats

  • Network: Monitor traffic flows, track bandwidth usage

  • Wi-Fi & Access Points: Discover AP7 access points, query channels/DFS state, Wi-Fi networks, and controller settings

  • Devices: Check device status, find offline devices

  • Rules: View firewall rules and their summary

  • Search: Advanced search across all data types

  • Analytics: Statistics, trends, and geographic analysis

  • Target Lists: View security target lists

Quick Reference

Security: get_active_alarms, get_specific_alarm
Network: get_flow_data, get_recent_flow_activity, get_bandwidth_usage
Wi-Fi: get_access_points, get_access_point_channels, get_wifi_networks, get_wifi_settings
Devices: get_device_status, get_offline_devices, get_boxes
Rules: get_network_rules, get_network_rules_summary
Target lists: get_target_lists, get_specific_target_list
Search: search_flows, search_alarms, search_rules, search_devices, search_target_lists
Analytics: get_simple_statistics, get_statistics_by_region, get_statistics_by_box, get_flow_insights, get_flow_trends, get_alarm_trends, get_rule_trends
Write (opt-in): create_rule, delete_rule, pause_rule, resume_rule, create_target_list, update_target_list, delete_target_list, rename_device, archive_alarm, mute_alarm, delete_alarm

Response format

Every read-only tool (readOnlyHint: true) takes an optional response_format. json, the default, returns the compact JSON response. markdown returns the same response as markdown for reading: a heading with the tool name and the number of records in each list, the fields as bullet lists, and each list of records as a table of at most 8 columns and DEFAULT_PAGE_SIZE rows (default 100). Fields with one value in every record are stated once above the table, fields that are not columns are named below it, and a list of one record is shown as a list of its fields. The last line says what the view leaves out (the response's meta block, rows past the cap, cells cut at 120 characters); response_format: json returns all of it. Values and field names are escaped, since device names, domains and alarm messages come from the network: a character that could start markdown or HTML ([, <, *, `, & before an entity, ://, @, ...) gets a backslash, so a renderer shows [a](https://...) or <img ...> as text. Addresses, MACs, gids and timestamps read as they are. Omitting response_format or sending null means json. An error is compact JSON in either format: only a successful read is rendered as markdown. The tools that change state do not take response_format.

get_device_status with {"limit": 2, "response_format": "markdown"}, shortened:

## get_device_status (devices: 2)

- **total_devices:** 2
- **online_devices:** 1
- **devices:** 2 records, under devices below

### devices (2 records)

Same in every record: gid = 00000000-0000-0000-0000-000000000000; ip_reserved = false.

| name | id | last_seen | ip | online |
| --- | --- | --- | --- | --- |
| nas | AA:BB:CC:DD:EE:01 | 2026-09-26T10:00:00.000Z | 192.168.1.10 | true |
| printer | AA:BB:CC:DD:EE:02 | 2026-09-25T18:30:00.000Z | 192.168.1.11 | false |

_This view leaves out the meta block (request_id req_1790000000000_abc123). Call again with `response_format: json` for the full JSON response._

Write tools (opt-in)

Every tool that changes something is off by default, so the server is read-only unless you turn them on. Set FIREWALLA_ENABLE_WRITE_TOOLS=true to register the 11 write tools: create_rule, delete_rule, pause_rule and resume_rule (rules), create_target_list, update_target_list and delete_target_list (target lists), rename_device (devices), and archive_alarm, mute_alarm and delete_alarm (alarms). Without it, calling one answers "Unknown tool" and sends nothing. MCP clients that honor tool annotations can ask before calling them; see Tool annotations.

Up to 1.5.0, pause_rule, resume_rule and the three target-list tools were always registered. If you use them, set FIREWALLA_ENABLE_WRITE_TOOLS=true; see Upgrading to 2.0.0.

The write tools need an MSP API token with write access. Firewalla said on 2026-09-08 that MSP 2.12 adds read-only API tokens, which cannot make changes. When a write gets HTTP 403, the error says the token may be read-only and that the write tools need a token with write access. A 403 can also mean the request named a box the token cannot access, and the error says that too; get_boxes lists the boxes the token can access.

IDs that go into a request path (id, rule_id, alarm_id, gid, device_id) are checked before anything is sent. One holding /, a backslash, ?, #, %, whitespace or a control character, or that is . or .., is refused as a validation error naming the argument; leading or trailing whitespace is refused too, not trimmed. Rule IDs such as <box gid>:<n>, MAC device IDs and ovpn: device IDs are accepted.

create_rule and rename_device act on one box: gid, else FIREWALLA_BOX_ID or FIREWALLA_DEFAULT_BOX_ID, else the account's only box. On a multi-box account with none of those, they refuse without writing anything, because the MSP API applies a rule with no gid to every box in the account, including boxes added later. delete_rule needs MSP 2.11.0 or later.

archive_alarm and mute_alarm need MSP 2.11.0 or later. archive_alarm takes an alarm out of the active alarms and does nothing else: future matching traffic can still raise new alarms. mute_alarm archives the alarm and has the box create a lasting silence exception. Its target_type says what is silenced: alarmType every future alarm of that alarm's type, whatever the destination; domain a domain and its subdomains; ip one address. Its scope_type says for which devices: all of them, or the one device, group, user or network named by scope_value. The mute is checked against the documented request model before anything is sent, and this server has no tool to remove the exception afterwards. delete_alarm deletes the alarm for good (measured on 2026-09-26: the alarm was gone afterwards); use archive_alarm to keep it among the archived alarms. Alarm IDs are per box, and the same ID can name different alarms on different boxes: all three tools use the alarm's gid, else FIREWALLA_BOX_ID, else check each box, and they refuse when several boxes have that alarm ID and none of them is FIREWALLA_DEFAULT_BOX_ID. All three read the alarm before writing, so a wrong ID fails without changing anything.

Tool annotations

Every tool carries MCP tool annotations: a title, readOnlyHint, and openWorldHint: true, since each one calls the Firewalla MSP API. All get_* and search_* tools are read-only. The tools that change state have readOnlyHint: false, and all of them need FIREWALLA_ENABLE_WRITE_TOOLS=true:

Tool

destructiveHint

idempotentHint

pause_rule, resume_rule

false

true

create_target_list

false

false

update_target_list, delete_target_list

true

true

create_rule

true

false

delete_rule

true

true

rename_device

false

true

archive_alarm

false

true

mute_alarm

true

false

delete_alarm

true

true

pause_rule and resume_rule check the rule's status first and change nothing when it is already paused or active. Clients that honor annotations can ask before calling any tool that is not read-only.

Development

Scripts

npm run dev          # Start development server with hot reload
npm run build        # Build TypeScript to JavaScript
npm run test         # Run all tests
npm run test:watch   # Run tests in watch mode
npm run lint         # Run ESLint
npm run lint:fix     # Fix ESLint issues

MCP Execution Methods

Why npx for MCP servers?

  • Version Management: Always uses the correct/latest version

  • Dependency Resolution: Handles package dependencies automatically

  • No global installation required: Works without global installation

  • MCP Standard: Follows Model Context Protocol conventions

  • Reliable: Works consistently across different environments

Alternative execution methods:

# Development (from source)
npm run mcp:start

# Production (npm installed)
npx firewalla-mcp-server

# Direct execution (from source after build)
node dist/server.js

Launch checks in CI

scripts/launch-smoke.mjs starts the server the way a user would and requires an answer to an MCP initialize over stdio. It uses dummy credentials, and initialize is answered locally, so nothing is sent to Firewalla.

  • The CI workflow's launch job packs the package and starts it through the global bin, npx and node dist/server.js: on Linux for a pull request, and on Linux, macOS and Windows for a push to main or a run by hand (Actions, CI, Run workflow). A change to docs/, top-level Markdown files or LICENSE alone runs no CI; no job reads them (format:check covers src/ only).

  • The Docker Build workflow runs on pull requests that touch the Dockerfile, the package files or the Docker workflows. It builds the image for amd64, arm64 and arm/v7, then runs the amd64 image with docker run -i --rm and requires serverInfo.version to equal package.json's version. Run the workflow by hand with the image input (for example amittell/firewalla-mcp-server:2.1.1) to pull and check a published image instead.

  • The Docker Build and Publish workflow, on a release tag, pushes the image and sets its Docker Hub description from DOCKERHUB.md, a short page that links here for the rest (Docker Hub keeps only the first 25,000 characters of a description). Run it by hand with description_only to update just the description.

To run the Docker check locally:

docker build -t firewalla-mcp-server:local .
node scripts/launch-smoke.mjs --docker firewalla-mcp-server:local

# A published image; the expected version comes from the tag
node scripts/launch-smoke.mjs --docker amittell/firewalla-mcp-server:2.1.1 --pull

Project Structure

firewalla-mcp-server/
├── src/
│   ├── server.ts           # Main MCP server
│   ├── firewalla/          # Firewalla API client
│   ├── tools/              # MCP tool implementations
│   ├── resources/          # MCP resource implementations
│   └── prompts/            # MCP prompt implementations
├── tests/                  # Test files
├── docs/
│   └── firewalla-api-reference.md  # API documentation
├── CLAUDE.md              # Comprehensive development guide
├── SPEC.md                # Technical specifications
└── README.md              # This file

Documentation

  • README.md (this file) - Setup and basic usage

  • USAGE.md - Simple usage guide with examples

  • TROUBLESHOOTING.md - Common issues and solutions

  • SPEC.md - What the server implements: protocol, transports, tools, errors and limits

  • docs/clients/ - Client-specific setup guides

  • CLAUDE.md - Development guide and commands

Security

See SECURITY.md to report a vulnerability. For the HTTP transport, see HTTP transport security.

  • MSP tokens are stored securely in environment variables

  • No credentials are logged or stored in code

  • Each server process paces its own requests to API_RATE_LIMIT per rolling 5 minutes (default 100, the limit measured on one MSP token; whether it is per token or per account was not measured). A request that cannot start within 20 s fails at once, saying when capacity returns. Every MCP client that starts the server over stdio starts its own process with its own count, so several of them on one token need API_RATE_LIMIT values that add up to 100 or less. See the rate-limiting guide

  • Input validation prevents injection attacks

  • Device names, domains and alarm messages are set by the devices and sites on your network, so the server treats them as untrusted: characters that do not display are shown as markers such as <U+E0041>, the prompts quote API data only inside a marked data block, and the initialize instructions tell the client to treat results as data. See Untrusted data

  • All API communications use HTTPS

Known Behaviors and Limitations

Category Classification

  • Flow Categories: Many network flows may show as empty category ("") in the Firewalla API response. This is expected behavior - Firewalla categorizes traffic when it recognizes the domain/service (e.g., "av" for audio/video, "social" for social media).

  • Target List Categories: Some target lists may show category as "unknown". This is normal for user-created or certain system lists.

  • Timeline: Category classification happens at the Firewalla device level and may take time to build up meaningful categorization data.

Data Characteristics

  • Response Sizes: The get_recent_flow_activity tool reads the 50 most recent flows in one request, so it covers minutes on a busy network. For larger datasets or historical analysis, use search_flows with time filters for more targeted queries.

  • Geographic Data: IP geolocation is enriched by the MCP server and includes country, city, and risk scores when available.

API Limitations

  • MSP only: The server sends every request to the MSP API at https://<FIREWALLA_MSP_ID> and never connects to the box on your LAN. It has no local mode, so it needs an MSP account with API access even for one box at home, and it sees only what the MSP API returns.

  • Alarm Deletion: In July 2025 the MSP API answered DELETE /v2/alarms/{gid}/{aid} with {"message": "success", "success": true} and kept the alarm, so delete_alarm was withdrawn. Measured again on 2026-09-26, the same request deleted the alarm (a GET of it then answered 404, and the archived alarm count fell by one), and delete_alarm is back as an opt-in write tool.

Troubleshooting

Quick Fixes

Server won't start:

# Clean and rebuild
npm run clean
npm run build

# If build fails, try:
npm install
npm run build

Authentication errors:

  • Check your MSP token is valid

  • Verify Box ID format (long UUID)

  • Confirm MSP domain is correct

No data returned:

  • Try broader queries: "last week" vs "last hour"

  • Check if Firewalla is online

  • Test with: "show me basic statistics"

Slow responses:

  • Add limits: "top 10 devices"

  • Use shorter time ranges

  • Restart the server

Debug Mode

Enable detailed logging:

DEBUG=firewalla:* npm run mcp:start

For more detailed troubleshooting, see TROUBLESHOOTING.md

Contributing

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Add tests for new functionality

  5. Run the test suite

  6. Submit a pull request

What's New

Release notes for every version, newest first, are in CHANGELOG.md. Changes that are merged but not yet released are listed there under Unreleased.

License

MIT License

Support

For issues and questions:


GitHub Repository

Repository: https://github.com/amittell/firewalla-mcp-server

Repository Stats

GitHub issues GitHub stars GitHub license

Available Tools

24 tools
get_active_alarmsA
Read-only

Retrieve active security alarms from the Firewalla MSP API (GET /v2/alarms): status:1 is added unless the query names a status (status:2 for archived alarms). Without a ts: qualifier the API covers the last 30 days. Returns up to limit alarms and a cursor for the next page, or groups with groupBy. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoResults per page (optional, default: 200, API maximum: 500)
queryNoSearch query for filtering alarms. Active alarms only (status:1) unless the query names a status. Use type:N where N is: 1=Security Activity, 2=Abnormal Upload, 3=Large Bandwidth Usage, 4=Monthly Data Plan, 5=New Device, 6=Device Back Online, 7=Device Offline, 8=Video Activity, 9=Gaming Activity, 10=Porn Activity, 11=VPN Activity, 12=VPN Connection Restored, 13=VPN Connection Error, 14=Open Port, 15=Internet Connectivity Update, 16=Large Upload. Examples: type:8 (video), type:10 (porn), region:US, device.ip:192.168.*. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes.
cursorNoPagination cursor from previous response
sortByNoSort alarms (default: ts:desc)
groupByNoFields to group by, comma-separated, e.g. "type", "status", "device" or "type,box". The API then returns groups instead of alarms: groups of { key, count }, where key holds the group fields (gid for box, device.id for device) and count the alarms in the group.
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint and openWorldHint already covering the safety profile, the description still adds real behavioral context: the implicit status:1 filter, the 30-day default window when no ts: qualifier is given, cursor-based pagination, and the FIREWALLA_BOX_ID-based scoping. It stops short of noting auth requirements or rate limits, so a 4 rather than 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four compact sentences, each carrying a distinct fact (scope, default filter, time window, pagination/grouping, box scoping) with no filler. The default-filter behavior is front-loaded, which is the most important thing to know before calling.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does the work of explaining returns: up to limit alarms plus a cursor, or { key, count } groups when groupBy is used. Combined with the default-window and scoping notes, an agent has nearly everything needed; only sibling disambiguation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema does not: the default 30-day time window for the query, the implicit status:1 injection, and how groupBy changes the return shape. These clarify parameter effects beyond the field-level documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (retrieve active security alarms) and even names the underlying endpoint GET /v2/alarms, so the operation is unambiguous. It does not explicitly differentiate itself from the close sibling search_alarms, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied through defaults: it explains that status:1 is injected unless the query names a status, and that status:2 yields archived alarms, which tells the agent how to reach both active and archived data. However, it never states when to pick this tool over search_alarms, get_specific_alarm, or get_alarm_trends, leaving the routing decision to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_bandwidth_usageA
Read-only

Top devices by upload plus download over the period, summed client-side from up to 10 times limit (1,000 at most) of the period's most recent flows (GET /v2/flows, 500 per request), so on a busy network the totals cover a sample. Scoped to box, else FIREWALLA_BOX_ID, else every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
boxNoOnly flows of this box (box gid). Defaults to FIREWALLA_BOX_ID; without either, every box.
limitNoNumber of top devices to return
periodYesTime period for bandwidth calculation
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint=true, openWorldHint=true), and the description adds meaningful behavior beyond that: the totals are summed client-side from a bounded, sampled set of recent flows, so on a busy network the result is approximate. Disclosing this accuracy caveat is real value. It doesn't cover auth or rate limits, but the sampling disclosure is the key trait.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is a single dense sentence, front-loading the purpose and then the derivation. It is appropriately sized and every clause carries information, though the run-on structure makes it harder to parse quickly than a couple of short sentences would.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only analytics tool with annotations covering safety and no output schema, the description supplies the essential behavioral context (sampling, scoping). It does not describe the shape of the returned device list, but the annotations and stated metric cover most of what an agent needs to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so baseline is 3, but the description adds genuine meaning: it explains that `limit` drives a fetch of up to 10×limit flows (1,000 max), revealing the cost/accuracy tradeoff behind that parameter. The `box` scoping logic is restated, which duplicates the schema slightly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific output (top devices by upload+download over a period), giving an agent a clear picture of the resource and metric. It does not explicitly differentiate itself from analytics siblings like get_simple_statistics or get_statistics_by_box, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explains the scoping logic (box, else FIREWALLA_BOX_ID, else every box), which is useful context for choosing inputs. However, it never states when to reach for this tool versus alternatives like search_flows or get_statistics_by_box, leaving usage selection implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_boxesA
Read-only

List the Firewalla boxes this MSP token can see (GET /v2/boxes, optionally one box group), with online status, model and version. Not limited by FIREWALLA_BOX_ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupNoGet boxes within a specific group (requires group ID)
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds the HTTP endpoint, the token-level scope, and the fields returned, but says nothing about pagination, result caps, or rate limits against a live MSP API.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence containing the verb, the scope, the endpoint, the optional filter, the returned fields, and the env-var carve-out. No filler, no repetition of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully names the payload fields (online status, model, version) and both parameters are fully documented in the schema. It is nearly complete, though pagination behavior and any error/empty-result semantics remain unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the group parameter's own description already states it needs a group ID, so the schema does the heavy lifting. The description's "optionally one box group" merely restates the group parameter rather than adding format, ID syntax, or interaction detail beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("List the Firewalla boxes"), names the backing endpoint (GET /v2/boxes), scopes it to the MSP token, and enumerates the returned fields (online status, model, version). No sibling tool in the list also enumerates boxes, so the agent can identify it immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clarifies the scoping rule that matters most here: "Not limited by FIREWALLA_BOX_ID" tells the agent this call spans all boxes the token sees, and "optionally one box group" describes the narrowing case. It offers no explicit when-not-to-use or named alternative, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_device_statusA
Read-only

Check online/offline status of devices on the Firewalla network. Reads the device list from GET /v2/devices (box, else FIREWALLA_BOX_ID, else every box; group limits it to a box group) and returns up to limit devices, sorted by name.

ParametersJSON Schema
NameRequiredDescriptionDefault
boxNoGet devices under a specific Firewalla box (requires box ID)
groupNoGet devices under a specific box group (requires group ID)
limitYesMaximum number of devices to return (required)
cursorNoThe next_cursor of a previous page, for the page after it; a cursor this tool did not issue is refused
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, setting a low bar, and the description adds real context: the resolution order (box, else FIREWALLA_BOX_ID, else every box), group scoping, the cap of 'up to limit' devices, and name-based sorting. This is genuinely useful behavioral detail beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with purpose, followed by the resolution and return behavior. Dense but no wasted clauses. The parenthetical resolution logic is compact rather than bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and strong annotation coverage, the description carries the load adequately: it states what is returned (online/offline status, up to limit devices, sorted by name) and how scope is resolved. It could say more about the status fields returned, but it's sufficient to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all five parameters are self-documented in the schema. The description reinforces box/group resolution semantics, but adds little syntax or format detail the schema doesn't already carry, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb+resource: 'Check online/offline status of devices on the Firewalla network.' It is clear what the tool returns, but it doesn't differentiate itself from close siblings like search_devices or get_offline_devices, which an agent must choose between.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use or when-not-to-use guidance. The description explains how box/group resolution works, but nothing tells the agent when this tool is preferable to search_devices or get_offline_devices, leaving selection to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_flow_dataA
Read-only

Query network traffic flows from the Firewalla MSP API (GET /v2/flows). Without a ts: qualifier the API covers the last 24 hours. Returns up to limit flows and a cursor for the next page, or groups with groupBy; coverage gives the oldest and newest ts returned and why paging stopped. A limit over 50 is streamed: pass nextContinuationToken back as cursor, or sessionId as streaming_session_id. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum results (optional, default: 200, at most 1000; read 500 per request, following the cursor)
queryNoSearch query for flows. Supports region:US for geographic filtering, protocol:tcp, status:blocked, domain:*, category:social, etc. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes.
cursorNoPagination cursor from a previous response: pagination.cursor, or the nextContinuationToken of a streamed one. Returns the page at that cursor, not streamed.
sortByNoSort flows (default: "ts:desc")
streamNotrue streams any limit, false returns a plain page with pagination.cursor (default: a limit over 50 is streamed)
groupByNoFields to group by, comma-separated, e.g. "category", "domain", "device", "box" or "device,category". The API then returns groups instead of flows: groups of { key, count, download, upload, total }, where key holds the group fields (gid for box; for device the device with its name, but only its id for "device,category") and the rest are the group's summed connection count and bytes.
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json
streaming_session_idNoThe sessionId of a streamed response: returns its next chunk, of the same size. A session expires 10 minutes after its latest chunk. Refused with stream: false; with a cursor, the cursor is read instead.

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds real behavioral context beyond that: the implicit 24-hour coverage window, the automatic switch to streaming above limit 50, the cursor/session continuation contract, and the FIREWALLA_BOX_ID scoping rule that changes the result set.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Five sentences, no filler, and the most decision-relevant facts (what it queries, default time window, streaming threshold) are front-loaded before result-shape and scoping details. Dense but readable; only slightly over-packed with paging mechanics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully describes the return shape itself: up to limit flows plus a cursor, groups when groupBy is set, and a coverage block reporting oldest/newest ts and why paging stopped. An agent can call it and interpret the response correctly; only sibling-level routing guidance is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description does add one genuinely external fact not in the schema — that omitting a ts: qualifier yields a 24-hour window — plus the cursor/stream interplay and box scoping, but most parameter meaning is already carried by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource with the backing endpoint: 'Query network traffic flows from the Firewalla MSP API (GET /v2/flows)'. An agent immediately knows it retrieves flow records. It does not, however, distinguish itself from close siblings like search_flows or get_recent_flow_activity, so the differentiation half of the criterion is unmet.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives operational guidance (default 24-hour window without a ts: qualifier, stream once limit exceeds 50, pass nextContinuationToken as cursor or sessionId as streaming_session_id) but this is mechanism rather than tool selection. There is no statement of when to prefer this tool over sibling flow tools such as search_flows or get_recent_flow_activity, and no explicit when-not condition.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_flow_insightsA
Read-only

Get category-based flow analysis for a period: top content categories and their domains, top devices by bandwidth, and optionally blocked traffic. Ideal for answering questions like "what porn sites were accessed" or "what social media was used". Computed client-side from the period's largest flows (GET /v2/flows by total bytes: up to 500 for categories, 200 for devices) and, with include_blocked, the 50 most frequent blocked flows, so on a busy network it covers the largest flows, not all of them. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
periodNoTime period for analysis (default: 24h)24h
categoriesNoFilter to specific content categories (optional)
include_blockedNoInclude blocked traffic analysis (default: false)
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/openWorld annotations by disclosing the sampling basis (GET /v2/flows by total bytes: up to 500 for categories, 200 for devices; 50 most frequent blocked flows with include_blocked), an explicit coverage caveat ('covers the largest flows, not all of them'), and the scope rule (FIREWALLA_BOX_ID when set, otherwise every box). These are exactly the behavioral traits an agent needs and cannot infer from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and use cases are front-loaded, and every sentence carries information. However, it is a single dense paragraph with deep nesting like '(GET /v2/flows by total bytes: up to 500 for categories, 200 for devices)', which slightly taxes readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering the safety profile and no output schema required, the description supplies the remaining essentials: use cases, computation method, sampling limits, and scoping. An agent has everything needed to call it correctly and to know when not to rely on it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3. The description still adds meaning beyond the schema by explaining what include_blocked actually pulls in (the 50 most frequent blocked flows) and how period-scoped aggregation is computed, exceeding the flat schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

It gives a specific verb and resource ('Get category-based flow analysis for a period') and enumerates the concrete payload: top categories/domains, top devices by bandwidth, and optional blocked traffic. The aggregate-analysis framing implicitly separates it from raw-flow siblings like search_flows and get_flow_data, but no sibling is named, so it stops short of explicit differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Strong when-to-use signal via the concrete example questions ('what porn sites were accessed', 'what social media was used'). The mention that it covers only the largest flows hints at when a raw-flow alternative is needed, but no alternative tool or explicit exclusion is named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_network_rulesA
Read-only

Retrieve firewall rules and conditions (GET /v2/rules). The API returns every matching rule; the tool returns the first limit. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitYesMaximum number of rules to return (required)
queryNoSearch conditions for filtering rules, e.g. action:block status:active. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. A word or quoted phrase with no field is free text, matched case-insensitively in the name, notes, action, target type or value, or scope of each rule (GET /v2/rules matches no free text, so the words are not sent and every rule the other terms match is read; free_text_coverage says how many were checked and whether that was all).
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds genuinely useful behavior beyond that: the API returns all matching rules while the tool truncates to "the first limit", and it discloses the box scoping default. This truncation warning is exactly the kind of context annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences with the core action front-loaded and the truncation/scoping caveats immediately after. Nothing is wasted, though it is terse enough that a routing hint toward siblings could have been added without bloat.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list tool with full schema coverage and no output schema, the description covers the essential call-time behavior: truncation semantics and scope resolution. The remaining gap is disambiguation from the sibling search/ summary tools, which the agent must resolve on its own.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3, but the description adds real meaning: it clarifies that limit truncates the full API result set to the first N, and explains the FIREWALLA_BOX_ID scoping that governs what the query filters over. These details go beyond the schema's bare "Maximum number of rules to return".

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Retrieve firewall rules and conditions") and names the underlying endpoint (GET /v2/rules). The purpose is unambiguous, but it never distinguishes this tool from the sibling search_rules or get_network_rules_summary, leaving the agent to infer the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains scoping (FIREWALLA_BOX_ID when set) and the effective limit, but gives no guidance on when to choose this over the closely related search_rules or get_network_rules_summary. There is no when-to-use or when-not-to-use statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_network_rules_summaryA
Read-only

Get overview counts of network rules by action, direction, status and target type (convenience wrapper): reads the rules from GET /v2/rules and counts them locally. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoHow many rules to read for the summary (default: 200)
rule_typeNoSummarize only rules with this action (default: 'all')all
active_onlyNoLeave paused rules out of the summary (default: true)
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safe-read profile (readOnlyHint=true, openWorldHint=true), and the description adds genuinely useful context beyond them: the counts are computed client-side from a rules read, which implies the summary is bounded by the limit parameter rather than a server-side aggregate. The scope rule for FIREWALLA_BOX_ID is also disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, output shape front-loaded, with the wrapper/implementation detail and scope following. No filler and nothing repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description's enumeration of the counting dimensions is what tells the agent what comes back, and it does that adequately. It could say more about what the counts omit (e.g. rules beyond the limit) for a summary tool, but nothing essential is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, including enum and default documentation for rule_type, active_only, limit, and response_format, so the schema carries the parameter burden. The description only adds the non-parameter FIREWALLA_BOX_ID scope note, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get overview counts of network rules') plus the grouping dimensions (action, direction, status, target type). The 'convenience wrapper' framing and the note that it reads GET /v2/rules and counts locally implicitly separates it from raw-rule siblings like get_network_rules and search_rules, though no sibling is named outright.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'convenience wrapper' phrasing and scope note ('Scoped to FIREWALLA_BOX_ID when set, otherwise every box') imply when the tool is appropriate, but it never says explicitly to prefer this over get_network_rules/search_rules when only counts are needed, nor when it is insufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_offline_devicesA
Read-only

List offline devices from the full device list (GET /v2/devices), most recently seen first by default, up to limit; total_offline_devices counts all of them. Scoped to box, else FIREWALLA_BOX_ID, else every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
boxNoFilter devices under a specific Firewalla box
limitNoMaximum number of offline devices to return
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json
sort_by_last_seenNoSort devices by last seen time (default: true)

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the bar is lower; the description still adds real context beyond them: default ordering ('most recently seen first by default'), the underlying endpoint, the scope fallback chain, and the meaning of the total_offline_devices counter. It stops short of describing pagination or error behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences, front-loaded with the primary action and scope fallback, with no filler. The semicolon-packed second clause is slightly dense but every element carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool with no output schema, the description covers scope resolution, default ordering, the limit bound, and the total-count semantics. Only return-shape and pagination details are left unstated, which is minor given the readOnly annotation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema itself documents box, limit, sort_by_last_seen and response_format, establishing a baseline of 3. The description reinforces the default sort direction and the scope precedence for 'box', but adds no format or syntax detail beyond what the schema already supplies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('List offline devices') and grounds it in the source endpoint 'GET /v2/devices', which is enough for an agent to grasp what it returns. It does not distinguish itself from the closely related sibling search_devices, so the agent has no in-description cue for why it would pick this over a general device search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the name and the scope-resolution note ('Scoped to box, else FIREWALLA_BOX_ID, else every box'), which is genuinely useful routing context. However there is no explicit when-to-use or when-not-to-use guidance versus search_devices or get_device_status.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_recent_flow_activityA
Read-only

Get a snapshot of the 50 most recent network flows (one GET /v2/flows request) with protocol, region and blocked/allowed counts; the minutes they span depend on how busy the network is. Use this for: "what's happening right now?", current security threats, immediate network issues. DO NOT use for: historical analysis, more than 50 flows, or daily/weekly patterns; use search_flows with time queries like "ts:>24h" for those. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, lowering the bar. The description adds real behavioral context beyond that: the 50-flow ceiling, that it maps to a single API request, that the covered time window is variable depending on network load, and that results are scoped to FIREWALLA_BOX_ID when set and otherwise span every box. It stops short of rate-limit or auth detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the snapshot definition, then use-for, then do-not-use, then scoping. Every sentence carries distinct information with no padding despite the density.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a snapshot tool with no output schema and annotations covering the safety profile, the definition covers purpose, scope, routing, and the returned fields (protocol, region, blocked/allowed counts) plus the variable time window. Return shape beyond those fields is left implicit, but nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter (response_format) with 100% schema description coverage, so the schema fully documents the enum and default behavior. The description adds nothing about the parameter, which is acceptable but not additive; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (get recent flow activity), the exact scope (50 most recent flows, one GET /v2/flows request), and the fields returned (protocol, region, blocked/allowed counts). An agent can distinguish it from get_flow_data, get_flow_insights, and search_flows without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names when to use it ("what's happening right now?", current threats, immediate issues) and when not to (historical analysis, more than 50 flows, daily/weekly patterns), routing the agent to the correct alternative with a concrete query example ("ts:>24h" on search_flows). Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_simple_statisticsA
Read-only

Get account-wide counts from GET /v2/stats/simple: online boxes, offline boxes, alarms and rules, optionally for one box group. Not limited by FIREWALLA_BOX_ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupNoGet statistics for specific box group
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds meaningful non-annotation context: this call is account-wide and explicitly not constrained by FIREWALLA_BOX_ID, which is a scoping behavior the agent could not infer from the structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with the API endpoint and returned metrics front-loaded, followed by the optional scoping and the exclusion note. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description appropriately names the counts returned (online/offline boxes, alarms, rules), and annotations cover the read-only nature. A brief note on pagination or result size would close the remaining gap, but the essentials for correct invocation are present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both 'group' and 'response_format' are already documented in the schema. The description only echoes the optional group filtering ('optionally for one box group') without adding format or syntax detail, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get account-wide counts from GET /v2/stats/simple') and enumerates exactly what is returned: online boxes, offline boxes, alarms and rules. The closing note 'Not limited by FIREWALLA_BOX_ID' distinguishes it from the box-scoped sibling get_statistics_by_box without needing to open either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description tells the agent when to use it (account-wide roll-up, optionally narrowed to one box group) and signals the contrast with box-ID-scoped statistics. It stops short of naming get_statistics_by_box explicitly as the alternative, so the routing is clear but not fully spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_specific_alarmA
Read-only

Get detailed information for one Firewalla alarm (GET /v2/alarms/{gid}/{aid}). Alarm IDs are per box: pass gid on a multi-box account, or each box is checked, one request per box, until one has the alarm.

ParametersJSON Schema
NameRequiredDescriptionDefault
gidNoBox the alarm belongs to (the gid field of get_active_alarms or search_alarms results). Defaults to FIREWALLA_BOX_ID; without either, each box on the account is checked.
alarm_idYesAlarm ID (required for API call): the aid from get_active_alarms or search_alarms, as a number or a string
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so safety is covered; the description adds real value beyond that by disclosing the fan-out behavior (one request per box until a match is found) on multi-box accounts. It omits rate-limit or auth details, which keeps it short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences, front-loaded with the purpose and endpoint before the multi-box caveat. No filler and nothing repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema the description should ideally say what the alarm payload contains, but for a single-record fetch whose annotations already flag it read-only and open-world, the multi-box retrieval mechanics and ID sourcing cover what an agent needs to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema alone does not convey: that alarm IDs are scoped per box and that gid should come from get_active_alarms or search_alarms results. That is genuine added semantic context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Get detailed information for one Firewalla alarm') and pins it to the exact endpoint GET /v2/alarms/{gid}/{aid}, making it clearly distinct from the list-oriented siblings get_active_alarms and search_alarms.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the operational context: pass gid on multi-box accounts, and that alarm IDs are per box so each box may be checked until one has the alarm. It does not explicitly name search_alarms as the alternative for finding an aid, but the required-parameter flow implies it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_specific_target_listA
Read-only

Retrieve one target list by ID, including its targets (GET /v2/target-lists/{id}).

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesTarget list ID (required)
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds useful context beyond the annotations by noting the response embeds the list's targets, but it says nothing about pagination, target-volume limits, or error behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, front-loaded with the verb and resource, with the endpoint appended as compact reference. Nothing is wasted and nothing needs reordering.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter read tool with full schema coverage and readOnly annotations, the description covers purpose and return scope. Without an output schema it could have sketched the returned shape slightly more, but the essential invocation information is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters (id, response_format) are fully documented in the schema, including the markdown/json enum semantics. The description adds no parameter detail beyond the required id, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Retrieve one target list by ID") and adds that the response includes the list's targets, which distinguishes it from the sibling get_target_lists. It never names a sibling explicitly, so an agent must infer the single-vs-collection split from the description alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase "by ID" implies the precondition (you already have a target list ID), which is adequate context, but there is no explicit when-to-use guidance and no mention of get_target_lists or search_target_lists as alternatives for discovering lists.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_statistics_by_boxA
Read-only

Top boxes by blocked flows (the default) or by Security Activity alarms, from GET /v2/stats/{type}, with each box's details from GET /v2/boxes; each box's value is the statistic, over about the last 30 days when measured. Not limited by FIREWALLA_BOX_ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
typeNoStatistics type to retrievetopBoxesByBlockedFlows
groupNoGet statistics for specific box group
limitNoMaximum number of results (optional, default: 5)
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, and the description adds meaningful context beyond them: the backing endpoints, that values are measured over roughly the last 30 days, and that scope is not constrained by FIREWALLA_BOX_ID. It stops short of describing ordering or output shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence that leads with the core purpose before the endpoint/scope details. It is slightly dense with semicolons and endpoint strings, but every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering the safety profile and a fully described schema, the remaining need is scope and data-window context, which the description supplies. No output schema exists, so return-value explanation is not required; the only minor gap is ordering/pagination.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning by translating the enum values into plain language (blocked flows vs Security Activity alarms) and confirming the default. It says nothing extra about 'group' or 'limit' beyond what the schema already documents.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource and dimension: top boxes ranked by blocked flows (default) or Security Activity alarms, distinguishing it from region-based stats. Sibling differentiation is implicit via the 'by_box' framing rather than explicitly naming alternatives, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Implies applicability by stating the default type and that results are 'not limited by FIREWALLA_BOX_ID', which hints this is an aggregate view. However, it names no alternative tools (e.g., get_statistics_by_region, get_simple_statistics) and gives no when-to-use/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_statistics_by_regionA
Read-only

Top regions by blocked flows, from GET /v2/stats/topRegionsByBlockedFlows, optionally for one box group; the API returned no more than 5 regions. Not limited by FIREWALLA_BOX_ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupNoGet statistics for specific box group
limitNoMaximum number of regions (optional, default: 5; the API returned no more than 5 when a larger limit was tried)
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the read-only/open-world profile, and the description adds genuine behavioral facts beyond them: the source endpoint, the hard cap of no more than 5 regions, and the FIREWALLA_BOX_ID scope exemption. It omits auth or rate-limit details, so it stops short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded: the purpose leads, then the endpoint, the optional-group note, and the cap. Every clause carries information, though the phrasing is slightly clipped and packed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only stats tool with no output schema, the description covers purpose, scope, and the retrieval cap, which is enough for an agent to call it correctly. Return-value shape is unspecified but not required given the read-only nature.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all three parameters are already documented with constraints and defaults. The description only lightly reinforces the group and limit semantics, adding little beyond the schema – the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Top regions by blocked flows') and names the exact endpoint, so the purpose is unambiguous. It implicitly separates itself from the region-of-a-box sibling but never names an alternative tool, keeping it just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Signals that the group parameter is optional ('optionally for one box group') and clarifies scope with 'Not limited by FIREWALLA_BOX_ID'. However, it never states when to reach for this versus get_statistics_by_box or the other statistics siblings, leaving usage to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_target_listsA
Read-only

Retrieve target lists (GET /v2/target-lists). Without owner the API returns the MSP's global lists and the Firewalla-managed lists; owner selects global lists, a box's lists, or several. entry_count is the number of entries in each list; the API does not return the entries of Firewalla-managed lists, so their targets is null. Returns up to limit lists.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitYesMaximum number of target lists to return (required)
ownerNoOnly lists with this owner: "global" (MSP lists), a box gid (that box's lists), or several comma-separated, e.g. "global,<box_gid>". Default: global and Firewalla-managed lists.
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the read-only and open-world profile, and the description goes further by disclosing that Firewalla-managed lists omit entries so their targets is null, and that entry_count reflects list size. The limit-based truncation behavior is also mentioned. It stops short of describing pagination beyond limit.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, front-loaded with the endpoint and the core default-versus-owner behavior, with no filler. Slightly dense in the middle but every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description usefully explains what a returned list contains (entry_count, and why targets can be null) and the result cap. For a 3-parameter read tool with full schema coverage and safety annotations, this is close to complete; only deeper pagination/response details are missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description reinforces owner's selection semantics and adds the return-shape consequence (null targets for Firewalla-managed lists), but adds little syntax beyond what the schema already documents.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource plus the underlying endpoint (GET /v2/target-lists), and 'Returns up to limit lists' signals this is a collection-listing tool. It does not explicitly distinguish itself from the sibling list-related tools (get_specific_target_list, search_target_lists), so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description spends its words on what happens without owner versus with owner, which is usage-relevant context, but it never states when to pick this tool over get_specific_target_list or search_target_lists. Usage is implied rather than directed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_alarmsA
Read-only

Search alarms using full-text or field filters. Alarm types: 1=Security Activity, 2=Abnormal Upload, 3=Large Bandwidth Usage, 4=Monthly Data Plan, 5=New Device, 6=Device Back Online, 7=Device Offline, 8=Video Activity, 9=Gaming Activity, 10=Porn Activity, 11=VPN Activity, 12=VPN Connection Restored, 13=VPN Connection Error, 14=Open Port, 15=Internet Connectivity Update, 16=Large Upload. Reads GET /v2/alarms, 500 per request, following the cursor up to limit. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum results (optional, default: 200, at most 1000; read 500 per request, following the cursor)
queryYesSearch query using Firewalla syntax. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. Supported fields: type:1-16 (see alarm types above), status:1/2 (active/archived), device.ip:192.168.*, region:US (country code), box.id:box_gid, device.name:*. Examples: "type:8 AND region:US" (video from US), "type:10 AND status:1" (active porn alerts), "device.ip:192.168.* AND status:1" (active alarms from the LAN), "porn" (free text: a term without a qualifier searches alarm text)
cursorNoPagination cursor from previous response
sortByNoSort alarms (default: ts:desc)
groupByNoFields to group by, comma-separated, e.g. "type", "status", "device" or "type,box". The API then returns groups instead of alarms: groups of { key, count }, where key holds the group fields (gid for box, device.id for device) and count the alarms in the group.
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json
geographic_filtersNoCountries of the remote end to limit the alarms to, sent as one remote.region: comma list ANDed with the query ({"countries": ["US", "CN"]} is sent as remote.region:US,CN, either country). remote.region, an ISO 3166 country code, is the one geographic alarm qualifier the MSP API documents; other geographic filters and names not listed here are refused as a validation error before any request.

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. Beyond that, the description adds real behavior: it reads GET /v2/alarms, fetches 500 per request while following the cursor up to limit, and scopes to FIREWALLA_BOX_ID when set otherwise all boxes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the purpose, then the required alarm-type legend that the schema explicitly points back to ('see alarm types above'), then the transport/behavior notes. The type list is long but load-bearing, so little is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully covers the API endpoint, pagination cadence, and box scoping, which is most of what an agent needs. It is thin on return-shape expectations beyond what groupBy/response_format already state in the schema, and silent on rate limits/auth.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all seven parameters are already documented in the schema (including the query syntax and geographic_filters). The description largely restates the alarm-type enumeration already referenced in the schema's query description, adding little syntax or format meaning beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (search alarms) plus the mode (full-text or field filters), which is clear enough for an agent to grasp intent. However, it never distinguishes itself from close siblings like get_active_alarms or get_specific_alarm, leaving the agent to infer which alarm-retrieval tool to pick.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when this tool is appropriate (searching with free-text/field filters) and gives some scoping context (FIREWALLA_BOX_ID set vs every box), but names no alternatives and states no when-not conditions against get_active_alarms, get_specific_alarm, or the trend tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_devicesA
Read-only

Search devices by name, IP, MAC or status (convenience wrapper with client-side filtering): reads the device list from GET /v2/devices (box, else FIREWALLA_BOX_ID, else every box) and filters it locally.

ParametersJSON Schema
NameRequiredDescriptionDefault
boxNoFilter devices under a specific Firewalla box
limitNoMaximum number of devices to return
queryYesSearch query using Firewalla syntax. A word or quoted phrase with no field is free text, found case-insensitively in the name, IP, MAC or id, vendor, or network or group name. ip: also takes an IPv4 CIDR block, and a comma list matches any of its values (name:tv,nas). Supported fields: mac:AA:BB:CC:DD:EE:FF, ip:192.168.1.*, name:*iPhone*, online:true/false, mac_vendor:Apple, gid:box_gid, network.name:*, group.name:*. Examples: "online:false AND mac_vendor:Apple", "ip:192.168.1.* AND name:*laptop*", "mac:AA:* OR name:*phone*", "ip:192.168.1.0/24", "nas", "kids AND online:true"
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds genuinely useful behavior beyond that: filtering happens client-side after reading the full device list, and the box scope resolves as box param -> FIREWALLA_BOX_ID -> every box. It does not disclose result shape or how 'limit' interacts with local filtering.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence names the resource and searchable fields first, with implementation detail confined to a trailing parenthetical. Dense but no wasted sentences; only the nested 'else ... else ...' clause requires a second read.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description still covers the data source, the filtering model, and box scoping, which is enough for correct invocation. Return format and limit semantics are left unaddressed, but these are secondary for a read-only search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning for the 'box' parameter by documenting the three-step resolution fallback (explicit box, environment variable, all boxes). The query syntax is fully specified in the schema, so no further description value is needed there.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Search devices') and enumerates the searchable dimensions (name, IP, MAC, status), plus discloses the implementation (client-side wrapper over GET /v2/devices). It does not name sibling alternatives such as get_offline_devices or get_device_status, so an agent must infer differentiation itself.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The mechanism note ('convenience wrapper with client-side filtering') implies this is the right tool for ad-hoc filtering of the device list, and the box fallback chain tells the agent what happens when 'box' is omitted. There is no explicit when-not-to-use guidance or named alternative for the device domain.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_flowsA
Read-only

Search network flows with advanced query filters. Use this for: historical analysis, specific time ranges, complex filtering, or when you need more than 50 flows. Supports pagination, time-based queries (e.g., "ts:>1h" for the last hour, or Unix seconds such as "ts:1735689600-1735693200"), and all flow fields including geographic filtering. For quick "what's happening now" snapshots, use get_recent_flow_activity instead. Reads GET /v2/flows, 500 per request, following the cursor up to limit; coverage gives the oldest and newest ts returned and why paging stopped. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum results (optional, default: 200, at most 1000; read 500 per request, following the cursor)
queryYesSearch query using Firewalla syntax. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. Supported fields: protocol:tcp/udp, direction:inbound/outbound/local, status:blocked/ok, total:>1MB (download + upload in B/KB/MB/GB/TB), download:>10MB, upload:>10MB, domain:example.com (the root domain: *.example.com matches nothing and *word* matches any domain containing word), region:US (country code), category:social/games/porn/etc, box.id:box_gid, device.ip:192.168.*, source.ip:*, destination.ip:*, ts:>1h. Examples: "region:US AND protocol:tcp", "status:blocked AND region:CN", "category:social OR category:games"
cursorNoPagination cursor from previous response
sortByNoSort flows (default: "ts:desc")
groupByNoFields to group by, comma-separated, e.g. "category", "domain", "device", "box" or "device,category". The API then returns groups instead of flows: groups of { key, count, download, upload, total }, where key holds the group fields (gid for box; for device the device with its name, but only its id for "device,category") and the rest are the group's summed connection count and bytes.
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json
geographic_filtersNoCountries to limit the flows to, sent as one region: comma list ANDed with the query ({"countries": ["US", "CN"]} is sent as region:US,CN, either country). region, an ISO 3166 country code, is the one geographic flow qualifier the MSP API documents; other geographic filters (continents, cities, ASNs, hosting providers, risk scores) and names not listed here are refused as a validation error before any request.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover safety (readOnlyHint, openWorldHint), and the description adds real behavioral context beyond them: it discloses the backing endpoint (GET /v2/flows), the 500-per-request page size and cursor-following up to limit, the contents of the 'coverage' field (oldest/newest ts and why paging stopped), and the FIREWALLA_BOX_ID scoping rule.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose sentence, then usage, then mechanics. Every sentence carries information, though the description is dense and the query-example list makes it longer than strictly necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description explains what comes back (flows vs. grouped {key,count,download,upload,total}, the coverage field, markdown table limits), which is exactly the burden the description must carry for a 7-parameter, open-world search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description adds meaning beyond the schema by tying the query, pagination and coverage semantics together and showing time syntax ('ts:>1h', Unix-seconds ranges). It slightly restates limit/query details that already live in the schema, keeping it just short of 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Search network flows') plus the scope (advanced query filters) and names the sibling it differs from (get_recent_flow_activity for 'what's happening now' snapshots). An agent can distinguish it from get_flow_data/get_flow_trends by the stated 'historical analysis, complex filtering, >50 flows' framing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly enumerates when to use it (historical analysis, specific time ranges, complex filtering, more than 50 flows) and names the alternative with the condition that selects it: get_recent_flow_activity for quick current snapshots. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_rulesA
Read-only

Search firewall rules by target, action or status; the MSP API applies the query (GET /v2/rules). Supports all rule fields. Scoped to FIREWALLA_BOX_ID when set, otherwise every box.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of rules to return
queryYesSearch query using Firewalla syntax. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. A word or quoted phrase with no field is free text, matched case-insensitively in the name, notes, action, target type or value, or scope of each rule (GET /v2/rules matches no free text, so the words are not sent and every rule the other terms match is read; free_text_coverage says how many were checked and whether that was all). Supported fields: action:allow/block/timelimit, target.type:domain/ip/device, target.value:*.facebook.com, status:active/paused, direction:bidirection/inbound/outbound, protocol:tcp/udp, box.id:box_gid, scope.type:device/network, notes:"description text". Examples: "action:block AND target.value:*.social.com", "status:paused", "target.type:domain AND action:block"
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnlyHint=true) and open-world scope, so little is owed here, yet the description still adds real behavioral context: that filtering is applied server-side by the MSP API via GET /v2/rules, and that results are confined to one box when FIREWALLA_BOX_ID is set and across all boxes otherwise. It stops short of describing pagination/limit interaction or result shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short clauses with zero filler; the core verb+resource and the endpoint are front-loaded, and the scope caveat follows. It is efficiently sized for a three-parameter tool, with only minor denseness from packing endpoint and scope into a semicolon clause.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description still gives the agent enough to call correctly: the operation is a read (annotations), the backend call and scope are named, and the full query grammar and response_format behavior live in the schema. Nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already carries full parameter meaning (including the detailed Firewalla query syntax and response_format semantics). The description's only added claim, 'Supports all rule fields', is too vague to add meaning beyond the enum of fields already enumerated in the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource ('Search firewall rules') plus the searchable facets (target, action, status) and the backing endpoint (GET /v2/rules), so the purpose is unambiguous. It does not, however, distinguish itself from sibling list/read tools such as get_network_rules or get_network_rules_summary, which is why it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to use this tool versus the sibling rules tools (get_network_rules, get_network_rules_summary, get_rule_trends). The only conditional is a scoping rule ('Scoped to FIREWALLA_BOX_ID when set, otherwise every box'), which describes result scope rather than tool selection, so an agent gets essentially no routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_target_listsA
Read-only

Search target lists (convenience wrapper with client-side filtering): reads GET /v2/target-lists, sending owner if given (without it, the global and Firewalla-managed lists), and applies the query locally.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of target lists to return
ownerNoOnly lists with this owner, sent to the API: "global" (MSP lists), a box gid (that box's lists), or several comma-separated, e.g. "global,<box_gid>". Default: global and Firewalla-managed lists.
queryYesSearch query for target lists. A word or quoted phrase with no field is free text, found case-insensitively in the name, the notes or an entry; a comma list matches any of its values (category:social,games). Supported fields: name:*Social*, owner:global/box_gid, category:social/games/ad/porn/etc, targets:*.facebook.com, notes:"description text", target_count:>100 (entries: n, >n, >=n, <n, <=n or a range 10-50), last_updated:>2026-09-01 (a date or Unix seconds, with the same comparisons). Examples: "category:social", "category:social,games", "owner:global AND name:*Block*", "targets:*.gaming.com", "target_count:>1000", "last_updated:<2026-01-01", "facebook"
response_formatNoResponse format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.json

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is covered. The description adds genuine behavioral context beyond that: it names the underlying call (GET /v2/target-lists), discloses that owner is sent upstream while the query is applied locally, and states the default scope when owner is omitted. It does not explain how client-side filtering interacts with limit or what the response looks like, so it falls short of 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence that front-loads the purpose and then the mechanism, with no filler. It is compact, though the parenthetical mechanics could have been split for readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the mechanism, the owner scope, and the filtering model, which is adequate for invoking the tool. However, with no output schema, it says nothing about the shape of returned target lists or how limit behaves relative to client-side filtering, leaving a real gap for a search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all four parameters in detail, including query syntax and owner values. The description's only added parameter meaning is the owner default ('without it, the global and Firewalla-managed lists'), which largely restates the schema. Baseline 3 applies when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Search target lists') and immediately characterizes the tool as a 'convenience wrapper with client-side filtering', which distinguishes it from a plain list fetch. It does not explicitly name the sibling it contrasts with (get_target_lists / get_specific_target_list), so an agent must infer the boundary from the 'convenience wrapper' phrasing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: the client-side filtering note suggests this is the tool for query-driven lookups, and the owner behavior is described. But there is no explicit when-to-use/when-not statement, no routing to get_target_lists or get_specific_target_list, and no mention of when the wrapper's local filtering is preferable to a direct API call.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 29 tool updatesv2.0.1
    • Removedcreate_target_list
    • Removeddelete_target_list
    • Changedget_active_alarms2 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"Search query for filtering alarms. Active alarms only (status:1) unless the query names a status. Use type:N where N is: 1=Security Activity, 2=Abnormal Upload, 3=Large Bandwidth Usage, 4=Monthly Data Plan, 5=New Device, 6=Device Back Online, 7=Device Offline, 8=Video Activity, 9=Gaming Activity, 10=Porn Activity, 11=VPN Activity, 12=VPN Connection Restored, 13=VPN Connection Error, 14=Open Port, 15=Internet Connectivity Update, 16=Large Upload. Examples: type:8 (video), type:10 (porn), region:US, device.ip:192.168.*"New value: +"Search query for filtering alarms. Active alarms only (status:1) unless the query names a status. Use type:N where N is: 1=Security Activity, 2=Abnormal Upload, 3=Large Bandwidth Usage, 4=Monthly Data Plan, 5=New Device, 6=Device Back Online, 7=Device Offline, 8=Video Activity, 9=Gaming Activity, 10=Porn Activity, 11=VPN Activity, 12=VPN Connection Restored, 13=VPN Connection Error, 14=Open Port, 15=Internet Connectivity Update, 16=Large Upload. Examples: type:8 (video), type:10 (porn), region:US, device.ip:192.168.*. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes."
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_alarm_trends3 fields changed
      • addedInput schema / properties / box
        Added value: +{
        +  "description": "Only alarms of this box (box gid), counted per day: 1 request plus 1 per day (~31 for 30d). Defaults to FIREWALLA_BOX_ID unless group is given; without either, every box. Not with group",
        +  "type": "string"
        +}
      • changedInput schema / properties / group / description
        Previous value: -"Get trends for a specific box group"New value: +"Get trends for a specific box group. Takes precedence over FIREWALLA_BOX_ID; not with box"
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_bandwidth_usage1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_boxes1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_device_status2 fields changed
      • addedInput schema / properties / cursor
        Added value: +{
        +  "description": "The next_cursor of a previous page, for the page after it; a cursor this tool did not issue is refused",
        +  "type": "string"
        +}
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_flow_data7 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Pagination cursor from previous response"New value: +"Pagination cursor from a previous response: pagination.cursor, or the nextContinuationToken of a streamed one. Returns the page at that cursor, not streamed."
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum results (optional, default: 200, API maximum: 500)"New value: +"Maximum results (optional, default: 200, at most 1000; read 500 per request, following the cursor)"
      • changedInput schema / properties / limit / maximum
        Previous value: -500New value: +1000
      • changedInput schema / properties / query / description
        Previous value: -"Search query for flows. Supports region:US for geographic filtering, protocol:tcp, status:blocked, domain:*, category:social, etc."New value: +"Search query for flows. Supports region:US for geographic filtering, protocol:tcp, status:blocked, domain:*, category:social, etc. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes."
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / stream
        Added value: +{
        +  "description": "true streams any limit, false returns a plain page with pagination.cursor (default: a limit over 50 is streamed)",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / streaming_session_id
        Added value: +{
        +  "description": "The sessionId of a streamed response: returns its next chunk, of the same size. A session expires 10 minutes after its latest chunk. Refused with stream: false; with a cursor, the cursor is read instead.",
        +  "type": "string"
        +}
    • Changedget_flow_insights1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Addedget_flow_trends
    • Changedget_network_rules2 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"Search conditions for filtering rules"New value: +"Search conditions for filtering rules, e.g. action:block status:active. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. A word or quoted phrase with no field is free text, matched case-insensitively in the name, notes, action, target type or value, or scope of each rule (GET /v2/rules matches no free text, so the words are not sent and every rule the other terms match is read; free_text_coverage says how many were checked and whether that was all)."
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_network_rules_summary6 fields changed
      • changedInput schema / properties / active_only / description
        Previous value: -"Only include active rules in summary (default: true)"New value: +"Leave paused rules out of the summary (default: true)"
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 200,
        +  "description": "How many rules to read for the summary (default: 200)",
        +  "maximum": 2000,
        +  "minimum": 1,
        +  "type": "number"
        +}
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / rule_type / default
        Added value: +"all"
      • changedInput schema / properties / rule_type / description
        Previous value: -"Filter by rule type"New value: +"Summarize only rules with this action (default: 'all')"
      • addedInput schema / properties / rule_type / enum
        Added value: +[
        +  "block",
        +  "allow",
        +  "timelimit",
        +  "all"
        +]
    • Changedget_offline_devices2 fields changed
      • changedInput schema / properties / limit / maximum
        Previous value: -500New value: +1000
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_recent_flow_activity1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_rule_trends3 fields changed
      • addedInput schema / properties / box
        Added value: +{
        +  "description": "Only rules of this box (box gid), counted from GET /v2/rules: 2 requests. Defaults to FIREWALLA_BOX_ID unless group is given; without either, every box. Not with group",
        +  "type": "string"
        +}
      • changedInput schema / properties / group / description
        Previous value: -"Get trends for a specific box group"New value: +"Get trends for a specific box group. Takes precedence over FIREWALLA_BOX_ID; not with box"
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_simple_statistics1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_specific_alarm1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_specific_target_list1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_statistics_by_box1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_statistics_by_region1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedget_target_lists1 field changed
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Removedpause_rule
    • Removedresume_rule
    • Changedsearch_alarms5 fields changed
      • addedInput schema / properties / geographic_filters
        Added value: +{
        +  "description": "Countries of the remote end to limit the alarms to, sent as one remote.region: comma list ANDed with the query ({\"countries\": [\"US\", \"CN\"]} is sent as remote.region:US,CN, either country). remote.region, an ISO 3166 country code, is the one geographic alarm qualifier the MSP API documents; other geographic filters and names not listed here are refused as a validation error before any request.",
        +  "properties": {
        +    "countries": {
        +      "description": "ISO 3166-1 alpha-2 country codes, any of which may match, e.g. [\"US\", \"CN\"]; a code that is not assigned is refused (put remote.region:<code> in the query to send one anyway). An empty list asks for nothing.",
        +      "items": {
        +        "pattern": "^[A-Za-z]{2}$",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "regions": {
        +      "description": "Country codes too, merged with countries: the API's remote.region is a country code",
        +      "items": {
        +        "pattern": "^[A-Za-z]{2}$",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum results (optional, default: 200, API maximum: 500)"New value: +"Maximum results (optional, default: 200, at most 1000; read 500 per request, following the cursor)"
      • changedInput schema / properties / limit / maximum
        Previous value: -500New value: +1000
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: type:1-16 (see alarm types above), status:1/2 (active/archived), device.ip:192.168.*, region:US (country code), box.id:box_gid, device.name:*. Examples: \"type:8 AND region:US\" (video from US), \"type:10 AND status:1\" (active porn alerts), \"device.ip:192.168.* AND status:1\" (active alarms from the LAN), \"porn\" (free text: a term without a qualifier searches alarm text)"New value: +"Search query using Firewalla syntax. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. Supported fields: type:1-16 (see alarm types above), status:1/2 (active/archived), device.ip:192.168.*, region:US (country code), box.id:box_gid, device.name:*. Examples: \"type:8 AND region:US\" (video from US), \"type:10 AND status:1\" (active porn alerts), \"device.ip:192.168.* AND status:1\" (active alarms from the LAN), \"porn\" (free text: a term without a qualifier searches alarm text)"
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedsearch_devices3 fields changed
      • changedInput schema / properties / limit / maximum
        Previous value: -500New value: +1000
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: mac:AA:BB:CC:DD:EE:FF, ip:192.168.1.*, name:*iPhone*, online:true/false, mac_vendor:Apple, gid:box_gid, network.name:*, group.name:*. Examples: \"online:false AND mac_vendor:Apple\", \"ip:192.168.1.* AND name:*laptop*\", \"mac:AA:* OR name:*phone*\""New value: +"Search query using Firewalla syntax. A word or quoted phrase with no field is free text, found case-insensitively in the name, IP, MAC or id, vendor, or network or group name. ip: also takes an IPv4 CIDR block, and a comma list matches any of its values (name:tv,nas). Supported fields: mac:AA:BB:CC:DD:EE:FF, ip:192.168.1.*, name:*iPhone*, online:true/false, mac_vendor:Apple, gid:box_gid, network.name:*, group.name:*. Examples: \"online:false AND mac_vendor:Apple\", \"ip:192.168.1.* AND name:*laptop*\", \"mac:AA:* OR name:*phone*\", \"ip:192.168.1.0/24\", \"nas\", \"kids AND online:true\""
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedsearch_flows5 fields changed
      • addedInput schema / properties / geographic_filters
        Added value: +{
        +  "description": "Countries to limit the flows to, sent as one region: comma list ANDed with the query ({\"countries\": [\"US\", \"CN\"]} is sent as region:US,CN, either country). region, an ISO 3166 country code, is the one geographic flow qualifier the MSP API documents; other geographic filters (continents, cities, ASNs, hosting providers, risk scores) and names not listed here are refused as a validation error before any request.",
        +  "properties": {
        +    "countries": {
        +      "description": "ISO 3166-1 alpha-2 country codes, any of which may match, e.g. [\"US\", \"CN\"]; a code that is not assigned is refused (put region:<code> in the query to send one anyway). An empty list asks for nothing.",
        +      "items": {
        +        "pattern": "^[A-Za-z]{2}$",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "exclude_cloud": {
        +      "description": "Only false, which asks for nothing: the MSP API has no qualifier for cloud providers, so true is refused",
        +      "enum": [
        +        false
        +      ],
        +      "type": "boolean"
        +    },
        +    "exclude_known_providers": {
        +      "description": "Only false, which asks for nothing: the MSP API has no qualifier for hosting providers, so true is refused",
        +      "enum": [
        +        false
        +      ],
        +      "type": "boolean"
        +    },
        +    "exclude_vpn": {
        +      "description": "Only false, which asks for nothing: the MSP API has no qualifier for VPN traffic, so true is refused",
        +      "enum": [
        +        false
        +      ],
        +      "type": "boolean"
        +    },
        +    "high_risk_countries": {
        +      "description": "Only false, which asks for nothing: the MSP API has no qualifier for country risk, so true is refused",
        +      "enum": [
        +        false
        +      ],
        +      "type": "boolean"
        +    },
        +    "regions": {
        +      "description": "Country codes too, merged with countries: the API's region is a country code",
        +      "items": {
        +        "pattern": "^[A-Za-z]{2}$",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "threat_analysis": {
        +      "description": "Only false, which asks for nothing: the flow search has no threat analysis, so true is refused",
        +      "enum": [
        +        false
        +      ],
        +      "type": "boolean"
        +    }
        +  },
        +  "type": "object"
        +}
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum results (optional, default: 200, API maximum: 500)"New value: +"Maximum results (optional, default: 200, at most 1000; read 500 per request, following the cursor)"
      • changedInput schema / properties / limit / maximum
        Previous value: -500New value: +1000
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: protocol:tcp/udp, direction:inbound/outbound/local, status:blocked/ok, total:>1MB (download + upload in B/KB/MB/GB/TB), download:>10MB, upload:>10MB, domain:*.example.com, region:US (country code), category:social/games/porn/etc, box.id:box_gid, device.ip:192.168.*, source.ip:*, destination.ip:*, ts:>1h. Examples: \"region:US AND protocol:tcp\", \"status:blocked AND region:CN\", \"category:social OR category:games\""New value: +"Search query using Firewalla syntax. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. Supported fields: protocol:tcp/udp, direction:inbound/outbound/local, status:blocked/ok, total:>1MB (download + upload in B/KB/MB/GB/TB), download:>10MB, upload:>10MB, domain:example.com (the root domain: *.example.com matches nothing and *word* matches any domain containing word), region:US (country code), category:social/games/porn/etc, box.id:box_gid, device.ip:192.168.*, source.ip:*, destination.ip:*, ts:>1h. Examples: \"region:US AND protocol:tcp\", \"status:blocked AND region:CN\", \"category:social OR category:games\""
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedsearch_rules5 fields changed
      • addedInput schema / properties / limit / default
        Added value: +200
      • addedInput schema / properties / limit / maximum
        Added value: +1000
      • addedInput schema / properties / limit / minimum
        Added value: +1
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: action:allow/block/timelimit, target.type:domain/ip/device, target.value:*.facebook.com, status:active/paused, direction:bidirection/inbound/outbound, protocol:tcp/udp, box.id:box_gid, scope.type:device/network, notes:\"description text\". Examples: \"action:block AND target.value:*.social.com\", \"status:paused\", \"target.type:domain AND action:block\""New value: +"Search query using Firewalla syntax. Terms joined by spaces or AND must all match; OR works between values of one field (sent as a comma list, e.g. region:US,CN) and is refused across fields; NOT or a leading - excludes. A word or quoted phrase with no field is free text, matched case-insensitively in the name, notes, action, target type or value, or scope of each rule (GET /v2/rules matches no free text, so the words are not sent and every rule the other terms match is read; free_text_coverage says how many were checked and whether that was all). Supported fields: action:allow/block/timelimit, target.type:domain/ip/device, target.value:*.facebook.com, status:active/paused, direction:bidirection/inbound/outbound, protocol:tcp/udp, box.id:box_gid, scope.type:device/network, notes:\"description text\". Examples: \"action:block AND target.value:*.social.com\", \"status:paused\", \"target.type:domain AND action:block\""
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedsearch_target_lists3 fields changed
      • changedInput schema / properties / limit / maximum
        Previous value: -500New value: +1000
      • changedInput schema / properties / query / description
        Previous value: -"Search query for target lists. Supported fields: name:*Social*, owner:global/box_gid, category:social/games/ad/porn/etc, targets:*.facebook.com, notes:\"description text\", target_count:>100 (entries: n, >n, >=n, <n, <=n or a range 10-50), last_updated:>2026-09-01 (a date or Unix seconds, with the same comparisons). Examples: \"category:social\", \"owner:global AND name:*Block*\", \"targets:*.gaming.com\", \"target_count:>1000\", \"last_updated:<2026-01-01\""New value: +"Search query for target lists. A word or quoted phrase with no field is free text, found case-insensitively in the name, the notes or an entry; a comma list matches any of its values (category:social,games). Supported fields: name:*Social*, owner:global/box_gid, category:social/games/ad/porn/etc, targets:*.facebook.com, notes:\"description text\", target_count:>100 (entries: n, >n, >=n, <n, <=n or a range 10-50), last_updated:>2026-09-01 (a date or Unix seconds, with the same comparisons). Examples: \"category:social\", \"category:social,games\", \"owner:global AND name:*Block*\", \"targets:*.gaming.com\", \"target_count:>1000\", \"last_updated:<2026-01-01\", \"facebook\""
      • addedInput schema / properties / response_format
        Added value: +{
        +  "default": "json",
        +  "description": "Response format. 'json' (default): the compact JSON response. 'markdown': the same response as readable markdown, its fields as bullet lists and each list of records as a table of up to 8 columns and DEFAULT_PAGE_SIZE rows (default 100), ending with a line that says what the view leaves out. Omitted or null: 'json'. Errors are JSON either way.",
        +  "enum": [
        +    "json",
        +    "markdown",
        +    null
        +  ],
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Removedupdate_target_list
  2. 15 tool updatesv1.5.0
    • Changedget_active_alarms2 fields changed
      • changedInput schema / properties / groupBy / description
        Previous value: -"Group alarms by field (e.g., type, box)"New value: +"Fields to group by, comma-separated, e.g. \"type\", \"status\", \"device\" or \"type,box\". The API then returns groups instead of alarms: groups of { key, count }, where key holds the group fields (gid for box, device.id for device) and count the alarms in the group."
      • changedInput schema / properties / query / description
        Previous value: -"Search query for filtering alarms (default: status:1 for active). Use type:N where N is: 1=Security Activity, 2=Abnormal Upload, 3=Large Bandwidth Usage, 4=Monthly Data Plan, 5=New Device, 6=Device Back Online, 7=Device Offline, 8=Video Activity, 9=Gaming Activity, 10=Porn Activity, 11=VPN Activity, 12=VPN Connection Restored, 13=VPN Connection Error, 14=Open Port, 15=Internet Connectivity Update, 16=Large Upload. Examples: type:8 (video), type:10 (porn), region:US, source_ip:*"New value: +"Search query for filtering alarms. Active alarms only (status:1) unless the query names a status. Use type:N where N is: 1=Security Activity, 2=Abnormal Upload, 3=Large Bandwidth Usage, 4=Monthly Data Plan, 5=New Device, 6=Device Back Online, 7=Device Offline, 8=Video Activity, 9=Gaming Activity, 10=Porn Activity, 11=VPN Activity, 12=VPN Connection Restored, 13=VPN Connection Error, 14=Open Port, 15=Internet Connectivity Update, 16=Large Upload. Examples: type:8 (video), type:10 (porn), region:US, device.ip:192.168.*"
    • Changedget_alarm_trends1 field changed
      • addedInput schema / properties / period
        Added value: +{
        +  "default": "30d",
        +  "description": "Return the days that overlap this period (default: 30d). The API has no finer resolution than a day, so 1h returns today so far and 24h returns yesterday and today",
        +  "enum": [
        +    "1h",
        +    "24h",
        +    "7d",
        +    "30d"
        +  ],
        +  "type": "string"
        +}
    • Changedget_bandwidth_usage1 field changed
      • changedInput schema / properties / box / description
        Previous value: -"Filter devices under a specific Firewalla box"New value: +"Only flows of this box (box gid). Defaults to FIREWALLA_BOX_ID; without either, every box."
    • Changedget_flow_data2 fields changed
      • changedInput schema / properties / groupBy / description
        Previous value: -"Group flows by specified values (e.g., \"domain,box\")"New value: +"Fields to group by, comma-separated, e.g. \"category\", \"domain\", \"device\", \"box\" or \"device,category\". The API then returns groups instead of flows: groups of { key, count, download, upload, total }, where key holds the group fields (gid for box; for device the device with its name, but only its id for \"device,category\") and the rest are the group's summed connection count and bytes."
      • changedInput schema / properties / query / description
        Previous value: -"Search query for flows. Supports region:US for geographic filtering, protocol:tcp, blocked:true, domain:*, category:social, etc."New value: +"Search query for flows. Supports region:US for geographic filtering, protocol:tcp, status:blocked, domain:*, category:social, etc."
    • Changedget_rule_trends1 field changed
      • addedInput schema / properties / period
        Added value: +{
        +  "default": "30d",
        +  "description": "Return the days that overlap this period (default: 30d). The API has no finer resolution than a day",
        +  "enum": [
        +    "1h",
        +    "24h",
        +    "7d",
        +    "30d"
        +  ],
        +  "type": "string"
        +}
    • Changedget_specific_alarm3 fields changed
      • changedInput schema / properties / alarm_id / description
        Previous value: -"Alarm ID (required for API call)"New value: +"Alarm ID (required for API call): the aid from get_active_alarms or search_alarms, as a number or a string"
      • changedInput schema / properties / alarm_id / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "number"
        +]
      • addedInput schema / properties / gid
        Added value: +{
        +  "description": "Box the alarm belongs to (the gid field of get_active_alarms or search_alarms results). Defaults to FIREWALLA_BOX_ID; without either, each box on the account is checked.",
        +  "type": "string"
        +}
    • Changedget_statistics_by_region1 field changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum number of results (optional, default: 5)"New value: +"Maximum number of regions (optional, default: 5; the API returned no more than 5 when a larger limit was tried)"
    • Changedget_target_lists1 field changed
      • addedInput schema / properties / owner
        Added value: +{
        +  "description": "Only lists with this owner: \"global\" (MSP lists), a box gid (that box's lists), or several comma-separated, e.g. \"global,<box_gid>\". Default: global and Firewalla-managed lists.",
        +  "type": "string"
        +}
    • Changedpause_rule3 fields changed
      • removedInput schema / properties / box
        Removed value: -{
        -  "description": "Box GID for context (required by API)",
        -  "type": "string"
        -}
      • removedInput schema / properties / duration
        Removed value: -{
        -  "default": 60,
        -  "description": "Duration in minutes to pause the rule (optional, default: 60, range: 1-1440)",
        -  "maximum": 1440,
        -  "minimum": 1,
        -  "type": "number"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "rule_id",
        -  "box"
        -]New value: +[
        +  "rule_id"
        +]
    • Changedresume_rule2 fields changed
      • removedInput schema / properties / box
        Removed value: -{
        -  "description": "Box GID for context (required by API)",
        -  "type": "string"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "rule_id",
        -  "box"
        -]New value: +[
        +  "rule_id"
        +]
    • Changedsearch_alarms2 fields changed
      • changedInput schema / properties / groupBy / description
        Previous value: -"Group alarms by specified fields (comma-separated)"New value: +"Fields to group by, comma-separated, e.g. \"type\", \"status\", \"device\" or \"type,box\". The API then returns groups instead of alarms: groups of { key, count }, where key holds the group fields (gid for box, device.id for device) and count the alarms in the group."
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: type:1-16 (see alarm types above), resolved:true/false, status:1/2 (active/archived), source_ip:192.168.*, region:US (country code), gid:box_id, device.name:*, message:\"text search\". Examples: \"type:8 AND region:US\" (video from US), \"type:10 AND status:1\" (active porn alerts), \"source_ip:192.168.* AND NOT resolved:true\""New value: +"Search query using Firewalla syntax. Supported fields: type:1-16 (see alarm types above), status:1/2 (active/archived), device.ip:192.168.*, region:US (country code), box.id:box_gid, device.name:*. Examples: \"type:8 AND region:US\" (video from US), \"type:10 AND status:1\" (active porn alerts), \"device.ip:192.168.* AND status:1\" (active alarms from the LAN), \"porn\" (free text: a term without a qualifier searches alarm text)"
    • Changedsearch_devices2 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: mac:AA:BB:CC:DD:EE:FF, ip:192.168.1.*, name:*iPhone*, online:true/false, vendor:Apple, gid:box_id, network.name:*, group.name:*. Examples: \"online:false AND vendor:Apple\", \"ip:192.168.1.* AND name:*laptop*\", \"mac:AA:* OR name:*phone*\""New value: +"Search query using Firewalla syntax. Supported fields: mac:AA:BB:CC:DD:EE:FF, ip:192.168.1.*, name:*iPhone*, online:true/false, mac_vendor:Apple, gid:box_gid, network.name:*, group.name:*. Examples: \"online:false AND mac_vendor:Apple\", \"ip:192.168.1.* AND name:*laptop*\", \"mac:AA:* OR name:*phone*\""
      • removedInput schema / properties / status
        Removed value: -{
        -  "default": "any",
        -  "description": "Filter by online status",
        -  "enum": [
        -    "online",
        -    "offline",
        -    "any"
        -  ],
        -  "type": "string"
        -}
    • Changedsearch_flows2 fields changed
      • changedInput schema / properties / groupBy / description
        Previous value: -"Group flows by specified values (e.g., \"domain,box\")"New value: +"Fields to group by, comma-separated, e.g. \"category\", \"domain\", \"device\", \"box\" or \"device,category\". The API then returns groups instead of flows: groups of { key, count, download, upload, total }, where key holds the group fields (gid for box; for device the device with its name, but only its id for \"device,category\") and the rest are the group's summed connection count and bytes."
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: protocol:tcp/udp, direction:inbound/outbound/local, blocked:true/false, bytes:>1MB, domain:*.example.com, region:US (country code), category:social/games/porn/etc, gid:box_id, device.ip:192.168.*, source_ip:*, destination_ip:*. Examples: \"region:US AND protocol:tcp\", \"blocked:true AND bytes:>1MB\", \"category:social OR category:games\""New value: +"Search query using Firewalla syntax. Supported fields: protocol:tcp/udp, direction:inbound/outbound/local, status:blocked/ok, total:>1MB (download + upload in B/KB/MB/GB/TB), download:>10MB, upload:>10MB, domain:*.example.com, region:US (country code), category:social/games/porn/etc, box.id:box_gid, device.ip:192.168.*, source.ip:*, destination.ip:*, ts:>1h. Examples: \"region:US AND protocol:tcp\", \"status:blocked AND region:CN\", \"category:social OR category:games\""
    • Changedsearch_rules1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Search query using Firewalla syntax. Supported fields: action:allow/block/timelimit, target.type:domain/ip/device, target.value:*.facebook.com, status:active/paused, direction:bidirection/inbound/outbound, protocol:tcp/udp, gid:box_id, scope.type:device/network, notes:\"description text\". Examples: \"action:block AND target.value:*.social.com\", \"status:paused\", \"target.type:domain AND action:block\""New value: +"Search query using Firewalla syntax. Supported fields: action:allow/block/timelimit, target.type:domain/ip/device, target.value:*.facebook.com, status:active/paused, direction:bidirection/inbound/outbound, protocol:tcp/udp, box.id:box_gid, scope.type:device/network, notes:\"description text\". Examples: \"action:block AND target.value:*.social.com\", \"status:paused\", \"target.type:domain AND action:block\""
    • Changedsearch_target_lists3 fields changed
      • removedInput schema / properties / category
        Removed value: -{
        -  "description": "Filter by category",
        -  "type": "string"
        -}
      • changedInput schema / properties / owner / description
        Previous value: -"Filter by owner (global or box gid)"New value: +"Only lists with this owner, sent to the API: \"global\" (MSP lists), a box gid (that box's lists), or several comma-separated, e.g. \"global,<box_gid>\". Default: global and Firewalla-managed lists."
      • changedInput schema / properties / query / description
        Previous value: -"Search query for target lists. Supported fields: name:*Social*, owner:global/box_gid, category:social/games/ad/porn/etc, targets:*.facebook.com, notes:\"description text\". Examples: \"category:social\", \"owner:global AND name:*Block*\", \"targets:*.gaming.com\""New value: +"Search query for target lists. Supported fields: name:*Social*, owner:global/box_gid, category:social/games/ad/porn/etc, targets:*.facebook.com, notes:\"description text\", target_count:>100 (entries: n, >n, >=n, <n, <=n or a range 10-50), last_updated:>2026-09-01 (a date or Unix seconds, with the same comparisons). Examples: \"category:social\", \"owner:global AND name:*Block*\", \"targets:*.gaming.com\", \"target_count:>1000\", \"last_updated:<2026-01-01\""
  3. 5 tool updatesv1.3.0
    • Changedsearch_alarms1 field changed
      • changedInput schema / required
        Previous value: -[]New value: +[
        +  "query"
        +]
    • Changedsearch_devices1 field changed
      • changedInput schema / required
        Previous value: -[]New value: +[
        +  "query"
        +]
    • Changedsearch_flows1 field changed
      • changedInput schema / required
        Previous value: -[]New value: +[
        +  "query"
        +]
    • Changedsearch_rules2 fields changed
      • addedInput schema / properties / limit
        Added value: +{
        +  "description": "Maximum number of rules to return",
        +  "type": "number"
        +}
      • changedInput schema / required
        Previous value: -[]New value: +[
        +  "query"
        +]
    • Changedsearch_target_lists1 field changed
      • changedInput schema / required
        Previous value: -[]New value: +[
        +  "query"
        +]
  4. 28 tool updates
    • First observedcreate_target_list
    • First observeddelete_target_list
    • First observedget_active_alarms
    • First observedget_alarm_trends
    • First observedget_bandwidth_usage
    • First observedget_boxes
    • First observedget_device_status
    • First observedget_flow_data
    • First observedget_flow_insights
    • First observedget_network_rules
    • First observedget_network_rules_summary
    • First observedget_offline_devices
    • First observedget_recent_flow_activity
    • First observedget_rule_trends
    • First observedget_simple_statistics
    • First observedget_specific_alarm
    • First observedget_specific_target_list
    • First observedget_statistics_by_box
    • First observedget_statistics_by_region
    • First observedget_target_lists
    • First observedpause_rule
    • First observedresume_rule
    • First observedsearch_alarms
    • First observedsearch_devices
    • First observedsearch_flows
    • First observedsearch_rules
    • First observedsearch_target_lists
    • First observedupdate_target_list

TDQS

A3.7/5.0

Scored across 24 tools

Disambiguation3/5

Several tools overlap in purpose: search_flows vs get_flow_data both query /v2/flows with similar filtering/pagination, search_rules vs get_network_rules both retrieve rules, and get_offline_devices/get_device_status/search_devices all cover device status. The descriptions provide some guidance (e.g., get_recent_flow_activity explicitly warns against historical use), but an agent could still misselect among the search/get pairs.

Naming Consistency5/5

All 24 tool names use snake_case with a consistent verb_noun pattern (get_*, search_*), and resource nouns are predictable (alarms, flows, rules, devices, target_lists). The only minor variation is get_specific_* and get_network_rules_summary, but these still fit the pattern.

Tool Count3/5

24 tools is borderline heavy per the rubric, and the set includes redundant wrappers (search_target_lists vs get_target_lists, search_devices vs get_device_status, search_flows vs get_flow_data). While the Firewalla MSP API is broad, consolidation of overlapping query tools would improve focus.

Completeness4/5

Assuming a read-only monitoring/analytics domain, the surface covers alarms, flows, rules, devices, boxes, target lists, statistics, and trends thoroughly, with multiple analytical angles. Gaps remain: no direct get-specific-device or get-specific-rule lookups (only search/list), no alarm acknowledgment/archival write, and no mutation tools for rules or target lists, which agents can work around for query tasks.

Maintenance

ActivityMaintained
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Provides real-time access to Firewalla firewall data through 28 specialized tools for network monitoring, security analysis, bandwidth tracking, and firewall rule management. Enables users to query security alerts, analyze network flows, monitor device status, and manage firewall configurations through natural language.
    495 npm
    1
    -
  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables interaction with Firewalla network security devices for network monitoring, device management, traffic analysis, and security rule configuration through MCP tools.
    -
  • A
    license
    A
    quality
    C
    maintenance
    A secure MCP server for managing OPNsense firewalls through AI assistants. Provides 81 tools across system, firewall, network, DNS, DHCP, VPN, HAProxy, services, diagnostics, and security domains.
    81
    11,779 PyPI
    21
    MIT