entraadm MCP
PYPI · ENTRAADM-MCP
MCP server for Microsoft Entra ID sign-in and audit-log triage (read-only by default)
Available components
Last scanned (21 hours ago)
Scores higher than 91% of PyPI servers
Category Security
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security100
- No malware found by supply-chain analysis.Pass
- No known CVEs affecting this package version or its production dependencies.Pass
- Runs setuptools.build_meta at install time, a recognised build step with no custom scripting around it. View diagnostics → Pass
- 1 of 41 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency97
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Cryptographically verified build provenance (signed, bound to shigechika/entraadm-mcp). View diagnostics → Pass
- Clear OSI-approved license (MIT).Pass
- Actively maintained (last published 2 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability52
- AI-judged instruction clarity (good).Pass
- Context-footprint check failed: tool/resource definitions use about 8946 tokens (~526/item across 17 items; 17 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management100
- No destabilizing schema changes in the last 30 days.Pass
Tool Coverage67
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 0% of tool parameters carry a description.Fail
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- We read all 17 captured tool definition(s), and no name or description among them implies an irreversible operation.Pass
- An AI judge read all 17 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a current MCP spec version (2026-07-28).Pass
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 3 Nov 26 +15
- Malware scan: unverified → pass ▲ security
- 2 Nov 26 −15
- Malware scan: pass → unverified ▼ security
- 1 Nov 26 0
- Stability: pass → unverified ▼ security
- Tool safety: pass → unverified ▼ security
- Schema quality: 7669 → 8946 ▼ functional
- Schema quality: 7669 → 8859 ▼ functional
- Capabilities: pass → unverified ▼ functional
- Tool coverage: 100 → unverified ▼ functional
- Package version: 1.9.0 → 1.10.1 functional
- Package version: 1.9.0 → 1.10.0 functional
- 31 Oct 26 0
- Package version: 1.8.1 → 1.9.0 functional
- 29 Oct 26 +15
- Malware scan: unverified → pass ▲ security
- 27 Oct 26 0
- Stability: 0.97 → pass security
- 26 Oct 26 0
- Stability: pass → 0.97 functional
- Package version: 1.8.0 → 1.8.1 functional
- 24 Oct 26 −1
- Schema quality: 402 → 506 ▼ functional
- Schema quality: 402 → 484 ▼ functional
- Schema quality: 402 → 461 ▼ functional
- Package version: 1.4.0 → 1.8.0 functional
- Package version: 1.4.0 → 1.7.0 functional
- Package version: 1.4.0 → 1.6.0 functional
- Package version: 1.4.0 → 1.5.0 functional
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 4 Nov 2026 · Analysed pypi/entraadm-mcp@1.10.1
Provenance Verified
A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.
| Result | Verified |
|---|---|
| Ecosystem | pypi |
| Reason | Verified |
| Discovered via | Registry attestation endpoint |
| Source repo | shigechika/entraadm-mcp |
| Certificate issuer | https://token.actions.githubusercontent.com |
| Certificate SAN | https://github.com/shigechika/entraadm-mcp/.github/workflows/release.yml@refs/tags/v1.10.1 |
| Rekor log index | 3629961529 |
| Predicate type | PyPI publish attestation https://docs.pypi.org/attestations/publish/v1 |
| Subject digest | sha256:8246fcc3678bfc5313193507886d14ba82836f950d27f749a46445ebd455d310 |
Background: How many MCP packages publish verified provenance →
Install scripts 1 script
| Hook | Tier | Command |
|---|---|---|
| build_backend | allowlisted | setuptools.build_meta |
Background: Why install scripts are a supply-chain risk →
Dependencies 41 packages
| Packages resolved | 41 |
|---|---|
| No linked repository | 1 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
audit_log_search_result ~582
Status and, once finished, the summarised records of an audit search started by audit_log_search_start. Reads Graph ``GET /security/auditLog/queries/{id}``. While ``status`` is ``notStarted`` or ``running`` the answer is the status only (``done: false``); call again in a minute or two. ``failed``, ``cancelled`` and any status this server does not know are ``done: true`` with an ``error``. Once ``succeeded``, the records are paged (``/records``) and come back as: - ``summary``: ``total_records``, ``first_seen``/``last_seen``, counts by operation, service, record type and user, and per client IP the count with first/last seen (top 25 of each list; ``distinct`` has the full counts); - ``records``: the newest ``top`` records, each with time, operation, service, record type, user, client IP, object id and a few ``details`` from ``auditData`` (client IP, user agent, result status, site URL, file name, the names of cmdlet parameters -- never their values), every string cut to 200 characters. The full ``auditData`` is not returned. ``capped=true`` means the counts are lower bounds: either the page budget or the deadline ran out before every record was read (call again with a larger ``max_pages``), or ``query.record_count_limit_exceeded`` is true -- Graph itself stopped collecting at its per-search record limit (``query.record_count_limit``), so the search holds only part of the matching records whatever ``max_pages`` is; narrow the window or the filters and start a new search. ``records_truncated`` means more records were read than ``top`` returns (the summary counts them all). An unknown id is ``found: false``: a search exists only in the tenant that created it, so pass the ``tenant`` its start call used. Permissions as audit_log_search_start; a 403 returns ``missing_permission``. Args: query_id: The ``query_id`` returned by audit_log_search_start (a GUID). top: Maximum records to return, clamped to [1, 500]. max_pages: Page budget for the reco…
| Name | Type | Req | Description |
|---|---|---|---|
| max_pages | – | – | – |
| query_id | string | yes | – |
| tenant | – | – | – |
| top | integer | – | – |
No output schema declared.
No examples provided.
audit_log_search_start ~695
Start a Microsoft Purview audit log search: what was done in OneDrive/SharePoint, Exchange, Entra. The follow-up to a confirmed takeover: the sign-in log shows which resources a session obtained tokens for, this shows what it then did (files opened or downloaded, mailbox rules and cmdlets, directory changes). A search runs asynchronously in Microsoft 365 and usually takes several minutes, so this only submits it (Graph ``POST /security/auditLog/queries``) and returns ``query_id``; call ``audit_log_search_result`` with that id (and the same ``tenant``) until ``done`` is true. The call creates a saved audit search object in the tenant (listed in the Purview audit search page); it changes no directory, mailbox or file data. Each call counts against the tenant's audit search limits (searches per day, searches running at once): a 429 comes back as ``rate_limited: true`` and is not retried. At least one filter besides the window is required, so a call never searches the whole tenant's log. The filters combine with AND. Needs the application permission ``AuditLogsQuery.Read.All``, or the service-scoped ``AuditLogsQuery-OneDrive.Read.All`` / ``-SharePoint.Read.All`` / ``-Exchange.Read.All`` / ``-Entra.Read.All`` (then set ``service`` to match). A 403 returns ``missing_permission``. ``created`` is true, false (nothing was created), or null (the answer was lost; a search may exist anyway). Args: user: Only records of this userPrincipalName. hours: How far back from ``end`` (or now) to search, clamped to [1, 4320] (180 days). Ignored with ``start``. start: Window start, ISO 8601 with an offset (e.g. 2026-10-21T22:00:00+09:00). end: Window end, ISO 8601 with an offset (default: now). record_types: auditLogRecordType values, e.g. ["sharePointFileOperation", "exchangeItem"]. operations: Operation names, e.g. ["FileDownloaded", "New-InboxRule", "MailItemsAccessed"]. ip_addresses: Client IPv4/IPv6 addresses. keyword: Free text matched against th…
| Name | Type | Req | Description |
|---|---|---|---|
| end | – | – | – |
| hours | integer | – | – |
| ip_addresses | – | – | – |
| keyword | – | – | – |
| object_ids | – | – | – |
| operations | – | – | – |
| record_types | – | – | – |
| service | – | – | – |
| start | – | – | – |
| tenant | – | – | – |
| user | – | – | – |
No output schema declared.
No examples provided.
compromise_suspects ~1,023
Accounts that were probably taken over, from one walk of the sign-in log. The success-side counterpart of ``signin_failure_stats``' spray suspects. Two findings, both from EXTERNAL source IPs (outside the address ranges in ENTRAADM_SITES_INI; without that file every IP counts as external): - ``spray_breaches`` (severity ``high``): an IP with credential failures (wrong password, unknown, locked or disabled account -- not MFA prompts, session interrupts or Conditional Access blocks, which follow a correct password) for ``min_spray_users`` or more distinct accounts in the window AND a successful sign-in as at least one account. Without internal ranges (ENTRAADM_SITES_INI unset or unreadable, ``internal_ranges_known: false``) these are ``medium`` with a ``note``: the tenant's own NAT egress can look like a spray source. One row per account and IP: ``failed_distinct_users`` from that IP, first/last success, client apps, countries, ``legacy_auth``. The strongest signal: a spray that found a working password. - ``foreign_successes``: successes from a country outside ENTRAADM_HOME_COUNTRIES (assigned ISO 3166-1 alpha-2 codes, e.g. "JP"; one invalid code disables this list, see ``home_countries.error``), grouped per account with countries, the most used IPs (10, with counts), client apps, first/last, and ``home_success_in_window`` (the account also signed in from a home country or an internal IP in the window; null when not seen in a capped scan, since the unread part may hold it). ``high`` when a legacy protocol was used (password only, no MFA), the account is also a high ``spray_breaches`` entry, the success came through Azure CLI / Azure PowerShell / Azure AD PowerShell (``admin_tools``; how stolen passwords are tested and used), or the successes span 3 or more countries within 24 hours (a rotating proxy pool, not a traveller); ``high_reasons`` names which. ``medium`` otherwise. Unset home countries: ``home_countries.conf…
| Name | Type | Req | Description |
|---|---|---|---|
| hours | integer | – | – |
| max_pages | – | – | – |
| min_spray_users | integer | – | – |
| tenant | – | – | – |
No output schema declared.
No examples provided.
daily_brief ~520
One-call morning-patrol summary: M365 service health, sign-in failures, spray suspects, admin actions. Combines ``m365_service_health``, ``signin_failure_stats``, ``directory_audits`` and ``compromise_suspects`` (its counts and the top 5 of each list; ``summary.compromise_suspects`` has the high/medium counts) into one result with a compact ``summary`` on top. The failure stats and the compromise suspects come from one walk of the sign-in log. The shape matches this fleet's other ``daily_brief`` tools. A permission failure in one section degrades only that section's contribution to ``summary`` -- the other sections still return in full. Azure VM backup is deliberately not a section here: its ARM fan-out is the one scan that could exhaust the shared budget, so call ``vm_backup_status`` separately. With a tenant file (``ENTRAADM_CONFIG``) it covers every tenant: the top-level keys stay the DEFAULT tenant's brief, exactly as in a single-tenant deployment, plus ``tenant`` (the default's name) and ``tenants`` (``{name: the same brief}`` for each other tenant). The call budget (``ENTRAADM_DEADLINE``) is split: default first, each tenant gets an even share of what is left, so a busy tenant cannot starve the next one (a tenant that finishes early leaves its rest to the others). With ``ENTRAADM_DEADLINE=0`` there is no budget and every tenant is scanned in full, one after another, which can outlast a client's ~60 s cutoff. Env-only results carry neither key. Runs both sections synchronously in one tool call, unlike the sibling gwsadm-mcp's job+poll ``daily_brief``. If this proves too slow for a tenant's sign-in volume against the client's tool-call timeout, port that job+poll pattern here (tracked in this repo's CLAUDE.md Roadmap). Args: hours: How far back to look, clamped to [1, 720] (30 days). max_pages: Page budget passed to both sections (default: ENTRAADM_MAX_PAGES_DEFAULT). samples: Reserved for a future drill-down sample size; currently unused.
| Name | Type | Req | Description |
|---|---|---|---|
| hours | integer | – | – |
| max_pages | – | – | – |
| samples | integer | – | – |
No output schema declared.
No examples provided.
directory_audits ~565
Who did what to the directory, and when -- the operator-side counterpart to signin_logs. Every admin action against a user object (block/unblock, password reset, role assignment, attribute edits) appears here, naming the actor (``initiated_by``) and the affected object(s) (``target_resources``). This is the record a manual "unblock and reset" intervention -- like the one that closed the 2026-08-21 case this server exists to shorten -- leaves behind; it is how a later triage can tell "already handled by a human" from "still open". ``user``, when given, matches audits where that account is either the initiator or a target resource. Graph's directoryAudits endpoint only supports server-side ``$filter`` on the *initiator* (``initiatedBy/user/userPrincipalName``), not on ``targetResources``, so this fetches the full time window and matches both sides client-side -- a window with many unrelated admin actions can need a larger ``max_pages`` budget than ``signin_logs``/``signin_failure_stats`` to find one specific user's audits; ``capped=true`` warns when that budget ran out before the window was fully scanned. Read-only (AuditLog.Read.All application permission, or -- for azure-cli auth -- the Reports Reader directory role). Entra ID retains directory audit logs for 30 days, same as sign-in logs. Time-bounded: the scan stops after ENTRAADM_DEADLINE seconds (default 45) and returns what it has with ``capped=true``. Over several days the log is dominated by device-registration noise ("Update device"), so pass ``category`` (for example ``UserManagement``, ``RoleManagement``, ``GroupManagement``, ``ApplicationManagement``) to have Graph filter server-side; that keeps a multi-day window inside the budget. Args: category: Only this Graph audit category (letters only; default: all). user: Restrict to audits naming this userPrincipalName as actor or target (default: all). hours: How far back to look, clamped to [1, 720] (30 days). top: Maximum records to retu…
| Name | Type | Req | Description |
|---|---|---|---|
| category | – | – | – |
| hours | integer | – | – |
| max_pages | – | – | – |
| tenant | – | – | – |
| top | integer | – | – |
| user | – | – | – |
No output schema declared.
No examples provided.
get_user ~503
One account's identity/lifecycle state -- the first thing to check on any triage report. ``account_enabled=false`` means the account itself is the whole story; stop there. A stale ``last_password_change_date_time`` alongside a fresh "wrong password" complaint (AADSTS50126 in ``signin_logs``) is the most common on-the-ground pattern: the password changed or expired somewhere, and a cached credential on one device is now stale. ``on_premises_sync_enabled=true`` means this account is synced from an on-premises directory (Entra Connect) -- Entra is a downstream copy of its password via Password Hash Sync, not the source of truth. ``licenses`` names are resolved from the tenant's SKU catalog (``/subscribedSkus``, page budget from ``ENTRAADM_MAX_PAGES_DEFAULT``); ``licenses_capped: true`` appears only when that scan was cut short before resolving one of this account's own licenses -- when present, one or more ``licenses`` entries is a raw skuId rather than a friendly name. ``sign_in_activity`` needs an additional Graph read (AuditLog.Read.All application permission, or -- for azure-cli auth -- the Reports Reader directory role) beyond what the rest of this tool needs. If that permission is missing, every other field above still returns and ``sign_in_activity`` alone degrades to ``{"error": ..., "missing_permission": "AuditLog.Read.All"}``. A nonexistent account is a normal answer, not a tool failure: the result is ``{"found": false, "user_principal_name": upn}`` rather than an ``error`` key, so a typo'd UPN in a triage report cannot be mistaken for this tool being broken. Read-only (User.Read.All application permission, or an equivalent delegated read). Requires an exact userPrincipalName, not a display name or partial match. Args: upn: The account's userPrincipalName, e.g. "user@example.edu". tenant: Search this tenant instead of the one claiming the UPN's domain (section name, domain or tenant id): for a <tenant>.onmicrosoft.com or #EXT# UPN, or a…
| Name | Type | Req | Description |
|---|---|---|---|
| tenant | – | – | – |
| upn | string | yes | – |
No output schema declared.
No examples provided.
get_user_auth_methods ~304
Registered authentication methods for one account -- is MFA actually set up? ``mfa_registered`` answers "would this account survive a password-spray hit": True iff at least one non-password method is registered (Authenticator app, phone, FIDO2 security key, Windows Hello, a temporary access pass, software OATH token, or a platform credential/passkey). ``password`` itself is excluded from that count -- every account has one, so its presence alone says nothing about MFA coverage. A nonexistent account is a normal answer, not a tool failure: the result is ``{"found": false, "user_principal_name": upn}`` rather than an ``error`` key, matching ``get_user``'s contract. Read-only (UserAuthenticationMethod.Read.All application permission). This endpoint is app-only only: it is not exposed to delegated (azure-cli) auth under this tenant's current role assignment, so it degrades to a permission error under azure-cli auth even when other tools work. Args: upn: The account's userPrincipalName. tenant: Search this tenant instead of the one claiming the UPN's domain (section name, domain or tenant id): for a <tenant>.onmicrosoft.com or #EXT# UPN, or a B2B guest's sign-ins in the host tenant.
| Name | Type | Req | Description |
|---|---|---|---|
| tenant | – | – | – |
| upn | string | yes | – |
No output schema declared.
No examples provided.
health_check ~785
Fleet-standard health probe: service/version/status plus two independent Graph probes. ``graph`` confirms Microsoft Graph is reachable at all (``GET /users`` with ``$top=1`` -- needs only ``User.Read.All``, the minimum permission every deployment of this server needs anyway). ``signin_probe`` additionally confirms the current credential can read sign-in logs -- the permission every other tool here except ``get_user`` depends on. Both probes always run, independently of each other: a tenant that has AuditLog.Read.All but not (yet) the baseline User.Read.All would otherwise have this report "Graph unreachable" -- a fabricated diagnosis, since Graph plainly *is* reachable if the other probe succeeds. ``status`` is derived from the two outcomes: ``healthy`` when both succeed, ``degraded`` when exactly one does (Graph is reachable but some permission is missing), ``error`` only when neither does. Read-only. Always returns the same keys regardless of outcome (``detail`` is null on success, a translated message on failure), so a caller never has to branch on which keys are present. ``sites`` reports the optional ENTRAADM_SITES_INI table (``configured``, ``ranges``, ``error``). A configured file that fails to load is an ``error`` here and leaves ``status`` no better than ``degraded``: without it every internal IP would be reported as an external spray suspect. With a tenant file (``ENTRAADM_CONFIG``) every tenant is probed. The top-level keys stay the DEFAULT tenant's, as in a single-tenant deployment, plus ``tenant`` (the default's name) and ``tenants``: ``{name: {default, domains, auth_mode, status, graph, signin_probe}}`` for every tenant, the default included. Each tenant's probes run under an even share of the call budget (``ENTRAADM_DEADLINE``), so a tenant that does not answer is an ``error`` there instead of stalling the call. Another tenant that is not ``healthy`` makes the top-level ``status`` at most ``degraded`` (the default tenant alone can make it ``error…
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
list_subscriptions ~141
The Azure subscriptions this credential can see, as ARM lists them. Azure Resource Manager in the DEFAULT tenant only, also when several tenants are configured (``ENTRAADM_CONFIG``). Read-only (``GET /subscriptions``; needs no RBAC beyond being able to see a subscription at all). The discovery anchor for ``vm_backup_status`` and ``vm_backup_policies``: both accept a subscription id or display name from this list. An empty list with ``auth`` working means no Reader role has been granted anywhere yet. ``capped`` marks a listing cut short by the page budget.
| Name | Type | Req | Description |
|---|---|---|---|
| max_pages | – | – | – |
No output schema declared.
No examples provided.
m365_service_health ~231
Microsoft 365 service status per service, and the open incidents and advisories. Two Graph reads (``ServiceHealth.Read.All``): ``healthOverviews`` (one row per service with its current status) and the unresolved ``serviceAnnouncement/issues``, newest first. A sign-in failure spike reads very differently while Entra or Exchange has an open incident, which is why ``daily_brief`` carries this section too. Each section degrades on its own (``{"error", "missing_permission"}``); ``summary`` is computed from whatever came back. ``capped`` means either collection was cut short by the page budget, so the counts are lower bounds. The message center (``messages``) needs a different permission and is not read. Args: max_pages: Page budget for each collection (default: ENTRAADM_MAX_PAGES_DEFAULT). tenant: Tenant to read: a [tenant.<name>] section name or one of its domains (default: the default tenant).
| Name | Type | Req | Description |
|---|---|---|---|
| max_pages | – | – | – |
| tenant | – | – | – |
No output schema declared.
No examples provided.
signin_by_ip ~400
Every sign-in from one source IP: who got in from it, who was tried, and when. The follow-up to a ``spray_suspects`` or ``shared_ips`` hit: Graph can filter sign-ins on ``ipAddress`` server-side, so this is one cheap query, not a log walk. ``users`` summarises the IP per account (successes, failures, first/last seen, up to 50 accounts) over every row fetched; ``events`` lists the newest ``top`` entries that match ``result`` ("all" by default, or "success" / "failure"), each carrying the account name and the same AADSTS annotation as ``signin_logs``. ``capped=true`` means the page budget or the deadline ran out before the window was fully read; ``events_truncated=true`` means more matching rows were read than ``top`` returns (the ``users`` summary still counts them). Read-only (AuditLog.Read.All application permission, or -- for azure-cli auth -- the Reports Reader directory role). Args: ip: The source IPv4 or IPv6 address, exactly as the sign-in log shows it. hours: How far back to look, clamped to [1, 720] (30 days). result: "all" (default), "success", or "failure" -- which events to list. top: Maximum events to return, clamped to [1, 500]. max_pages: Page budget (default: ENTRAADM_MAX_PAGES_DEFAULT). tenant: Tenant to read: a [tenant.<name>] section name or one of its domains (default: the default tenant).
| Name | Type | Req | Description |
|---|---|---|---|
| hours | integer | – | – |
| ip | string | yes | – |
| max_pages | – | – | – |
| result | string | – | – |
| tenant | – | – | – |
| top | integer | – | – |
No output schema declared.
No examples provided.
signin_failure_stats ~580
Tenant-wide sign-in failure aggregation -- the Entra ID counterpart to the RADIUS failure patrol. Time-bounded (ENTRAADM_DEADLINE, default 45 s): on a wide window or a busy day the scan stops early and ``capped=true`` marks the counts as a lower bound; narrow ``hours`` for a full count. Aggregates failed sign-ins across the whole tenant into four views: top AADSTS error codes (with the same meaning annotations as ``signin_logs``), top failing users, top applications, and top source IPs. ``spray_suspects`` flags any IP with failed sign-ins against 5 or more distinct users -- Entra's smart lockout is per-account, so a low-and-slow password spray from one IP across many accounts does not trip it the way a brute force against one account does; this is the observation a per-account view cannot make on its own. This mirrors the KeyCloak-side spray detection this fleet already relies on; neither the official Microsoft MCP Server for Enterprise nor Graph itself offers this aggregation. When ENTRAADM_SITES_INI names the tenant's own address ranges, IPs inside them get ``site`` (and ``site_name``) in ``top_ips``, and suspects inside them move from ``spray_suspects`` to ``internal_spray_suspects``: a NAT egress or a room of shared workstations fails for many users every day, and would otherwise bury an outside source. Internal suspects are kept, not dropped, because an internal host can be compromised too. ``sites`` reports whether the file loaded; an invalid CIDR is reported in ``sites.error`` while the valid ranges are still used, and a file that cannot be read leaves every IP in ``spray_suspects``. Read-only (AuditLog.Read.All application permission, or -- for azure-cli auth -- the Reports Reader directory role). Graph cannot filter sign-ins on status/errorCode server-side, so this walks up to ``max_pages`` of the full sign-in log for the window and aggregates client-side -- ``capped=true`` means the page budget ran out before the window was fully scanned, so the count…
| Name | Type | Req | Description |
|---|---|---|---|
| hours | integer | – | – |
| max_pages | – | – | – |
| tenant | – | – | – |
No output schema declared.
No examples provided.
signin_logs ~474
One user's recent sign-in events, AADSTS-annotated. The most direct answer to "why can't this person log in": each entry's ``error_code_meaning`` translates the raw AADSTS code (e.g. 50126 -> "invalid credentials (wrong password)") so triage rarely needs a second lookup. ``result`` filters client-side after the Graph fetch (Graph cannot filter sign-ins on status/errorCode server-side): "failure" (the default) keeps only failed attempts, "success" keeps only clean ones, "all" keeps everything. Because the filter is client-side, this walks pages until it has collected ``top`` matching entries or exhausts ``max_pages`` -- a mostly- successful user can otherwise mean paging through hundreds of rows to find a handful of failures. ``capped=true`` means the page budget ran out (or ``top`` was reached) before the whole window was scanned; a low match count alongside ``capped=true`` is evidence of "no more found within the budget", not "no more exist". Read-only (AuditLog.Read.All application permission, or -- for azure-cli auth -- the Reports Reader directory role). Entra ID P1 retains sign-in logs for 30 days; ``hours`` beyond that returns an empty result, not an error. Args: user: The account's userPrincipalName. hours: How far back to look, clamped to [1, 720] (30 days). result: "failure" (default), "success", or "all". top: Maximum matching entries to return, clamped to [1, 500]. max_pages: Page budget for the client-side filter walk (default: ENTRAADM_MAX_PAGES_DEFAULT). tenant: Search this tenant instead of the one claiming the UPN's domain (section name, domain or tenant id): for a <tenant>.onmicrosoft.com or #EXT# UPN, or a B2B guest's sign-ins in the host tenant.
| Name | Type | Req | Description |
|---|---|---|---|
| hours | integer | – | – |
| max_pages | – | – | – |
| result | string | – | – |
| tenant | – | – | – |
| top | integer | – | – |
| user | string | yes | – |
No output schema declared.
No examples provided.
signin_success_stats ~526
Tenant-wide *successful* sign-in aggregation by source IP -- the view that finds a breach. ``signin_failure_stats`` shows who is being attacked; this shows whether anyone got in. The breach signature is one source IP signing in successfully as several different accounts, most often over a legacy protocol (``clientAppUsed`` such as "Authenticated SMTP" or "IMAP4", which carry no MFA). ``shared_ips`` lists the IPs with successes for ``min_distinct_users`` or more distinct accounts, most-shared first (up to 50 IPs, ``shared_ips_capped`` when more qualified; account names up to 25 per IP), with the client apps and the countries seen. ``legacy_auth_users`` lists the accounts that succeeded over a legacy protocol at all, with how many IPs and countries they came from. A campus NAT, a VDI farm or a shared proxy also puts many accounts behind one IP, so a shared IP is a lead, not a verdict: the caller excludes its own egress ranges and reads the client apps and countries before calling anything a breach. Graph cannot filter sign-ins on ``status/errorCode`` server-side, so like ``signin_failure_stats`` this walks the sign-in log for the window and aggregates client-side. The walk covers interactive sign-ins only (Graph's default listing): every legacy-protocol authentication is logged as interactive, so none is missed, but non-interactive token refreshes are not counted; ``capped=true`` means the page budget or the deadline (ENTRAADM_DEADLINE, default 45 s) ran out first and the counts are a lower bound -- narrow ``hours`` for a full count. Read-only (AuditLog.Read.All application permission, or -- for azure-cli auth -- the Reports Reader directory role). Args: hours: How far back to look, clamped to [1, 720] (30 days). max_pages: Page budget (default: ENTRAADM_MAX_PAGES_DEFAULT). min_distinct_users: Distinct accounts an IP needs to appear in ``shared_ips`` (default 2, clamped to >= 2). tenant: Tenant to read: a [tenant.<name>] section name or on…
| Name | Type | Req | Description |
|---|---|---|---|
| hours | integer | – | – |
| max_pages | – | – | – |
| min_distinct_users | integer | – | – |
| tenant | – | – | – |
No output schema declared.
No examples provided.
vm_activity ~716
What happened to an Azure VM in a time window: operations people ran, and events Azure raised. Azure Resource Manager in the DEFAULT tenant only, also when several tenants are configured (``ENTRAADM_CONFIG``). Answers "did someone restart this VM, or did Azure?" from the Azure activity log of the VM's subscription. Read-only; needs the Reader role on the subscription. Two reads: the VM's resource group (server-side filter, so a busy subscription cannot crowd the VM's own events out of the page budget) feeds ``health`` and ``vm_operations``; the whole subscription feeds the rest. - ``health``: Resource Health events of the VM (``cause`` ``PlatformInitiated`` = Azure, e.g. ``Redeploying due to host failure``; ``UserInitiated`` = an operation on the VM, or ``Reboot initiated from inside the machine`` for a restart from the guest OS). - ``vm_operations``: Administrative events on the VM and its sub-resources (restart, deallocate, redeploy, extension changes), with ``caller`` (a user principal name or an application id). One operation usually logs several events (Started, Accepted, Succeeded) sharing a ``correlation_id``. - ``other_operations``: Administrative events on any other resource of the subscription (``count`` of events, ``operations`` = distinct correlation ids, ``items`` = the newest 50). Zero here and in ``vm_operations`` means nobody operated the subscription through Azure Resource Manager in the window; it says nothing about actions inside the guest OS. - ``service_health``: Azure incidents and planned maintenance announced to the subscription. - ``other_categories``: counts of the remaining categories (Alert, Policy, other resources' health events, ...). ``vm`` is the VM name, matched case-insensitively across the subscriptions this credential can see; narrow it with ``subscription`` (id or display name from ``list_subscriptions``) or ``resource_group`` when the name is not unique (``ambiguous`` lists the candidates). A sin…
| Name | Type | Req | Description |
|---|---|---|---|
| end | – | – | – |
| hours | integer | – | – |
| max_pages | – | – | – |
| resource_group | – | – | – |
| start | – | – | – |
| subscription | – | – | – |
| vm | string | yes | – |
No output schema declared.
No examples provided.
vm_backup_policies ~288
Schedule and retention of the Azure VM backup policies in each Recovery Services vault. Azure Resource Manager in the DEFAULT tenant only, also when several tenants are configured (``ENTRAADM_CONFIG``). Read-only; needs the Reader role on the subscription. Per policy: ``policy_type`` (``V1`` = ``SimpleSchedulePolicy``, ``V2`` = ``SimpleSchedulePolicyV2`` with an hourly / daily / weekly schedule), ``schedule`` (frequency, run times in UTC, weekdays, or the hourly window), ``retention`` (``LongTermRetentionPolicy``: daily / weekly / monthly / yearly blocks with their day selection and duration), ``instant_rp_days``, ``timezone`` and ``protected_item_count`` (ARM's own count). A schedule or retention type this server does not know is returned as ``{"type": <name>, "unparsed": true}``, never dropped. ``subscription`` is an id or display name from ``list_subscriptions`` (``None`` = every visible subscription); an unknown or ambiguous name returns ``found: false``. Coverage flags as in ``vm_backup_status``: ``skipped_vaults`` per subscription, ``skipped`` subscriptions when the budget ran out, ``capped`` overall.
| Name | Type | Req | Description |
|---|---|---|---|
| max_pages | – | – | – |
| subscription | – | – | – |
No output schema declared.
No examples provided.
vm_backup_status ~613
Which Azure VMs are backed up by Azure Backup, and whether the last backup succeeded. Azure Resource Manager in the DEFAULT tenant only, also when several tenants are configured (``ENTRAADM_CONFIG``). Joins every VM of a subscription (``Microsoft.Compute/virtualMachines``) with the Azure-VM protected items of its Recovery Services vaults, on the VM's ARM id (case-insensitively; ARM casing differs between the two services). Read-only; needs the Reader role on the subscription. Per VM: - ``protected``: ``true`` when a protected item exists with ``protectionState`` ``Protected`` or ``IRPending`` (first backup not done yet, ``note`` says so); ``false`` when there is none, or it is ``ProtectionStopped`` / ``ProtectionPaused`` / ``ProtectionError`` (data may be retained, backups are not running -- the state is shown); ``null`` (unknown) when no item was found but the vault scan was incomplete (``capped``, a skipped vault, an unreadable or unreached subscription -- in the global join, any subscription's) -- a partial scan never produces a false "unprotected". - ``backup_ok``: ``true`` for ``lastBackupStatus`` ``Completed`` / ``CompletedWithWarnings``, ``false`` for ``Failed``, ``null`` before any backup ran or while one is running (``InProgress``, ``Warning``, an unknown value). An item whose ``protectionState`` is missing is ``protected: null`` with a note. When two vaults hold an item for one VM, the one still backing up wins (``duplicate_items`` counts them). - ``stale``: no recovery point (``lastRecoveryPoint``, the proof of a successful backup) within ``stale_hours``. The default 36 h suits daily policies; raise it for weekly ones (``policy_name`` is in the row). ``subscription`` is an id or display name from ``list_subscriptions``; ``None`` scans every visible subscription and joins globally, so a VM backed up to a vault in another subscription is matched. With one subscription named, only its vaults are scanned (``coverage`` says…
| Name | Type | Req | Description |
|---|---|---|---|
| max_pages | – | – | – |
| stale_hours | integer | – | – |
| subscription | – | – | – |
No output schema declared.
No examples provided.
How do I install the entraadm MCP server?
entraadm MCP runs locally as a PyPI package, launched with uvx entraadm-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
pypi · entraadm-mcp
claude mcp add shigechika-entraadm-mcp -- uvx entraadm-mcp
What is the entraadm MCP server?
entraadm MCP is listed in the public MCP registry as io.github.shigechika/entraadm-mcp. MCP server for Microsoft Entra ID sign-in and audit-log triage (read-only by default). This page covers its PyPI package (entraadm-mcp).
Is the entraadm MCP server safe to use?
entraadm MCP scores 88 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 4 November 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the entraadm MCP server expose?
entraadm MCP exposes 17 tools: health_check, get_user, signin_logs, signin_by_ip, signin_failure_stats, and 12 more. Their descriptions and schemas cost roughly 8,946 tokens of context every time the server is loaded.
Is the entraadm MCP server still maintained?
entraadm MCP is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What licence is the entraadm MCP server under?
entraadm MCP declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.