feat(microsoft_ad): licensing, security, audit, role, and device operations (#6742)

* feat(microsoft_ad): licensing, security, audit, role, and device operations

Deepens the Microsoft Entra ID block from 12 to 36 tools against the Microsoft
Graph v1.0 reference: license assignment and tenant SKUs, password set/reset,
sign-in session revocation, authentication methods, sign-in and directory audit
logs, app role and directory role assignments, service principals, device reads,
and conditional access policy reads.

Device write (device-update, device-delete) is deliberately excluded. Both
document Directory.AccessAsUser.All as their only delegated scope, with the
higher-privileged read documented as unavailable, so supporting them would mean
requesting tenant-wide act-as-the-user directory access for two operations that
additionally require the caller to hold Intune Administrator.

Also drops an undocumented ?$select= from create_user that was silently nulling
department and accountEnabled in the response.

* fix(microsoft_ad): resolve OData filter and search by owning operation

The params mapper assigned result.filter from each filter subBlock in turn, so
the last non-empty one won regardless of the selected operation. Because a
subBlock keeps its value after the operation changes, a filter written for one
endpoint was sent to every other collection operation — invalid OData against a
different Graph resource, or a silently wrong page.

Resolves the filter and search terms from an explicit operation-to-field map
instead, so each operation reads only the field it owns.

* fix(microsoft_ad): clear non-owning filter and search on the merged inputs

The executor merges { ...inputs, ...transformedParams }, so declining to copy a
stale filter is not enough — the serialized value survives the merge and still
reaches the tool. Advanced-mode subBlocks are serialized on non-emptiness alone
and never have their condition evaluated, so the value is present even when the
field is hidden.

Write filter and search on every operation, as undefined when the operation owns
neither, so the merge clears them.

* fix(microsoft_ad): clear the MFA flag and let paged user operations continue without a User ID

The set_password MFA dropdown only wrote its key when non-empty, so the "No Change"
empty string survived `{ ...inputs, ...transformedParams }` and reached Graph in place
of a boolean. Assign it explicitly, including as `undefined`, the same way `filter` and
`search` are handled.

`list_user_app_role_assignments` and `list_user_devices` page by `@odata.nextLink`, and
both tools already treat `userId` as optional once a continuation URL is supplied. Drop
them from the required set when Next Page is filled in so pagination-only runs pass block
validation.

Also note on the reset_password output that a generated password reaches workflow outputs,
run history, and the model, matching how other tools that return secrets document exposure.

* fix(microsoft_ad): require the service principal ID only on the first page

Every other single-resource ID field pairs its condition with a matching required
rule; servicePrincipalId had none, so a first-page run could pass block validation
with an empty ID and fail inside the tool instead. Require it unless a continuation
URL is supplied, matching the paged per-user operations.

* fix(microsoft_ad): reject a continuation URL from a different collection

Every paged operation reads the one shared Next Page field, and a subBlock keeps
its value after the operation changes. Paging /users and then switching the block
to /devices short-circuited back to the user page, silently returning the previous
collection instead of the selected one.

Assert the continuation URL's terminal path segment against the collection the tool
actually reads, which also rejects a nextLink pasted from an unrelated response.
This commit is contained in:
Waleed
2026-08-15 19:39:40 -07:00
committed by GitHub
parent fed891f69d
commit 257029a60c
39 changed files with 3966 additions and 42 deletions
@@ -1,6 +1,6 @@
---
title: Azure AD
description: Manage users and groups in Azure AD (Microsoft Entra ID)
description: Manage identities, licenses, roles, and access in Azure AD (Microsoft Entra ID)
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -31,7 +31,7 @@ If you encounter issues with the Azure AD integration, contact us at [help@sim.a
## Usage Instructions
Integrate Azure Active Directory into your workflows. List, create, update, and delete users and groups. Manage group memberships programmatically.
Integrate Azure Active Directory into your workflows. Create, update, and delete users and groups, manage group memberships, assign and remove licenses, reset passwords, revoke sign-in sessions, read sign-in and directory audit logs, grant and revoke app and directory roles, and read registered devices and conditional access policies. Device writes are not supported.
@@ -117,10 +117,10 @@ Create a new user in Azure AD (Microsoft Entra ID)
| ↳ `userPrincipalName` | string | User principal name \(email\) |
| ↳ `mail` | string | Email address |
| ↳ `jobTitle` | string | Job title |
| ↳ `department` | string | Department |
| ↳ `officeLocation` | string | Office location |
| ↳ `mobilePhone` | string | Mobile phone number |
| ↳ `accountEnabled` | boolean | Whether the account is enabled |
| ↳ `businessPhones` | array | Business phone numbers |
| ↳ `preferredLanguage` | string | Preferred language |
### Update Azure AD User
@@ -339,4 +339,469 @@ Remove a member from a group in Azure AD (Microsoft Entra ID)
| `groupId` | string | Group ID |
| `memberId` | string | Member ID that was removed |
### Assign Microsoft Entra ID License
Add or remove subscription licenses (SKUs) on a user in Microsoft Entra ID. Removing a license immediately revokes the access it granted to the associated services.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name to change licenses for |
| `addSkuIds` | string | No | Comma-separated SKU IDs \(GUIDs\) of the licenses to assign. Leave empty to only remove licenses. |
| `removeSkuIds` | string | No | Comma-separated SKU IDs \(GUIDs\) of the licenses to remove. Leave empty to only add licenses. |
| `disabledPlanIds` | string | No | Comma-separated service plan IDs \(GUIDs\) to disable on every license being assigned |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `userId` | string | ID of the user whose licenses changed |
| `displayName` | string | Display name of the user |
| `userPrincipalName` | string | User principal name of the user |
| `assignedLicenses` | array | Licenses assigned to the user after the change |
### List Microsoft Entra ID User Licenses
List the subscription licenses assigned to a user in Microsoft Entra ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `licenses` | array | Licenses assigned to the user |
| `licenseCount` | number | Number of licenses returned |
### List Microsoft Entra ID Subscribed SKUs
List the subscription SKUs the tenant owns, including how many license units are prepaid and how many are consumed
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `skus` | array | Subscription SKUs owned by the tenant |
| `skuCount` | number | Number of SKUs returned |
### Revoke Microsoft Entra ID Sign-In Sessions
Invalidate every refresh token and session cookie issued to a user, forcing them to sign in again on all applications and devices. Revocation can take a few minutes to take effect and does not apply to external users.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name whose sessions should be revoked |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `revoked` | boolean | Whether Microsoft Graph confirmed the sessions were revoked |
| `userId` | string | ID of the user whose sessions were revoked |
### Set Microsoft Entra ID User Password
Set a specific password on a user by updating their password profile. Cannot be used for federated users. Requires an administrator role in Microsoft Entra ID.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name whose password should be set |
| `password` | string | Yes | The new password. Must satisfy the tenant password policy. |
| `forceChangePasswordNextSignIn` | boolean | No | Whether the user must change this password at their next sign-in. Defaults to true. |
| `forceChangePasswordNextSignInWithMfa` | boolean | No | Whether the user must complete multifactor authentication before being forced to change the password |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `updated` | boolean | Whether the password was set successfully |
| `userId` | string | ID of the user whose password was set |
| `forceChangePasswordNextSignIn` | boolean | Whether the user must change the password at their next sign-in |
### Reset Microsoft Entra ID User Password
Reset another user's password through their password authentication method. Leave the new password empty to have Microsoft generate one and return it. The user is prompted to change the password at their next sign-in. Cannot be run against your own account.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name whose password should be reset |
| `newPassword` | string | No | The new password. Required for tenants with hybrid password scenarios. Leave empty for a cloud-only password to have Microsoft generate and return one. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `accepted` | boolean | Whether Microsoft Graph accepted the password reset operation |
| `userId` | string | ID of the user whose password was reset |
| `newPassword` | string | The system-generated password, returned only when no new password was supplied in the request. Like every tool output it appears in workflow outputs and run history, and is sent to the model when an agent calls this tool, so prefer supplying your own password when the value must not leave the workflow. |
| `operationLocation` | string | URL to poll for the status of the long-running password reset operation |
### List Microsoft Entra ID Authentication Methods
List the authentication methods a user has registered, such as passwords, phone numbers, FIDO2 keys, and authenticator apps
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `methods` | array | Authentication methods registered by the user |
| `methodCount` | number | Number of authentication methods returned |
### List Microsoft Entra ID Sign-Ins
List sign-in events from the Microsoft Entra ID sign-in logs, newest first. Requires a Microsoft Entra ID P1 or P2 license. Apply a date filter to keep large queries from timing out.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of sign-ins to return \(default and maximum page size is 1000\) |
| `filter` | string | No | OData filter expression. Filterable fields include userPrincipalName, userId, appId, appDisplayName, ipAddress, createdDateTime, conditionalAccessStatus, riskState and status/errorCode. Example: "createdDateTime ge 2024-01-01T00:00:00Z and status/errorCode ne 0". |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `signIns` | array | Sign-in events |
| `signInCount` | number | Number of sign-ins returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### List Microsoft Entra ID Directory Audits
List directory audit records showing who changed what in Microsoft Entra ID, such as user creation, group membership changes, and role assignments
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of audit records to return |
| `filter` | string | No | OData filter expression. Filterable fields include activityDateTime, activityDisplayName, correlationId, loggedByService, initiatedBy and targetResources. Example: "activityDateTime ge 2024-01-01T00:00:00Z". |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `audits` | array | Directory audit records |
| `auditCount` | number | Number of audit records returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### List Microsoft Entra ID User App Role Assignments
List the application role assignments granted to a user, including assignments the user inherits from groups they are a direct member of
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | No | User ID or user principal name. Not needed when Next Page is provided to fetch a later page. |
| `top` | number | No | Maximum number of assignments to return |
| `filter` | string | No | OData filter expression. Filterable fields include id, resourceId and principalDisplayName. Example: "resourceId eq 8e881353-1735-45af-af21-ee1344582a4d". |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `assignments` | array | App role assignments granted to the user |
| `assignmentCount` | number | Number of assignments returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### Grant Microsoft Entra ID App Role To User
Grant a user an application role on a service principal, giving them access to that application
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name to grant the app role to |
| `resourceId` | string | Yes | Object ID of the resource service principal that defines the app role. Use List Service Principals to find it. |
| `appRoleId` | string | Yes | ID of the app role to grant. Use the all-zero GUID 00000000-0000-0000-0000-000000000000 to assign access without a specific role. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `assignment` | object | The created app role assignment |
| ↳ `id` | string | App role assignment ID, used when removing the assignment |
| ↳ `appRoleId` | string | ID of the app role. All-zero GUID means the assignment grants access without a specific role. |
| ↳ `createdDateTime` | string | When the assignment was created |
| ↳ `principalId` | string | ID of the assigned principal |
| ↳ `principalDisplayName` | string | Display name of the assigned principal |
| ↳ `principalType` | string | Principal type: User, Group, or ServicePrincipal |
| ↳ `resourceId` | string | ID of the resource service principal that defines the app role |
| ↳ `resourceDisplayName` | string | Display name of the resource |
### Revoke Microsoft Entra ID App Role From User
Revoke an application role assignment from a user, removing their access to that application. Takes the assignment's own ID, not the app role ID.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name the assignment belongs to |
| `appRoleAssignmentId` | string | Yes | ID of the app role assignment to remove, taken from the "id" field of List User App Role Assignments |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `removed` | boolean | Whether the assignment was removed successfully |
| `userId` | string | ID of the user the assignment belonged to |
| `appRoleAssignmentId` | string | ID of the removed app role assignment |
### List Microsoft Entra ID Service Principals
List the enterprise applications and service principals in the tenant, including the app roles each one exposes
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of service principals to return \(default and maximum page size is 100\) |
| `filter` | string | No | OData filter expression. Example: "servicePrincipalType eq \'Application\'" or "startsWith\(displayName, \'Salesforce\'\)". |
| `search` | string | No | Search string matched against the service principal display name |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `servicePrincipals` | array | Service principals in the tenant |
| `servicePrincipalCount` | number | Number of service principals returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### List Microsoft Entra ID Application Assignments
List every user, group, and service principal assigned to an application, by reading the app role assignments on its service principal. Recently granted or removed assignments can take time to appear.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `servicePrincipalId` | string | No | Object ID of the service principal. Use List Service Principals to find it. Not needed when Next Page is provided to fetch a later page. |
| `filter` | string | No | OData filter expression supporting eq and startswith. Example: "principalType eq \'User\'". |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `assignments` | array | Principals assigned to the application |
| `assignmentCount` | number | Number of assignments returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### List Microsoft Entra ID Directory Roles
List the administrator roles that are activated in the tenant, such as Global Administrator and User Administrator. Roles that have never been activated are not returned.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `roles` | array | Activated directory roles |
| `roleCount` | number | Number of directory roles returned |
### List Microsoft Entra ID Directory Role Members
List the principals holding an administrator role. Returns up to 1000 members; this endpoint does not support paging.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `directoryRoleId` | string | Yes | Object ID of the directory role. Use List Directory Roles to find it. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `members` | array | Principals holding the directory role |
| `memberCount` | number | Number of members returned |
### Add Microsoft Entra ID Directory Role Member
Grant a user an administrator role in Microsoft Entra ID. This is a privileged change that expands what the user can do across the tenant.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `directoryRoleId` | string | Yes | Object ID of the directory role. Use List Directory Roles to find it. |
| `memberId` | string | Yes | Object ID of the user to grant the role to |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `added` | boolean | Whether the member was added successfully |
| `directoryRoleId` | string | ID of the directory role |
| `memberId` | string | ID of the member that was added |
### Remove Microsoft Entra ID Directory Role Member
Revoke an administrator role from a user in Microsoft Entra ID. Removes only the role membership; the user account itself is not deleted.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `directoryRoleId` | string | Yes | Object ID of the directory role. Use List Directory Roles to find it. |
| `memberId` | string | Yes | Object ID of the user to remove the role from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `removed` | boolean | Whether the member was removed successfully |
| `directoryRoleId` | string | ID of the directory role |
| `memberId` | string | ID of the member that was removed |
### List Microsoft Entra ID Devices
List the devices registered in Microsoft Entra ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of devices to return |
| `filter` | string | No | OData filter expression. Example: "accountEnabled eq false" or "operatingSystem eq \'Windows\'". |
| `search` | string | No | Search string matched against the device display name |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `devices` | array | Registered devices |
| `deviceCount` | number | Number of devices returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### Get Microsoft Entra ID Device
Get a registered device by its object ID from Microsoft Entra ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `deviceObjectId` | string | Yes | Device object ID \(the "id" field\), not the "deviceId" registration identifier |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `device` | object | Device details |
| ↳ `id` | string | Device object ID, used to get, update, or delete the device |
| ↳ `deviceId` | string | Unique device identifier set during registration |
| ↳ `displayName` | string | Display name of the device |
| ↳ `operatingSystem` | string | Operating system of the device |
| ↳ `operatingSystemVersion` | string | Operating system version of the device |
| ↳ `accountEnabled` | boolean | Whether the device is enabled |
| ↳ `isCompliant` | boolean | Whether the device complies with MDM policies |
| ↳ `isManaged` | boolean | Whether the device is managed by an MDM app |
| ↳ `trustType` | string | Device registration type: Workplace, AzureAd, or ServerAd |
| ↳ `profileType` | string | Device profile type: RegisteredDevice, SecureVM, Printer, Shared, or IoT |
| ↳ `manufacturer` | string | Manufacturer of the device |
| ↳ `model` | string | Model of the device |
| ↳ `approximateLastSignInDateTime` | string | Approximate time the device last signed in |
| ↳ `registrationDateTime` | string | When the device was registered |
### List Microsoft Entra ID User Devices
List the devices a user has registered or owns. Devices the caller cannot read are returned with only their ID and the remaining fields null.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | No | User ID or user principal name. Not needed when Next Page is provided to fetch a later page. |
| `deviceRelationship` | string | No | Which devices to list: "registered" for devices the user registered, or "owned" for devices the user owns. Defaults to "registered". |
| `top` | number | No | Maximum number of devices to return |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `devices` | array | Devices linked to the user |
| `deviceCount` | number | Number of devices returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### List Microsoft Entra ID Conditional Access Policies
List the conditional access policies configured in the tenant, including their state and the conditions and controls they enforce. Read-only.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of policies to return |
| `filter` | string | No | OData filter expression. Example: "state eq \'enabled\'". |
| `nextLink` | string | No | Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `policies` | array | Conditional access policies |
| `policyCount` | number | Number of policies returned |
| `nextLink` | string | Continuation URL for the next page of results, or null if there are no more |
### Get Microsoft Entra ID Conditional Access Policy
Get a single conditional access policy by ID, including the conditions it matches and the controls it enforces. Read-only.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `policyId` | string | Yes | Conditional access policy ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `policy` | object | Conditional access policy details |
| ↳ `id` | string | Conditional access policy ID |
| ↳ `displayName` | string | Display name of the policy |
| ↳ `state` | string | Policy state: enabled, disabled, or enabledForReportingButNotEnforced |
| ↳ `templateId` | string | ID of the template the policy was created from |
| ↳ `createdDateTime` | string | When the policy was created |
| ↳ `modifiedDateTime` | string | When the policy was last modified |
| ↳ `conditions` | json | Conditions that trigger the policy \(users, applications, platforms, locations, risk levels\) |
| ↳ `grantControls` | json | Controls enforced when the policy applies, or null when none are configured |
| ↳ `sessionControls` | json | Session controls enforced when the policy applies, or null when none are set |
+479 -21
View File
@@ -4,12 +4,110 @@ import type { BlockConfig, BlockMeta } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
import type { MicrosoftAdResponse } from '@/tools/microsoft_ad/types'
/** Operations that act on a single user and therefore require the User ID field. */
const USER_ID_OPERATIONS = [
'get_user',
'update_user',
'delete_user',
'assign_license',
'list_user_licenses',
'revoke_sign_in_sessions',
'set_password',
'reset_password',
'list_authentication_methods',
'list_user_app_role_assignments',
'add_user_app_role_assignment',
'remove_user_app_role_assignment',
'list_user_devices',
]
/**
* Per-user operations that page through an `@odata.nextLink`. The continuation URL already
* addresses the user, so these are the only per-user operations that can run without a User ID,
* and only when continuing from a previous page.
*/
const PAGED_USER_ID_OPERATIONS = ['list_user_app_role_assignments', 'list_user_devices']
/** Per-user operations that always require a User ID, whichever page is being fetched. */
const ALWAYS_USER_ID_OPERATIONS = USER_ID_OPERATIONS.filter(
(operation) => !PAGED_USER_ID_OPERATIONS.includes(operation)
)
/** Collection operations that accept a page size and an @odata.nextLink continuation URL. */
const PAGED_OPERATIONS = [
'list_users',
'list_groups',
'list_group_members',
'list_sign_ins',
'list_directory_audits',
'list_user_app_role_assignments',
'list_service_principals',
'list_devices',
'list_user_devices',
'list_conditional_access_policies',
]
/**
* Operations that return an `@odata.nextLink` continuation URL. This is a superset of
* `PAGED_OPERATIONS`: `appRoleAssignedTo` pages through `@odata.nextLink` but the tool does not
* expose a `$top` page size.
*/
const NEXT_LINK_OPERATIONS = [...PAGED_OPERATIONS, 'list_service_principal_app_role_assignments']
/** Operations that own the shared `filter` and `search` fields. */
const SHARED_FILTER_OPERATIONS = ['list_users', 'list_groups']
/**
* The subBlock each operation reads its OData `$filter` from.
*
* A subBlock keeps its value after the operation changes, so the filter is resolved by
* ownership rather than by letting later assignments overwrite earlier ones. Otherwise a clause
* written for `/users` would still be sent when the block is switched to `/auditLogs/signIns` or
* `/devices`, where it is invalid.
*
* The mapper must write `filter` and `search` on every operation, including as `undefined`. The
* executor merges `{ ...inputs, ...transformedParams }`, so a key that is merely omitted here
* leaves the stale serialized value in place rather than clearing it. The same applies to every
* optional field the mapper coerces out of a subBlock string, such as
* `forceChangePasswordNextSignInWithMfa`: omitting the key on "No Change" would forward the raw
* empty string to Graph in place of a boolean.
*/
const FILTER_FIELD_BY_OPERATION: Record<string, string> = {
list_users: 'filter',
list_groups: 'filter',
list_sign_ins: 'signInFilter',
list_directory_audits: 'auditFilter',
list_user_app_role_assignments: 'appRoleFilter',
list_service_principal_app_role_assignments: 'appRoleFilter',
list_service_principals: 'servicePrincipalFilter',
list_devices: 'deviceFilter',
list_conditional_access_policies: 'policyFilter',
}
/** The subBlock each operation reads its `$search` term from. */
const SEARCH_FIELD_BY_OPERATION: Record<string, string> = {
list_users: 'search',
list_groups: 'search',
list_service_principals: 'servicePrincipalSearch',
list_devices: 'deviceSearch',
}
/** Operations that act on a single device object. */
const DEVICE_ID_OPERATIONS = ['get_device']
/** Operations that act on a single directory role. */
const DIRECTORY_ROLE_OPERATIONS = [
'list_directory_role_members',
'add_directory_role_member',
'remove_directory_role_member',
]
export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
type: 'microsoft_ad',
name: 'Azure AD',
description: 'Manage users and groups in Azure AD (Microsoft Entra ID)',
description: 'Manage identities, licenses, roles, and access in Azure AD (Microsoft Entra ID)',
longDescription:
'Integrate Azure Active Directory into your workflows. List, create, update, and delete users and groups. Manage group memberships programmatically.',
'Integrate Azure Active Directory into your workflows. Create, update, and delete users and groups, manage group memberships, assign and remove licenses, reset passwords, revoke sign-in sessions, read sign-in and directory audit logs, grant and revoke app and directory roles, and read registered devices and conditional access policies. Device writes are not supported.',
docsLink: 'https://docs.sim.ai/integrations/microsoft_ad',
category: 'tools',
integrationType: IntegrationType.Security,
@@ -58,6 +156,63 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
{ text: 'Remove member', field: 'memberId', core: true },
{ text: 'from group', field: 'groupId', core: true },
],
assign_license: [
{ text: 'Change licenses for', field: 'userId', core: true },
{ text: ', adding', field: 'addSkuIds' },
{ text: ', removing', field: 'removeSkuIds' },
],
list_user_licenses: [{ text: 'List licenses for user', field: 'userId', core: true }],
list_subscribed_skus: ['List tenant subscription SKUs'],
revoke_sign_in_sessions: [
{ text: 'Revoke sign-in sessions for', field: 'userId', core: true },
],
set_password: [{ text: 'Set the password for user', field: 'userId', core: true }],
reset_password: [{ text: 'Reset the password for user', field: 'userId', core: true }],
list_authentication_methods: [
{ text: 'List authentication methods for user', field: 'userId', core: true },
],
list_sign_ins: ['List sign-ins', { text: ', where', field: 'signInFilter' }],
list_directory_audits: ['List directory audits', { text: ', where', field: 'auditFilter' }],
list_user_app_role_assignments: [
{ text: 'List app role assignments for user', field: 'userId', core: true },
],
add_user_app_role_assignment: [
{ text: 'Grant app role', field: 'appRoleId', core: true },
{ text: 'to user', field: 'userId', core: true },
],
remove_user_app_role_assignment: [
{ text: 'Revoke app role assignment', field: 'appRoleAssignmentId', core: true },
{ text: 'from user', field: 'userId', core: true },
],
list_service_principals: [
'List service principals',
{ text: ', matching', field: 'servicePrincipalSearch' },
],
list_service_principal_app_role_assignments: [
{ text: 'List assignments for application', field: 'servicePrincipalId', core: true },
],
list_directory_roles: ['List directory roles'],
list_directory_role_members: [
{ text: 'List members of directory role', field: 'directoryRoleId', core: true },
],
add_directory_role_member: [
{ text: 'Grant directory role', field: 'directoryRoleId', core: true },
{ text: 'to', field: 'memberId', core: true },
],
remove_directory_role_member: [
{ text: 'Revoke directory role', field: 'directoryRoleId', core: true },
{ text: 'from', field: 'memberId', core: true },
],
list_devices: ['List devices', { text: ', matching', field: 'deviceSearch' }],
get_device: [{ text: 'Fetch device', field: 'deviceObjectId', core: true }],
list_user_devices: [
{ text: 'List devices for user', field: 'userId', core: true },
{ text: ', linked as', field: 'deviceRelationship' },
],
list_conditional_access_policies: ['List conditional access policies'],
get_conditional_access_policy: [
{ text: 'Fetch conditional access policy', field: 'policyId', core: true },
],
},
},
},
@@ -81,6 +236,35 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
{ label: 'List Group Members', id: 'list_group_members' },
{ label: 'Add Group Member', id: 'add_group_member' },
{ label: 'Remove Group Member', id: 'remove_group_member' },
{ label: 'Assign License', id: 'assign_license' },
{ label: 'List User Licenses', id: 'list_user_licenses' },
{ label: 'List Subscribed SKUs', id: 'list_subscribed_skus' },
{ label: 'Revoke Sign-In Sessions', id: 'revoke_sign_in_sessions' },
{ label: 'Set Password', id: 'set_password' },
{ label: 'Reset Password', id: 'reset_password' },
{ label: 'List Authentication Methods', id: 'list_authentication_methods' },
{ label: 'List Sign-Ins', id: 'list_sign_ins' },
{ label: 'List Directory Audits', id: 'list_directory_audits' },
{ label: 'List User App Role Assignments', id: 'list_user_app_role_assignments' },
{ label: 'Grant App Role To User', id: 'add_user_app_role_assignment' },
{ label: 'Revoke App Role From User', id: 'remove_user_app_role_assignment' },
{ label: 'List Service Principals', id: 'list_service_principals' },
{
label: 'List Application Assignments',
id: 'list_service_principal_app_role_assignments',
},
{ label: 'List Directory Roles', id: 'list_directory_roles' },
{ label: 'List Directory Role Members', id: 'list_directory_role_members' },
{ label: 'Add Directory Role Member', id: 'add_directory_role_member' },
{ label: 'Remove Directory Role Member', id: 'remove_directory_role_member' },
{ label: 'List Devices', id: 'list_devices' },
{ label: 'Get Device', id: 'get_device' },
{ label: 'List User Devices', id: 'list_user_devices' },
{
label: 'List Conditional Access Policies',
id: 'list_conditional_access_policies',
},
{ label: 'Get Conditional Access Policy', id: 'get_conditional_access_policy' },
],
value: () => 'list_users',
},
@@ -98,8 +282,11 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'User ID',
type: 'short-input',
placeholder: 'User ID or user principal name (e.g., user@example.com)',
condition: { field: 'operation', value: ['get_user', 'update_user', 'delete_user'] },
required: { field: 'operation', value: ['get_user', 'update_user', 'delete_user'] },
condition: { field: 'operation', value: USER_ID_OPERATIONS },
required: (values) =>
values?.nextLink
? { field: 'operation', value: ALWAYS_USER_ID_OPERATIONS }
: { field: 'operation', value: USER_ID_OPERATIONS },
},
// Create user fields
{
@@ -131,8 +318,8 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'Password',
type: 'short-input',
placeholder: 'Initial password',
condition: { field: 'operation', value: 'create_user' },
required: { field: 'operation', value: 'create_user' },
condition: { field: 'operation', value: ['create_user', 'set_password'] },
required: { field: 'operation', value: ['create_user', 'set_password'] },
password: true,
},
{
@@ -213,10 +400,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'Max Results',
type: 'short-input',
placeholder: 'e.g., 100 (max 999)',
condition: {
field: 'operation',
value: ['list_users', 'list_groups', 'list_group_members'],
},
condition: { field: 'operation', value: PAGED_OPERATIONS },
mode: 'advanced',
},
{
@@ -224,7 +408,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'Filter',
type: 'short-input',
placeholder: "e.g., department eq 'Sales'",
condition: { field: 'operation', value: ['list_users', 'list_groups'] },
condition: { field: 'operation', value: SHARED_FILTER_OPERATIONS },
mode: 'advanced',
},
{
@@ -232,7 +416,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'Search',
type: 'short-input',
placeholder: 'Search by name or email',
condition: { field: 'operation', value: ['list_users', 'list_groups'] },
condition: { field: 'operation', value: SHARED_FILTER_OPERATIONS },
mode: 'advanced',
},
{
@@ -240,10 +424,7 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'Next Page',
type: 'short-input',
placeholder: "Paste the previous response's nextLink to fetch the next page",
condition: {
field: 'operation',
value: ['list_users', 'list_groups', 'list_group_members'],
},
condition: { field: 'operation', value: NEXT_LINK_OPERATIONS },
mode: 'advanced',
},
// Group ID field
@@ -364,8 +545,233 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
title: 'Member ID',
type: 'short-input',
placeholder: 'User ID to add or remove',
condition: { field: 'operation', value: ['add_group_member', 'remove_group_member'] },
required: { field: 'operation', value: ['add_group_member', 'remove_group_member'] },
condition: {
field: 'operation',
value: [
'add_group_member',
'remove_group_member',
'add_directory_role_member',
'remove_directory_role_member',
],
},
required: {
field: 'operation',
value: [
'add_group_member',
'remove_group_member',
'add_directory_role_member',
'remove_directory_role_member',
],
},
},
{
id: 'addSkuIds',
title: 'Licenses To Add',
type: 'short-input',
placeholder: 'Comma-separated SKU IDs (GUIDs)',
condition: { field: 'operation', value: 'assign_license' },
},
{
id: 'removeSkuIds',
title: 'Licenses To Remove',
type: 'short-input',
placeholder: 'Comma-separated SKU IDs (GUIDs)',
condition: { field: 'operation', value: 'assign_license' },
},
{
id: 'disabledPlanIds',
title: 'Disabled Service Plans',
type: 'short-input',
placeholder: 'Comma-separated service plan IDs to disable on the added licenses',
condition: { field: 'operation', value: 'assign_license' },
mode: 'advanced',
},
{
id: 'forceChangePasswordNextSignIn',
title: 'Force Password Change At Next Sign-In',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'true',
condition: { field: 'operation', value: 'set_password' },
},
{
id: 'forceChangePasswordNextSignInWithMfa',
title: 'Require MFA Before Password Change',
type: 'dropdown',
options: [
{ label: 'No Change', id: '' },
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => '',
condition: { field: 'operation', value: 'set_password' },
mode: 'advanced',
},
{
id: 'newPassword',
title: 'New Password',
type: 'short-input',
placeholder: 'Leave empty to have Microsoft generate and return one',
condition: { field: 'operation', value: 'reset_password' },
password: true,
},
{
id: 'signInFilter',
title: 'Filter',
type: 'short-input',
placeholder: 'e.g., createdDateTime ge 2024-01-01T00:00:00Z and status/errorCode ne 0',
condition: { field: 'operation', value: 'list_sign_ins' },
wandConfig: {
enabled: true,
prompt:
'Generate a Microsoft Graph OData $filter expression for the auditLogs/signIns endpoint. Filterable fields include userPrincipalName, userId, appId, appDisplayName, ipAddress, createdDateTime, conditionalAccessStatus, riskState and status/errorCode. Return ONLY the filter expression.',
generationType: 'timestamp',
},
},
{
id: 'auditFilter',
title: 'Filter',
type: 'short-input',
placeholder: 'e.g., activityDateTime ge 2024-01-01T00:00:00Z',
condition: { field: 'operation', value: 'list_directory_audits' },
wandConfig: {
enabled: true,
prompt:
'Generate a Microsoft Graph OData $filter expression for the auditLogs/directoryAudits endpoint. Filterable fields include activityDateTime, activityDisplayName, correlationId, loggedByService, initiatedBy and targetResources. Return ONLY the filter expression.',
generationType: 'timestamp',
},
},
{
id: 'appRoleFilter',
title: 'Filter',
type: 'short-input',
placeholder: "e.g., resourceId eq '8e881353-1735-45af-af21-ee1344582a4d'",
condition: {
field: 'operation',
value: ['list_user_app_role_assignments', 'list_service_principal_app_role_assignments'],
},
mode: 'advanced',
},
{
id: 'resourceId',
title: 'Service Principal ID',
type: 'short-input',
placeholder: 'Object ID of the service principal that defines the app role',
condition: { field: 'operation', value: 'add_user_app_role_assignment' },
required: { field: 'operation', value: 'add_user_app_role_assignment' },
},
{
id: 'appRoleId',
title: 'App Role ID',
type: 'short-input',
placeholder: 'App role GUID, or 00000000-0000-0000-0000-000000000000 for no specific role',
condition: { field: 'operation', value: 'add_user_app_role_assignment' },
required: { field: 'operation', value: 'add_user_app_role_assignment' },
},
{
id: 'appRoleAssignmentId',
title: 'App Role Assignment ID',
type: 'short-input',
placeholder: 'ID of the assignment to revoke',
condition: { field: 'operation', value: 'remove_user_app_role_assignment' },
required: { field: 'operation', value: 'remove_user_app_role_assignment' },
},
{
id: 'servicePrincipalId',
title: 'Service Principal ID',
type: 'short-input',
placeholder: 'Object ID of the service principal',
condition: {
field: 'operation',
value: 'list_service_principal_app_role_assignments',
},
/**
* The continuation URL already addresses the service principal, so the ID is only
* required for the first page. An empty operation list matches nothing, which is how
* the function form of `required` expresses "not required".
*/
required: (values) =>
values?.nextLink
? { field: 'operation', value: [] }
: { field: 'operation', value: 'list_service_principal_app_role_assignments' },
},
{
id: 'servicePrincipalSearch',
title: 'Search',
type: 'short-input',
placeholder: 'Search by application name',
condition: { field: 'operation', value: 'list_service_principals' },
mode: 'advanced',
},
{
id: 'servicePrincipalFilter',
title: 'Filter',
type: 'short-input',
placeholder: "e.g., servicePrincipalType eq 'Application'",
condition: { field: 'operation', value: 'list_service_principals' },
mode: 'advanced',
},
{
id: 'directoryRoleId',
title: 'Directory Role ID',
type: 'short-input',
placeholder: 'Object ID of the directory role',
condition: { field: 'operation', value: DIRECTORY_ROLE_OPERATIONS },
required: { field: 'operation', value: DIRECTORY_ROLE_OPERATIONS },
},
{
id: 'deviceObjectId',
title: 'Device Object ID',
type: 'short-input',
placeholder: 'Device object ID (not the deviceId registration identifier)',
condition: { field: 'operation', value: DEVICE_ID_OPERATIONS },
required: { field: 'operation', value: DEVICE_ID_OPERATIONS },
},
{
id: 'deviceSearch',
title: 'Search',
type: 'short-input',
placeholder: 'Search by device name',
condition: { field: 'operation', value: 'list_devices' },
mode: 'advanced',
},
{
id: 'deviceFilter',
title: 'Filter',
type: 'short-input',
placeholder: 'e.g., accountEnabled eq false',
condition: { field: 'operation', value: 'list_devices' },
mode: 'advanced',
},
{
id: 'deviceRelationship',
title: 'Device Link',
type: 'dropdown',
options: [
{ label: 'Registered By User', id: 'registered' },
{ label: 'Owned By User', id: 'owned' },
],
value: () => 'registered',
condition: { field: 'operation', value: 'list_user_devices' },
},
{
id: 'policyId',
title: 'Policy ID',
type: 'short-input',
placeholder: 'Conditional access policy ID',
condition: { field: 'operation', value: 'get_conditional_access_policy' },
required: { field: 'operation', value: 'get_conditional_access_policy' },
},
{
id: 'policyFilter',
title: 'Filter',
type: 'short-input',
placeholder: "e.g., state eq 'enabled'",
condition: { field: 'operation', value: 'list_conditional_access_policies' },
mode: 'advanced',
},
],
tools: {
@@ -383,15 +789,45 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
'microsoft_ad_list_group_members',
'microsoft_ad_add_group_member',
'microsoft_ad_remove_group_member',
'microsoft_ad_assign_license',
'microsoft_ad_list_user_licenses',
'microsoft_ad_list_subscribed_skus',
'microsoft_ad_revoke_sign_in_sessions',
'microsoft_ad_set_password',
'microsoft_ad_reset_password',
'microsoft_ad_list_authentication_methods',
'microsoft_ad_list_sign_ins',
'microsoft_ad_list_directory_audits',
'microsoft_ad_list_user_app_role_assignments',
'microsoft_ad_add_user_app_role_assignment',
'microsoft_ad_remove_user_app_role_assignment',
'microsoft_ad_list_service_principals',
'microsoft_ad_list_service_principal_app_role_assignments',
'microsoft_ad_list_directory_roles',
'microsoft_ad_list_directory_role_members',
'microsoft_ad_add_directory_role_member',
'microsoft_ad_remove_directory_role_member',
'microsoft_ad_list_devices',
'microsoft_ad_get_device',
'microsoft_ad_list_user_devices',
'microsoft_ad_list_conditional_access_policies',
'microsoft_ad_get_conditional_access_policy',
],
config: {
tool: (params) => `microsoft_ad_${params.operation}`,
params: (params) => {
const result: Record<string, unknown> = {}
if (params.top) result.top = Number(params.top)
if (params.filter) result.filter = params.filter
if (params.search) result.search = params.search
if (params.nextLink) result.nextLink = params.nextLink
const values = params as Record<string, unknown>
result.filter = values[FILTER_FIELD_BY_OPERATION[params.operation]] || undefined
result.search = values[SEARCH_FIELD_BY_OPERATION[params.operation]] || undefined
if (params.operation === 'set_password') {
result.forceChangePasswordNextSignIn = params.forceChangePasswordNextSignIn !== 'false'
result.forceChangePasswordNextSignInWithMfa = params.forceChangePasswordNextSignInWithMfa
? params.forceChangePasswordNextSignInWithMfa === 'true'
: undefined
}
if (params.operation === 'update_user') {
if (params.accountEnabled) result.accountEnabled = params.accountEnabled === 'true'
} else if (params.operation === 'create_user') {
@@ -444,12 +880,34 @@ export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
visibility: { type: 'string' },
visibilityCreate: { type: 'string' },
memberId: { type: 'string' },
addSkuIds: { type: 'string' },
removeSkuIds: { type: 'string' },
disabledPlanIds: { type: 'string' },
forceChangePasswordNextSignIn: { type: 'string' },
forceChangePasswordNextSignInWithMfa: { type: 'string' },
newPassword: { type: 'string' },
signInFilter: { type: 'string' },
auditFilter: { type: 'string' },
appRoleFilter: { type: 'string' },
resourceId: { type: 'string' },
appRoleId: { type: 'string' },
appRoleAssignmentId: { type: 'string' },
servicePrincipalId: { type: 'string' },
servicePrincipalSearch: { type: 'string' },
servicePrincipalFilter: { type: 'string' },
directoryRoleId: { type: 'string' },
deviceObjectId: { type: 'string' },
deviceSearch: { type: 'string' },
deviceFilter: { type: 'string' },
deviceRelationship: { type: 'string' },
policyId: { type: 'string' },
policyFilter: { type: 'string' },
},
outputs: {
response: {
type: 'json',
description:
'Azure AD operation response. User operations return id, displayName, userPrincipalName, mail, jobTitle, department. Group operations return id, displayName, description, mailEnabled, securityEnabled, groupTypes. Member operations return id, displayName, mail, odataType. List operations also return nextLink for fetching additional pages.',
'Microsoft Entra ID operation response. User operations return id, displayName, userPrincipalName, mail, jobTitle, department. Group operations return id, displayName, description, mailEnabled, securityEnabled, groupTypes. Member operations return id, displayName, mail, odataType. Licensing operations return skuId, skuPartNumber, consumedUnits, prepaidUnits, and servicePlans. Sign-in and audit operations return the event id, timestamp, actor, target, and result. App role and directory role operations return assignment and role ids with their principals. Device operations return id, deviceId, displayName, operatingSystem, accountEnabled, isCompliant, isManaged, and trustType. Conditional access operations return id, displayName, state, conditions, grantControls, and sessionControls. List operations also return nextLink for fetching additional pages.',
},
},
}
+95 -3
View File
@@ -2169,8 +2169,8 @@
"type": "microsoft_ad",
"slug": "azure-ad",
"name": "Azure AD",
"description": "Manage users and groups in Azure AD (Microsoft Entra ID)",
"longDescription": "Integrate Azure Active Directory into your workflows. List, create, update, and delete users and groups. Manage group memberships programmatically.",
"description": "Manage identities, licenses, roles, and access in Azure AD (Microsoft Entra ID)",
"longDescription": "Integrate Azure Active Directory into your workflows. Create, update, and delete users and groups, manage group memberships, assign and remove licenses, reset passwords, revoke sign-in sessions, read sign-in and directory audit logs, grant and revoke app and directory roles, and read registered devices and conditional access policies. Device writes are not supported.",
"bgColor": "#0078D4",
"iconName": "AzureIcon",
"docsUrl": "https://docs.sim.ai/integrations/microsoft_ad",
@@ -2226,9 +2226,101 @@
{
"name": "Remove Group Member",
"description": "Remove a member from a group in Azure AD (Microsoft Entra ID)"
},
{
"name": "Assign License",
"description": "Add or remove subscription licenses (SKUs) on a user in Microsoft Entra ID. Removing a license immediately revokes the access it granted to the associated services."
},
{
"name": "List User Licenses",
"description": "List the subscription licenses assigned to a user in Microsoft Entra ID"
},
{
"name": "List Subscribed SKUs",
"description": "List the subscription SKUs the tenant owns, including how many license units are prepaid and how many are consumed"
},
{
"name": "Revoke Sign-In Sessions",
"description": "Invalidate every refresh token and session cookie issued to a user, forcing them to sign in again on all applications and devices. Revocation can take a few minutes to take effect and does not apply to external users."
},
{
"name": "Set Password",
"description": "Set a specific password on a user by updating their password profile. Cannot be used for federated users. Requires an administrator role in Microsoft Entra ID."
},
{
"name": "Reset Password",
"description": "Reset another user's password through their password authentication method. Leave the new password empty to have Microsoft generate one and return it. The user is prompted to change the password at their next sign-in. Cannot be run against your own account."
},
{
"name": "List Authentication Methods",
"description": "List the authentication methods a user has registered, such as passwords, phone numbers, FIDO2 keys, and authenticator apps"
},
{
"name": "List Sign-Ins",
"description": "List sign-in events from the Microsoft Entra ID sign-in logs, newest first. Requires a Microsoft Entra ID P1 or P2 license. Apply a date filter to keep large queries from timing out."
},
{
"name": "List Directory Audits",
"description": "List directory audit records showing who changed what in Microsoft Entra ID, such as user creation, group membership changes, and role assignments"
},
{
"name": "List User App Role Assignments",
"description": "List the application role assignments granted to a user, including assignments the user inherits from groups they are a direct member of"
},
{
"name": "Grant App Role To User",
"description": "Grant a user an application role on a service principal, giving them access to that application"
},
{
"name": "Revoke App Role From User",
"description": "Revoke an application role assignment from a user, removing their access to that application. Takes the assignment's own ID, not the app role ID."
},
{
"name": "List Service Principals",
"description": "List the enterprise applications and service principals in the tenant, including the app roles each one exposes"
},
{
"name": "List Application Assignments",
"description": "List every user, group, and service principal assigned to an application, by reading the app role assignments on its service principal. Recently granted or removed assignments can take time to appear."
},
{
"name": "List Directory Roles",
"description": "List the administrator roles that are activated in the tenant, such as Global Administrator and User Administrator. Roles that have never been activated are not returned."
},
{
"name": "List Directory Role Members",
"description": "List the principals holding an administrator role. Returns up to 1000 members; this endpoint does not support paging."
},
{
"name": "Add Directory Role Member",
"description": "Grant a user an administrator role in Microsoft Entra ID. This is a privileged change that expands what the user can do across the tenant."
},
{
"name": "Remove Directory Role Member",
"description": "Revoke an administrator role from a user in Microsoft Entra ID. Removes only the role membership; the user account itself is not deleted."
},
{
"name": "List Devices",
"description": "List the devices registered in Microsoft Entra ID"
},
{
"name": "Get Device",
"description": "Get a registered device by its object ID from Microsoft Entra ID"
},
{
"name": "List User Devices",
"description": "List the devices a user has registered or owns. Devices the caller cannot read are returned with only their ID and the remaining fields null."
},
{
"name": "List Conditional Access Policies",
"description": "List the conditional access policies configured in the tenant, including their state and the conditions and controls they enforce. Read-only."
},
{
"name": "Get Conditional Access Policy",
"description": "Get a single conditional access policy by ID, including the conditions it matches and the controls it enforces. Read-only."
}
],
"operationCount": 13,
"operationCount": 36,
"triggers": [],
"triggerCount": 0,
"authType": "oauth",
+8
View File
@@ -339,6 +339,14 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
'Group.ReadWrite.All',
'GroupMember.ReadWrite.All',
'Directory.Read.All',
'LicenseAssignment.ReadWrite.All',
'UserAuthenticationMethod.ReadWrite.All',
'AuditLog.Read.All',
'Application.Read.All',
'AppRoleAssignment.ReadWrite.All',
'RoleManagement.ReadWrite.Directory',
'Device.Read.All',
'Policy.Read.All',
'offline_access',
],
},
+9
View File
@@ -260,6 +260,15 @@ export const SCOPE_DESCRIPTIONS: Record<string, string> = {
'User.ReadWrite.All': 'Read and write all user profiles',
'GroupMember.ReadWrite.All': 'Read and write all group memberships',
'Directory.Read.All': 'Read directory data',
'LicenseAssignment.ReadWrite.All': 'Assign and remove user licenses',
'UserAuthenticationMethod.ReadWrite.All':
'Read and reset authentication methods and passwords for all users',
'AuditLog.Read.All': 'Read sign-in and directory audit logs',
'Application.Read.All': 'Read all applications and service principals',
'AppRoleAssignment.ReadWrite.All': 'Grant and revoke application role assignments',
'RoleManagement.ReadWrite.Directory': 'Read and manage directory role assignments',
'Device.Read.All': 'Read all devices',
'Policy.Read.All': 'Read conditional access and other policies',
// Reddit scopes
identity: 'Access Reddit identity',
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,78 @@
import type {
MicrosoftAdAddDirectoryRoleMemberParams,
MicrosoftAdAddDirectoryRoleMemberResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const addDirectoryRoleMemberTool: ToolConfig<
MicrosoftAdAddDirectoryRoleMemberParams,
MicrosoftAdAddDirectoryRoleMemberResponse
> = {
id: 'microsoft_ad_add_directory_role_member',
name: 'Add Microsoft Entra ID Directory Role Member',
description:
'Grant a user an administrator role in Microsoft Entra ID. This is a privileged change that expands what the user can do across the tenant.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
directoryRoleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Object ID of the directory role. Use List Directory Roles to find it.',
},
memberId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Object ID of the user to grant the role to',
},
},
request: {
url: (params) => {
const directoryRoleId = params.directoryRoleId?.trim()
if (!directoryRoleId) throw new Error('Directory role ID is required')
return `https://graph.microsoft.com/v1.0/directoryRoles/${encodeURIComponent(directoryRoleId)}/members/$ref`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const memberId = params.memberId?.trim()
if (!memberId) throw new Error('Member ID is required')
return {
'@odata.id': `https://graph.microsoft.com/v1.0/directoryObjects/${memberId}`,
}
},
},
transformResponse: async (
_response: Response,
params?: MicrosoftAdAddDirectoryRoleMemberParams
) => {
return {
success: true,
output: {
added: true,
directoryRoleId: params?.directoryRoleId ?? '',
memberId: params?.memberId ?? '',
},
}
},
outputs: {
added: { type: 'boolean', description: 'Whether the member was added successfully' },
directoryRoleId: { type: 'string', description: 'ID of the directory role' },
memberId: { type: 'string', description: 'ID of the member that was added' },
},
}
@@ -0,0 +1,95 @@
import type {
MicrosoftAdAddUserAppRoleAssignmentParams,
MicrosoftAdAddUserAppRoleAssignmentResponse,
} from '@/tools/microsoft_ad/types'
import { APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const addUserAppRoleAssignmentTool: ToolConfig<
MicrosoftAdAddUserAppRoleAssignmentParams,
MicrosoftAdAddUserAppRoleAssignmentResponse
> = {
id: 'microsoft_ad_add_user_app_role_assignment',
name: 'Grant Microsoft Entra ID App Role To User',
description:
'Grant a user an application role on a service principal, giving them access to that application',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name to grant the app role to',
},
resourceId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Object ID of the resource service principal that defines the app role. Use List Service Principals to find it.',
},
appRoleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'ID of the app role to grant. Use the all-zero GUID 00000000-0000-0000-0000-000000000000 to assign access without a specific role.',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/appRoleAssignments`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const resourceId = params.resourceId?.trim()
const appRoleId = params.appRoleId?.trim()
const principalId = params.userId?.trim()
if (!resourceId) throw new Error('Resource ID is required')
if (!appRoleId) throw new Error('App role ID is required')
return { principalId, resourceId, appRoleId }
},
},
transformResponse: async (response: Response) => {
const assignment = await response.json()
return {
success: true,
output: {
assignment: {
id: assignment.id ?? null,
appRoleId: assignment.appRoleId ?? null,
createdDateTime: assignment.createdDateTime ?? null,
principalId: assignment.principalId ?? null,
principalDisplayName: assignment.principalDisplayName ?? null,
principalType: assignment.principalType ?? null,
resourceId: assignment.resourceId ?? null,
resourceDisplayName: assignment.resourceDisplayName ?? null,
},
},
}
},
outputs: {
assignment: {
type: 'object',
description: 'The created app role assignment',
properties: APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,107 @@
import type {
MicrosoftAdAssignLicenseParams,
MicrosoftAdAssignLicenseResponse,
} from '@/tools/microsoft_ad/types'
import { ASSIGNED_LICENSE_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { parseIdList } from '@/tools/microsoft_ad/utils'
import type { ToolConfig } from '@/tools/types'
export const assignLicenseTool: ToolConfig<
MicrosoftAdAssignLicenseParams,
MicrosoftAdAssignLicenseResponse
> = {
id: 'microsoft_ad_assign_license',
name: 'Assign Microsoft Entra ID License',
description:
'Add or remove subscription licenses (SKUs) on a user in Microsoft Entra ID. Removing a license immediately revokes the access it granted to the associated services.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name to change licenses for',
},
addSkuIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated SKU IDs (GUIDs) of the licenses to assign. Leave empty to only remove licenses.',
},
removeSkuIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated SKU IDs (GUIDs) of the licenses to remove. Leave empty to only add licenses.',
},
disabledPlanIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated service plan IDs (GUIDs) to disable on every license being assigned',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/assignLicense`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const addSkuIds = parseIdList(params.addSkuIds)
const removeLicenses = parseIdList(params.removeSkuIds)
if (addSkuIds.length === 0 && removeLicenses.length === 0) {
throw new Error('Provide at least one SKU ID to add or remove')
}
const disabledPlans = parseIdList(params.disabledPlanIds)
return {
addLicenses: addSkuIds.map((skuId) => ({ skuId, disabledPlans })),
removeLicenses,
}
},
},
transformResponse: async (response: Response) => {
const user = await response.json()
return {
success: true,
output: {
userId: user.id ?? null,
displayName: user.displayName ?? null,
userPrincipalName: user.userPrincipalName ?? null,
assignedLicenses: (user.assignedLicenses ?? []).map((license: Record<string, unknown>) => ({
skuId: license.skuId ?? null,
disabledPlans: license.disabledPlans ?? [],
})),
},
}
},
outputs: {
userId: { type: 'string', description: 'ID of the user whose licenses changed' },
displayName: { type: 'string', description: 'Display name of the user' },
userPrincipalName: { type: 'string', description: 'User principal name of the user' },
assignedLicenses: {
type: 'array',
description: 'Licenses assigned to the user after the change',
properties: ASSIGNED_LICENSE_OUTPUT_PROPERTIES,
},
},
}
+5 -5
View File
@@ -2,7 +2,7 @@ import type {
MicrosoftAdCreateUserParams,
MicrosoftAdCreateUserResponse,
} from '@/tools/microsoft_ad/types'
import { USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { CREATED_USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const createUserTool: ToolConfig<
@@ -93,7 +93,7 @@ export const createUserTool: ToolConfig<
},
},
request: {
url: 'https://graph.microsoft.com/v1.0/users?$select=id,displayName,givenName,surname,userPrincipalName,mail,jobTitle,department,officeLocation,mobilePhone,accountEnabled',
url: 'https://graph.microsoft.com/v1.0/users',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
@@ -132,10 +132,10 @@ export const createUserTool: ToolConfig<
userPrincipalName: user.userPrincipalName ?? null,
mail: user.mail ?? null,
jobTitle: user.jobTitle ?? null,
department: user.department ?? null,
officeLocation: user.officeLocation ?? null,
mobilePhone: user.mobilePhone ?? null,
accountEnabled: user.accountEnabled ?? null,
businessPhones: user.businessPhones ?? [],
preferredLanguage: user.preferredLanguage ?? null,
},
},
}
@@ -144,7 +144,7 @@ export const createUserTool: ToolConfig<
user: {
type: 'object',
description: 'Created user details',
properties: USER_OUTPUT_PROPERTIES,
properties: CREATED_USER_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,64 @@
import type {
MicrosoftAdGetConditionalAccessPolicyParams,
MicrosoftAdGetConditionalAccessPolicyResponse,
} from '@/tools/microsoft_ad/types'
import { CONDITIONAL_ACCESS_POLICY_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { mapConditionalAccessPolicy } from '@/tools/microsoft_ad/utils'
import type { ToolConfig } from '@/tools/types'
export const getConditionalAccessPolicyTool: ToolConfig<
MicrosoftAdGetConditionalAccessPolicyParams,
MicrosoftAdGetConditionalAccessPolicyResponse
> = {
id: 'microsoft_ad_get_conditional_access_policy',
name: 'Get Microsoft Entra ID Conditional Access Policy',
description:
'Get a single conditional access policy by ID, including the conditions it matches and the controls it enforces. Read-only.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
policyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Conditional access policy ID',
},
},
request: {
url: (params) => {
const policyId = params.policyId?.trim()
if (!policyId) throw new Error('Policy ID is required')
return `https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/${encodeURIComponent(policyId)}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const policy = await response.json()
return {
success: true,
output: {
policy: mapConditionalAccessPolicy(policy),
},
}
},
outputs: {
policy: {
type: 'object',
description: 'Conditional access policy details',
properties: CONDITIONAL_ACCESS_POLICY_OUTPUT_PROPERTIES,
},
},
}
+60
View File
@@ -0,0 +1,60 @@
import type {
MicrosoftAdGetDeviceParams,
MicrosoftAdGetDeviceResponse,
} from '@/tools/microsoft_ad/types'
import { DEVICE_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { DEVICE_SELECT, mapDevice } from '@/tools/microsoft_ad/utils'
import type { ToolConfig } from '@/tools/types'
export const getDeviceTool: ToolConfig<MicrosoftAdGetDeviceParams, MicrosoftAdGetDeviceResponse> = {
id: 'microsoft_ad_get_device',
name: 'Get Microsoft Entra ID Device',
description: 'Get a registered device by its object ID from Microsoft Entra ID',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
deviceObjectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Device object ID (the "id" field), not the "deviceId" registration identifier',
},
},
request: {
url: (params) => {
const deviceObjectId = params.deviceObjectId?.trim()
if (!deviceObjectId) throw new Error('Device object ID is required')
return `https://graph.microsoft.com/v1.0/devices/${encodeURIComponent(deviceObjectId)}?$select=${DEVICE_SELECT}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const device = await response.json()
return {
success: true,
output: {
device: mapDevice(device),
},
}
},
outputs: {
device: {
type: 'object',
description: 'Device details',
properties: DEVICE_OUTPUT_PROPERTIES,
},
},
}
+47
View File
@@ -1,14 +1,37 @@
import { addDirectoryRoleMemberTool } from '@/tools/microsoft_ad/add_directory_role_member'
import { addGroupMemberTool } from '@/tools/microsoft_ad/add_group_member'
import { addUserAppRoleAssignmentTool } from '@/tools/microsoft_ad/add_user_app_role_assignment'
import { assignLicenseTool } from '@/tools/microsoft_ad/assign_license'
import { createGroupTool } from '@/tools/microsoft_ad/create_group'
import { createUserTool } from '@/tools/microsoft_ad/create_user'
import { deleteGroupTool } from '@/tools/microsoft_ad/delete_group'
import { deleteUserTool } from '@/tools/microsoft_ad/delete_user'
import { getConditionalAccessPolicyTool } from '@/tools/microsoft_ad/get_conditional_access_policy'
import { getDeviceTool } from '@/tools/microsoft_ad/get_device'
import { getGroupTool } from '@/tools/microsoft_ad/get_group'
import { getUserTool } from '@/tools/microsoft_ad/get_user'
import { listAuthenticationMethodsTool } from '@/tools/microsoft_ad/list_authentication_methods'
import { listConditionalAccessPoliciesTool } from '@/tools/microsoft_ad/list_conditional_access_policies'
import { listDevicesTool } from '@/tools/microsoft_ad/list_devices'
import { listDirectoryAuditsTool } from '@/tools/microsoft_ad/list_directory_audits'
import { listDirectoryRoleMembersTool } from '@/tools/microsoft_ad/list_directory_role_members'
import { listDirectoryRolesTool } from '@/tools/microsoft_ad/list_directory_roles'
import { listGroupMembersTool } from '@/tools/microsoft_ad/list_group_members'
import { listGroupsTool } from '@/tools/microsoft_ad/list_groups'
import { listServicePrincipalAppRoleAssignmentsTool } from '@/tools/microsoft_ad/list_service_principal_app_role_assignments'
import { listServicePrincipalsTool } from '@/tools/microsoft_ad/list_service_principals'
import { listSignInsTool } from '@/tools/microsoft_ad/list_sign_ins'
import { listSubscribedSkusTool } from '@/tools/microsoft_ad/list_subscribed_skus'
import { listUserAppRoleAssignmentsTool } from '@/tools/microsoft_ad/list_user_app_role_assignments'
import { listUserDevicesTool } from '@/tools/microsoft_ad/list_user_devices'
import { listUserLicensesTool } from '@/tools/microsoft_ad/list_user_licenses'
import { listUsersTool } from '@/tools/microsoft_ad/list_users'
import { removeDirectoryRoleMemberTool } from '@/tools/microsoft_ad/remove_directory_role_member'
import { removeGroupMemberTool } from '@/tools/microsoft_ad/remove_group_member'
import { removeUserAppRoleAssignmentTool } from '@/tools/microsoft_ad/remove_user_app_role_assignment'
import { resetPasswordTool } from '@/tools/microsoft_ad/reset_password'
import { revokeSignInSessionsTool } from '@/tools/microsoft_ad/revoke_sign_in_sessions'
import { setPasswordTool } from '@/tools/microsoft_ad/set_password'
import { updateGroupTool } from '@/tools/microsoft_ad/update_group'
import { updateUserTool } from '@/tools/microsoft_ad/update_user'
@@ -25,3 +48,27 @@ export const microsoftAdDeleteGroupTool = deleteGroupTool
export const microsoftAdListGroupMembersTool = listGroupMembersTool
export const microsoftAdAddGroupMemberTool = addGroupMemberTool
export const microsoftAdRemoveGroupMemberTool = removeGroupMemberTool
export const microsoftAdAssignLicenseTool = assignLicenseTool
export const microsoftAdListUserLicensesTool = listUserLicensesTool
export const microsoftAdListSubscribedSkusTool = listSubscribedSkusTool
export const microsoftAdRevokeSignInSessionsTool = revokeSignInSessionsTool
export const microsoftAdSetPasswordTool = setPasswordTool
export const microsoftAdResetPasswordTool = resetPasswordTool
export const microsoftAdListAuthenticationMethodsTool = listAuthenticationMethodsTool
export const microsoftAdListSignInsTool = listSignInsTool
export const microsoftAdListDirectoryAuditsTool = listDirectoryAuditsTool
export const microsoftAdListUserAppRoleAssignmentsTool = listUserAppRoleAssignmentsTool
export const microsoftAdAddUserAppRoleAssignmentTool = addUserAppRoleAssignmentTool
export const microsoftAdRemoveUserAppRoleAssignmentTool = removeUserAppRoleAssignmentTool
export const microsoftAdListServicePrincipalsTool = listServicePrincipalsTool
export const microsoftAdListServicePrincipalAppRoleAssignmentsTool =
listServicePrincipalAppRoleAssignmentsTool
export const microsoftAdListDirectoryRolesTool = listDirectoryRolesTool
export const microsoftAdListDirectoryRoleMembersTool = listDirectoryRoleMembersTool
export const microsoftAdAddDirectoryRoleMemberTool = addDirectoryRoleMemberTool
export const microsoftAdRemoveDirectoryRoleMemberTool = removeDirectoryRoleMemberTool
export const microsoftAdListDevicesTool = listDevicesTool
export const microsoftAdGetDeviceTool = getDeviceTool
export const microsoftAdListUserDevicesTool = listUserDevicesTool
export const microsoftAdListConditionalAccessPoliciesTool = listConditionalAccessPoliciesTool
export const microsoftAdGetConditionalAccessPolicyTool = getConditionalAccessPolicyTool
@@ -0,0 +1,70 @@
import type {
MicrosoftAdListAuthenticationMethodsParams,
MicrosoftAdListAuthenticationMethodsResponse,
} from '@/tools/microsoft_ad/types'
import { AUTHENTICATION_METHOD_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const listAuthenticationMethodsTool: ToolConfig<
MicrosoftAdListAuthenticationMethodsParams,
MicrosoftAdListAuthenticationMethodsResponse
> = {
id: 'microsoft_ad_list_authentication_methods',
name: 'List Microsoft Entra ID Authentication Methods',
description:
'List the authentication methods a user has registered, such as passwords, phone numbers, FIDO2 keys, and authenticator apps',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/authentication/methods`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const methods = (data.value ?? []).map((method: Record<string, unknown>) => ({
id: method.id ?? null,
odataType: (method['@odata.type'] as string) ?? null,
createdDateTime: method.createdDateTime ?? null,
}))
return {
success: true,
output: {
methods,
methodCount: methods.length,
},
}
},
outputs: {
methods: {
type: 'array',
description: 'Authentication methods registered by the user',
properties: AUTHENTICATION_METHOD_OUTPUT_PROPERTIES,
},
methodCount: { type: 'number', description: 'Number of authentication methods returned' },
},
}
@@ -0,0 +1,93 @@
import type {
MicrosoftAdListConditionalAccessPoliciesParams,
MicrosoftAdListConditionalAccessPoliciesResponse,
} from '@/tools/microsoft_ad/types'
import { CONDITIONAL_ACCESS_POLICY_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import {
assertGraphNextPageUrlForCollection,
buildGraphCollectionUrl,
mapConditionalAccessPolicy,
} from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listConditionalAccessPoliciesTool: ToolConfig<
MicrosoftAdListConditionalAccessPoliciesParams,
MicrosoftAdListConditionalAccessPoliciesResponse
> = {
id: 'microsoft_ad_list_conditional_access_policies',
name: 'List Microsoft Entra ID Conditional Access Policies',
description:
'List the conditional access policies configured in the tenant, including their state and the conditions and controls they enforce. Read-only.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of policies to return',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'OData filter expression. Example: "state eq \'enabled\'".',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrlForCollection(params.nextLink, ['policies'])
return buildGraphCollectionUrl('identity/conditionalAccess/policies', {
top: params.top,
filter: params.filter,
})
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const policies = (data.value ?? []).map(mapConditionalAccessPolicy)
return {
success: true,
output: {
policies,
policyCount: policies.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
policies: {
type: 'array',
description: 'Conditional access policies',
properties: CONDITIONAL_ACCESS_POLICY_OUTPUT_PROPERTIES,
},
policyCount: { type: 'number', description: 'Number of policies returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
+107
View File
@@ -0,0 +1,107 @@
import type {
MicrosoftAdListDevicesParams,
MicrosoftAdListDevicesResponse,
} from '@/tools/microsoft_ad/types'
import { DEVICE_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import {
assertGraphNextPageUrlForCollection,
buildGraphCollectionUrl,
DEVICE_SELECT,
mapDevice,
} from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listDevicesTool: ToolConfig<
MicrosoftAdListDevicesParams,
MicrosoftAdListDevicesResponse
> = {
id: 'microsoft_ad_list_devices',
name: 'List Microsoft Entra ID Devices',
description: 'List the devices registered in Microsoft Entra ID',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of devices to return',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'OData filter expression. Example: "accountEnabled eq false" or "operatingSystem eq \'Windows\'".',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search string matched against the device display name',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrlForCollection(params.nextLink, ['devices'])
const search = params.search?.trim()
return buildGraphCollectionUrl('devices', {
select: DEVICE_SELECT,
top: params.top,
filter: params.filter,
search: search
? `"displayName:${search.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`
: undefined,
count: Boolean(search || params.filter),
})
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
ConsistencyLevel: 'eventual',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const devices = (data.value ?? []).map(mapDevice)
return {
success: true,
output: {
devices,
deviceCount: devices.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
devices: {
type: 'array',
description: 'Registered devices',
properties: DEVICE_OUTPUT_PROPERTIES,
},
deviceCount: { type: 'number', description: 'Number of devices returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,122 @@
import type {
MicrosoftAdListDirectoryAuditsParams,
MicrosoftAdListDirectoryAuditsResponse,
} from '@/tools/microsoft_ad/types'
import { DIRECTORY_AUDIT_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import {
assertGraphNextPageUrlForCollection,
buildGraphCollectionUrl,
} from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listDirectoryAuditsTool: ToolConfig<
MicrosoftAdListDirectoryAuditsParams,
MicrosoftAdListDirectoryAuditsResponse
> = {
id: 'microsoft_ad_list_directory_audits',
name: 'List Microsoft Entra ID Directory Audits',
description:
'List directory audit records showing who changed what in Microsoft Entra ID, such as user creation, group membership changes, and role assignments',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of audit records to return',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'OData filter expression. Filterable fields include activityDateTime, activityDisplayName, correlationId, loggedByService, initiatedBy and targetResources. Example: "activityDateTime ge 2024-01-01T00:00:00Z".',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink)
return assertGraphNextPageUrlForCollection(params.nextLink, ['directoryAudits'])
return buildGraphCollectionUrl('auditLogs/directoryAudits', {
top: params.top,
filter: params.filter,
orderby: 'activityDateTime desc',
})
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const audits = (data.value ?? []).map((audit: Record<string, unknown>) => {
const initiatedBy = (audit.initiatedBy ?? null) as Record<string, unknown> | null
const initiatingUser = (initiatedBy?.user ?? null) as Record<string, unknown> | null
const initiatingApp = (initiatedBy?.app ?? null) as Record<string, unknown> | null
const targetResources = Array.isArray(audit.targetResources) ? audit.targetResources : []
return {
id: audit.id ?? null,
activityDateTime: audit.activityDateTime ?? null,
activityDisplayName: audit.activityDisplayName ?? null,
category: audit.category ?? null,
correlationId: audit.correlationId ?? null,
loggedByService: audit.loggedByService ?? null,
operationType: audit.operationType ?? null,
result: audit.result ?? null,
resultReason: audit.resultReason ?? null,
initiatedByUserId: initiatingUser?.id ?? null,
initiatedByUserPrincipalName: initiatingUser?.userPrincipalName ?? null,
initiatedByUserDisplayName: initiatingUser?.displayName ?? null,
initiatedByAppId: initiatingApp?.appId ?? null,
initiatedByAppDisplayName: initiatingApp?.displayName ?? null,
targetResources: targetResources.map((target: Record<string, unknown>) => ({
id: target.id ?? null,
displayName: target.displayName ?? null,
type: target.type ?? null,
userPrincipalName: target.userPrincipalName ?? null,
})),
}
})
return {
success: true,
output: {
audits,
auditCount: audits.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
audits: {
type: 'array',
description: 'Directory audit records',
properties: DIRECTORY_AUDIT_OUTPUT_PROPERTIES,
},
auditCount: { type: 'number', description: 'Number of audit records returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,71 @@
import type {
MicrosoftAdListDirectoryRoleMembersParams,
MicrosoftAdListDirectoryRoleMembersResponse,
} from '@/tools/microsoft_ad/types'
import { MEMBER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const listDirectoryRoleMembersTool: ToolConfig<
MicrosoftAdListDirectoryRoleMembersParams,
MicrosoftAdListDirectoryRoleMembersResponse
> = {
id: 'microsoft_ad_list_directory_role_members',
name: 'List Microsoft Entra ID Directory Role Members',
description:
'List the principals holding an administrator role. Returns up to 1000 members; this endpoint does not support paging.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
directoryRoleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Object ID of the directory role. Use List Directory Roles to find it.',
},
},
request: {
url: (params) => {
const directoryRoleId = params.directoryRoleId?.trim()
if (!directoryRoleId) throw new Error('Directory role ID is required')
return `https://graph.microsoft.com/v1.0/directoryRoles/${encodeURIComponent(directoryRoleId)}/members?$select=id,displayName,mail`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const members = (data.value ?? []).map((member: Record<string, unknown>) => ({
id: member.id ?? null,
displayName: member.displayName ?? null,
mail: member.mail ?? null,
odataType: (member['@odata.type'] as string) ?? null,
}))
return {
success: true,
output: {
members,
memberCount: members.length,
},
}
},
outputs: {
members: {
type: 'array',
description: 'Principals holding the directory role',
properties: MEMBER_OUTPUT_PROPERTIES,
},
memberCount: { type: 'number', description: 'Number of members returned' },
},
}
@@ -0,0 +1,61 @@
import type {
MicrosoftAdListDirectoryRolesParams,
MicrosoftAdListDirectoryRolesResponse,
} from '@/tools/microsoft_ad/types'
import { DIRECTORY_ROLE_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const listDirectoryRolesTool: ToolConfig<
MicrosoftAdListDirectoryRolesParams,
MicrosoftAdListDirectoryRolesResponse
> = {
id: 'microsoft_ad_list_directory_roles',
name: 'List Microsoft Entra ID Directory Roles',
description:
'List the administrator roles that are activated in the tenant, such as Global Administrator and User Administrator. Roles that have never been activated are not returned.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
},
request: {
url: 'https://graph.microsoft.com/v1.0/directoryRoles?$select=id,displayName,description,roleTemplateId',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const roles = (data.value ?? []).map((role: Record<string, unknown>) => ({
id: role.id ?? null,
displayName: role.displayName ?? null,
description: role.description ?? null,
roleTemplateId: role.roleTemplateId ?? null,
}))
return {
success: true,
output: {
roles,
roleCount: roles.length,
},
}
},
outputs: {
roles: {
type: 'array',
description: 'Activated directory roles',
properties: DIRECTORY_ROLE_OUTPUT_PROPERTIES,
},
roleCount: { type: 'number', description: 'Number of directory roles returned' },
},
}
@@ -3,7 +3,8 @@ import type {
MicrosoftAdListGroupMembersResponse,
} from '@/tools/microsoft_ad/types'
import { MEMBER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import { assertGraphNextPageUrlForCollection } from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listGroupMembersTool: ToolConfig<
@@ -48,7 +49,7 @@ export const listGroupMembersTool: ToolConfig<
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrl(params.nextLink)
if (params.nextLink) return assertGraphNextPageUrlForCollection(params.nextLink, ['members'])
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
const queryParts = ['$select=id,displayName,mail']
+3 -2
View File
@@ -3,7 +3,8 @@ import type {
MicrosoftAdListGroupsResponse,
} from '@/tools/microsoft_ad/types'
import { GROUP_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import { assertGraphNextPageUrlForCollection } from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listGroupsTool: ToolConfig<
@@ -54,7 +55,7 @@ export const listGroupsTool: ToolConfig<
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrl(params.nextLink)
if (params.nextLink) return assertGraphNextPageUrlForCollection(params.nextLink, ['groups'])
const queryParts: string[] = []
queryParts.push(
'$select=id,displayName,description,mail,mailEnabled,mailNickname,securityEnabled,groupTypes,visibility,createdDateTime'
@@ -0,0 +1,101 @@
import type {
MicrosoftAdListAppRoleAssignmentsResponse,
MicrosoftAdListServicePrincipalAppRoleAssignmentsParams,
} from '@/tools/microsoft_ad/types'
import { APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrlForCollection } from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listServicePrincipalAppRoleAssignmentsTool: ToolConfig<
MicrosoftAdListServicePrincipalAppRoleAssignmentsParams,
MicrosoftAdListAppRoleAssignmentsResponse
> = {
id: 'microsoft_ad_list_service_principal_app_role_assignments',
name: 'List Microsoft Entra ID Application Assignments',
description:
'List every user, group, and service principal assigned to an application, by reading the app role assignments on its service principal. Recently granted or removed assignments can take time to appear.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
servicePrincipalId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Object ID of the service principal. Use List Service Principals to find it. Not needed when Next Page is provided to fetch a later page.',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'OData filter expression supporting eq and startswith. Example: "principalType eq \'User\'".',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink)
return assertGraphNextPageUrlForCollection(params.nextLink, ['appRoleAssignedTo'])
const servicePrincipalId = params.servicePrincipalId?.trim()
if (!servicePrincipalId) throw new Error('Service principal ID is required')
const base = `https://graph.microsoft.com/v1.0/servicePrincipals/${encodeURIComponent(servicePrincipalId)}/appRoleAssignedTo`
return params.filter ? `${base}?$filter=${encodeURIComponent(params.filter)}` : base
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const assignments = (data.value ?? []).map((assignment: Record<string, unknown>) => ({
id: assignment.id ?? null,
appRoleId: assignment.appRoleId ?? null,
createdDateTime: assignment.createdDateTime ?? null,
principalId: assignment.principalId ?? null,
principalDisplayName: assignment.principalDisplayName ?? null,
principalType: assignment.principalType ?? null,
resourceId: assignment.resourceId ?? null,
resourceDisplayName: assignment.resourceDisplayName ?? null,
}))
return {
success: true,
output: {
assignments,
assignmentCount: assignments.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
assignments: {
type: 'array',
description: 'Principals assigned to the application',
properties: APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES,
},
assignmentCount: { type: 'number', description: 'Number of assignments returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,145 @@
import type {
MicrosoftAdListServicePrincipalsParams,
MicrosoftAdListServicePrincipalsResponse,
} from '@/tools/microsoft_ad/types'
import {
APP_ROLE_OUTPUT_PROPERTIES,
SERVICE_PRINCIPAL_OUTPUT_PROPERTIES,
} from '@/tools/microsoft_ad/types'
import {
assertGraphNextPageUrlForCollection,
buildGraphCollectionUrl,
} from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
const SERVICE_PRINCIPAL_SELECT =
'id,appId,displayName,servicePrincipalType,accountEnabled,appOwnerOrganizationId,signInAudience,tags,appRoles'
export const listServicePrincipalsTool: ToolConfig<
MicrosoftAdListServicePrincipalsParams,
MicrosoftAdListServicePrincipalsResponse
> = {
id: 'microsoft_ad_list_service_principals',
name: 'List Microsoft Entra ID Service Principals',
description:
'List the enterprise applications and service principals in the tenant, including the app roles each one exposes',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description:
'Maximum number of service principals to return (default and maximum page size is 100)',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'OData filter expression. Example: "servicePrincipalType eq \'Application\'" or "startsWith(displayName, \'Salesforce\')".',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search string matched against the service principal display name',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink)
return assertGraphNextPageUrlForCollection(params.nextLink, ['servicePrincipals'])
const search = params.search?.trim()
return buildGraphCollectionUrl('servicePrincipals', {
select: SERVICE_PRINCIPAL_SELECT,
top: params.top,
filter: params.filter,
search: search
? `"displayName:${search.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`
: undefined,
count: Boolean(search),
})
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
ConsistencyLevel: 'eventual',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const servicePrincipals = (data.value ?? []).map(
(servicePrincipal: Record<string, unknown>) => ({
id: servicePrincipal.id ?? null,
appId: servicePrincipal.appId ?? null,
displayName: servicePrincipal.displayName ?? null,
servicePrincipalType: servicePrincipal.servicePrincipalType ?? null,
accountEnabled: servicePrincipal.accountEnabled ?? null,
appOwnerOrganizationId: servicePrincipal.appOwnerOrganizationId ?? null,
signInAudience: servicePrincipal.signInAudience ?? null,
tags: servicePrincipal.tags ?? [],
appRoles: (Array.isArray(servicePrincipal.appRoles) ? servicePrincipal.appRoles : []).map(
(appRole: Record<string, unknown>) => ({
id: appRole.id ?? null,
displayName: appRole.displayName ?? null,
description: appRole.description ?? null,
value: appRole.value ?? null,
isEnabled: appRole.isEnabled ?? null,
allowedMemberTypes: appRole.allowedMemberTypes ?? [],
})
),
})
)
return {
success: true,
output: {
servicePrincipals,
servicePrincipalCount: servicePrincipals.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
servicePrincipals: {
type: 'array',
description: 'Service principals in the tenant',
properties: {
...SERVICE_PRINCIPAL_OUTPUT_PROPERTIES,
appRoles: {
type: 'array',
description: 'App roles exposed by the associated application',
properties: APP_ROLE_OUTPUT_PROPERTIES,
},
},
},
servicePrincipalCount: {
type: 'number',
description: 'Number of service principals returned',
},
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,127 @@
import type {
MicrosoftAdListSignInsParams,
MicrosoftAdListSignInsResponse,
} from '@/tools/microsoft_ad/types'
import { SIGN_IN_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import {
assertGraphNextPageUrlForCollection,
buildGraphCollectionUrl,
} from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listSignInsTool: ToolConfig<
MicrosoftAdListSignInsParams,
MicrosoftAdListSignInsResponse
> = {
id: 'microsoft_ad_list_sign_ins',
name: 'List Microsoft Entra ID Sign-Ins',
description:
'List sign-in events from the Microsoft Entra ID sign-in logs, newest first. Requires a Microsoft Entra ID P1 or P2 license. Apply a date filter to keep large queries from timing out.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of sign-ins to return (default and maximum page size is 1000)',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'OData filter expression. Filterable fields include userPrincipalName, userId, appId, appDisplayName, ipAddress, createdDateTime, conditionalAccessStatus, riskState and status/errorCode. Example: "createdDateTime ge 2024-01-01T00:00:00Z and status/errorCode ne 0".',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrlForCollection(params.nextLink, ['signIns'])
return buildGraphCollectionUrl('auditLogs/signIns', {
top: params.top,
filter: params.filter,
})
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const signIns = (data.value ?? []).map((signIn: Record<string, unknown>) => {
const status = (signIn.status ?? null) as Record<string, unknown> | null
const deviceDetail = (signIn.deviceDetail ?? null) as Record<string, unknown> | null
const location = (signIn.location ?? null) as Record<string, unknown> | null
return {
id: signIn.id ?? null,
createdDateTime: signIn.createdDateTime ?? null,
userId: signIn.userId ?? null,
userDisplayName: signIn.userDisplayName ?? null,
userPrincipalName: signIn.userPrincipalName ?? null,
appId: signIn.appId ?? null,
appDisplayName: signIn.appDisplayName ?? null,
resourceId: signIn.resourceId ?? null,
resourceDisplayName: signIn.resourceDisplayName ?? null,
ipAddress: signIn.ipAddress ?? null,
clientAppUsed: signIn.clientAppUsed ?? null,
correlationId: signIn.correlationId ?? null,
conditionalAccessStatus: signIn.conditionalAccessStatus ?? null,
isInteractive: signIn.isInteractive ?? null,
riskDetail: signIn.riskDetail ?? null,
riskLevelAggregated: signIn.riskLevelAggregated ?? null,
riskState: signIn.riskState ?? null,
errorCode: status?.errorCode ?? null,
failureReason: status?.failureReason ?? null,
deviceDisplayName: deviceDetail?.displayName ?? null,
deviceId: deviceDetail?.deviceId ?? null,
deviceOperatingSystem: deviceDetail?.operatingSystem ?? null,
deviceBrowser: deviceDetail?.browser ?? null,
deviceIsCompliant: deviceDetail?.isCompliant ?? null,
deviceIsManaged: deviceDetail?.isManaged ?? null,
locationCity: location?.city ?? null,
locationState: location?.state ?? null,
locationCountryOrRegion: location?.countryOrRegion ?? null,
}
})
return {
success: true,
output: {
signIns,
signInCount: signIns.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
signIns: {
type: 'array',
description: 'Sign-in events',
properties: SIGN_IN_OUTPUT_PROPERTIES,
},
signInCount: { type: 'number', description: 'Number of sign-ins returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,74 @@
import type {
MicrosoftAdListSubscribedSkusParams,
MicrosoftAdListSubscribedSkusResponse,
} from '@/tools/microsoft_ad/types'
import { SUBSCRIBED_SKU_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { mapServicePlans } from '@/tools/microsoft_ad/utils'
import type { ToolConfig } from '@/tools/types'
export const listSubscribedSkusTool: ToolConfig<
MicrosoftAdListSubscribedSkusParams,
MicrosoftAdListSubscribedSkusResponse
> = {
id: 'microsoft_ad_list_subscribed_skus',
name: 'List Microsoft Entra ID Subscribed SKUs',
description:
'List the subscription SKUs the tenant owns, including how many license units are prepaid and how many are consumed',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
},
request: {
url: 'https://graph.microsoft.com/v1.0/subscribedSkus',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const skus = (data.value ?? []).map((sku: Record<string, unknown>) => {
const prepaidUnits = (sku.prepaidUnits ?? null) as Record<string, unknown> | null
return {
id: sku.id ?? null,
skuId: sku.skuId ?? null,
skuPartNumber: sku.skuPartNumber ?? null,
appliesTo: sku.appliesTo ?? null,
capabilityStatus: sku.capabilityStatus ?? null,
consumedUnits: sku.consumedUnits ?? null,
prepaidUnits: {
enabled: prepaidUnits?.enabled ?? null,
suspended: prepaidUnits?.suspended ?? null,
warning: prepaidUnits?.warning ?? null,
lockedOut: prepaidUnits?.lockedOut ?? null,
},
servicePlans: mapServicePlans(sku.servicePlans),
}
})
return {
success: true,
output: {
skus,
skuCount: skus.length,
},
}
},
outputs: {
skus: {
type: 'array',
description: 'Subscription SKUs owned by the tenant',
properties: SUBSCRIBED_SKU_OUTPUT_PROPERTIES,
},
skuCount: { type: 'number', description: 'Number of SKUs returned' },
},
}
@@ -0,0 +1,110 @@
import type {
MicrosoftAdListAppRoleAssignmentsResponse,
MicrosoftAdListUserAppRoleAssignmentsParams,
} from '@/tools/microsoft_ad/types'
import { APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrlForCollection } from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listUserAppRoleAssignmentsTool: ToolConfig<
MicrosoftAdListUserAppRoleAssignmentsParams,
MicrosoftAdListAppRoleAssignmentsResponse
> = {
id: 'microsoft_ad_list_user_app_role_assignments',
name: 'List Microsoft Entra ID User App Role Assignments',
description:
'List the application role assignments granted to a user, including assignments the user inherits from groups they are a direct member of',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'User ID or user principal name. Not needed when Next Page is provided to fetch a later page.',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of assignments to return',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'OData filter expression. Filterable fields include id, resourceId and principalDisplayName. Example: "resourceId eq 8e881353-1735-45af-af21-ee1344582a4d".',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink)
return assertGraphNextPageUrlForCollection(params.nextLink, ['appRoleAssignments'])
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
const queryParts = ['$count=true']
if (params.top) queryParts.push(`$top=${params.top}`)
if (params.filter) queryParts.push(`$filter=${encodeURIComponent(params.filter)}`)
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/appRoleAssignments?${queryParts.join('&')}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
ConsistencyLevel: 'eventual',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const assignments = (data.value ?? []).map((assignment: Record<string, unknown>) => ({
id: assignment.id ?? null,
appRoleId: assignment.appRoleId ?? null,
createdDateTime: assignment.createdDateTime ?? null,
principalId: assignment.principalId ?? null,
principalDisplayName: assignment.principalDisplayName ?? null,
principalType: assignment.principalType ?? null,
resourceId: assignment.resourceId ?? null,
resourceDisplayName: assignment.resourceDisplayName ?? null,
}))
return {
success: true,
output: {
assignments,
assignmentCount: assignments.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
assignments: {
type: 'array',
description: 'App role assignments granted to the user',
properties: APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES,
},
assignmentCount: { type: 'number', description: 'Number of assignments returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,109 @@
import type {
MicrosoftAdListDevicesResponse,
MicrosoftAdListUserDevicesParams,
} from '@/tools/microsoft_ad/types'
import { DEVICE_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrlForCollection, mapDevice } from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
const RELATIONSHIP_PATHS: Record<string, string> = {
registered: 'registeredDevices',
owned: 'ownedDevices',
}
export const listUserDevicesTool: ToolConfig<
MicrosoftAdListUserDevicesParams,
MicrosoftAdListDevicesResponse
> = {
id: 'microsoft_ad_list_user_devices',
name: 'List Microsoft Entra ID User Devices',
description:
'List the devices a user has registered or owns. Devices the caller cannot read are returned with only their ID and the remaining fields null.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'User ID or user principal name. Not needed when Next Page is provided to fetch a later page.',
},
deviceRelationship: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Which devices to list: "registered" for devices the user registered, or "owned" for devices the user owns. Defaults to "registered".',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of devices to return',
},
nextLink: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Continuation URL from a previous response\'s "nextLink" output, used to fetch the next page of results',
},
},
request: {
url: (params) => {
if (params.nextLink)
return assertGraphNextPageUrlForCollection(params.nextLink, [
'registeredDevices',
'ownedDevices',
])
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
const relationship = params.deviceRelationship?.trim() || 'registered'
const path = RELATIONSHIP_PATHS[relationship]
if (!path) throw new Error('Device relationship must be "registered" or "owned"')
const base = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/${path}`
return params.top ? `${base}?$top=${params.top}` : base
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const devices = (data.value ?? []).map(mapDevice)
return {
success: true,
output: {
devices,
deviceCount: devices.length,
nextLink: getGraphNextPageUrl(data) ?? null,
},
}
},
outputs: {
devices: {
type: 'array',
description: 'Devices linked to the user',
properties: DEVICE_OUTPUT_PROPERTIES,
},
deviceCount: { type: 'number', description: 'Number of devices returned' },
nextLink: {
type: 'string',
description: 'Continuation URL for the next page of results, or null if there are no more',
optional: true,
},
},
}
@@ -0,0 +1,71 @@
import type {
MicrosoftAdListUserLicensesParams,
MicrosoftAdListUserLicensesResponse,
} from '@/tools/microsoft_ad/types'
import { LICENSE_DETAIL_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { mapServicePlans } from '@/tools/microsoft_ad/utils'
import type { ToolConfig } from '@/tools/types'
export const listUserLicensesTool: ToolConfig<
MicrosoftAdListUserLicensesParams,
MicrosoftAdListUserLicensesResponse
> = {
id: 'microsoft_ad_list_user_licenses',
name: 'List Microsoft Entra ID User Licenses',
description: 'List the subscription licenses assigned to a user in Microsoft Entra ID',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/licenseDetails`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const licenses = (data.value ?? []).map((license: Record<string, unknown>) => ({
id: license.id ?? null,
skuId: license.skuId ?? null,
skuPartNumber: license.skuPartNumber ?? null,
servicePlans: mapServicePlans(license.servicePlans),
}))
return {
success: true,
output: {
licenses,
licenseCount: licenses.length,
},
}
},
outputs: {
licenses: {
type: 'array',
description: 'Licenses assigned to the user',
properties: LICENSE_DETAIL_OUTPUT_PROPERTIES,
},
licenseCount: { type: 'number', description: 'Number of licenses returned' },
},
}
+3 -2
View File
@@ -3,7 +3,8 @@ import type {
MicrosoftAdListUsersResponse,
} from '@/tools/microsoft_ad/types'
import { USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import { assertGraphNextPageUrlForCollection } from '@/tools/microsoft_ad/utils'
import { getGraphNextPageUrl } from '@/tools/sharepoint/utils'
import type { ToolConfig } from '@/tools/types'
export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdListUsersResponse> = {
@@ -51,7 +52,7 @@ export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdLi
},
request: {
url: (params) => {
if (params.nextLink) return assertGraphNextPageUrl(params.nextLink)
if (params.nextLink) return assertGraphNextPageUrlForCollection(params.nextLink, ['users'])
const queryParts: string[] = []
queryParts.push(
'$select=id,displayName,givenName,surname,userPrincipalName,mail,jobTitle,department,officeLocation,mobilePhone,accountEnabled'
@@ -0,0 +1,72 @@
import type {
MicrosoftAdRemoveDirectoryRoleMemberParams,
MicrosoftAdRemoveDirectoryRoleMemberResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const removeDirectoryRoleMemberTool: ToolConfig<
MicrosoftAdRemoveDirectoryRoleMemberParams,
MicrosoftAdRemoveDirectoryRoleMemberResponse
> = {
id: 'microsoft_ad_remove_directory_role_member',
name: 'Remove Microsoft Entra ID Directory Role Member',
description:
'Revoke an administrator role from a user in Microsoft Entra ID. Removes only the role membership; the user account itself is not deleted.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
directoryRoleId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Object ID of the directory role. Use List Directory Roles to find it.',
},
memberId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Object ID of the user to remove the role from',
},
},
request: {
url: (params) => {
const directoryRoleId = params.directoryRoleId?.trim()
const memberId = params.memberId?.trim()
if (!directoryRoleId) throw new Error('Directory role ID is required')
if (!memberId) throw new Error('Member ID is required')
return `https://graph.microsoft.com/v1.0/directoryRoles/${encodeURIComponent(directoryRoleId)}/members/${encodeURIComponent(memberId)}/$ref`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (
_response: Response,
params?: MicrosoftAdRemoveDirectoryRoleMemberParams
) => {
return {
success: true,
output: {
removed: true,
directoryRoleId: params?.directoryRoleId ?? '',
memberId: params?.memberId ?? '',
},
}
},
outputs: {
removed: { type: 'boolean', description: 'Whether the member was removed successfully' },
directoryRoleId: { type: 'string', description: 'ID of the directory role' },
memberId: { type: 'string', description: 'ID of the member that was removed' },
},
}
@@ -0,0 +1,73 @@
import type {
MicrosoftAdRemoveUserAppRoleAssignmentParams,
MicrosoftAdRemoveUserAppRoleAssignmentResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const removeUserAppRoleAssignmentTool: ToolConfig<
MicrosoftAdRemoveUserAppRoleAssignmentParams,
MicrosoftAdRemoveUserAppRoleAssignmentResponse
> = {
id: 'microsoft_ad_remove_user_app_role_assignment',
name: 'Revoke Microsoft Entra ID App Role From User',
description:
"Revoke an application role assignment from a user, removing their access to that application. Takes the assignment's own ID, not the app role ID.",
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name the assignment belongs to',
},
appRoleAssignmentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'ID of the app role assignment to remove, taken from the "id" field of List User App Role Assignments',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
const appRoleAssignmentId = params.appRoleAssignmentId?.trim()
if (!userId) throw new Error('User ID is required')
if (!appRoleAssignmentId) throw new Error('App role assignment ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/appRoleAssignments/${encodeURIComponent(appRoleAssignmentId)}`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (
_response: Response,
params?: MicrosoftAdRemoveUserAppRoleAssignmentParams
) => {
return {
success: true,
output: {
removed: true,
userId: params?.userId ?? '',
appRoleAssignmentId: params?.appRoleAssignmentId ?? '',
},
}
},
outputs: {
removed: { type: 'boolean', description: 'Whether the assignment was removed successfully' },
userId: { type: 'string', description: 'ID of the user the assignment belonged to' },
appRoleAssignmentId: { type: 'string', description: 'ID of the removed app role assignment' },
},
}
@@ -0,0 +1,105 @@
import type {
MicrosoftAdResetPasswordParams,
MicrosoftAdResetPasswordResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
/**
* Microsoft Graph exposes a user's password authentication method under a fixed, publicly
* documented object id that is identical for every user in every tenant. It is a route
* segment, not a credential.
*
* @see https://learn.microsoft.com/en-us/graph/api/resources/passwordauthenticationmethod
*/
const PASSWORD_METHOD_ROUTE_SEGMENT = '28c10230-6103-485e-b985-444c60001490'
export const resetPasswordTool: ToolConfig<
MicrosoftAdResetPasswordParams,
MicrosoftAdResetPasswordResponse
> = {
id: 'microsoft_ad_reset_password',
name: 'Reset Microsoft Entra ID User Password',
description:
"Reset another user's password through their password authentication method. Leave the new password empty to have Microsoft generate one and return it. The user is prompted to change the password at their next sign-in. Cannot be run against your own account.",
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name whose password should be reset',
},
newPassword: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'The new password. Required for tenants with hybrid password scenarios. Leave empty for a cloud-only password to have Microsoft generate and return one.',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/authentication/methods/${PASSWORD_METHOD_ROUTE_SEGMENT}/resetPassword`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const newPassword = params.newPassword?.trim()
return newPassword ? { newPassword } : {}
},
},
transformResponse: async (response: Response, params?: MicrosoftAdResetPasswordParams) => {
const text = await response.text()
let newPassword: string | null = null
if (text) {
try {
newPassword = (JSON.parse(text).newPassword as string) ?? null
} catch {
newPassword = null
}
}
return {
success: true,
output: {
accepted: true,
userId: params?.userId ?? '',
newPassword,
operationLocation: response.headers.get('Location'),
},
}
},
outputs: {
accepted: {
type: 'boolean',
description: 'Whether Microsoft Graph accepted the password reset operation',
},
userId: { type: 'string', description: 'ID of the user whose password was reset' },
newPassword: {
type: 'string',
description:
'The system-generated password, returned only when no new password was supplied in the request. Like every tool output it appears in workflow outputs and run history, and is sent to the model when an agent calls this tool, so prefer supplying your own password when the value must not leave the workflow.',
optional: true,
},
operationLocation: {
type: 'string',
description: 'URL to poll for the status of the long-running password reset operation',
optional: true,
},
},
}
@@ -0,0 +1,72 @@
import type {
MicrosoftAdRevokeSignInSessionsParams,
MicrosoftAdRevokeSignInSessionsResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const revokeSignInSessionsTool: ToolConfig<
MicrosoftAdRevokeSignInSessionsParams,
MicrosoftAdRevokeSignInSessionsResponse
> = {
id: 'microsoft_ad_revoke_sign_in_sessions',
name: 'Revoke Microsoft Entra ID Sign-In Sessions',
description:
'Invalidate every refresh token and session cookie issued to a user, forcing them to sign in again on all applications and devices. Revocation can take a few minutes to take effect and does not apply to external users.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name whose sessions should be revoked',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/revokeSignInSessions`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params?: MicrosoftAdRevokeSignInSessionsParams) => {
const text = await response.text()
let revoked = true
if (text) {
try {
revoked = JSON.parse(text).value !== false
} catch {
revoked = true
}
}
return {
success: true,
output: {
revoked,
userId: params?.userId ?? '',
},
}
},
outputs: {
revoked: {
type: 'boolean',
description: 'Whether Microsoft Graph confirmed the sessions were revoked',
},
userId: { type: 'string', description: 'ID of the user whose sessions were revoked' },
},
}
@@ -0,0 +1,98 @@
import type {
MicrosoftAdSetPasswordParams,
MicrosoftAdSetPasswordResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const setPasswordTool: ToolConfig<
MicrosoftAdSetPasswordParams,
MicrosoftAdSetPasswordResponse
> = {
id: 'microsoft_ad_set_password',
name: 'Set Microsoft Entra ID User Password',
description:
'Set a specific password on a user by updating their password profile. Cannot be used for federated users. Requires an administrator role in Microsoft Entra ID.',
version: '1.0.0',
errorExtractor: 'nested-error-object',
oauth: {
required: true,
provider: 'microsoft-ad',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Microsoft Graph API access token',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID or user principal name whose password should be set',
},
password: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The new password. Must satisfy the tenant password policy.',
},
forceChangePasswordNextSignIn: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description:
'Whether the user must change this password at their next sign-in. Defaults to true.',
},
forceChangePasswordNextSignInWithMfa: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description:
'Whether the user must complete multifactor authentication before being forced to change the password',
},
},
request: {
url: (params) => {
const userId = params.userId?.trim()
if (!userId) throw new Error('User ID is required')
return `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}`
},
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const password = params.password?.trim()
if (!password) throw new Error('Password is required')
const passwordProfile: Record<string, unknown> = {
password,
forceChangePasswordNextSignIn: params.forceChangePasswordNextSignIn !== false,
}
if (params.forceChangePasswordNextSignInWithMfa !== undefined) {
passwordProfile.forceChangePasswordNextSignInWithMfa =
params.forceChangePasswordNextSignInWithMfa
}
return { passwordProfile }
},
},
transformResponse: async (_response: Response, params?: MicrosoftAdSetPasswordParams) => {
return {
success: true,
output: {
updated: true,
userId: params?.userId ?? '',
forceChangePasswordNextSignIn: params?.forceChangePasswordNextSignIn !== false,
},
}
},
outputs: {
updated: { type: 'boolean', description: 'Whether the password was set successfully' },
userId: { type: 'string', description: 'ID of the user whose password was set' },
forceChangePasswordNextSignIn: {
type: 'boolean',
description: 'Whether the user must change the password at their next sign-in',
},
},
}
+585
View File
@@ -94,6 +94,127 @@ export interface MicrosoftAdRemoveGroupMemberParams extends MicrosoftAdBaseParam
memberId: string
}
export interface MicrosoftAdAssignLicenseParams extends MicrosoftAdBaseParams {
userId: string
addSkuIds?: string
removeSkuIds?: string
disabledPlanIds?: string
}
export interface MicrosoftAdListUserLicensesParams extends MicrosoftAdBaseParams {
userId: string
}
export type MicrosoftAdListSubscribedSkusParams = MicrosoftAdBaseParams
export interface MicrosoftAdRevokeSignInSessionsParams extends MicrosoftAdBaseParams {
userId: string
}
export interface MicrosoftAdSetPasswordParams extends MicrosoftAdBaseParams {
userId: string
password: string
forceChangePasswordNextSignIn?: boolean
forceChangePasswordNextSignInWithMfa?: boolean
}
export interface MicrosoftAdResetPasswordParams extends MicrosoftAdBaseParams {
userId: string
newPassword?: string
}
export interface MicrosoftAdListAuthenticationMethodsParams extends MicrosoftAdBaseParams {
userId: string
}
export interface MicrosoftAdListSignInsParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
nextLink?: string
}
export interface MicrosoftAdListDirectoryAuditsParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
nextLink?: string
}
export interface MicrosoftAdListUserAppRoleAssignmentsParams extends MicrosoftAdBaseParams {
userId?: string
top?: number
filter?: string
nextLink?: string
}
export interface MicrosoftAdAddUserAppRoleAssignmentParams extends MicrosoftAdBaseParams {
userId: string
resourceId: string
appRoleId: string
}
export interface MicrosoftAdRemoveUserAppRoleAssignmentParams extends MicrosoftAdBaseParams {
userId: string
appRoleAssignmentId: string
}
export interface MicrosoftAdListServicePrincipalsParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
search?: string
nextLink?: string
}
export interface MicrosoftAdListServicePrincipalAppRoleAssignmentsParams
extends MicrosoftAdBaseParams {
servicePrincipalId?: string
filter?: string
nextLink?: string
}
export type MicrosoftAdListDirectoryRolesParams = MicrosoftAdBaseParams
export interface MicrosoftAdListDirectoryRoleMembersParams extends MicrosoftAdBaseParams {
directoryRoleId: string
}
export interface MicrosoftAdAddDirectoryRoleMemberParams extends MicrosoftAdBaseParams {
directoryRoleId: string
memberId: string
}
export interface MicrosoftAdRemoveDirectoryRoleMemberParams extends MicrosoftAdBaseParams {
directoryRoleId: string
memberId: string
}
export interface MicrosoftAdListDevicesParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
search?: string
nextLink?: string
}
export interface MicrosoftAdGetDeviceParams extends MicrosoftAdBaseParams {
deviceObjectId: string
}
export interface MicrosoftAdListUserDevicesParams extends MicrosoftAdBaseParams {
userId?: string
deviceRelationship?: string
top?: number
nextLink?: string
}
export interface MicrosoftAdListConditionalAccessPoliciesParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
nextLink?: string
}
export interface MicrosoftAdGetConditionalAccessPolicyParams extends MicrosoftAdBaseParams {
policyId: string
}
export const USER_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'User ID' },
displayName: { type: 'string', description: 'Display name' },
@@ -108,6 +229,25 @@ export const USER_OUTPUT_PROPERTIES = {
accountEnabled: { type: 'boolean', description: 'Whether the account is enabled' },
} as const satisfies Record<string, OutputProperty>
/**
* The properties `POST /users` returns by default. The create endpoint does not document
* `$select`, so the response is limited to this set — notably it omits `department` and
* `accountEnabled`, which the read endpoints do return.
*/
export const CREATED_USER_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'User ID' },
displayName: { type: 'string', description: 'Display name' },
givenName: { type: 'string', description: 'First name' },
surname: { type: 'string', description: 'Last name' },
userPrincipalName: { type: 'string', description: 'User principal name (email)' },
mail: { type: 'string', description: 'Email address' },
jobTitle: { type: 'string', description: 'Job title' },
officeLocation: { type: 'string', description: 'Office location' },
mobilePhone: { type: 'string', description: 'Mobile phone number' },
businessPhones: { type: 'array', description: 'Business phone numbers' },
preferredLanguage: { type: 'string', description: 'Preferred language' },
} as const satisfies Record<string, OutputProperty>
export const GROUP_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Group ID' },
displayName: { type: 'string', description: 'Display name' },
@@ -128,6 +268,430 @@ export const MEMBER_OUTPUT_PROPERTIES = {
odataType: { type: 'string', description: 'Directory object type' },
} as const satisfies Record<string, OutputProperty>
export const SERVICE_PLAN_OUTPUT_PROPERTIES = {
servicePlanId: { type: 'string', description: 'Service plan ID' },
servicePlanName: { type: 'string', description: 'Service plan name' },
provisioningStatus: { type: 'string', description: 'Provisioning status of the service plan' },
appliesTo: { type: 'string', description: 'Whether the plan applies to "User" or "Company"' },
} as const satisfies Record<string, OutputProperty>
export const ASSIGNED_LICENSE_OUTPUT_PROPERTIES = {
skuId: { type: 'string', description: 'SKU ID of the assigned license' },
disabledPlans: { type: 'array', description: 'Service plan IDs disabled on this license' },
} as const satisfies Record<string, OutputProperty>
export const LICENSE_DETAIL_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'License detail ID' },
skuId: { type: 'string', description: 'SKU ID of the license' },
skuPartNumber: { type: 'string', description: 'SKU part number (e.g., "ENTERPRISEPACK")' },
servicePlans: {
type: 'array',
description: 'Service plans included in the license',
properties: SERVICE_PLAN_OUTPUT_PROPERTIES,
},
} as const satisfies Record<string, OutputProperty>
export const PREPAID_UNITS_OUTPUT_PROPERTIES = {
enabled: { type: 'number', description: 'Number of units that are enabled' },
suspended: { type: 'number', description: 'Number of units that are suspended' },
warning: { type: 'number', description: 'Number of units that are in warning status' },
lockedOut: { type: 'number', description: 'Number of units that are locked out' },
} as const satisfies Record<string, OutputProperty>
export const SUBSCRIBED_SKU_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Subscribed SKU object ID' },
skuId: { type: 'string', description: 'SKU ID, used when assigning or removing licenses' },
skuPartNumber: { type: 'string', description: 'SKU part number (e.g., "ENTERPRISEPACK")' },
appliesTo: { type: 'string', description: 'Whether the SKU applies to "User" or "Company"' },
capabilityStatus: { type: 'string', description: 'Capability status of the subscription' },
consumedUnits: { type: 'number', description: 'Number of licenses currently assigned' },
prepaidUnits: {
type: 'object',
description: 'Prepaid license unit counts by status',
properties: PREPAID_UNITS_OUTPUT_PROPERTIES,
},
servicePlans: {
type: 'array',
description: 'Service plans included in the SKU',
properties: SERVICE_PLAN_OUTPUT_PROPERTIES,
},
} as const satisfies Record<string, OutputProperty>
export const AUTHENTICATION_METHOD_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Authentication method ID' },
odataType: {
type: 'string',
description:
'Authentication method type (e.g., "#microsoft.graph.phoneAuthenticationMethod"). Method-specific details vary by type.',
},
createdDateTime: {
type: 'string',
description: 'When the authentication method was registered',
},
} as const satisfies Record<string, OutputProperty>
export const SIGN_IN_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Sign-in event ID' },
createdDateTime: { type: 'string', description: 'When the sign-in was initiated' },
userId: { type: 'string', description: 'ID of the user who signed in' },
userDisplayName: { type: 'string', description: 'Display name of the user' },
userPrincipalName: { type: 'string', description: 'User principal name of the user' },
appId: { type: 'string', description: 'ID of the application used to sign in' },
appDisplayName: { type: 'string', description: 'Display name of the application' },
resourceId: { type: 'string', description: 'ID of the resource that was accessed' },
resourceDisplayName: { type: 'string', description: 'Display name of the resource' },
ipAddress: { type: 'string', description: 'IP address the sign-in came from' },
clientAppUsed: { type: 'string', description: 'Legacy client app used to sign in' },
correlationId: { type: 'string', description: 'Correlation ID for the sign-in request' },
conditionalAccessStatus: {
type: 'string',
description: 'Conditional access result: success, failure, notApplied, or unknownFutureValue',
},
isInteractive: { type: 'boolean', description: 'Whether the sign-in was interactive' },
riskDetail: { type: 'string', description: 'Reason behind a specific risk state' },
riskLevelAggregated: { type: 'string', description: 'Aggregated risk level for the sign-in' },
riskState: { type: 'string', description: 'Risk state of the user or sign-in' },
errorCode: {
type: 'number',
description: 'Sign-in status error code. 0 indicates a successful sign-in.',
},
failureReason: { type: 'string', description: 'Failure reason from the sign-in status' },
deviceDisplayName: { type: 'string', description: 'Display name of the device used' },
deviceId: { type: 'string', description: 'ID of the device used' },
deviceOperatingSystem: { type: 'string', description: 'Operating system of the device used' },
deviceBrowser: { type: 'string', description: 'Browser used to sign in' },
deviceIsCompliant: { type: 'boolean', description: 'Whether the device is compliant' },
deviceIsManaged: { type: 'boolean', description: 'Whether the device is managed' },
locationCity: { type: 'string', description: 'City the sign-in came from' },
locationState: { type: 'string', description: 'State the sign-in came from' },
locationCountryOrRegion: {
type: 'string',
description: 'Two-letter country or region code the sign-in came from',
},
} as const satisfies Record<string, OutputProperty>
export const DIRECTORY_AUDIT_TARGET_RESOURCE_PROPERTIES = {
id: { type: 'string', description: 'ID of the target resource' },
displayName: { type: 'string', description: 'Display name of the target resource' },
type: { type: 'string', description: 'Type of the target resource (e.g., User, Group)' },
userPrincipalName: {
type: 'string',
description: 'User principal name of the target, null for non-user resources',
},
} as const satisfies Record<string, OutputProperty>
export const DIRECTORY_AUDIT_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Audit record ID' },
activityDateTime: { type: 'string', description: 'When the activity took place' },
activityDisplayName: { type: 'string', description: 'Name of the activity' },
category: { type: 'string', description: 'Category of the activity' },
correlationId: { type: 'string', description: 'Correlation ID for the activity' },
loggedByService: { type: 'string', description: 'Service that logged the activity' },
operationType: { type: 'string', description: 'Operation type (e.g., Add, Update, Delete)' },
result: {
type: 'string',
description: 'Result of the activity: success, failure, timeout, or unknownFutureValue',
},
resultReason: { type: 'string', description: 'Reason for the result' },
initiatedByUserId: { type: 'string', description: 'ID of the user who initiated the activity' },
initiatedByUserPrincipalName: {
type: 'string',
description: 'User principal name of the initiating user',
},
initiatedByUserDisplayName: {
type: 'string',
description: 'Display name of the initiating user',
},
initiatedByAppId: { type: 'string', description: 'App ID that initiated the activity' },
initiatedByAppDisplayName: {
type: 'string',
description: 'Display name of the app that initiated the activity',
},
targetResources: {
type: 'array',
description: 'Resources the activity acted on',
properties: DIRECTORY_AUDIT_TARGET_RESOURCE_PROPERTIES,
},
} as const satisfies Record<string, OutputProperty>
export const APP_ROLE_ASSIGNMENT_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'App role assignment ID, used when removing the assignment' },
appRoleId: {
type: 'string',
description:
'ID of the app role. All-zero GUID means the assignment grants access without a specific role.',
},
createdDateTime: { type: 'string', description: 'When the assignment was created' },
principalId: { type: 'string', description: 'ID of the assigned principal' },
principalDisplayName: { type: 'string', description: 'Display name of the assigned principal' },
principalType: {
type: 'string',
description: 'Principal type: User, Group, or ServicePrincipal',
},
resourceId: {
type: 'string',
description: 'ID of the resource service principal that defines the app role',
},
resourceDisplayName: { type: 'string', description: 'Display name of the resource' },
} as const satisfies Record<string, OutputProperty>
export const SERVICE_PRINCIPAL_OUTPUT_PROPERTIES = {
id: {
type: 'string',
description: 'Service principal object ID, used as the resource ID of an app role assignment',
},
appId: { type: 'string', description: 'Application ID associated with the service principal' },
displayName: { type: 'string', description: 'Display name of the service principal' },
servicePrincipalType: {
type: 'string',
description: 'Type of service principal (e.g., Application, ManagedIdentity, Legacy)',
},
accountEnabled: {
type: 'boolean',
description: 'Whether users can sign in to the associated application',
},
appOwnerOrganizationId: {
type: 'string',
description: 'Tenant ID where the application is registered',
},
signInAudience: {
type: 'string',
description: 'Which Microsoft accounts are supported by the associated application',
},
tags: { type: 'array', description: 'Custom strings used to categorize the service principal' },
} as const satisfies Record<string, OutputProperty>
export const APP_ROLE_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'App role ID, used when granting an app role assignment' },
displayName: { type: 'string', description: 'Display name of the app role' },
description: { type: 'string', description: 'Description of the app role' },
value: { type: 'string', description: 'Value included in the roles claim for this app role' },
isEnabled: { type: 'boolean', description: 'Whether the app role can be assigned' },
allowedMemberTypes: {
type: 'array',
description: 'Principal types the app role can be assigned to (User and/or Application)',
},
} as const satisfies Record<string, OutputProperty>
export const DIRECTORY_ROLE_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Directory role object ID' },
displayName: { type: 'string', description: 'Display name of the directory role' },
description: { type: 'string', description: 'Description of the directory role' },
roleTemplateId: { type: 'string', description: 'ID of the directory role template' },
} as const satisfies Record<string, OutputProperty>
export const DEVICE_OUTPUT_PROPERTIES = {
id: {
type: 'string',
description: 'Device object ID, used to get, update, or delete the device',
},
deviceId: { type: 'string', description: 'Unique device identifier set during registration' },
displayName: { type: 'string', description: 'Display name of the device' },
operatingSystem: { type: 'string', description: 'Operating system of the device' },
operatingSystemVersion: { type: 'string', description: 'Operating system version of the device' },
accountEnabled: { type: 'boolean', description: 'Whether the device is enabled' },
isCompliant: { type: 'boolean', description: 'Whether the device complies with MDM policies' },
isManaged: { type: 'boolean', description: 'Whether the device is managed by an MDM app' },
trustType: {
type: 'string',
description: 'Device registration type: Workplace, AzureAd, or ServerAd',
},
profileType: {
type: 'string',
description: 'Device profile type: RegisteredDevice, SecureVM, Printer, Shared, or IoT',
},
manufacturer: { type: 'string', description: 'Manufacturer of the device' },
model: { type: 'string', description: 'Model of the device' },
approximateLastSignInDateTime: {
type: 'string',
description: 'Approximate time the device last signed in',
},
registrationDateTime: { type: 'string', description: 'When the device was registered' },
} as const satisfies Record<string, OutputProperty>
export const CONDITIONAL_ACCESS_POLICY_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Conditional access policy ID' },
displayName: { type: 'string', description: 'Display name of the policy' },
state: {
type: 'string',
description: 'Policy state: enabled, disabled, or enabledForReportingButNotEnforced',
},
templateId: { type: 'string', description: 'ID of the template the policy was created from' },
createdDateTime: { type: 'string', description: 'When the policy was created' },
modifiedDateTime: { type: 'string', description: 'When the policy was last modified' },
conditions: {
type: 'json',
description:
'Conditions that trigger the policy (users, applications, platforms, locations, risk levels)',
},
grantControls: {
type: 'json',
description: 'Controls enforced when the policy applies, or null when none are configured',
},
sessionControls: {
type: 'json',
description: 'Session controls enforced when the policy applies, or null when none are set',
},
} as const satisfies Record<string, OutputProperty>
export interface MicrosoftAdAssignLicenseResponse extends ToolResponse {
output: {
userId: string | null
displayName: string | null
userPrincipalName: string | null
assignedLicenses: Array<Record<string, unknown>>
}
}
export interface MicrosoftAdListUserLicensesResponse extends ToolResponse {
output: {
licenses: Array<Record<string, unknown>>
licenseCount: number
}
}
export interface MicrosoftAdListSubscribedSkusResponse extends ToolResponse {
output: {
skus: Array<Record<string, unknown>>
skuCount: number
}
}
export interface MicrosoftAdRevokeSignInSessionsResponse extends ToolResponse {
output: {
revoked: boolean
userId: string
}
}
export interface MicrosoftAdSetPasswordResponse extends ToolResponse {
output: {
updated: boolean
userId: string
forceChangePasswordNextSignIn: boolean
}
}
export interface MicrosoftAdResetPasswordResponse extends ToolResponse {
output: {
accepted: boolean
userId: string
newPassword: string | null
operationLocation: string | null
}
}
export interface MicrosoftAdListAuthenticationMethodsResponse extends ToolResponse {
output: {
methods: Array<Record<string, unknown>>
methodCount: number
}
}
export interface MicrosoftAdListSignInsResponse extends ToolResponse {
output: {
signIns: Array<Record<string, unknown>>
signInCount: number
nextLink: string | null
}
}
export interface MicrosoftAdListDirectoryAuditsResponse extends ToolResponse {
output: {
audits: Array<Record<string, unknown>>
auditCount: number
nextLink: string | null
}
}
export interface MicrosoftAdListAppRoleAssignmentsResponse extends ToolResponse {
output: {
assignments: Array<Record<string, unknown>>
assignmentCount: number
nextLink: string | null
}
}
export interface MicrosoftAdAddUserAppRoleAssignmentResponse extends ToolResponse {
output: {
assignment: Record<string, unknown>
}
}
export interface MicrosoftAdRemoveUserAppRoleAssignmentResponse extends ToolResponse {
output: {
removed: boolean
userId: string
appRoleAssignmentId: string
}
}
export interface MicrosoftAdListServicePrincipalsResponse extends ToolResponse {
output: {
servicePrincipals: Array<Record<string, unknown>>
servicePrincipalCount: number
nextLink: string | null
}
}
export interface MicrosoftAdListDirectoryRolesResponse extends ToolResponse {
output: {
roles: Array<Record<string, unknown>>
roleCount: number
}
}
export interface MicrosoftAdListDirectoryRoleMembersResponse extends ToolResponse {
output: {
members: Array<Record<string, unknown>>
memberCount: number
}
}
export interface MicrosoftAdAddDirectoryRoleMemberResponse extends ToolResponse {
output: {
added: boolean
directoryRoleId: string
memberId: string
}
}
export interface MicrosoftAdRemoveDirectoryRoleMemberResponse extends ToolResponse {
output: {
removed: boolean
directoryRoleId: string
memberId: string
}
}
export interface MicrosoftAdListDevicesResponse extends ToolResponse {
output: {
devices: Array<Record<string, unknown>>
deviceCount: number
nextLink: string | null
}
}
export interface MicrosoftAdGetDeviceResponse extends ToolResponse {
output: {
device: Record<string, unknown>
}
}
export interface MicrosoftAdListConditionalAccessPoliciesResponse extends ToolResponse {
output: {
policies: Array<Record<string, unknown>>
policyCount: number
nextLink: string | null
}
}
export interface MicrosoftAdGetConditionalAccessPolicyResponse extends ToolResponse {
output: {
policy: Record<string, unknown>
}
}
export interface MicrosoftAdListUsersResponse extends ToolResponse {
output: {
users: Array<Record<string, unknown>>
@@ -234,3 +798,24 @@ export type MicrosoftAdResponse =
| MicrosoftAdListGroupMembersResponse
| MicrosoftAdAddGroupMemberResponse
| MicrosoftAdRemoveGroupMemberResponse
| MicrosoftAdAssignLicenseResponse
| MicrosoftAdListUserLicensesResponse
| MicrosoftAdListSubscribedSkusResponse
| MicrosoftAdRevokeSignInSessionsResponse
| MicrosoftAdSetPasswordResponse
| MicrosoftAdResetPasswordResponse
| MicrosoftAdListAuthenticationMethodsResponse
| MicrosoftAdListSignInsResponse
| MicrosoftAdListDirectoryAuditsResponse
| MicrosoftAdListAppRoleAssignmentsResponse
| MicrosoftAdAddUserAppRoleAssignmentResponse
| MicrosoftAdRemoveUserAppRoleAssignmentResponse
| MicrosoftAdListServicePrincipalsResponse
| MicrosoftAdListDirectoryRolesResponse
| MicrosoftAdListDirectoryRoleMembersResponse
| MicrosoftAdAddDirectoryRoleMemberResponse
| MicrosoftAdRemoveDirectoryRoleMemberResponse
| MicrosoftAdListDevicesResponse
| MicrosoftAdGetDeviceResponse
| MicrosoftAdListConditionalAccessPoliciesResponse
| MicrosoftAdGetConditionalAccessPolicyResponse
+125
View File
@@ -0,0 +1,125 @@
import { assertGraphNextPageUrl } from '@/tools/sharepoint/utils'
/**
* Splits a comma or newline separated list of identifiers into a trimmed, de-duplicated array.
* Used for Microsoft Graph parameters that take GUID collections (SKU IDs, service plan IDs).
*/
export function parseIdList(value: string | undefined | null): string[] {
if (!value) return []
const seen = new Set<string>()
for (const raw of value.split(/[\n,]/)) {
const trimmed = raw.trim()
if (trimmed) seen.add(trimmed)
}
return [...seen]
}
/**
* Maps a Microsoft Graph `servicePlanInfo` collection to the documented subset of fields.
* Shared by `licenseDetails` and `subscribedSku` responses, which both embed this type.
*/
export function mapServicePlans(value: unknown): Array<Record<string, unknown>> {
if (!Array.isArray(value)) return []
return value.map((plan: Record<string, unknown>) => ({
servicePlanId: plan.servicePlanId ?? null,
servicePlanName: plan.servicePlanName ?? null,
provisioningStatus: plan.provisioningStatus ?? null,
appliesTo: plan.appliesTo ?? null,
}))
}
/**
* Builds a Microsoft Graph collection URL, appending only the OData parameters that were
* supplied. `select` is passed through verbatim; `filter` and `search` are URL-encoded.
*/
export function buildGraphCollectionUrl(
path: string,
options: {
select?: string
top?: number
filter?: string
search?: string
orderby?: string
count?: boolean
}
): string {
const queryParts: string[] = []
if (options.select) queryParts.push(`$select=${options.select}`)
if (options.top) queryParts.push(`$top=${options.top}`)
if (options.filter) queryParts.push(`$filter=${encodeURIComponent(options.filter)}`)
if (options.search) queryParts.push(`$search=${encodeURIComponent(options.search)}`)
if (options.orderby) queryParts.push(`$orderby=${encodeURIComponent(options.orderby)}`)
if (options.count) queryParts.push('$count=true')
return queryParts.length > 0
? `https://graph.microsoft.com/v1.0/${path}?${queryParts.join('&')}`
: `https://graph.microsoft.com/v1.0/${path}`
}
/**
* Validates an `@odata.nextLink` and asserts it continues the collection the caller is querying.
*
* Every paged operation reads the one shared Next Page field, and a subBlock keeps its value
* after the operation changes. Without this check, paging `/users` and then switching the block
* to `/devices` would short-circuit back to the user page, silently returning the previous
* collection. Comparing the final path segment also rejects a continuation URL pasted from an
* unrelated response.
*/
export function assertGraphNextPageUrlForCollection(
nextPageUrl: string,
expectedSegments: string[]
): string {
const url = new URL(assertGraphNextPageUrl(nextPageUrl))
const segments = url.pathname.split('/').filter(Boolean)
const collection = segments[segments.length - 1]
if (!collection || !expectedSegments.includes(collection)) {
throw new Error(
`Next Page URL continues "${collection ?? 'an unknown collection'}", but this operation reads "${expectedSegments.join('" or "')}". Clear the Next Page field when switching operations.`
)
}
return url.toString()
}
/** The `device` properties the Microsoft Entra ID tools project into their outputs. */
export const DEVICE_SELECT =
'id,deviceId,displayName,operatingSystem,operatingSystemVersion,accountEnabled,isCompliant,isManaged,trustType,profileType,manufacturer,model,approximateLastSignInDateTime,registrationDateTime'
/**
* Maps a Microsoft Graph `conditionalAccessPolicy` resource. The `conditions`, `grantControls`,
* and `sessionControls` members are passed through as-is because their shape varies with the
* controls a tenant has configured.
*/
export function mapConditionalAccessPolicy(
policy: Record<string, unknown>
): Record<string, unknown> {
return {
id: policy.id ?? null,
displayName: policy.displayName ?? null,
state: policy.state ?? null,
templateId: policy.templateId ?? null,
createdDateTime: policy.createdDateTime ?? null,
modifiedDateTime: policy.modifiedDateTime ?? null,
conditions: policy.conditions ?? null,
grantControls: policy.grantControls ?? null,
sessionControls: policy.sessionControls ?? null,
}
}
/** Maps a Microsoft Graph `device` resource to the documented subset of fields. */
export function mapDevice(device: Record<string, unknown>): Record<string, unknown> {
return {
id: device.id ?? null,
deviceId: device.deviceId ?? null,
displayName: device.displayName ?? null,
operatingSystem: device.operatingSystem ?? null,
operatingSystemVersion: device.operatingSystemVersion ?? null,
accountEnabled: device.accountEnabled ?? null,
isCompliant: device.isCompliant ?? null,
isManaged: device.isManaged ?? null,
trustType: device.trustType ?? null,
profileType: device.profileType ?? null,
manufacturer: device.manufacturer ?? null,
model: device.model ?? null,
approximateLastSignInDateTime: device.approximateLastSignInDateTime ?? null,
registrationDateTime: device.registrationDateTime ?? null,
}
}
+47
View File
@@ -2703,17 +2703,40 @@ import {
import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from '@/tools/mem0'
import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory'
import {
microsoftAdAddDirectoryRoleMemberTool,
microsoftAdAddGroupMemberTool,
microsoftAdAddUserAppRoleAssignmentTool,
microsoftAdAssignLicenseTool,
microsoftAdCreateGroupTool,
microsoftAdCreateUserTool,
microsoftAdDeleteGroupTool,
microsoftAdDeleteUserTool,
microsoftAdGetConditionalAccessPolicyTool,
microsoftAdGetDeviceTool,
microsoftAdGetGroupTool,
microsoftAdGetUserTool,
microsoftAdListAuthenticationMethodsTool,
microsoftAdListConditionalAccessPoliciesTool,
microsoftAdListDevicesTool,
microsoftAdListDirectoryAuditsTool,
microsoftAdListDirectoryRoleMembersTool,
microsoftAdListDirectoryRolesTool,
microsoftAdListGroupMembersTool,
microsoftAdListGroupsTool,
microsoftAdListServicePrincipalAppRoleAssignmentsTool,
microsoftAdListServicePrincipalsTool,
microsoftAdListSignInsTool,
microsoftAdListSubscribedSkusTool,
microsoftAdListUserAppRoleAssignmentsTool,
microsoftAdListUserDevicesTool,
microsoftAdListUserLicensesTool,
microsoftAdListUsersTool,
microsoftAdRemoveDirectoryRoleMemberTool,
microsoftAdRemoveGroupMemberTool,
microsoftAdRemoveUserAppRoleAssignmentTool,
microsoftAdResetPasswordTool,
microsoftAdRevokeSignInSessionsTool,
microsoftAdSetPasswordTool,
microsoftAdUpdateGroupTool,
microsoftAdUpdateUserTool,
} from '@/tools/microsoft_ad'
@@ -8818,6 +8841,30 @@ export const tools: Record<string, ToolConfig> = {
microsoft_ad_list_group_members: microsoftAdListGroupMembersTool,
microsoft_ad_add_group_member: microsoftAdAddGroupMemberTool,
microsoft_ad_remove_group_member: microsoftAdRemoveGroupMemberTool,
microsoft_ad_assign_license: microsoftAdAssignLicenseTool,
microsoft_ad_list_user_licenses: microsoftAdListUserLicensesTool,
microsoft_ad_list_subscribed_skus: microsoftAdListSubscribedSkusTool,
microsoft_ad_revoke_sign_in_sessions: microsoftAdRevokeSignInSessionsTool,
microsoft_ad_set_password: microsoftAdSetPasswordTool,
microsoft_ad_reset_password: microsoftAdResetPasswordTool,
microsoft_ad_list_authentication_methods: microsoftAdListAuthenticationMethodsTool,
microsoft_ad_list_sign_ins: microsoftAdListSignInsTool,
microsoft_ad_list_directory_audits: microsoftAdListDirectoryAuditsTool,
microsoft_ad_list_user_app_role_assignments: microsoftAdListUserAppRoleAssignmentsTool,
microsoft_ad_add_user_app_role_assignment: microsoftAdAddUserAppRoleAssignmentTool,
microsoft_ad_remove_user_app_role_assignment: microsoftAdRemoveUserAppRoleAssignmentTool,
microsoft_ad_list_service_principals: microsoftAdListServicePrincipalsTool,
microsoft_ad_list_service_principal_app_role_assignments:
microsoftAdListServicePrincipalAppRoleAssignmentsTool,
microsoft_ad_list_directory_roles: microsoftAdListDirectoryRolesTool,
microsoft_ad_list_directory_role_members: microsoftAdListDirectoryRoleMembersTool,
microsoft_ad_add_directory_role_member: microsoftAdAddDirectoryRoleMemberTool,
microsoft_ad_remove_directory_role_member: microsoftAdRemoveDirectoryRoleMemberTool,
microsoft_ad_list_devices: microsoftAdListDevicesTool,
microsoft_ad_get_device: microsoftAdGetDeviceTool,
microsoft_ad_list_user_devices: microsoftAdListUserDevicesTool,
microsoft_ad_list_conditional_access_policies: microsoftAdListConditionalAccessPoliciesTool,
microsoft_ad_get_conditional_access_policy: microsoftAdGetConditionalAccessPolicyTool,
microsoft_teams_read_chat: microsoftTeamsReadChatTool,
microsoft_teams_write_chat: microsoftTeamsWriteChatTool,
microsoft_teams_read_channel: microsoftTeamsReadChannelTool,