Provider REST API
SecurityConnect Claude to Microsoft Entra ID
Read a tenant's users, groups, applications, directory roles, devices and sign-in logs through Microsoft Graph. Toolspoke puts 12 of its actions behind one MCP endpoint that Claude, Cursor, and Codex all speak.
- Connection
- Provider REST API
- Authentication
- Service principal (client credentials)
- Actions exposed
- 12
- Cost per call (typical)
- 1 credit
- Adapter
- Maintained by Toolspoke
Connected in three steps
- 1
Install Microsoft Entra ID
Open the marketplace in your workspace, add Microsoft Entra ID to the project your agents work in, and it appears on the gateway immediately.
- 2
Connect the credential
Authenticate with service principal (client credentials). Where to get one, and what it has to be able to reach, is the next section.
- 3
Point your agent at the gateway
Give your client one address, https://toolspoke.com/mcp. Claude Code takes it as a command, Claude and Claude Desktop add it as a custom connector, and Cursor, Codex and VS Code each read it from a config file of their own.
.mcp.json{
"mcpServers": {
"toolspoke": {
"type": "http",
"url": "https://toolspoke.com/mcp"
}
}
}One block covers every tool you have installed. Microsoft Entra ID shows up in the client as soon as your policy allows it, and so does everything else you install later.
Where the address goes, per client
- Claude Code
Run it in your project, then /mcp to sign in
claude mcp add --transport http toolspoke https://toolspoke.com/mcp- Claude and Claude Desktop
Settings, then Connectors, then Add custom connector
https://toolspoke.com/mcp- Cursor
~/.cursor/mcp.json, or .cursor/mcp.json for one project
{ "mcpServers": { "toolspoke": { "url": "https://toolspoke.com/mcp" } } }- Codex
~/.codex/config.toml
[mcp_servers.toolspoke] url = "https://toolspoke.com/mcp"- VS Code
.vscode/mcp.json, or the MCP: Add Server command
{ "servers": { "toolspoke": { "type": "http", "url": "https://toolspoke.com/mcp" } } }
What Microsoft Entra ID asks for
Service principal (client credentials). You provide it once, when you install the connector. Toolspoke encrypts it at rest and decrypts it only for the length of a single call, and the gateway attaches it to the outbound request itself, so it is never part of the arguments an agent sends.
- Directory (tenant) IDRequired
- Entra admin centre → App registrations → your app → Overview. It names the directory the sign-in happens in, and goes into the token endpoint address.
- 00000000-0000-0000-0000-000000000000
- Application (client) IDRequired
- Shown beside the tenant ID on the app's Overview page. This is the service principal Graph will see as the caller.
- 00000000-0000-0000-0000-000000000000
- Client secretRequired
- App registration → Certificates & secrets → New client secret. Copy the Value, not the Secret ID - the Value is shown once and never again. Grant the application permissions separately, under API permissions, and remember to click Grant admin consent: User.Read.All, Group.Read.All, Application.Read.All, RoleManagement.Read.Directory, AuditLog.Read.All, Device.Read.All and Organization.Read.All cover every action in this connector, and none of them permits a change.
- Paste the secret value
What Claude can do in Microsoft Entra ID
12 actions, each one declared and named by the connector rather than discovered at runtime. A workspace policy grants a person all of them, a hand-picked selection, everything on the read side, everything on the write side, or none.
- Reads
- 12Reads
- Writes
- 0Writes
- Destructive
- 0Destructive
Reads
12Fetches data and changes nothing.
list_usersList the people in the tenant, with the properties named in `select`. This is the entry point for the user side: get_user, list_group_members and the role-assignment reads all key on the id returned here. `filter` takes a simple OData expression - "accountEnabled eq false", "userType eq 'Guest'", "startsWith(displayName,'Jane')" - but not endsWith or $search, because this connector does not send the ConsistencyLevel header that Graph's advanced query requires. Page with the @odata.nextLink Graph returns rather than an offset.
get_userFetch one person by object id or userPrincipalName, with the properties named in `select`. Graph accepts an address such as [email protected] here as well as a GUID, so this is the way to resolve a sign-in name without a list first. `accountEnabled` false means the account is blocked from signing in.
list_groupsList the tenant's groups with their id, name, description and type. The combination of `groupTypes`, `securityEnabled` and `mailEnabled` is what distinguishes a Microsoft 365 group from a plain security group; a group with 'DynamicMembership' in groupTypes has its membership computed from a rule rather than assigned. Groups carry application and resource access, so this is the map of what is granted in bulk before any individual is looked at.
get_groupFetch one group by object id, with its name, description, type and membership rule. Read it to confirm which group an id refers to before calling list_group_members, which returns the people in it.
list_group_membersList the direct members of one group. Members can be users, other groups, devices or service principals, so each entry carries an @odata.type saying which it is. Membership is direct only - a person who is in a nested group appears under that group rather than here.
list_applicationsList the app registrations in the tenant - the applications this directory owns, as opposed to the service principals that represent them. Each carries its appId, display name, sign-in audience and requested permissions. Read it to answer "what have we registered" and to find the appId that a sign-in log entry names. Client secrets and certificates are never returned in plaintext by Graph, and no operation here returns one.
list_service_principalsList the service principals in the tenant - the local identity of every application that can sign in here, including third-party apps somebody consented to and managed identities Azure created. This is the surface an access review actually cares about: an app registration is a definition, a service principal is a thing with permissions. `appOwnerOrganizationId` different from your own tenant id marks a multi-tenant app from someone else. Withheld from the audit log because a managed identity or an enterprise app is frequently named after the person or the resource it acts for.
list_directory_rolesList the directory roles that are activated in the tenant, with each one's id, display name, description and roleTemplateId. Only activated roles appear - a role nobody has ever been assigned may be missing entirely, which is Entra's behaviour and not an error. The `id` returned here is a directory role instance; list_role_assignments keys on roleDefinitionId, which for a built-in role is the same GUID as roleTemplateId. $filter supports `eq` only.
list_role_assignmentsList who holds which directory role. Each assignment pairs a principalId with a roleDefinitionId and a scope, and `expand: principal` resolves the principal into the user, group or service principal it names - which is what turns a list of GUIDs into the answer to "who are our Global Administrators". Filter by roleDefinitionId to ask about one role (62e90394-69f5-4237-9190-012177145e10 is Global Administrator) or by principalId to ask about one person. Withheld from the audit log: this is the list of named people holding privileged access.
list_sign_in_logsRead the tenant's sign-in logs - who signed in, from which IP address and location, on which device, to which application, and whether it worked. This is the operation to reach for after a suspicious sign-in alert. Always pass a `filter` with a time range or Graph times the request out: "createdDateTime ge 2026-08-01T00:00:00Z and createdDateTime le 2026-08-07T23:59:59Z". Other filterable properties are userId, userPrincipalName, appId, appDisplayName, status/errorCode, ipAddress, clientAppUsed and riskLevelDuringSignIn. Only $filter, $top and $skiptoken are supported - no $select and no $orderby - and only the retention window Entra keeps is available. The response is named employees, their addresses and their locations, so it is never written to the audit log.
list_devicesList the devices registered in the directory, with each one's display name, operating system and version, trust type, compliance and management state, and when it was last seen. Use it to answer "is this laptop enrolled and compliant" and to find the deviceId a sign-in log entry names. `trustType` is AzureAd for a joined device, ServerAd for a hybrid-joined one and Workplace for a registered personal device. A device record is named after the employee who uses it, so the response is withheld from the audit log.
get_organizationRead the tenant's own record - display name, verified domains, country, directory size limit and creation date. It takes no arguments and needs only Organization.Read.All, so it is the cheapest call that proves the app registration, the client secret and the admin consent are all actually in place, which is why an install runs it to test itself. It describes the tenant rather than anybody in it, so unlike most reads here its response is kept in the audit log.
What it will not do
Enforced by the gateway rather than left to convention, which is why each of these can be stated flatly.
- It cannot call anything else
- The 12 actions above are the whole of it. A call to any other name is refused before it reaches Microsoft Entra ID rather than forwarded on, and connecting your account does not add to the list: it is fixed by the connector, not discovered at run time.
- It only reads
- Every action here reads. Nothing this connector can do changes anything in Microsoft Entra ID.
- It reaches no further than your credential
- Toolspoke holds no access to Microsoft Entra ID of its own. Every call carries the credential you stored and nothing besides, so whatever that credential cannot reach, this connector cannot reach either.
- It never hears from Microsoft Entra ID
- Nothing is pushed to it. There is no webhook, no subscription and no polling, so this connector cannot notice by itself that something changed in Microsoft Entra ID. An agent has to ask.
- It does not smooth over provider limits
- Toolspoke does not retry, queue or back off around Microsoft Entra ID's own rate limits. A call that Microsoft Entra ID refuses comes back to the agent as a failed call.
Before you connect it
What can Claude do in Microsoft Entra ID?
12 named actions: 12 that only read. They include list_users, get_user and list_groups. Nothing outside that list is reachable: the connector declares each operation by name rather than proxying whatever an agent asks for.
What credentials does the Microsoft Entra ID connector need?
Service principal (client credentials). The connector asks for directory (tenant) id, application (client) id and client secret. Values are encrypted at rest and attached to the outbound request by the gateway, so they are never part of the arguments an agent sends and never reach the audit log.
Does the Microsoft Entra ID connector work with Cursor and Codex, or only Claude?
Any client that speaks MCP, and every one of them gets the same 12 actions. There is a single address, https://toolspoke.com/mcp. Claude Code adds it with claude mcp add --transport http, Claude and Claude Desktop take it as a custom connector in settings, Cursor reads it from .cursor/mcp.json, Codex from ~/.codex/config.toml, and VS Code from .vscode/mcp.json. Each of them signs in to the gateway itself, so there is no key to paste.
What does the Microsoft Entra ID connector not do?
The 12 actions above are the whole of it. A call to any other name is refused before it reaches Microsoft Entra ID rather than forwarded on, and connecting your account does not add to the list: it is fixed by the connector, not discovered at run time. Every action here reads. Nothing this connector can do changes anything in Microsoft Entra ID. Toolspoke holds no access to Microsoft Entra ID of its own. Every call carries the credential you stored and nothing besides, so whatever that credential cannot reach, this connector cannot reach either. Nothing is pushed to it. There is no webhook, no subscription and no polling, so this connector cannot notice by itself that something changed in Microsoft Entra ID. An agent has to ask. Toolspoke does not retry, queue or back off around Microsoft Entra ID's own rate limits. A call that Microsoft Entra ID refuses comes back to the agent as a failed call.
Can I limit which actions an agent can call?
Yes, in two places. The project switches Microsoft Entra ID's actions on and off one at a time, for everyone in the project at once, and the screen groups them by read, write and destructive so turning off everything that deletes is one click. An individual agent key can then be narrowed further, to particular toolkits in a project and to particular actions in a toolkit. Whatever it was granted, a key never reaches a project its owner cannot.
What gets recorded when an agent calls Microsoft Entra ID?
Every attempt, with the agent that made it and the person that agent belongs to, the full request payload, the response payload, the status, the duration, and the credits spent. Values whose key names a secret are masked out before the record is shown to anyone. An operation the connector marks as not retained never has its response body written at all, so the gateway keeps no second copy of what was read.