Entra Identity Posture MCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Entra Identity Posture MCPshow me the current Conditional Access policies and their status"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Entra Identity Posture MCP
An Agentic Security and Governance MCP server built with FastMCP and the Microsoft Graph API for auditing Microsoft Entra ID app registrations and Conditional Access policies against Zero-Trust principles — and generating dry-run remediation scripts an AI agent (or human) can review and run.
Status: Alpha — under active development. Interfaces and tool surfaces may change.
Overview
This server exposes Microsoft Entra ID (Azure AD) security posture data as a full MCP surface — Tools, a Resource, and a Prompt — so that AI agents (e.g., Claude Desktop, VS Code Copilot Chat) can:
Audit app registrations for expiring/long-lived credentials, over-privileged Graph permissions, insecure redirect URIs, and risky multi-tenant configurations
Scan Conditional Access policies for admin MFA exclusions and policies stuck in report-only mode
Run both scans concurrently with a single
run_posture_scancall, with optional severity/rule/app filtering, when an agent wants one combined pass instead of two separate tool callsGenerate a Markdown Zero-Trust security report plus ready-to-run (dry-run only) Azure CLI / Microsoft Graph PowerShell remediation commands
Query the most recent scan results directly as an MCP Resource, without re-invoking a tool
Kick off a guided triage workflow via a predefined MCP Prompt
The server is read-only against Microsoft Graph (Application.Read.All, Policy.Read.All). It never calls a Graph write endpoint — remediation tools only generate text commands for a human or CI pipeline to execute.
Architecture
flowchart LR
subgraph Client["MCP Client (Claude Desktop / VS Code Copilot Chat)"]
A[AI Agent]
end
subgraph Server["entra-identity-posture-mcp (FastMCP, stdio)"]
T1[Tool: audit_app_registrations]
T2[Tool: scan_conditional_access_gaps]
T5[Tool: run_posture_scan]
T3[Tool: generate_remediation_plan]
T4[Tool: revoke_or_disable_app_registration]
R1[Resource: entra://posture/latest]
P1[Prompt: security_triage_prompt]
Rules[Rules Engine\napp_registration_rules.py\nconditional_access_rules.py]
Cache[(In-memory\nposture cache\nper category)]
end
subgraph Graph["Microsoft Graph API"]
G1[/applications/]
G2[/servicePrincipals/]
G3[/identity/conditionalAccess/policies/]
end
Auth[auth.py\nMSAL cert-based\nConfidentialClientApplication]
A -->|JSON-RPC over stdio| T1 & T2 & T5 & T3 & T4 & R1 & P1
T1 --> Rules
T2 --> Rules
T5 --> Rules
T1 -->|GET| G1 & G2
T2 -->|GET| G3
T5 -->|GET| G1 & G2 & G3
T1 & T2 & T5 -.->|auth token| Auth
Auth -->|client cert| G1
T1 & T2 & T5 --> Cache
R1 --> Cache
T3 -->|renders| Report[[security_report.md.j2]]Related MCP server: Microsoft MCP Server for Enterprise
Requirements
Python 3.12+
A Microsoft Entra ID app registration configured with a certificate credential (client secrets are not supported by
auth.py)Admin-consented Microsoft Graph application permissions:
Application.Read.AllandPolicy.Read.All
1. Clone the repository
git clone https://github.com/henrymbuguakiarie/entra-identity-posture-mcp.git
cd entra-identity-posture-mcp2. Install dependencies
Install with uv (recommended):
uv syncOr install with pip:
pip install -e .Include test and lint tooling for development:
uv sync --group dev3. Create the Entra app registration and certificate credential
Authenticate the server with a certificate, not a client secret — auth.py only supports MSAL's certificate-based confidential client flow. Complete these steps once, manually; the server does not automate app registration or admin consent.
3.1 Register the app
Open the Entra admin center and go to Identity → Applications → App registrations.
Select New registration, name it (e.g.
entra-identity-posture-mcp), keep the default single-tenant account type, and select Register.Copy the Application (client) ID and Directory (tenant) ID from the app's Overview page — you'll need both for
.env.
3.2 Generate a certificate
Generate the certificate on the machine that will run the server, so the private key never leaves your workstation.
Windows (PowerShell):
# Generate a self-signed certificate and store it in your user certificate store
$cert = New-SelfSignedCertificate `
-Subject "CN=entra-identity-posture-mcp" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-NotAfter (Get-Date).AddYears(1)
# Export the public certificate to upload to Entra
Export-Certificate -Cert $cert -FilePath "$HOME\entra-mcp-cert.cer"
# Export the private key as a password-protected PFX
$securePwd = Read-Host -Prompt "Set a temporary PFX password" -AsSecureString
Export-PfxCertificate -Cert $cert -FilePath "$HOME\entra-mcp-cert.pfx" -Password $securePwdConvert the PFX to the PEM private key format auth.py expects — this requires OpenSSL, which ships with Git for Windows at C:\Program Files\Git\mingw64\bin\openssl.exe:
openssl pkcs12 -in ~/entra-mcp-cert.pfx -nocerts -nodes -out ~/entra-mcp-cert.key.pemDelete the PFX once you have the PEM file — you no longer need it:
Remove-Item "$HOME\entra-mcp-cert.pfx" -ForcemacOS/Linux (OpenSSL, cross-platform):
# Generate a private key and matching self-signed public certificate in one step
openssl req -x509 -newkey rsa:2048 -keyout entra-mcp-cert.key.pem -out entra-mcp-cert.cer \
-days 365 -nodes -subj "/CN=entra-identity-posture-mcp"Either path produces two files:
entra-mcp-cert.cer— the public certificate. Upload this one to Entra.entra-mcp-cert.key.pem— the private key. Keep this file local and never commit it (see .gitignore);ENTRA_CERT_PATHpoints to it.
3.3 Upload the certificate
Open Certificates & secrets → Certificates on your app registration.
Select Upload certificate and choose
entra-mcp-cert.cer— upload only the public certificate, never the private key.Copy the certificate's Thumbprint after the upload completes — you'll need it for
.env.
3.4 Grant API permissions
Open API permissions → Add a permission → Microsoft Graph → Application permissions.
Add
Application.Read.AllandPolicy.Read.All, then select Add permissions.Select Grant admin consent for <tenant> and confirm. Both permissions must show a green check under Status before the server can call Graph.
4. Configure environment variables
The server authenticates to Microsoft Graph via MSAL certificate-based confidential client auth. Copy .env.example to .env:
cp .env.example .envFill in the values you collected in step 3:
Variable | Description |
| The Directory (tenant) ID from the app registration's Overview page |
| The Application (client) ID from the app registration's Overview page |
| Path to the PEM-encoded private key file ( |
| Thumbprint of the certificate you uploaded to the app registration |
| Days-until-expiry threshold for the |
| Max credential lifespan in days before flagging |
Note:
ENTRA_CERT_PATHmust point to the PEM private key, not the.cerfile you uploaded to Entra —auth.pyreads this file and passes its contents to MSAL as the client credential.
5. Run the server
Start the MCP server directly over stdio:
uv run entra-posture-mcpVS Code (mcp.json)
{
"servers": {
"entra-identity-posture": {
"command": "uv",
"args": ["run", "entra-posture-mcp"],
"cwd": "${workspaceFolder}",
},
},
}Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"entra-identity-posture": {
"command": "uv",
"args": [
"run",
"--directory",
"C:\\Work\\Automation\\entra-identity-posture-mcp",
"entra-posture-mcp",
],
},
},
}MCP surface reference
Kind | Name | Description |
Tool |
| Scans app registrations for expiring/long-lived secrets, risky permissions, and insecure redirect URIs. Returns structured findings ( |
Tool |
| Scans Conditional Access policies for admin MFA exclusions and report-only status. Returns structured findings plus a text |
Tool |
| Runs both scans above concurrently and returns one merged, optionally severity/rule/app-filtered result. Updates both cache categories |
Tool |
| Renders a Markdown Zero-Trust report + dry-run CLI/PowerShell snippets from findings |
Tool |
| Generates a dry-run Azure CLI/PowerShell command to disable sign-in, rotate a password or certificate credential (type-specific), remove a credential, or remove a permission |
Resource |
| Cached JSON from the most recent scan of each category (app registrations, Conditional Access), queryable without re-invoking a tool |
Prompt |
| Predefined Zero-Trust triage prompt to prioritize findings and recommend fixes |
Sample JSON-RPC request/response
MCP clients talk to the server over stdio using JSON-RPC 2.0 — every tool call is one request/response pair on stdin/stdout. Here's what actually crosses the wire when a client calls revoke_or_disable_app_registration (captured against this server):
Request (client → server, on stdin):
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "revoke_or_disable_app_registration",
"arguments": {
"app_id": "abc-123",
"action": "disable_sign_in",
},
},
}Response (server → client, on stdout):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "🔒 Dry-Run Remediation Output for App ID 'abc-123':\n\n```bash\n# PowerShell (MgGraph): Disable user sign-in\nUpdate-MgServicePrincipal -ServicePrincipalId abc-123 -AccountEnabled:$false\n```\n*(Note: Read-only mode active. Run the script manually or via CI pipeline to execute).*",
},
],
"structuredContent": {
"result": "🔒 Dry-Run Remediation Output for App ID 'abc-123':\n\n```bash\n# PowerShell (MgGraph): Disable user sign-in\nUpdate-MgServicePrincipal -ServicePrincipalId abc-123 -AccountEnabled:$false\n```\n*(Note: Read-only mode active. Run the script manually or via CI pipeline to execute).*",
},
"isError": false,
},
}The MCP Inspector CLI commands in step 6 print just the result payload — reproduce the raw envelope above by driving the server directly with the MCP Python SDK client, or watch stdin/stdout while a client like Claude Desktop or VS Code Copilot Chat drives it.
revoke_or_disable_app_registration's action argument distinguishes password vs. certificate credentials, so the generated command matches the credential type flagged by a scan finding's evidence.credential_type:
// action: "rotate_password_credential"
"az ad app credential reset --id abc-123 --append"
// action: "rotate_certificate_credential"
"az ad app credential reset --id abc-123 --append --create-cert"--append is always included — it adds a new credential alongside existing ones for zero-downtime rotation, instead of the Azure CLI's destructive default of clearing all existing credentials.
Example agent workflow
User: Run a Zero-Trust audit on my Entra tenant and tell me what to fix first.
Agent: [calls run_posture_scan]
[calls generate_remediation_plan with the combined findings]
Agent: I found 2 CRITICAL and 3 HIGH severity issues:
1. [CRITICAL] "Vendor Sync App" has Directory.ReadWrite.All combined with a
multi-tenant (AzureADMultipleOrgs) sign-in audience.
→ Update-MgApplication -ApplicationId <id>
2. [CRITICAL] Conditional Access policy "Require MFA for Admins" excludes the
Global Administrator role from enforcement.
→ Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId <id>
Full report and remaining dry-run commands are in the Markdown report above.
Review each command before running it — nothing has been changed in your tenant.6. Verify the server
Before you wire the server into an LLM client, validate the JSON-RPC tool/resource/prompt schemas in isolation using the official MCP Inspector CLI:
npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp --method tools/list
npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp --method resources/list
npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp --method prompts/list
# Exercise a tool that doesn't require live Graph credentials
npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp \
--method tools/call --tool-name revoke_or_disable_app_registration \
--tool-arg app_id=abc-123 --tool-arg action=disable_sign_inOnce you configure .env against a real test tenant, run the stdio entrypoint and invoke audit_app_registrations / scan_conditional_access_gaps / run_posture_scan to confirm known findings (an expiring secret, a risky permission, or a report-only Conditional Access policy) surface correctly — then repeat the workflow through Claude Desktop or VS Code Copilot Chat using the configs above.
Live MCP Inspector CLI session against a real test tenant, captured verbatim (tenant/app IDs redacted). Scan tools return structured
metadata+issuesalongside asummarytext field — shown below trimmed to the first/last finding for brevity. Noteevidence.credential_type("certificate"or"password") on theEXCESSIVE_LIFESPANfinding, which drives which ofrotate_password_credential/rotate_certificate_credentiala matchingIMMINENT_EXPIRATIONfinding would recommend:$ npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp --method tools/call --tool-name audit_app_registrations { "content": [ { "type": "text", "text": "{\n \"metadata\": {\n \"tenant_id\": \"248c1b45-...\",\n \"scanned_at\": \"2026-07-29T09:35:26.482128Z\",\n \"rule_version\": \"1.1\"\n },\n \"issues\": [\n {\n \"app_id\": \"fd0486bd-...\",\n \"app_name\": \"InsomniaWebApp\",\n \"severity\": \"HIGH\",\n \"rule_id\": \"DANGEROUS_REDIRECT_URI\",\n \"issue\": \"Insecure redirect URIs detected: http://localhost.\",\n \"evidence\": {\"redirect_uris\": [\"http://localhost\"]},\n \"remediation_action\": null,\n ... 3 more DANGEROUS_REDIRECT_URI findings ...\n },\n {\n \"app_id\": \"c712c7f1-...\",\n \"app_name\": \"entra-identity-posture-mcp\",\n \"severity\": \"MEDIUM\",\n \"rule_id\": \"EXCESSIVE_LIFESPAN\",\n \"issue\": \"Credential key_id '5e44aaeb-...' has an excessive lifespan of 365 days.\",\n \"evidence\": {\"key_id\": \"5e44aaeb-...\", \"credential_type\": \"certificate\", \"lifespan_days\": 365},\n \"remediation_action\": \"remove_credential\",\n \"remediation_params\": {\"app_id\": \"c712c7f1-...\", \"key_id\": \"5e44aaeb-...\"}\n }\n ],\n \"summary\": \"Found 6 app registration security issues:\\n\\n- [HIGH] InsomniaWebApp (fd0486bd-...): Insecure redirect URIs detected: http://localhost.\\n... 4 more ...\\n- [MEDIUM] entra-identity-posture-mcp (c712c7f1-...): Credential key_id '5e44aaeb-...' has an excessive lifespan of 365 days.\"\n}" } ], "isError": false } $ npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp --method tools/call --tool-name scan_conditional_access_gaps { "content": [ { "type": "text", "text": "{\n \"metadata\": {\n \"tenant_id\": \"248c1b45-...\",\n \"scanned_at\": \"2026-07-29T09:11:33.227421Z\",\n \"rule_version\": \"1.1\"\n },\n \"issues\": [],\n \"summary\": \"✅ Conditional Access scan complete: All policies comply with Zero-Trust standards.\"\n}" } ], "isError": false } $ npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp --method tools/call --tool-name run_posture_scan { "content": [ { "type": "text", "text": "{\n \"metadata\": {\n \"tenant_id\": \"248c1b45-...\",\n \"scanned_at\": \"2026-07-29T09:24:31.284144Z\",\n \"rule_version\": \"app:1.1;ca:1.1\"\n },\n \"issues\": [ ... 6 combined app-registration + Conditional Access findings ... ],\n \"summary\": \"Found 6 posture issues (of 6 total): ...\"\n}" } ], "isError": false }
severityis a PydanticLiteral["CRITICAL", "HIGH", "MEDIUM", "LOW"]at the MCP schema boundary, so an invalid value onrun_posture_scanfails validation before the scan runs — it never silently returns zero results:$ npx @modelcontextprotocol/inspector --cli uv run entra-posture-mcp \ --method tools/call --tool-name run_posture_scan --tool-arg severity=NOT_A_LEVEL { "content": [ { "type": "text", "text": "Error executing tool run_posture_scan: 1 validation error for run_posture_scanArguments\nseverity\n Input should be 'CRITICAL', 'HIGH', 'MEDIUM' or 'LOW' [type=literal_error, input_value='NOT_A_LEVEL', input_type=str]\n For further information visit https://errors.pydantic.dev/2.13/v/literal_error" } ], "isError": true }A GIF/screenshot of the same workflow running through Claude Desktop or VS Code Copilot Chat will replace this transcript once captured.
Development
Run tests:
uv run pytestLint and format:
uv run ruff check .
uv run ruff format .Continuous integration runs ruff check and pytest on every push/PR via .github/workflows/ci.yml.
Roadmap
Deliberately out of scope for v1:
Terraform file generation (e.g.
azuread_application_password,azuread_application_pre_authorized) for remediation — v1 only emits dry-run Azure CLI / PowerShell snippets.Automated GitHub PR creation for remediation changes — v1 leaves execution and change management entirely to the human/CI pipeline.
Both are fast-follow candidates now that v1 has been validated against a live tenant.
License
MIT © Henry Mbugua
Available Tools
5 toolsaudit_app_registrationsA
Scans Entra ID app registrations for expiring secrets, excessive lifespans, risky permissions, and insecure redirect URIs. Returns structured findings (metadata + issues) alongside a human-readable summary.
| Name | Required | Description | Default |
|---|---|---|---|
| imminent_expiry_days | No | ||
| excessive_lifespan_days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| issues | No | Structured findings produced by the scan. |
| summary | Yes | Human-readable recap of the findings. |
| metadata | Yes | Scan-level context: tenant, timestamp, rule version. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must convey behavioral traits. It mentions scanning and returning results but does not state whether the tool is read-only, requires specific permissions, or has side effects. For a security audit tool, read-only behavior is likely but not confirmed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences long, front-loads the core purpose, and contains no unnecessary words. Every phrase adds value: the scanning targets and the output structure are clearly stated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description adequately covers the tool's purpose and output (structured findings + summary), and the presence of an output schema reduces the need for return value details. However, the lack of parameter descriptions and usage context leaves gaps in completeness for a tool with two configurable thresholds.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description does not mention the two parameters (imminent_expiry_days, excessive_lifespan_days) or explain their meaning. With 0% schema description coverage, the description should compensate, but it only hints at the concepts (expiring, excessive) without connecting them to the parameters. The agent must infer from parameter names alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool scans Entra ID app registrations for specific issues (expiring secrets, excessive lifespans, risky permissions, insecure redirect URIs) and returns structured findings with a summary. This distinguishes it from sibling tools like scan_conditional_access_gaps or run_posture_scan.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage when auditing app registration security but provides no explicit when-to-use guidance, conditions, or alternatives. Given siblings exist for similar tasks, some direction would help an agent decide between this and other audit tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_remediation_planA
Generates a Zero-Trust Markdown security report and dry-run CLI remediation commands from findings.
| Name | Required | Description | Default |
|---|---|---|---|
| issues | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description bears full burden. It accurately states the tool generates a report and CLI commands, but omits details about side effects, required permissions, or whether the report is persisted. The description is not misleading but lacks depth.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence that directly conveys purpose without superfluous words. It is concise but could benefit from slight restructuring to front-load key actions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has one required parameter and no annotations, the description is minimally adequate. The existence of an output schema partly compensates, but the description does not elaborate on what the returned report or commands contain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% for the only parameter 'issues', and the description adds no additional meaning beyond the schema type definition. It says 'from findings', but does not explain what the issues array should contain (e.g., structure, required fields).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it generates a Zero-Trust Markdown security report and dry-run CLI remediation commands. It distinguishes from sibling tools like audit_app_registrations, scan_conditional_access_gaps, and revoke_or_disable_app_registration by focusing on plan generation rather than auditing, scanning, or revoking.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage 'from findings' but does not explicitly state when to use this tool vs. alternatives. No exclusion criteria or prerequisites are mentioned, leaving the agent to infer context from sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
revoke_or_disable_app_registrationC
Generates dry-run Azure CLI or PowerShell commands to disable sign-in or revoke credentials.
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | ||
| app_id | Yes | ||
| key_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses that the tool only generates dry-run commands rather than executing them, which is key. However, it fails to mention other behaviors like authentication requirements, error handling, or output format.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence. It front-loads the key concept (dry-run generation) without extraneous detail, making it easy to read. However, it could benefit from brief bullet points for clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 3 parameters, an enum, and no annotations, the description is too sparse. It omits the output format, error scenarios, and prerequisites. The existing output schema is not referenced, so the agent lacks complete context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so description must add meaning. It mentions 'disable sign-in or revoke credentials' which maps to some action enum values but not all. It does not explain the app_id or key_id parameters, leaving their semantics unclear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states it generates dry-run commands for disabling sign-in or revoking credentials, but the action enum includes additional actions like remove_permission and rotations, which are not mentioned. It identifies the verb 'generates' and the resource 'app registration commands', but misses full scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus siblings like audit_app_registrations or generate_remediation_plan. The description implies it's for generating remediation commands, but without explicit context, the agent may misuse it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_posture_scanB
Runs the app registration and Conditional Access scans concurrently and returns one combined, optionally filtered PostureScanResult. Updates the shared entra://posture/latest resource cache with both scan categories.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | No | ||
| rule_id | No | ||
| severity | No | ||
| imminent_expiry_days | No | ||
| excessive_lifespan_days | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| issues | No | Structured findings produced by the scan. |
| summary | Yes | Human-readable recap of the findings. |
| metadata | Yes | Scan-level context: tenant, timestamp, rule version. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses that scans run concurrently, results are combined and optionally filtered, and a shared cache is updated. However, with no annotations, it lacks details on side effects (e.g., whether scans modify data), permissions needed, or runtime implications.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences long, with the first sentence providing the core action and the second adding cache update context. It is concise and front-loaded, but could include parameter details without becoming verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 5 parameters, no annotations, and no schema descriptions, the description is insufficient for correct invocation. It lacks parameter explanations, usage context, and behavioral details beyond the basic operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description does not explain the parameters. Terms like app_id, rule_id, severity, and expiry/duration fields are mentioned only as parameter names, so the description adds no meaning beyond the names themselves.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool runs app registration and Conditional Access scans concurrently and returns a combined, optionally filtered PostureScanResult. It also mentions updating a cache, which distinguishes it from sibling tools like 'audit_app_registrations' and 'scan_conditional_access_gaps' that likely run single scans.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is for running both scan types together and filtering results, but it does not explicitly state when to use this tool versus the individual sibling tools or provide any when-not scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scan_conditional_access_gapsA
Scans Entra Conditional Access policies for admin MFA exclusions and policies stuck in report-only mode. Returns structured findings alongside a human-readable summary.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| issues | No | Structured findings produced by the scan. |
| summary | Yes | Human-readable recap of the findings. |
| metadata | Yes | Scan-level context: tenant, timestamp, rule version. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description must fully disclose behavior. It states 'scans' and 'returns structured findings' but does not explicitly state it is read-only. No mention of authentication, rate limits, or scope (e.g., all policies or filtered). Even though it's a scan, the agent cannot be sure it doesn't trigger actions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, minimal waste. Each sentence adds value: first states action and targets, second describes output format. No redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-parameter tool with output schema, the description covers the core purpose and output format. It specifies exactly what it checks (admin MFA exclusions, report-only mode). Could mention it is safe to run frequently, but given low complexity, it is mostly complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, schema coverage 100%. Baseline per rule is 4. Description adds no parameter info, but none needed. The tool operates without inputs, so clarity on purpose is sufficient.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly identifies the tool scans Entra Conditional Access policies for specific security gaps (admin MFA exclusions, report-only mode). It distinguishes from siblings like audit_app_registrations (different resource) and run_posture_scan (broader scan). Verb 'scans' + resource 'Entra Conditional Access policies' + specific focus.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool versus siblings. Given sibling names like run_posture_scan (broader security scan) and audit_app_registrations (audit of app registrations), the description should state when to choose this tool (e.g., for CA-specific gap analysis). The AI agent lacks decision context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
audit_app_registrations - First observed
generate_remediation_plan - First observed
revoke_or_disable_app_registration - First observed
run_posture_scan - First observed
scan_conditional_access_gaps
TDQS
Scored across 5 tools
Each tool has a distinct purpose: two separate scans, a combined scan, a report generator, and a remediation command generator. There is no functional overlap.
All tool names use a consistent verb_noun pattern in snake_case (e.g., audit_app_registrations, generate_remediation_plan), making them predictable and readable.
With 5 tools, the server is well-scoped for its purpose of assessing and remediating Entra ID posture without being too sparse or bloated.
The tools cover auditing, combined scanning, remediation planning, and disabling/revoking registrations. Missing a tool to execute remediation commands directly is a minor gap, but the set is largely complete.
Maintenance
Related MCP Connectors
CTEM for your Trusteed tenant: security summary, findings, compliance gaps, and scans via OAuth.
Permissioned access to Outlook, OneDrive and Teams via the user's own Microsoft account
- ZopDev MCPOAuthdev.zop
Cloud cost, inventory and governance on AWS/Azure/GCP. Read-only by default, optional scoped writes
Copilot connector permission audits with owner signoff receipts.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceProvides secure access to Microsoft Entra ID (Azure AD) resources including users, devices, and applications through Microsoft Graph API. Enables querying organizational data with comprehensive audit logging to Azure Blob Storage.-
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to query Microsoft Entra data using natural language, converting requests into Microsoft Graph API calls for read-only enterprise IT scenarios.53CC BY-4.0
- FlicenseNot gradedqualityCmaintenanceRead-only MCP server for Microsoft Entra ID (Azure AD) that enables querying user sign-in logs, group memberships, and assigned Microsoft 365 licenses via Microsoft Graph API. Provides security and audit visibility without any write operations.-
- AlicenseAqualityBmaintenanceEnables security analysts to investigate Microsoft Entra ID security logs through natural language, exposing read-only tools for user context, sign-ins, risky users, risk detections, directory audits, and conditional access policies.6MIT