The RAG query loop for the Microsoft Graph MCP Server for Enterprise — microsoft_graph_suggest_queries to retrieve vetted candidate Graph calls, microsoft_graph_get to execute them, microsoft_graph_list_properties for entity schema — with worked identity and directory examples, result-presentation guidance, and the read-only, RBAC-scoped, rate-limited constraints on what comes back.
Installs into .claude/skills of the current project.
Are you the author of Querying?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/wyre-ai-querying)
---
name: "microsoft-graph-querying"
description: >
The RAG query loop for the Microsoft Graph MCP Server for Enterprise —
microsoft_graph_suggest_queries to retrieve vetted candidate Graph calls,
microsoft_graph_get to execute them, microsoft_graph_list_properties for entity
schema — with worked identity and directory examples, result-presentation
guidance, and the read-only, RBAC-scoped, rate-limited constraints on what comes
back.
when_to_use: >-
When asked any question about a tenant's users, groups, applications, devices, licenses, sign-in
activity, or directory roles that should be answered through the Microsoft Graph MCP server —
count users, find guests, find inactive accounts, audit MFA registration, list app inventory,
check license usage. Use when: microsoft graph query, query entra, how many users, guest users,
inactive accounts, users without mfa, license usage, app inventory, directory roles, suggest
queries, graph get, or list properties.
---
# Querying Microsoft Entra with the Graph Enterprise MCP
The Microsoft Graph MCP Server for Enterprise is **not** a raw Graph API passthrough. It is built around a Retrieval-Augmented-Generation (RAG) workflow: you describe what you want, the server hands you vetted candidate API calls from a curated catalog, and you execute the one that fits. Following this loop is the difference between correct answers and made-up endpoints.
## Anti-triggers
Three plugins reach the same Microsoft 365 tenant with near-identical
vocabulary. This one answers questions; it cannot change anything.
- **Any change to the tenant** — this server issues `GET` only, so
there is no write path to fall back on. A single-tenant change
(disable an account, assign a licence, set out-of-office) belongs to
the `m365` plugin; a packaged multi-tenant action (offboard, reset
MFA, revoke sessions) belongs to the `cipp` plugin.
- **Fleet-wide questions** — "across all our tenants" needs a
multi-tenant control plane, not one delegated token scoped to one
signed-in user in one consented tenant; use the `cipp` plugin
(`cipp-tenants`, `cipp-standards`).
- **A tenant that authenticates but returns nothing** — that is
per-tenant admin consent, not a bad query; use
`microsoft-graph-connection`.
The short rule: **ask one tenant → this skill; change one tenant →
`m365`; do either across the fleet → `cipp`.**
## The core rule
**Always start with `microsoft_graph_suggest_queries`. Do not invent raw Graph endpoints.**
Microsoft Graph is enormous and its URL/`$filter`/`$select` syntax is full of sharp edges (advanced query parameters, `ConsistencyLevel` headers, OData casts). The `suggest_queries` tool exists precisely so the model doesn't have to guess. Hand-writing a Graph URL and passing it to `microsoft_graph_get` is an error pattern — even when you "know" the endpoint, route through `suggest_queries` so you get the catalog's correct, tested form.
## The three tools
| Tool | Role in the loop |
|------|------------------|
| `microsoft_graph_suggest_queries` | RAG search over a curated catalog of Graph API examples. Input: a natural-language intent. Output: candidate Graph API calls (endpoint, parameters, description) ranked by relevance. |
| `microsoft_graph_get` | Executes a read-only Graph `GET`. Input: a Graph API call (ideally one returned by `suggest_queries`). Honors the caller's roles, granted scopes, and Graph throttling. |
| `microsoft_graph_list_properties` | Returns the schema for a Graph entity — its properties and relationships. Use it when you need to know what's selectable before constructing or refining a call. |
## The query loop
```
1. suggest_queries(intent) → candidate Graph calls
2. (model) select the candidate that best matches the question
3. get(selected candidate) → data
4. (optional) list_properties(entity) → schema, to refine $select or pick the right entity
5. (model) translate the JSON result into a plain-language answer
```
Steps 2 and 5 are *your* job — `suggest_queries` proposes, the model disposes. When several candidates look plausible, prefer the one whose description most precisely matches the user's intent and whose parameters you can fill confidently. If a candidate needs a property you're unsure exists, call `list_properties` first.
## Worked examples
### "How many users are in the tenant?"
1. `microsoft_graph_suggest_queries("count of all users in the tenant")` → returns candidates such as a call against the `users` collection with a count.
2. Pick the candidate that returns a count rather than a full user list (cheaper, directly answers the question).
3. `microsoft_graph_get(<that candidate>)`.
4. Answer: "Contoso has **412** licensed and unlicensed user accounts in Entra ID."
### "Which users don't have MFA registered?"
1. `microsoft_graph_suggest_queries("users who have not registered for multi-factor authentication")` → expect candidates around authentication-methods registration reporting (admin reporting surface).
2. The right candidate is typically a user-registration-details report filtered to users where MFA is not registered — not a per-user method enumeration.
3. `microsoft_graph_get(<that candidate>)`.
4. If you need to also show each user's department or job title, call `microsoft_graph_list_properties("user")` to confirm those properties, then re-suggest/refine.
5. Answer with a named list and a count: "9 of 412 accounts have no MFA method registered — including 2 with admin roles, which is the priority to fix."
### "List the guest (external) users."
1. `microsoft_graph_suggest_queries("external guest users in the directory")` → candidates filtering the `users` collection on guest user type.
2. `microsoft_graph_get(<that candidate>)`.
3. Answer with the guest accounts, their invitation/external state, and a count. Flag guests with no recent sign-in as cleanup candidates.
### "Find inactive accounts that still hold Copilot licenses."
This needs two reads joined by the model:
1. `microsoft_graph_suggest_queries("user accounts with no recent sign-in activity")` → an inactive-users / sign-in-activity reporting candidate. Run it with `microsoft_graph_get`.
2. `microsoft_graph_suggest_queries("users assigned a Microsoft 365 Copilot license")` → a licensed-users candidate. Run it with `microsoft_graph_get`.
3. The model intersects the two result sets: accounts that appear in both are inactive *and* Copilot-licensed — wasted spend.
4. Answer with the overlap list and the reclaimable license count.
> The example endpoints above are illustrative of what `suggest_queries` typically returns — don't hard-code them. Always run `suggest_queries` for the live, tenant-appropriate candidate.
## Using `list_properties`
Call `microsoft_graph_list_properties` when you need the schema of an entity — for example to know whether `user` exposes `signInActivity`, `assignedLicenses`, or `createdDateTime`, or what relationships `group` has. Use it to:
- choose the right `$select` properties so a `get` returns exactly what the question needs;
- confirm an entity actually has the field you're about to filter on;
- decide which entity to query when a question could map to several (`user` vs `servicePrincipal` vs `device`).
## Constraints to respect
- **Read-only.** Everything here reports. There is no way to create, edit, or delete directory objects through this server. If the user asks for a change, say so and point them to a write-capable tool (e.g. the `cipp` plugin).
- **RBAC-honoring.** Results are scoped to the signed-in caller's Entra roles and the app's granted scopes. If a query returns less than expected, it may be a permissions boundary, not missing data — and per-tenant admin consent must already be in place (see the `microsoft-graph-connection` skill).
- **Rate limit.** 100 calls/min/user plus Graph's own throttling. Don't fan out speculative `get` calls — `suggest_queries` exists so one good call beats ten guesses.
- **Preview.** The example catalog and behavior may change; verify anything material in the Entra admin center.
## Presenting results
Raw Graph JSON is not an answer. Always translate: lead with the direct answer (a count, a yes/no, a named list), then supporting detail, then — if it's an audit-style question — a recommendation. Non-technical readers should never see GUIDs or JSON unless they ask.