eset-protect
Use when the user asks about ESET PROTECT across every customer: which customers have devices that need attention, are muted or stopped checking in, the morning detection and incident queue, which cus
SKILL.mdskills/eset-protect/SKILL.md
GitHubESET PROTECT CLI
Prerequisites: Install the CLI
This skill drives the eset-protect-cli binary. You must verify the CLI is installed before invoking any command from this skill. If it is missing, install it first:
- macOS / Linux:
bash <(curl -fsSL https://raw.githubusercontent.com/Servosity/msp-skills/main/skills/eset-protect/install.sh) - Windows (PowerShell):
iwr -useb https://raw.githubusercontent.com/Servosity/msp-skills/main/skills/eset-protect/install.ps1 | iex - Verify:
eset-protect-cli --version - Ensure
~/.local/bin(macOS / Linux) or%LOCALAPPDATA%\Programs\msp-skills(Windows) is on$PATH.
The installer downloads the eset-protect-cli and eset-protect-mcp binaries into ~/.local/bin
(macOS / Linux) or %LOCALAPPDATA%\Programs\msp-skills (Windows). It does not
register the skill with your agent and writes no MCP client config - see
mcp-install.md for that wire-up.
If --version reports "command not found" after install, the runtime cannot see the binary directory on $PATH. Do not proceed with skill commands until verification succeeds.
Covers devices, detections, incidents, EDR rules, automation, vulnerabilities, patches, policies, quarantine, installers and IAM, with the password-grant token minted and refreshed for you (the jpn region has no quarantine, user-management or v2 detection list). Sync before you ask: 'fleet health', 'fleet gaps' and 'triage' answer from the local mirror (re-sync detections and incidents before each triage run, and run 'sync --full' now and then so devices and groups deleted in ESET leave the mirror), while 'exposure', 'patches backlog' and 'report customer' scan ESET live and use the mirror to name each customer.
When to Use This CLI
Use this CLI for ESET PROTECT cloud (ESET Connect) work: listing and changing devices, detections, incidents, EDR rules, automation tasks, policies, quarantine and installers, and for per-customer MSP questions such as which customer has stale or unprotected devices, who is exposed to a CVE, what patches are pending, and a customer's QBR numbers. Every command speaks JSON. 'respond' and 'policies clone' preview unless --yes; every other write executes immediately, so run it with --dry-run first.
Anti-triggers
Do not use this CLI for:
- ESET PROTECT On-Prem consoles (POST /GetTokens on port 9443): this CLI targets the ESET Connect cloud API only
- ESET licensing, seat counts or usage billing: those live in the ESET MSP Administrator 2 / PROTECT Hub API, not ESET Connect
- Editing the settings inside an existing policy: ESET Connect has no policy update call; decode it with 'policies decode' and create a new policy instead
- Real-time detection streaming into a SIEM: use ESET's Elastic, Sentinel or Splunk integrations
- Quarantine, Cloud Office users and the v2 detection list in the jpn region: ESET Connect does not offer them there
Unique Capabilities
Per-customer rollups
fleet health- One row per customer with device count, attention-required and attention-recommended devices, problem count, muted, stale and unenrolled devices, worst customer first.Reach for this to answer 'which customer needs attention today' across the whole MSP tree in one call.
eset-protect-cli fleet health --stale-days 7 --agentreport customer- One customer's QBR one-pager: current device health, detections by severity and response, incidents with triage and response times, top CVEs, pending and failed security patches, and vulnerability-scan coverage (ESET keeps only a few days of scan and patching history).Use it to build a customer QBR or answer 'how is this customer doing' without five separate queries.
eset-protect-cli report customer "Acme Dental" --since 90d --agentfleet gaps- Devices missing a component or product their customer's other devices have, running an older component version, or not finished enrolling.Use it in the weekly agent-health sweep to find unprotected or out-of-date machines per customer.
eset-protect-cli fleet gaps --only missing,outdated --agent
Vulnerability and patch intelligence
exposure- Which customers and devices have a given CVE or vulnerable application, with risk score, first detection and whether a patch is available.Use it the moment a CVE makes the news and a customer asks 'are we affected?'.
eset-protect-cli exposure --cve CVE-2024-3400 --agentpatches backlog- Available-but-not-installed patches ranked by the devices and CVE risk they fix per customer, plus recent failed patch attempts by reason.Use it after Patch Tuesday to decide what to push first and to see what failed.
eset-protect-cli patches backlog --security-only --since 30d --agent
Triage and response
detections spread- How far each detected file hash has spread across devices and customers in a window, joined to whether that executable is blocked, its signer and its LiveGrid reputation when executables are synced.Use it to decide false positive versus outbreak before resolving or writing an exclusion.
eset-protect-cli detections spread --since 7d --min-customers 2respond- Scan, isolate or release a device by name (or scan a whole customer): prints the exact ESET task first and creates it only with --yes; with --wait it polls the task runs until they finish. Over MCP the root --yes is never forwarded, so the tool only previews.Use it to act on a finding by device name without hand-building an automation task payload.
eset-protect-cli respond scan FRONTDESK-01 --yes --wait 10m
Everyday ESET workflows
triage- Endpoint detections in a window (v1 has no resolved state) and open incidents in one ranked queue, worst first, with device, customer and a suggested next command.Use it for the morning queue instead of paging detections and incidents separately.
eset-protect-cli triage --since 24h --agentfleet stale- Devices that stopped checking in for N days, most stale first, with customer and group path.Use it to find offboarding candidates or broken agents per customer.
eset-protect-cli fleet stale --days 14 --agentdetections latest- The newest endpoint detections straight from ESET for a time window and minimum severity, with device and customer names when the mirror is synced.Use it when you need what fired in the last hours without a sync.
eset-protect-cli detections latest --since 6h --min-severity high --agentdevices profile- Everything about one device in one call: health, recent detections, CVEs, pending patches and recent scans, with partial-failure reporting.Use it before touching a device or answering a ticket about it.
eset-protect-cli devices profile FRONTDESK-01 --agentincidents context- One incident with its comments and every linked detection and device, falling back from v1 to v2 detection lookups.Use it to work one incident end to end.
eset-protect-cli incidents context 8d4f2a17-3b6c-4e1d-9a2f-5c7e1b0d4f6a --agentreport vulnerabilities- Vulnerable devices, CVE counts by severity, top CVEs and patchable share per customer from a live vulnerability scan.Use it for a monthly vulnerability review across customers.
eset-protect-cli report vulnerabilities --top 10 --agentexecutables find- Turn an MD5, SHA-1 or SHA-256 hash or a name into the executable UUID that 'executables block' needs, with blocked state and signer.Use it before blocking or excluding a file.
eset-protect-cli executables find --hash 3395856ce81f2b7382dee72602f798b642f14140 --agentpolicies decode- Decode a policy's base64 and LZMA settings blobs into readable JSON, list settings that mention a word, or pull one value by path.Use it to see what a policy actually sets before cloning or assigning it.
eset-protect-cli policies decode 0f8c2a4e-1b2d-4c3e-9f00-6a7b8c9d0e1f --find firewallpolicies clone- Copy an existing policy's feature settings byte for byte into a new policy under a new name; preview unless --yes.Use it to start a customer-specific policy from a known-good one.
eset-protect-cli policies clone 0f8c2a4e-1b2d-4c3e-9f00-6a7b8c9d0e1f --name "Acme Dental - Workstations"
Command Reference
application-patching-processes - Manage application patching processes
eset-protect-cli application-patching-processes- List the patching details of all application patching attempts performed recently (within the last few days).
detection-groups - Manage detection groups
eset-protect-cli detection-groups get- Get details about a detection group.eset-protect-cli detection-groups list- Return a list of all the [detection group]s matching criteria.eset-protect-cli detection-groups resolve- Mark all the [detections] in a group as resolved.
detection-groups-search - Manage detection groups search
eset-protect-cli detection-groups-search- Search a collection for [detection groups]s matching criteria defined by the filter (if given).
detections - Manage detections
eset-protect-cli detections get- Endpoint detection from ESET PROTECT and ESET Inspect (v1).eset-protect-cli detections list- Endpoint detections from ESET PROTECT and ESET Inspect (v1). Return a list of all the [detection]s matching criteria.
detections-v2 - Manage detections v2
eset-protect-cli detections-v2 batch-get- Batch-get detections from the v2 API (ESET Inspect and ESET Cloud Office Security). Batch-get detections.eset-protect-cli detections-v2 get- Detection by UUID from the v2 API (ESET Inspect and ESET Cloud Office Security). Get detection.eset-protect-cli detections-v2 list- ESET Cloud Office Security detections only (v2).eset-protect-cli detections-v2 resolve- Resolve an ESET Inspect detection (v2). Mark a [detection] as resolved.
device-groups - Manage device groups
eset-protect-cli device-groups- Hierarchy can be reconstructed from the returneddevice_groups.
device-os-vulnerabilities - Manage device os vulnerabilities
eset-protect-cli device-os-vulnerabilities- List vulnerabilities of the operating system of the given device.
device-patches - Manage device patches
eset-protect-cli device-patches- If the same application, package or operating system is unpatched on multiple devices
device-tasks - Manage device tasks
eset-protect-cli device-tasks create- Create a [device task].eset-protect-cli device-tasks delete- Delete the specified task. Deletion of the template leads to deletion of all templated tasks.eset-protect-cli device-tasks get- Get the task entity. For a complete history, callListTaskRuns().eset-protect-cli device-tasks list- List all device tasks matching criteria.eset-protect-cli device-tasks update-targets- Update task targets.eset-protect-cli device-tasks update-triggers- Update the list of task triggers.
device-vulnerabilities - Manage device vulnerabilities
eset-protect-cli device-vulnerabilities- List device vulnerabilities where the vulnerabilities fit the criteria from the request.
devices - Manage devices
eset-protect-cli devices get- Get details of a [device].eset-protect-cli devices list- List all the managed [device]s.eset-protect-cli devices move- Move [Device] under new [Group]. It is possible to move Groups only in the scope of the same tenant.eset-protect-cli devices rename- Update the device's display name.
devices-batch-get - Manage devices batch get
eset-protect-cli devices-batch-get- Retrieves a specific set of devices at a consistent time point.
devices-batch-import - Manage devices batch import
eset-protect-cli devices-batch-import- Import a batch of devices into device management. Imported devices cannot be managed until properly enrolled.
edr-rule-exclusions - Manage edr rule exclusions
eset-protect-cli edr-rule-exclusions create- Create an [EDR rule exclusion].eset-protect-cli edr-rule-exclusions delete- Delete an [EDR rule exclusion].eset-protect-cli edr-rule-exclusions get- Get details of an [EDR rule exclusion].eset-protect-cli edr-rule-exclusions list- List [EDR rule exclusion]s matching criteria.eset-protect-cli edr-rule-exclusions update-definition- Update the definition of an [EDR rule exclusion].
edr-rules - Manage edr rules
eset-protect-cli edr-rules create- Create an [EDR rule]. Only the rule with validxml_definitioncan be accepted.eset-protect-cli edr-rules delete- Delete the given [EDR rule].eset-protect-cli edr-rules disable- Disable the given [EDR rule].eset-protect-cli edr-rules enable- Enable the given [EDR rule].eset-protect-cli edr-rules get- Get [EDR rule] details.eset-protect-cli edr-rules list- List [EDR rule]s matching the criteria.eset-protect-cli edr-rules update-definition- Update the [EDR rule] definition. If the rule_definition is invalid, an error 400 BAD REQUEST is returned.
executables - Manage executables
eset-protect-cli executables block- Blocked executables will not be executed. Idempotenteset-protect-cli executables get- Get details of a specific executable.eset-protect-cli executables list- List all the executables.eset-protect-cli executables unblock- Unblock a file to be executable again. Idempotent
gpo-sccm-files-generate - Manage gpo sccm files generate
eset-protect-cli gpo-sccm-files-generate- Generates a GPO/SCCM-compatible configuration file for deploying the security product via centralized IT management
groups - Manage groups
eset-protect-cli groups create- Create a [Group] in the hierarchy and assign a UUID to it.eset-protect-cli groups delete- Delete the group referenced by the uuid and any groups under it.eset-protect-cli groups move- Move a [Group] under another [Group]. It is possible to move groups only in the scope of the same tenant.eset-protect-cli groups rename- Update the group's display name.
incidents - Manage incidents
eset-protect-cli incidents close- Close an incident.eset-protect-cli incidents get- Get details of a specific incident.eset-protect-cli incidents list- Returns a list of incidents matching thefilter. The list is ordered according toorder_by.eset-protect-cli incidents reopen- Reopening an incident causes the change of the status to 'Open'.
installers - Manage installers
eset-protect-cli installers create- Create [installer].eset-protect-cli installers delete- Delete [installer].eset-protect-cli installers get- Get details of the requested [installer].eset-protect-cli installers list- List all installers available to the caller.
ip-sets - Manage ip sets
eset-protect-cli ip-sets get- Get details about an IP set.eset-protect-cli ip-sets list- List all IP sets.eset-protect-cli ip-sets update- Update the selected IP set; attributes that can be updated
mobile-devices-batch-activate-product - Manage mobile devices batch activate product
eset-protect-cli mobile-devices-batch-activate-product- Create a product activation task with mobile devices as its targets.
mobile-devices-batch-get-enrollment-links - Manage mobile devices batch get enrollment links
eset-protect-cli mobile-devices-batch-get-enrollment-links- Get enrollment links for a batch of mobile devices in device management.
patching-process-details - Manage patching process details
eset-protect-cli patching-process-details- History is pruned repeatedly to prevent database flooding.
permissions - Manage permissions
eset-protect-cli permissions- List all possible permissions.
policies - Manage policies
eset-protect-cli policies create- Create a policy. All the included feature policies must be valid.eset-protect-cli policies delete- Delete the given policy.eset-protect-cli policies get- Get policy details.eset-protect-cli policies list- List all the policies accessible to the caller (based on the token).
policy-assignments - Manage policy assignments
eset-protect-cli policy-assignments assign-policy- Create a policy assignment. Assignments to the same targets are ordered and prioritized.eset-protect-cli policy-assignments get- Get details of the given policy assignment.eset-protect-cli policy-assignments list- List all the policy assignments available to the caller.eset-protect-cli policy-assignments unassign-policy- Delete the policy assignment. The remaining assignments to the target are reordered.eset-protect-cli policy-assignments update-ranking- Move the policy assignment within ranks.
quarantined-objects - Manage quarantined objects
eset-protect-cli quarantined-objects get- Requires ESET Cloud Office Security access (the API user must sign in to that console once)eset-protect-cli quarantined-objects get-count- Requires ESET Cloud Office Security access (the API user must sign in to that console once)eset-protect-cli quarantined-objects list- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
quarantined-objects-batch-delete - Manage quarantined objects batch delete
eset-protect-cli quarantined-objects-batch-delete- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
quarantined-objects-batch-download - Manage quarantined objects batch download
eset-protect-cli quarantined-objects-batch-download- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
quarantined-objects-batch-restore - Manage quarantined objects batch restore
eset-protect-cli quarantined-objects-batch-restore- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
quarantined-objects-download - Manage quarantined objects download
eset-protect-cli quarantined-objects-download- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
quarantined-objects-purge - Manage quarantined objects purge
eset-protect-cli quarantined-objects-purge- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
quarantined-objects-restore - Manage quarantined objects restore
eset-protect-cli quarantined-objects-restore- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
role-assignments - Manage role assignments
eset-protect-cli role-assignments- List [role]s of [subject]s. > Info: > Not only Users are [subject]s, but anything that can have roles attached.
role-assignments-assign-role - Manage role assignments assign role
eset-protect-cli role-assignments-assign-role- Assign a role to a subject. Eventually, the subject ends with its existing roles and those assigned by this operation.
role-assignments-revoke-role - Manage role assignments revoke role
eset-protect-cli role-assignments-revoke-role- Remove the assigned role from the subject.
roles - Manage roles
eset-protect-cli roles create- Creates a new custom [Role]. The role name must be unique within the scope in which the role is created.eset-protect-cli roles delete- Delete specified role.
scans - Manage scans
eset-protect-cli scans- List the scan details of all vulnerability scan attempts performed recently (within the last few days).
users - Manage users
eset-protect-cli users get- Requires ESET Cloud Office Security access (the API user must sign in to that console once)eset-protect-cli users list- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
users-batch-get-users - Manage users batch get users
eset-protect-cli users-batch-get-users- Requires ESET Cloud Office Security access (the API user must sign in to that console once)
vulnerable-devices - Manage vulnerable devices
eset-protect-cli vulnerable-devices- List vulnerable devices matching the criteria from the request.
web-address-rules - Manage web address rules
eset-protect-cli web-address-rules <policyUuid>- List all address rules containing given domain
detections
eset-protect-cli detections spread- How far each detected file hash has spread across devices and customers in a window, joined to whether that executable is blocked, its signer and its LiveGrid reputation when executables are synced.eset-protect-cli detections latest- The newest endpoint detections straight from ESET for a time window and minimum severity, with device and customer names when the mirror is synced.
devices
eset-protect-cli devices profile- Everything about one device in one call: health, recent detections, CVEs, pending patches and recent scans, with partial-failure reporting.
executables
eset-protect-cli executables find- Turn an MD5, SHA-1 or SHA-256 hash or a name into the executable UUID that 'executables block' needs, with blocked state and signer.
exposure
eset-protect-cli exposure- Which customers and devices have a given CVE or vulnerable application, with risk score, first detection and whether a patch is available.
fleet
eset-protect-cli fleet health- One row per customer with device count, attention-required and attention-recommended devices, problem count, muted, stale and unenrolled devices, worst customer first.eset-protect-cli fleet gaps- Devices missing a component or product their customer's other devices have, running an older component version, or not finished enrolling.eset-protect-cli fleet stale- Devices that stopped checking in for N days, most stale first, with customer and group path.
incidents
eset-protect-cli incidents context- One incident with its comments and every linked detection and device, falling back from v1 to v2 detection lookups.
patches
eset-protect-cli patches backlog- Available-but-not-installed patches ranked by the devices and CVE risk they fix per customer, plus recent failed patch attempts by reason.
policies
eset-protect-cli policies decode- Decode a policy's base64 and LZMA settings blobs into readable JSON, list settings that mention a word, or pull one value by path.eset-protect-cli policies clone- Copy an existing policy's feature settings byte for byte into a new policy under a new name; preview unless --yes.
report
eset-protect-cli report customer- One customer's QBR one-pager: current device health, detections by severity and response, incidents with triage and response times, top CVEs, pending and failed security patches, and vulnerability-scan coverage (ESET keeps only a few days of scan and patching history).eset-protect-cli report vulnerabilities- Vulnerable devices, CVE counts by severity, top CVEs and patchable share per customer from a live vulnerability scan.
respond
eset-protect-cli respond- Scan, isolate or release a device by name (or scan a whole customer): prints the exact ESET task first and creates it only with --yes; with --wait it polls the task runs until they finish. Over MCP the root --yes is never forwarded, so the tool only previews.
triage
eset-protect-cli triage- Endpoint detections in a window (v1 has no resolved state) and open incidents in one ranked queue, worst first, with device, customer and a suggested next command.
Finding the right command
When you know what you want to do but not which command does it, ask the CLI directly:
eset-protect-cli which "<capability in your own words>"
which resolves a natural-language capability query to the best matching command from this CLI's curated feature index. Exit code 0 means at least one match; exit code 2 means no confident match - fall back to --help or use a narrower query. --json (and other machine formats) keep that exit-2 contract and write {"matches":[]} on stdout so agents can inspect the envelope without treating a miss as success.
Recipes
Morning triage
eset-protect-cli triage --since 24h --agent
Endpoint detections from the last day (v1 has no resolved state) and open incidents in one ranked queue with device and customer names.
Are we affected by a CVE
eset-protect-cli exposure --cve CVE-2024-3400 --agent --select customers.customer,customers.affected_devices,customers.patch_available,truncated,scanned_vulnerabilities,note
Per-customer exposure; truncated, scanned_vulnerabilities and note show whether the live scan was complete before you tell a customer they are clear.
Outbreak or false positive
eset-protect-cli detections spread --since 7d --min-customers 2
Hashes seen at two or more customers this week (devices the mirror cannot place count as possible customers, and thresholds_proven is false when only they reach the threshold); blocked state and signer appear when executables are synced.
Customer QBR
eset-protect-cli report customer "Acme Dental" --since 90d --agent
One customer's health, detections, incidents, CVEs and patches; failed live sections are null and listed in fetch_failures.
Isolate a machine
eset-protect-cli respond isolate FRONTDESK-01
Prints the exact StartNetworkIsolation task for the named device; nothing is sent until you add --yes, and 'respond release' ends the isolation.
Auth Setup
ESET Connect has no API keys. Create a dedicated API user: a Superuser or Root adds it in ESET PROTECT Hub (Users), ESET Business Account or ESET MSP Administrator 2 (User management, Access Rights) and turns on the Integrations toggle. The new user accepts the invitation email and finishes account setup, and a Hub user must also sign in to ESET PROTECT Hub once. Then set ESET_PROTECT_USERNAME and ESET_PROTECT_PASSWORD (export VAR=value on macOS/Linux, $env:VAR = "value" in PowerShell) and set ESET_PROTECT_REGION to the tenant's data center (eu, de, us, ca or jpn; default eu). The CLI exchanges them for a one-hour bearer token with the OAuth2 password grant, caches it for that region and user, and refreshes it automatically. ESET_PROTECT_TOKEN overrides with a token you minted yourself.
Run eset-protect-cli doctor to verify setup.
Agent Mode
Add --agent to any command. Expands to: --json --compact --no-input --no-color.
Global format flags share one contract on promoted, novel, sync, and --deliver paths:
--json- one JSON document on stdout (sync progress events go to stderr)--compact- keep identity/status/timestamp fields; does not change the document vs stream shape--csv/--plain- tabular rows (collection envelopes unwrap to the row array).--csvprefixes'on a cell or header key that starts with=,+,-,@, tab, CR, or LF (fullwidth=+-@too) so a spreadsheet does not run it as a formula; a plain signed number such as-5or+2.5e3is unchanged.--quiet- one identity value per row, no envelopePipeable - JSON on stdout, errors on stderr
Filterable -
--selectkeeps a subset of fields. Dotted paths descend into nested structures; arrays traverse element-wise. Critical for keeping context small on verbose APIs:eset-protect-cli detection-groups list --agent --select category,circumstances,cloudOfficeTenantUuidPreviewable -
--dry-runshows the request without sendingOffline-friendly - sync/search commands can use the local SQLite store when available
Non-interactive - never prompts, every input is a flag
Explicit confirmation -
--agentdoes not imply--yes; pass--yesseparately only after the target, arguments, and side effects are clearExplicit retries - use
--idempotentonly when an already-existing create should count as success, and use--ignore-missingonly when a missing delete target should count as success
Response envelope
Commands that read from the local store or the API wrap output in a provenance envelope:
{
"meta": {"source": "live" | "local", "synced_at": "...", "reason": "..."},
"results": <data>
}
Parse .results for data and .meta.source to know whether it's live or local. A human-readable N results (live) summary is printed to stderr only when stdout is a terminal AND no machine-format flag (--json, --csv, --compact, --quiet, --plain, --select) is set - piped/agent consumers and explicit-format runs get pure JSON on stdout.
Paths and state
Agents should treat the CLI's path resolver as part of the runtime contract:
Use
--home <dir>for one invocation, or setESET_PROTECT_HOME=<dir>to relocate all four path kinds under one root.Use per-kind env vars only when a specific kind must diverge:
ESET_PROTECT_CONFIG_DIR,ESET_PROTECT_DATA_DIR,ESET_PROTECT_STATE_DIR,ESET_PROTECT_CACHE_DIR.Resolution order is per-kind env var,
--home,ESET_PROTECT_HOME, XDG (XDG_CONFIG_HOME,XDG_DATA_HOME,XDG_STATE_HOME,XDG_CACHE_HOME), then platform defaults.configcontains settings likeconfig.tomland profiles.datacontainscredentials.toml(the cached access and refresh tokens), the synced mirrordata-<hash>.db(the hash is scoped to region and API user;doctorprints the exact path), cookies, and auth sidecars.statecontains persisted queries, jobs, andteach.log.cachecontains regenerable HTTP/cache files.Stored secrets live in
credentials.tomlunder the data dir. Existing legacyconfig.tomlsecrets are read for compatibility and leaveconfig.tomlon the first auth write.Run
eset-protect-cli doctor --fail-on warnto surface path and credential-location warnings.agent-contextexposes a schema v4pathsblock for agents that need the resolved dirs.For MCP, pass relocation through the MCP host config. The MCP binary does not inherit CLI flags:
{ "mcpServers": { "eset-protect": { "command": "eset-protect-mcp", "env": { "ESET_PROTECT_HOME": "/srv/eset-protect" } } } }On Windows, use a Windows path with escaped backslashes, for example
"ESET_PROTECT_HOME": "C:\\Users\\<you>\\AppData\\Local\\eset-protect"(a folder under your own profile, so other local users cannot read the mirror or the cached tokens).
Fleet precedence: an inherited per-kind env var overrides an explicit --home for that kind. Use ESET_PROTECT_HOME or per-kind vars as durable fleet levers, and use --home only for a single invocation. Relocation is not reversible by unsetting env vars; move files manually before clearing ESET_PROTECT_HOME, or doctor will not find credentials left under the former root.
Local data is unencrypted and relies on OS account permissions plus disk encryption. Reads in the default --data-source auto mode are written to the SQLite store; GET responses are cached for five minutes under the cache dir. local-data purge previews what it would erase (store files, response cache, learn journal) and deletes only with --yes; it is not exposed over MCP. ESET_PROTECT_NO_LOCAL_DATA=1 disables the response cache and store writes, turns auto reads into live reads, and blocks sync and workflow archive; --data-source local still reads an existing store, and ESET_PROTECT_DATA_SOURCE sets the --data-source default.
Automatic learning
This CLI ships a self-capturing learning loop. The CLI does its own bookkeeping: every invocation is journaled locally, a failed flag followed by a corrected retry auto-derives a flag_alias candidate, and a teach on a query family without a playbook auto-synthesizes a playbook_candidate from the session's journal. Your job is judgment only: recall first, act on surfaced candidates, teach the final answer, playbook amend when you observe a correction. You never record failures by hand.
Step 1: recall before any discovery
Before list/search/drill commands on a new user question, pass the question as an argv or MCP tool argument to recall --agent. Do not interpolate user-controlled text into a shell command line.
Quoted recall "<question>" breaks on an apostrophe, which is ordinary English. A quoted heredoc breaks when a body line equals the delimiter, and that delimiter is published in these docs. Write the question with a non-shell file-writing tool, then read it back as data:
# Write the question verbatim with your file-writing tool (no shell involved).
# Command substitution on a file only ever yields data - the shell never
# parses the file's bytes as syntax.
QUERY=$(cat /path/to/question.txt)
eset-protect-cli recall "$QUERY" --agent
Prefer MCP: pass the question as the tool's query argument. "$QUERY" after a file read is argv-safe; putting the question itself in the command text is not.
The response envelope:
{
"query": "...",
"normalized": "<normalized form>",
"query_entities": ["..."],
"found": true | false,
"match_score": 0.0,
"results": [
{ "resource_id": "...", "resource_type": "...", "venue": "...",
"confidence": 2, "entity_match": "exact|partial|unknown",
"source": "taught|preseed|pattern", "warnings": ["..."] }
],
"mismatches": [ /* only when --debug-mismatches */ ],
"warnings": [ /* top-level */ ],
"candidates": [
{ "id": 12, "class": "flag_alias | playbook_candidate",
"summary": "...", "sightings": 3, "last_seen": "...",
"rationale": "...",
"next_action": ["<trial command>", "eset-protect-cli learnings confirm 12"] }
],
"playbook": {
"query_family": "...",
"playbook": {
"steps": [ { "cmd": "<command with {slot} substitution>", "purpose": "..." } ],
"entity_slots": ["$ENTITY"],
"expected_tool_calls": 3
},
"slots_resolved": { "$ENTITY": { "token": "<live token>", "canonical": "<canonical>" } },
"notes": "<workarounds + gotchas for this query family>"
},
"notes": "<duplicate surface for non-playbook callers>"
}
Empty-store short-circuit: if the store has no learnings, playbooks, or candidates yet (recall finds nothing and learnings list and learnings candidates are both empty), skip recall for the rest of this session instead of taxing every query; resume recall-first once something has been taught.
Step 2: decision tree
Read candidates, playbook, notes, results[0], and warnings in that order:
if Candidates present (warnings include "candidates_present"):
-> candidates are try-then-confirm, never facts. Follow each candidate's
two-step next_action verbatim: run the trial command first, then run
`learnings confirm <id>` only after the trial verified the behavior.
Reject a wrong candidate with `learnings reject <id>`.
-> NEVER re-teach something recall surfaced as a candidate; confirm or
reject that candidate instead of teaching a duplicate.
-> candidates ride alongside playbooks and resource hits, not instead of
them; continue with the branches below after acting on them.
if Playbook present:
-> READ Playbook.notes verbatim FIRST (workarounds + gotchas the CLI surface doesn't expose)
-> replay Playbook.steps in order, substituting Playbook.slots_resolved entries
for the entity slot tokens. If a step's slot is unresolved, fall back to
discovery for that step only.
-> the Playbook's expected_tool_calls is a budget; if you find yourself running
materially more, record the divergence via `eset-protect-cli playbook amend`
at end-of-session.
elif Notes present (no Playbook):
-> read Notes verbatim before any discovery step; they carry known gotchas
for this query family even when no structured choreography exists yet.
elif Found AND Results[0].EntityMatch == "exact" AND Results[0].Confidence >= 2:
-> skip discovery; fetch live data for Results[*].ResourceID in parallel
elif Found AND Results[0].EntityMatch == "partial":
-> candidate hint, NOT a hit; read the resource title to validate before trusting
elif (any row in Mismatches[] when --debug-mismatches was passed):
-> treat as cold start; the stored learning is for a different entity
(different canonical resolved from query_entities)
else: // Found == false, no playbook, no notes
-> cold start; run discovery normally; teach the answer afterward (Step 4).
If the family has no playbook yet, that teach auto-synthesizes a
playbook candidate from this session's journal - you do not need to
record one by hand.
Playbook and Notes are orthogonal to the per-resource path. A recall response can carry both a Playbook AND a Results[] hit - use both: the Playbook tells you which choreography to run; the resource hits short-circuit specific steps. Default to skipping mismatches; pass --debug-mismatches only when investigating cold-start surprises.
Candidate judgment details: learnings confirm <id> prints the candidate's full payload before materializing it - check that the printed payload matches the behavior you verified. learnings reject <id> tombstones the derivation signature so the same candidate does not resurface. The envelope carries only the few candidates worth acting on now; eset-protect-cli learnings candidates lists the full open set.
Graceful degradation: if learnings confirm is an unknown command, you are driving an older binary - ignore the candidates guidance and follow the rest of the protocol.
Step 3: always read warnings
low_confidence: row exists atconfidence<2. Treat as a hint, not a skip-discovery hit.resource_not_in_store: the local store doesn't have the resource the learning points at. The match validator couldn't classify entities - direct-fetch and re-evaluate.cross_alias_match(per-result): the row was taught under a different alias and matched the live query's canonical viaentity_lookups(e.g., a "USA" teach satisfying a "United States" recall). Trust the resource_id.similar_shape_different_entity:<canonical>(top-level): a structurally matching row exists but its canonical entity differs from the live query's. Treated as cold start; the warning carries the conflicting canonical as a hint, but the row is NOT promoted into Results.ambiguous_alias(top-level): a single query entity resolved to multiple canonicals (e.g., "Cards" → Arizona Cardinals + St. Louis Cardinals). Surface the ambiguity from context before committing to a resource.candidates_present(top-level): the envelope carries acandidatessection. Handle it via the candidates branch in Step 2 before anything else.lookup_refresh_available(top-level): an entity in the query has no lookup row yet, but synced data could provide one. Runeset-protect-cli syncto refresh entity lookups.- Top-level
no_learnings_for_query_family: the table had no rows above the Jaccard floor. Pure cold start.
Step 4: teach & after finalizing your response - always
Teaching is unconditional. After resolving a query the store could not answer, background-teach the final resource mapping - no call-count threshold, no judging whether it was "worth" learning. The teach is the anchor of the loop: it triggers playbook synthesis for a family without a playbook, and same-referent phrasings fold into one family so near-duplicate teaches do not fragment the store. Fire it after assembling your user-facing response but BEFORE emitting it, with a shell & so the call returns immediately. Pass the query the same way as recall - argv/MCP, or file-then-$QUERY. Do not splice the question into the command text:
QUERY=$(cat /path/to/question.txt)
eset-protect-cli teach --query "$QUERY" --resource-type <type> --resource <id1> --resource <id2>
# (append shell `&` to background it)
Silent on success. Errors only land in teach.log under the resolved state dir. Teach the most specific resource - if the user asked a broad question and you walked through parent records to find the specific answer, teach the leaf id, not the parent. The CLI uses seeded entity_lookups for cross-alias resolution at recall time, so a teach under one alias (e.g., "Niners") satisfies future queries under another alias (e.g., "49ers", "San Francisco") automatically.
PII rule: teach the structural question with identifiers stripped - never include names, emails, phone numbers, account ids, or other personal identifiers in taught queries or notes. The CLI scans teach queries for obvious email/phone shapes and warns, but does not block; strip before teaching rather than relying on the warning.
Step 5: playbooks - optional flags, automatic synthesis
You do not need to decide whether a session "deserves" a playbook: a teach on a family without one auto-synthesizes a playbook_candidate from the session's journal, and the next session judges it via confirm/reject. Attach explicit playbook flags only when you already hold choreography worth recording verbatim - workarounds the CLI didn't surface (silently-dropped flags, undocumented params, pagination tricks, payload gotchas). Prefer the integrated one-call form - record the resource learning and the playbook in the same teach invocation:
# Common case: record both the resource learning AND the playbook in one call.
QUERY=$(cat /path/to/question.txt)
eset-protect-cli teach \
--query "$QUERY" \
--resource <id> \
--playbook-file ~/playbooks/<shape>.json \
--playbook-notes-file ~/playbooks/<shape>-notes.md
# (append shell `&` to background it)
# Alternate: playbook-only (no resource to record alongside).
QUERY=$(cat /path/to/question.txt)
eset-protect-cli teach-playbook \
--query "$QUERY" \
--playbook-file ~/playbooks/<shape>.json \
--notes-file ~/playbooks/<shape>-notes.md
Playbook files are JSON with steps, entity_slots, expected_tool_calls. Notes files are markdown carrying the gotchas verbatim. File-free callers (MCP-only agents) pass the same content inline: --playbook-json and --playbook-notes on the integrated teach form, --playbook-json and --notes on teach-playbook. On the integrated teach form, the playbook flags are optional - omit them entirely for a resource-only teach. On the standalone teach-playbook form, at least one of the playbook and notes flags must be set; both empty is rejected. Playbooks are keyed on the structural query family (entities stripped) so a recipe taught from one entity-shaped query applies to every other query of the same shape, with slots_resolved binding the live query's canonical at recall time.
When you DO find a playbook on a future recall, treat it as ground truth: replay the steps with slots_resolved substitutions, skip the discovery that the choreography already documents, and read notes before any step.
Step 6: playbook amend & when your debug response identifies a correction
If your debug-protocol response identifies a concrete correction the notes or playbook should know - a workaround, an undocumented endpoint shape, a stale field name, observed schema drift, an empty-payload fallback - fire playbook amend BEFORE emitting your user-facing response. Same fire-and-forget posture as teach. Pass the query and note as argv/MCP arguments, or write each with a non-shell file tool and read them back (QUERY=$(cat ...), NOTE=$(cat ...)). Do not interpolate either string into the command text:
QUERY=$(cat /path/to/question.txt)
NOTE=$(cat /path/to/note.txt)
eset-protect-cli playbook amend \
--query "$QUERY" \
--add-note "$NOTE"
# (append shell `&` to background it)
What counts as worth amending: a behavior you OBSERVED this session that future-you would benefit from knowing. Examples worth amending:
- A workaround for a CLI surface that silently drops or misorders a flag.
- An undocumented endpoint shape (response wrapped in
{meta, results}, payload nested two levels deeper than the docs claim). - Observed schema drift (a field renamed, an index that shifted between seasons, a category label that the API now returns lower-cased).
What does NOT belong in notes:
- The year-specific or entity-specific answer to the user's question. That's the response, not a learning.
- Per-team / per-athlete / per-row data the playbook already retrieves at runtime.
- Statements that paraphrase what the existing notes already say.
The amend command appends to the family's existing notes with a timestamped marker ([amend YYYY-MM-DDTHH:MMZ]: <text>). Multiple amends accumulate; the audit trail is visible. If no playbook exists yet for the family, amend creates a notes-only one (so cold-start corrections still land).
PII discipline for amend notes
playbook amend notes are designed to potentially flow upstream as shared knowledge in future versions of the CLI generator. Keep them clean of user-identifying content so the upstream-contribution path stays open without retroactive scrubbing:
- Do NOT embed paths to user filesystems, personal API keys or tokens, user email addresses, user GitHub handles, or specific query histories tied to a single user.
- Acceptable: endpoint shapes, undocumented field names, API gotchas, observed schema drift, workarounds for CLI surfaces, generalizable pagination or retry tactics.
If a correction is only meaningful with user-specific context, it belongs in a personal note, not in the playbook amend.
Measuring the loop
eset-protect-cli learnings stats reports recall hit rate, teach-to-reuse, playbook resolution rate, and candidate confirm/reject counts from the local learn_events table. Rates are null until they have a denominator; everything stays on this machine. Use it to check whether the loop is earning its keep for this CLI.
Disabling learning
--no-learnon a single command short-circuits bothrecalland theteachwrite path. Use for deterministic agent flows or tests that must not be affected by accumulated learnings.ESET_PROTECT_NO_LEARN=truein the environment globally disables the pipeline.
Agent Feedback
When you (or the agent) notice something off about this CLI, record it:
eset-protect-cli feedback "the --since flag is inclusive but docs say exclusive"
eset-protect-cli feedback --stdin < notes.txt
eset-protect-cli feedback list --json --limit 10
Entries are stored locally as feedback.jsonl under the resolved data dir. They are never POSTed unless ESET_PROTECT_FEEDBACK_ENDPOINT is set AND either --send is passed or ESET_PROTECT_FEEDBACK_AUTO_SEND=true. Default behavior is local-only.
Write what surprised you, not a bug report. Short, specific, one line: that is the part that compounds.
Output Delivery
Every command accepts --deliver <sink>. The output goes to the named sink in addition to (or instead of) stdout, so agents can route command results without hand-piping. Three sinks are supported:
| Sink | Effect |
|---|---|
stdout |
Default; write to stdout only |
file:<path> |
Atomically write output to <path> as a private 0600 file (exclusive randomly named temp in the destination directory, then rename; a planted symlink is not followed). Binary-response commands write decoded payload bytes (not the base64 JSON envelope) and print a small JSON receipt on stdout; --json/--csv do not refuse when this sink is set. |
webhook:<url> |
POST the output body to the URL (application/json). Redirects are not followed; a 3xx response fails the delivery and the body is not sent to the Location. |
Unknown schemes are refused with a structured error naming the supported set. Webhook failures, including redirects, return non-zero and log the URL and HTTP status on stderr.
Named Profiles
A profile is a saved set of flag values, reused across invocations. Use it when a scheduled or recurring agent reuses the same saved flags while providing different input each run.
eset-protect-cli profile save briefing --json
eset-protect-cli --profile briefing detection-groups list
eset-protect-cli profile list --json
eset-protect-cli profile show briefing
eset-protect-cli profile delete briefing --yes
Explicit flags always win over profile values; profile values win over defaults. agent-context lists all available profiles under available_profiles so introspecting agents discover them at runtime.
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 2 | Usage error (wrong arguments) |
| 3 | Resource not found |
| 4 | Authentication required |
| 5 | API error (upstream issue) |
| 6 | Partial failure |
| 7 | Rate limited (wait and retry) |
| 10 | Config error |
Argument Parsing
Parse $ARGUMENTS:
- Empty,
help, or--help→ showeset-protect-cli --helpoutput - Starts with
install→ ends withmcp→ MCP installation; otherwise → see Prerequisites above - Anything else → Direct Use (execute as CLI command with
--agent)
MCP Server Installation
- Install the MCP binary (run the install script from the Prerequisites section, or see mcp-install.md for per-agent wire-up).
- Register with Claude Code:
claude mcp add eset-protect-mcp -- eset-protect-mcp - Verify:
claude mcp list
For a remote host, eset-protect-mcp --transport http requires ESET_PROTECT_MCP_HTTP_TOKEN set to at least 32 characters (callers send Authorization: Bearer <token>; openssl rand -hex 32 prints 64 hex characters and meets that minimum), serves http://127.0.0.1:7777/mcp by default, and needs --tls-cert/--tls-key for any non-loopback --addr. Requests with no Origin header are accepted. Loopback origins are accepted; any other Origin is refused unless listed in ESET_PROTECT_MCP_HTTP_ALLOWED_ORIGINS (comma-separated scheme://host[:port] values). ESET_PROTECT_MCP_READ_ONLY=1 drops mirrored write commands (local-write commands that only touch the CLI's own learn store stay) and makes the raw execute tool refuse state-changing calls.
Direct Use
- Check if installed:
which eset-protect-cliIf not found, offer to install (see Prerequisites at the top of this skill). - Match the user query to the best command from the Unique Capabilities and Command Reference above.
- Execute with the
--agentflag:eset-protect-cli fleet health --agent - If ambiguous, drill into subcommand help, e.g.
eset-protect-cli respond isolate --help.