Reference¶
Tools¶
All tools are read-only. Every one accepts since_hours (float, default 26)
except health_check.
daily_brief(since_hours=26)¶
Morning summary in one call: enforced DENYs by rule priority, the home-region
false-positive lens (only when CLOUDARMOR_HOME_REGION is set), and preview
DENYs. A section whose query fails is rendered inline as
query failed — <reason>; the other sections still run.
enforce_denies(since_hours=26)¶
Counts of enforced DENYs grouped by rule priority, highest first.
preview_denies(since_hours=26)¶
Same, for rules in preview (dry-run) mode.
home_region_denies(since_hours=26)¶
Enforced DENYs whose source IP geolocates to CLOUDARMOR_HOME_REGION.
Priorities in known_normal_priorities are counted and suppressed; the rest
are listed with source IP and request URL (40 lines maximum, then a count).
Returns an explanatory sentence instead of an error when the home region is
not configured.
health_check()¶
Returns a fixed-shape dict — the keys never vary, so a monitoring caller does not have to branch on their presence:
| Key | Meaning |
|---|---|
status |
healthy (config + probe query OK), degraded (config OK, probe failed), error (config unusable) |
service |
Always cloudarmor-mcp |
version |
Package version |
project |
Resolved project ID, or null |
backend_services |
Resolved filter list |
home_region |
Resolved region code, or null |
rules_ini |
true when a rules INI was loaded with content |
probe |
ok, or the failure reason |
Environment variables¶
| Variable | Required | Default | Meaning |
|---|---|---|---|
CLOUDARMOR_PROJECT |
yes | — | GCP project ID holding the load-balancer logs |
GOOGLE_APPLICATION_CREDENTIALS |
yes | — | Service-account key path (roles/logging.viewer) |
CLOUDARMOR_BACKEND_SERVICES |
no | all | Comma-separated backend service names |
CLOUDARMOR_HOME_REGION |
no | disabled | ISO region code for the false-positive lens |
CLOUDARMOR_RULES_INI |
no | none | Rule labels and known-normal priorities |
CLOUDARMOR_MAX_ENTRIES |
no | 2000 | Entries fetched per query; unparsable values fall back to the default |
CLI¶
cloudarmor-mcp # run as an MCP server over stdio
cloudarmor-mcp --version # print version and exit
cloudarmor-mcp --check # verify config + API access
cloudarmor-mcp --brief # print daily_brief to stdout
cloudarmor-mcp deny-export --date YYYY-MM-DD [--tz ZONE] [--kind both|enforced|preview] [--max-entries N] [--backend a,b]
# one day of DENY entries as JSON (see the README)
cloudarmor-mcp traffic-export --date YYYY-MM-DD [--tz ZONE] [--sample RATE] [--max-entries N] [--backend a,b]
# a sampled day of all requests as JSON (see the README)
Exit codes:
| Command | 0 | 1 | 2 |
|---|---|---|---|
--check |
healthy | missing CLOUDARMOR_PROJECT |
degraded (probe failed) |
--brief |
every section rendered | at least one section's query failed | — |
deny-export |
exported | the Cloud Logging query failed (stdout may hold an unterminated document), or the reader closed stdout | usage or configuration error, including an unset CLOUDARMOR_PROJECT, a client that cannot be created (credentials) and a day that has not ended |
traffic-export |
as deny-export |
as deny-export |
as deny-export, plus a --sample outside 0.000001–1 |
--brief is the convenient form for cron jobs and smoke tests: the non-zero
exit distinguishes "the WAF was quiet" from "we could not read the logs",
which a text report alone does not.
deny-export document¶
| Field | Meaning |
|---|---|
schema |
cloudarmor-mcp/deny-export/1 |
window |
start / end of the day as RFC 3339 UTC, half-open |
entries[] |
one record per log entry, oldest first |
entries[].enforced / .preview |
{policy, priority, action, outcome, rule_ids}; preview is null when no preview rule matched. priority is a string ("1002"); the default rule shows as "2147483647" |
entries[].ip, .region, .asn |
httpRequest.remoteIp, remoteIpInfo.regionCode, remoteIpInfo.asn (integer or null) |
entries[].host, .path, .query_len, .path_truncated |
from requestUrl: host lower-cased without port, path cut at 2048 characters, query string length only |
entries[].method, .status, .ua, .backend, .status_details, .ts, .id, .partial |
request method, HTTP status, User-Agent (200 chars), backend service, statusDetails, timestamp (UTC), insertId, and whether any field could not be read |
count, fetched, malformed, capped |
records written, entries read (at most max_entries), entries that could not be turned into a record, and whether the cap stopped the export |
filter |
the Cloud Logging filter that was used |
traffic-export document¶
The same layout and record fields as deny-export, with these differences:
| Field | Meaning |
|---|---|
schema |
cloudarmor-mcp/traffic-export/1 |
sample |
the rate passed to sample(insertId, ...); replaces kind |
entries[] |
every load-balancer request kept by the sample — allowed, denied and cache-served |
entries[].cache_hit |
true when the response came from the CDN cache (also present in deny-export records, always false there in practice) |
entries[].enforced |
null when no backend security policy evaluated the request (a CDN cache hit, for example); region and asn are null then too. Use cache_hit to tell cache hits apart |
capped |
as in deny-export; when true the export is the early part of the day only (entries are read oldest first), so do not scale it by 1 / sample |
Log filters¶
For reference, the Cloud Logging filters the server builds:
resource.type="http_load_balancer"
jsonPayload.enforcedSecurityPolicy.outcome="DENY"
[jsonPayload.securityPolicyRequestData.remoteIpInfo.regionCode="JP"]
[resource.labels.backend_service_name="..." | =("a" OR "b")]
timestamp >= "<RFC3339 UTC>"
Preview queries substitute
jsonPayload.previewSecurityPolicy.configuredAction="DENY" for the outcome
line. Entries are fetched newest-first.
traffic-export has no outcome line: after the day's window
(timestamp >= and timestamp <) it appends sample(insertId, RATE), omitted
when RATE is 1. Its entries are fetched oldest-first.