entraadm MCP
MCP server for Microsoft Entra ID sign-in and audit-log triage (read-only by default)
Installation
Ajoutez ceci à la configuration MCP de votre client (Claude Desktop : claude_desktop_config.json ; Cursor : .cursor/mcp.json ; VS Code et les autres utilisent la même forme « mcpServers »).
entraadm-mcpclaude mcp add entraadm-mcp -- uvx entraadm-mcp==1.8.1
{
"mcpServers": {
"entraadm-mcp": {
"command": "uvx",
"args": [
"entraadm-mcp==1.8.1"
]
}
}
}
Documentation
Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.
Sommaire
entraadm-mcp
English | 日本語
MCP server for Microsoft Entra ID sign-in and audit-log triage, plus the two Azure-side questions a morning patrol asks with the same credential: are our Azure VMs backed up, and is Microsoft 365 itself degraded right now. 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 fourteen 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/errorCodeserver-side, and has no built-in password-spray view.signin_failure_statsaggregates 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 |
signin_success_stats | Tenant-wide success aggregation by source IP: IPs shared by several accounts, legacy-auth (SMTP/IMAP) successes — the view that finds a breach |
signin_by_ip | Every sign-in from one source IP: who got in from it, who was tried, and when |
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? |
list_subscriptions | Which Azure subscriptions can this credential see? (ARM) |
vm_backup_status | Which Azure VMs are backed up by Azure Backup, did the last backup succeed, and is any recovery point stale? (ARM) |
vm_backup_policies | What schedule and retention do the Azure VM backup policies have? (ARM) |
vm_activity | Did someone restart this Azure VM, or did Azure (host failure, maintenance)? Activity log and Resource Health for a time window (ARM) |
m365_service_health | Is a Microsoft 365 service degraded, and which incidents and advisories are open? |
daily_brief | One-call summary combining m365_service_health, 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.
The four Azure Resource Manager tools live in an Entra-named server on
purpose: they use the same app registration and the same azure-identity
credential object (a second token scope, https://management.azure.com/.default),
run in the same deployment and are covered by the same health_check. A
separate server for these tools would double the operational surface. They
are governance reads, not resource management; a subscription the credential
has no Reader role on simply does not appear in list_subscriptions.
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, signin_success_stats, signin_by_ip, 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 |
m365_service_health, daily_brief's service_health section | ServiceHealth.Read.All | Under delegated auth the signed-in user also needs an Entra role that may read service health (for example Service Support Administrator or Global Reader) |
Required Azure RBAC
| Tool(s) | Role | Notes |
|---|---|---|
list_subscriptions | none | Lists whatever the credential can see; an empty list means no role has been granted anywhere yet |
vm_backup_status, vm_backup_policies, vm_activity | Reader on each subscription | Reader is */read and covers every endpoint used. Backup Reader alone is not enough (it has no Microsoft.Compute/virtualMachines/read). Granted to the service principal (app-only) or to the signed-in user (azure-cli) |
Azure RBAC is optional: health_check reports ARM reachability and the number
of visible subscriptions as an informational arm probe that never changes
status, so a deployment used only for Entra triage stays healthy.
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
# Your own address ranges (NAT egress, offices, labs). Spray suspects inside them are
# listed separately as internal_spray_suspects, labelled with the site.
export ENTRAADM_SITES_INI=/path/to/sites.ini
ENTRAADM_SITES_INI uses the same format as keycloak-mcp's KEYCLOAK_SITES_INI,
so one file can serve both (see sites.ini.example). Lookups are
longest-prefix. A file that is set but cannot be read makes health_check report
degraded, and every IP is then treated as external. An invalid CIDR is reported the
same way, but the valid ranges are still used.
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 (plus an informational ARM line), exit 0 (or 1 on config error) |
Notes
- Coverage contract. Every result that walks a paged Graph collection
carries a
cappedboolean when its window was not fully scanned — a partial scan is never reported as if it were exhaustive. found: falseis not an error.get_userandget_user_auth_methodsanswer a nonexistent account with{"found": false, ...}, not anerrorkey — 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
Questions fréquentes
Qu'est-ce que entraadm MCP ?
MCP server for Microsoft Entra ID sign-in and audit-log triage (read-only by default)
Comment installer entraadm MCP ?
Il s'exécute sur votre machine. Dans Claude Code : claude mcp add entraadm-mcp -- uvx entraadm-mcp==1.8.1. Pour les autres clients, collez la configuration JSON de cette page dans leurs réglages MCP.
Faut-il une clé API pour entraadm MCP ?
Son entrée au registre n'indique aucune clé ni secret obligatoire. Certaines fonctions peuvent tout de même demander un compte auprès du service auquel il se connecte.
entraadm MCP est-il open source ?
Oui. Son code est sur GitHub sous licence MIT.
Qui maintient entraadm MCP ?
Il est publié par shigechika. AI.tn le référence sans le maintenir ni le recommander.
Issu du registre MCP officiel. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.