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.0.tar.gz (62.8 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.0-py3-none-any.whl (100.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: mde_client-0.1.0.tar.gz
  • Upload date:
  • Size: 62.8 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.0.tar.gz
Algorithm Hash digest
SHA256 c0ea5d2e4cf414f1eddd7ad58fe5f5f10ce7411b5885200063bb5128ba997faf
MD5 8629d19346827914e9bd3d7d929c9476
BLAKE2b-256 79f9f5da0ce02406cf7f7257811b4129ffef798c3daed67cb76aabfdfb196ba0

See more details on using hashes here.

Provenance

The following attestation bundles were made for mde_client-0.1.0.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.0-py3-none-any.whl.

File metadata

  • Download URL: mde_client-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 100.3 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c1fe711023b44474384c0e04499667cb7bb96bf08389a0bd059bb890ab9b43f3
MD5 010bf9d294266c479e9fe32b9ead92b0
BLAKE2b-256 7a0ee44d73ec5114ea128257c4ca7a1da9ecd1b97fb3c92ea1e6fd94d6843743

See more details on using hashes here.

Provenance

The following attestation bundles were made for mde_client-0.1.0-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