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.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add WYRE-AI/msp-claude-plugins --skill querying --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Querying?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/wyre-ai-querying)More formats (shields.io, HTML) on the badges page.
---
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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!