Skip to content

nitzpo/mitre-attack-cli

Repository files navigation

mitre-attack-cli

A fast, dependency-light Python CLI for MITRE ATT&CK. Built for coding agents to invoke through the Bash tool: clean JSON on stdout, useful errors on stderr, predictable exit codes. No MCP daemon required.

Why

Coding agents that need MITRE ATT&CK knowledge currently rely on MCP servers (e.g. stoyky/mitre-attack-mcp, MHaggis/mitre-attack-mcp). An MCP brings infrastructure — a long-lived process, a transport, an MCP-aware host — to what is fundamentally a read-only data lookup. This CLI is just a binary on $PATH, easier to install (uvx mitre-attack-cli ...), easier to use in CI or sandboxes, and equally agent-friendly: every command returns parseable JSON.

Quickstart

# One-shot via uvx (no install)
uvx mitre-attack-cli technique get T1059.001

# Or install globally
uv tool install mitre-attack-cli
mitre technique get T1059.001

First invocation downloads the ATT&CK Enterprise STIX bundle (~25–45 MB) into ~/.cache/mitre-attack-cli/. Subsequent calls are local.

Output

Default is compact JSON on stdout. Errors are JSON envelopes on stderr.

$ mitre technique get T1059.001
{"attack_id":"T1059.001","name":"PowerShell","tactics":["execution"],"platforms":["Windows"],"is_subtechnique":true}

$ mitre technique get T9999
# stdout: empty
# stderr:
{"error":"Not found: 'T9999' in enterprise@v19.0.","code":"NOT_FOUND","hint":"Try `mitre search T9999` or `mitre update`."}
# exit code: 3

Three formats:

Flag Behavior
--format json (default) Compact JSON. Single line per object, single array for lists.
--format jsonl Newline-delimited JSON. Best for piping large lists to jq.
--pretty (or --format pretty) Markdown for objects, table for lists. For humans.

Exit codes: 0 success · 2 invalid usage · 3 not found · 4 missing data · 5 network · 1 other.

Subcommands

mitre technique     get | list | subtechniques | parent | groups-using | software-using
                    | mitigations | detections | procedures | tactics
mitre tactic        get | list | techniques
mitre group         get | list | techniques | software | campaigns
mitre software      get | list | groups-using | techniques | campaigns
mitre mitigation    get | list | techniques
mitre campaign      get | list | groups | techniques | software
mitre data-source   get | list | components | techniques
mitre search        <query> [--type … --regex --limit N]
mitre update        [--domain … --all-domains --force]
mitre info          # diagnostic JSON: cache path, active version, etc.

Global flags must precede the subcommand:

--domain {enterprise|mobile|ics}     # default: enterprise
--attack-version v19.0               # default; or MITRE_ATTACK_VERSION env
--data-path PATH                     # pre-downloaded STIX dir/file (or MITRE_ATTACK_DATA_PATH)
--format {json|jsonl|pretty}         # default: json
--pretty                             # human-readable
--fields a,b,c                       # whitelisted field projection
--full                               # all fields
--include-revoked                    # include revoked/deprecated objects
--offline                            # fail rather than download
--quiet                              # suppress stderr progress logs

Examples

# Lookup
mitre --pretty technique get T1059.001
mitre group get APT28                       # alias resolves to G0007

# Relationships
mitre technique groups-using T1059.001
mitre technique mitigations T1059.001
mitre group techniques G0007

# Listings + filtering + field selection
mitre technique list --tactic lateral-movement --platform Windows
mitre --fields attack_id,name --format jsonl technique list

# Search
mitre search "kerberoasting" --limit 5
mitre search '^Power' --regex --type technique

# Diagnostics
mitre info

Data: bring your own or let it download

Three resolution paths, in order:

  1. --data-path PATH (or MITRE_ATTACK_DATA_PATH env). Accepts:
    • A direct file (/path/to/enterprise-attack.json)
    • A flat directory containing enterprise-attack.json
    • A {version}/{domain}-attack.json layout (e.g. ~/cache/v19.0/enterprise-attack.json). This matches common ETL cache layouts, so pre-downloaded bundles are reused without re-downloading.
  2. Local cache at ~/.cache/mitre-attack-cli/{version}/{domain}-attack.json (honors XDG_CACHE_HOME).
  3. Download from https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master/{domain}-attack/{domain}-attack-{version}.json unless --offline.

Refresh is explicit via mitre update. The pinned default version is v19.0; override with --attack-version v15.1 or MITRE_ATTACK_VERSION=v15.1. The version is the reproducibility contract — same CLI version + same --attack-version returns the same answers.

Only the domain you request is downloaded. --domain mobile will fetch mobile-attack lazily; enterprise and ics aren't touched.

Performance

Coding agents fire many short-lived mitre ... invocations. Loading the full ATT&CK STIX bundle through stix2 every time would cost ~2.4 s per call. To avoid that, the CLI maintains a compiled snapshot cache next to each STIX file:

~/.cache/mitre-attack-cli/v19.0/
├── enterprise-attack.json          # raw STIX (downloaded on demand)
└── enterprise-attack.cache.msgpack # compiled snapshot — used by every warm run
Run Wall clock What happens
First (cold) ~25–30 s mitreattack-python loads the STIX bundle once, builds a msgpack snapshot with every relationship pre-resolved.
Warm ~150–300 ms mitreattack-python is never imported. The hot path is typer + httpx + msgpack + pure-dict lookups.
Cache invalid ~25–30 s Rebuilt automatically when the STIX file's mtime/size changes, when the CLI version changes, or when you run mitre update --force.

The snapshot uses msgpack — a binary, data-only format. It lives in your own writable cache dir even when the STIX bundle itself comes from --data-path, so a read-only data path remains read-only.

Install

uv tool install mitre-attack-cli         # recommended
pipx install mitre-attack-cli            # alternative
uvx mitre-attack-cli technique get T1059 # no install, one-shot

Bundled Claude Code skill

A drop-in Claude Code skill lives at skill/ that teaches Claude to invoke this CLI whenever the user asks anything ATT&CK-shaped — technique/group/mitigation IDs, threat-actor names, TTPs, log-to-framework questions — instead of recalling stale ATT&CK content from training data.

In a quick-smoke evaluation across three realistic prompts the skill version cited only verified IDs (100% accuracy) versus 69% for the baseline that relied on recall. See skill/SKILL.md for what it teaches.

To activate it locally, symlink the folder into your ~/.claude/skills/:

ln -s "$(pwd)/skill" ~/.claude/skills/mitre-expert

Claude Code will pick it up on the next session.

Development

git clone https://github.com/nitzpo/mitre-attack-cli
cd mitre-attack-cli
uv sync --all-groups
uv run python tests/fixtures/build_tiny_bundle.py   # build test fixture
uv run pytest                                       # 114 tests, ~1s
uv run ruff check . && uv run ruff format --check .

The project uses uv for dependency management, ruff for lint+format, ty for type checking, and pytest for tests. CI runs on Python 3.12 and 3.13 across Ubuntu and macOS.

Architecture

Four layers, each independently testable:

cli/      # Typer commands, global flags, CliContext
└─ output/   # JSON/JSONL/markdown formatters, field projection, error envelope
   └─ domain/  # ranked search, per-kind list/get/filter logic
      └─ repo/   # AttackRepository wrapping mitreattack-python's MitreAttackData
         └─ data/  # path resolution, httpx download with atomic-rename

The data layer is intentionally agnostic about ATT&CK schema specifics so future loaders (D3FEND, CAPEC, Atomic Red Team) can plug in without disturbing the CLI surface.

Differences vs the existing MCPs

  • No daemon, no MCP server, no transport. Just a binary on $PATH.
  • JSON-by-default for agents; --pretty for humans.
  • Lazy per-domain download — only fetches what you ask for, not all three.
  • Pre-downloaded path interop — points at any existing STIX bundle including versioned {version}/{domain}-attack.json layouts.
  • Stable error envelope with documented codes (NOT_FOUND, MISSING_DATA, …) and useful hints in every error.

License

MIT. See LICENSE.

About

A fast, dependency-light Python CLI for MITRE ATT&CK. Built for coding agents to invoke through the Bash tool: clean JSON on stdout, useful errors on stderr, predictable exit codes. No MCP daemon required.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages