Skip to main content

Python client for the Microsoft Defender for Endpoint API with lazy results and Arrow/Polars output.

Project description

mde-client

Python client for the Microsoft Defender for Endpoint API.

mde-client is organized around a single top-level MDEClient with lazy endpoint wrappers. Endpoint calls return result handles that fetch on first materialization, cache the payload, and can be rendered as Python dictionaries, JSON, PyArrow tables, or Polars DataFrames.

Highlights

  • Client-credentials authentication through MSAL.
  • Lazy endpoint results with shared materialization methods.
  • Coverage across machine inventory, alerts, investigations, authenticated scans, advanced hunting, assessments, remediation, and machine actions.
  • Built-in support for Defender file-export endpoints through ViaFiles.
  • Constructor injection for httpx.Client and msal.TokenCache to keep tests and custom transports straightforward.

Documentation

Use this README for the package overview and quick start.

Install

uv add mde-client

The package also defines optional extras when you want to declare dataframe support explicitly in your environment:

uv add "mde-client[arrow]"
uv add "mde-client[arrow,polars]"

If you are developing inside this monorepo, use the root workflow instead:

uv sync --all-packages --all-groups

Authentication Prerequisites

You need an Azure AD app registration that can use the OAuth 2.0 client-credentials flow against Microsoft Defender for Endpoint.

  • Tenant ID
  • Client ID
  • Client secret
  • Defender application permissions granted to the app registration

MDEClient uses MSAL and the Defender default scope under the hood.

Quick Start

from mde_client import AuthenticationError, MDEClient
from mde_client.endpoints.machines import MachinesQuery

try:
    with MDEClient(
        tenant_id="YOUR_TENANT_ID",
        client_id="YOUR_CLIENT_ID",
        client_secret="YOUR_CLIENT_SECRET",
    ) as client:
        machines = client.machines.get_all(
            MachinesQuery(healthStatus="Active", page_size=500)
        ).to_dicts()

        print(f"Fetched {len(machines)} machines")

        machine = client.machines.get("machine-guid").to_dicts()[0]
        print(machine["computerDnsName"])

        logon_users = client.machines.logonusers("machine-guid").to_dicts()
        print(logon_users[:3])
except AuthenticationError as exc:
    print(f"Authentication failed: {exc}")

Public Imports

The top-level package exports:

  • MDEClient
  • AuthenticationError
  • ViaFiles
  • ViaFilesConfig
  • EmptyExportBlobError

Result Model

Most endpoint methods return a lazy results wrapper rather than an eager list or model.

results = client.machines.get_all(MachinesQuery(healthStatus="Active"))

# No request is made until a terminal method runs.
rows = results.to_dicts()
payload = results.to_json()
table = results.to_arrow()
frame = results.to_polars()

# Drop the cached payload and fetch again on the next terminal call.
fresh_rows = results.refresh().to_dicts()

Behavior to account for:

  • Collection and single-item lookups use the same wrapper style.
  • Pagination is automatic for list endpoints unless you set $top or $skip through a query model.
  • Materialization methods reuse cached data until refresh() is called.
  • to_dicts() is the simplest Python-native representation for downstream code.
  • Write helpers return either lazy result wrappers or bool, depending on whether the underlying API returns an entity payload or an empty success response.

Endpoint Surface

MDEClient exposes endpoint properties for the current Defender surface. The most commonly used groups are:

Property What it covers
machines Machine inventory, machine lookups, related users and alerts, installed software, vulnerabilities, recommendations, machine-scoped actions, and several assessment exports
alerts Alert listing, alert relationships, create-by-reference flows, and batch updates
authenticated_definitions / authenticated_agents Authenticated scan definitions, scanner agents, and scan history workflows
advanced_queries Advanced hunting query execution
software, vulnerabilities, recommendations, remediations, score, baseline_configurations Exposure, remediation, score, and baseline-related datasets
browser_extension, certificate_inventory, device_av_health Assessment inventory and export-backed datasets
files, domain, ips, user, investigations, indicators, library, machine_actions Related entity lookups, response actions, indicator management, and live response library operations

The full property list is defined on MDEClient, but the important pattern is consistent: endpoint methods return a results wrapper or a small success value, and the wrapper APIs stay uniform across endpoint families.

Query Models

Query models inherit from a shared base that supports:

  • page_size mapped to pageSize
  • top mapped to $top
  • skip mapped to $skip
  • OData-style $filter construction from non-null model fields

Defender-specific query fields intentionally preserve upstream API names such as healthStatus, machineTags, and lastSeen instead of normalizing everything to snake_case.

File Exports And ViaFiles

Some Defender endpoints return exportFiles SAS URLs instead of embedding the final dataset in the initial API response. For those endpoints, the result wrappers use ViaFiles internally to:

  • download blobs concurrently
  • stream gzip or plain NDJSON responses
  • parse records incrementally
  • append them into an ArrowRecordContainer

Most callers do not need to use ViaFiles directly because export-backed endpoint wrappers already handle it. Use ViaFiles yourself when you already have export URLs and want to stream them into your own Arrow container with custom tuning through ViaFilesConfig.

EmptyExportBlobError is the internal signal used when a blob returns 200 OK but contains no records. The public downloader retries and then skips confirmed-empty blobs.

Customization And Testing

MDEClient accepts both a custom httpx.Client and an optional MSAL token cache:

import httpx
import msal

client = MDEClient(
    tenant_id="YOUR_TENANT_ID",
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    http_client=httpx.Client(transport=mock_transport),
    token_cache=msal.TokenCache(),
)

This makes it straightforward to test request construction, inject custom transports, or persist tokens outside the default in-memory cache.

Errors And Operational Notes

  • AuthenticationError is raised when MSAL cannot acquire a token.
  • HTTP failures bubble up through httpx status handling.
  • Export-file downloads retry transient failures and raise a runtime error if a blob cannot be fetched successfully; confirmed-empty blobs are skipped after retry logging.
  • The client is a context manager; use with MDEClient(...) as client: when possible so the underlying HTTP session is closed promptly.

License

Apache-2.0

Releasing

mde-client is published to PyPI from this monorepo via a tag-driven GitHub Actions workflow that authenticates with PyPI Trusted Publishing (OIDC, no API tokens).

To cut a release:

  1. Bump version in pyproject.toml.
  2. Add a matching [<version>] - <date> section to CHANGELOG.md and move items out of [Unreleased].
  3. Run just quality locally (includes uv build + twine check).
  4. Commit the version bump and changelog on main.
  5. Tag the commit mde-client-v<version> (e.g. mde-client-v0.1.0) and push the tag. The release workflow verifies the tag matches the pyproject version, builds the sdist and wheel, and publishes to PyPI.

Future vendor packages in this monorepo follow the same scheme: <distribution-name>-v<version>.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mde_client-0.1.1.tar.gz (62.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mde_client-0.1.1-py3-none-any.whl (100.4 kB view details)

Uploaded Python 3

File details

Details for the file mde_client-0.1.1.tar.gz.

File metadata

  • Download URL: mde_client-0.1.1.tar.gz
  • Upload date:
  • Size: 62.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mde_client-0.1.1.tar.gz
Algorithm Hash digest
SHA256 89389b93b0a6e606c38b0a07fa8d8e6a1105a73258fec52f2e1a5055e3785b30
MD5 2cbfbc5b62d5ccf02b3dd39736e69b96
BLAKE2b-256 47ff29defd4863d7f5502ecf817e08ea0bacc552818d183741b3d45445ac04c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for mde_client-0.1.1.tar.gz:

Publisher: release.yml on ProxayFox/pysec-clients

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mde_client-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: mde_client-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 100.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mde_client-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0bad5aa1c510db63b86f9f33a284f4fd66539a952c43b8f2eafe37ee169df7ab
MD5 d049a253388485a5425739efe85f0ec0
BLAKE2b-256 f3e9b69bcaf68751e507394f1e2b2d006ccbd47e3487b1a584bacfdd34124a48

See more details on using hashes here.

Provenance

The following attestation bundles were made for mde_client-0.1.1-py3-none-any.whl:

Publisher: release.yml on ProxayFox/pysec-clients

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page