Skip to main content

entraadm-mcp

English | 日本語

MCP server for Microsoft Entra ID sign-in and audit-log triage. Read-only.

Why this instead of the official Microsoft MCP Server for Enterprise

Microsoft ships an official MCP Server for Enterprise for Entra ID data. It is a good fit for an interactive admin at a keyboard, and is not a fit for an unattended triage bot:

  • Delegated auth only. The official server does not support app-only (client credentials) auth, so it cannot run headless behind a service account. entraadm-mcp is built for that case: app-only in production, with a delegated (az login) fallback for local development.
  • A general-purpose Graph query tool, not a fixed tool set. The official server exposes one tool that lets the model construct arbitrary GET/schema-discovery calls against Microsoft Graph. That is flexible for a human, and awkward to put behind an allow-list for an automated triage profile. entraadm-mcp exposes seven fixed, read-only tools instead.
  • No AADSTS translation. Sign-in failures come back as raw error codes; triage still needs a lookup table. entraadm-mcp annotates every sign-in failure with what the code actually means.
  • No cross-request aggregation. Microsoft Graph itself cannot filter sign-ins on status/errorCode server-side, and has no built-in password-spray view. signin_failure_stats aggregates client-side and flags IPs with failed sign-ins against many distinct users — the pattern Entra's per-account smart lockout does not catch on its own.

Tools

Tool What it answers
health_check Is Graph reachable, and can this credential read sign-in logs?
get_user Is this account enabled, synced from on-prem, and what are its licenses?
signin_logs Why did this user's sign-in fail (or succeed), with the AADSTS code translated?
signin_failure_stats Tenant-wide failure aggregation: top error codes, users, apps, source IPs, and password-spray suspects
directory_audits Who changed what in the directory (block/unblock, attribute edits), and when?
get_user_auth_methods Is MFA actually registered for this account?
daily_brief One-call summary combining signin_failure_stats and directory_audits

Every tool is read-only. Write operations (unblocking an account, resetting a password, revoking a session) are out of scope for this server.

Auth model

Two auth modes, selected by which environment variables are set:

Mode When Env vars
app-only All three set ENTRAADM_TENANT_ID, ENTRAADM_CLIENT_ID, ENTRAADM_CLIENT_SECRET
azure-cli None set (uses the current az login session)

Setting one or two of the three app-only variables is a configuration error and the server refuses to start, rather than silently falling back to a different auth mode than intended.

Required Graph permissions

Tool(s) Permission Notes
get_user (base fields) User.Read.All
signin_logs, signin_failure_stats, directory_audits, get_user's sign_in_activity field AuditLog.Read.All (app-only) or the Reports Reader directory role (delegated)
get_user_auth_methods UserAuthenticationMethod.Read.All App-only only; not available under delegated (az login) auth in a typical tenant role assignment

A missing permission never crashes a tool. It degrades that tool (or that one field) to {"error": "...", "missing_permission": "..."} with a human-readable explanation of what role or permission is needed, so health_check and every other tool stay usable even before full permissions are granted.

Setup

uv tool install entraadm-mcp
# or
pip install entraadm-mcp

Configuration

Set the three app-only variables for production/unattended use:

export ENTRAADM_TENANT_ID=00000000-0000-0000-0000-000000000000
export ENTRAADM_CLIENT_ID=00000000-0000-0000-0000-000000000000
export ENTRAADM_CLIENT_SECRET=your-client-secret

Or leave all three unset and run az login first for local development.

Optional:

# Default page cap for the log-scanning tools (1-50, default 50).
export ENTRAADM_MAX_PAGES_DEFAULT=50
# Wall-clock budget per tool call in seconds (default 45; 0 disables). A hosted MCP
# client cuts a call off at about 60 s, so a scan stops at the budget and returns
# what it has with capped=true.
export ENTRAADM_DEADLINE=45

Usage

Claude Code (plugin)

/plugin marketplace add shigechika/entraadm-mcp
/plugin install entraadm-mcp@entraadm-mcp

Claude Code (manual)

Add to .mcp.json:

{
  "mcpServers": {
    "entraadm-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["entraadm-mcp"],
      "env": {
        "ENTRAADM_TENANT_ID": "${ENTRAADM_TENANT_ID:-}",
        "ENTRAADM_CLIENT_ID": "${ENTRAADM_CLIENT_ID:-}",
        "ENTRAADM_CLIENT_SECRET": "${ENTRAADM_CLIENT_SECRET:-}"
      }
    }
  }
}

Direct execution

entraadm-mcp

CLI options

Option Effect
--version Print the version and exit
--check Resolve auth, probe Graph reachability and sign-in log access, print a report, exit 0 (or 1 on config error)

Notes

  • Coverage contract. Every result that walks a paged Graph collection carries a capped boolean when its window was not fully scanned — a partial scan is never reported as if it were exhaustive.
  • found: false is not an error. get_user and get_user_auth_methods answer a nonexistent account with {"found": false, ...}, not an error key — a typo'd userPrincipalName should never look like this server being broken.
  • Retention. Entra ID P1 retains sign-in and directory audit logs for 30 days. A window beyond that returns an empty result, not an error.

Development

uv sync --dev
uv run pytest -v
uv run ruff check .
uv run ruff format --check .

Live smoke test

uv run python scripts/smoke_test.py

Read-only, no payloads printed (tool names/statuses/row counts only), and bounded (small explicit windows/page caps) — nothing here writes to the tenant or scans more than a day of logs.

Releasing

This repository uses release-please driven by Conventional Commits. Merge a feat:/fix: PR to main, and release-please opens (or updates) a release PR; merging that PR tags a release and triggers the publish pipeline (PyPI, MCP Registry).

License

MIT

Metadata

Release files for entraadm-mcp 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for entraadm-mcp 1.0.1
File Size Uploaded
entraadm_mcp-1.0.1.tar.gz 42.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for entraadm-mcp 1.0.1
File Interpreter ABI Platform
entraadm_mcp-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 68.2 kB

Release files / entraadm_mcp-1.0.1.tar.gz

Download URL entraadm_mcp-1.0.1.tar.gz
Size 42.2 kB
Tags Source
SHA-256 checksum
How to use checksums
1b7da2016aba8967a68b2870086e96e95311294fb35b835fc6813c8f97df466c
BLAKE2b-256 checksum
How to use checksums
3cc72b661679050e1ddc6a043fd8e77c22f503ffaf463605eaf312638fe1cd7e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / entraadm_mcp-1.0.1-py3-none-any.whl

Download URL entraadm_mcp-1.0.1-py3-none-any.whl
Size 26.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd40863069338a7491f4896a11cf5e71b5e2608f746958d19aa42b5ba343b4d5
BLAKE2b-256 checksum
How to use checksums
421a276d6eef0793ebc1ecb347097dccdd0ddfe18c3409ce65fb1c80685e05d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

1.1.0

2 release files

This release

1.0.1 This release

2 release files

1.0.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page