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 5).
export ENTRAADM_MAX_PAGES_DEFAULT=5

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

Download files

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

Source Distribution

entraadm_mcp-0.1.0.tar.gz (37.6 kB view details)

Uploaded Source

Built Distribution

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

entraadm_mcp-0.1.0-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: entraadm_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 37.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for entraadm_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2cea4424542b5b6209d1ccb63f4ab61b12c704ac97043b2bcd3d7a2b4c8d8bf5
MD5 d810f47c320e805ca193717278e2c389
BLAKE2b-256 8a47510db8134bffe12014f8a0ace1850f539261d56341113d599c64349b9248

See more details on using hashes here.

Provenance

The following attestation bundles were made for entraadm_mcp-0.1.0.tar.gz:

Publisher: release.yml on shigechika/entraadm-mcp

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

File details

Details for the file entraadm_mcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: entraadm_mcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 23.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for entraadm_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c436b559e13fc37e20edbefb825c5044a0bda7c3e88d42e57739a93669e35298
MD5 24e230a263006432c955182234304123
BLAKE2b-256 04eb51f637bbe3205fd4afbcae2f671b0bfac023e341083fa127d5a2ff782c69

See more details on using hashes here.

Provenance

The following attestation bundles were made for entraadm_mcp-0.1.0-py3-none-any.whl:

Publisher: release.yml on shigechika/entraadm-mcp

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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

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