feat(microsoft-ad): add Azure AD (Entra ID) integration (#3686)

* feat(microsoft-ad): add Azure AD (Entra ID) integration

Add complete Azure AD integration with 13 tools for managing users
and groups via Microsoft Graph API v1.0. Includes OAuth config with
PKCE, block definition with conditional subBlocks, and generated docs.

Tools: list/get/create/update/delete users, list/get/create/update/delete
groups, list/add/remove group members.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(microsoft-ad): add $search/$filter guard, $count=true, and memberId validation

- Prevent using $search and $filter together (Graph API rejects this)
- Add $count=true when $search is used (required with ConsistencyLevel: eventual)
- Validate and trim memberId in add_group_member body before use

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(microsoft-ad): fix docsLink underscore and accountEnabled update safety

- Change docsLink from microsoft-ad to microsoft_ad to match docs routing
- Split accountEnabled dropdown into separate create/update subBlocks
- Update operation shows "No Change" default (empty string) to prevent
  silently re-enabling disabled accounts when updating other fields
- Create operation keeps "Yes" default as before

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(microsoft-ad): prevent visibility from always being sent on group update

Split visibility dropdown into separate create/update subBlocks with
"No Change" default for update_group, preventing silent overwrite of
group visibility when updating other fields like description.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(microsoft-ad): prevent empty values leaking into PATCH requests

- Use operation-aware checks for accountEnabled and visibility in block
  params to prevent create defaults bleeding into update operations
- Change tool body guards from `!== undefined` to truthy checks so
  empty-string inputs from unfilled subBlocks are omitted from PATCH

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waleed
2026-03-19 15:49:17 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 0d22cc3186
commit d3daab743f
26 changed files with 2245 additions and 1 deletions
+2
View File
@@ -16,6 +16,7 @@ import {
AsanaIcon,
AshbyIcon,
AttioIcon,
AzureIcon,
BoxCompanyIcon,
BrainIcon,
BrandfetchIcon,
@@ -270,6 +271,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
mailgun: MailgunIcon,
mem0: Mem0Icon,
memory: BrainIcon,
microsoft_ad: AzureIcon,
microsoft_dataverse: MicrosoftDataverseIcon,
microsoft_excel_v2: MicrosoftExcelIcon,
microsoft_planner: MicrosoftPlannerIcon,
@@ -95,6 +95,7 @@
"mailgun",
"mem0",
"memory",
"microsoft_ad",
"microsoft_dataverse",
"microsoft_excel",
"microsoft_planner",
@@ -0,0 +1,336 @@
---
title: Azure AD
description: Manage users and groups in Azure AD (Microsoft Entra ID)
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="microsoft_ad"
color="#0078D4"
/>
{/* MANUAL-CONTENT-START:intro */}
[Azure Active Directory](https://entra.microsoft.com) (now Microsoft Entra ID) is Microsoft's cloud-based identity and access management service. It helps organizations manage users, groups, and access to applications and resources across cloud and on-premises environments.
With the Azure AD integration in Sim, you can:
- **Manage users**: List, create, update, and delete user accounts in your directory
- **Manage groups**: Create and configure security groups and Microsoft 365 groups
- **Control group membership**: Add and remove members from groups programmatically
- **Query directory data**: Search and filter users and groups using OData expressions
- **Automate onboarding/offboarding**: Create new user accounts with initial passwords and enable/disable accounts as part of HR workflows
In Sim, the Azure AD integration enables your agents to programmatically manage your organization's identity infrastructure. This allows for automation scenarios such as provisioning new employees, updating user profiles in bulk, managing team group memberships, and auditing directory data. By connecting Sim with Azure AD, you can streamline identity lifecycle management and ensure your directory stays in sync with your organization's needs.
## Need Help?
If you encounter issues with the Azure AD integration, contact us at [help@sim.ai](mailto:help@sim.ai)
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Azure Active Directory into your workflows. List, create, update, and delete users and groups. Manage group memberships programmatically.
## Tools
### `microsoft_ad_list_users`
List users in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of users to return \(default 100, max 999\) |
| `filter` | string | No | OData filter expression \(e.g., "department eq \'Sales\'"\) |
| `search` | string | No | Search string to filter users by displayName or mail |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `users` | array | List of users |
| `userCount` | number | Number of users returned |
### `microsoft_ad_get_user`
Get a user by ID or user principal name from Azure AD
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name \(e.g., "user@example.com"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `user` | object | User details |
| ↳ `id` | string | User ID |
| ↳ `displayName` | string | Display name |
| ↳ `givenName` | string | First name |
| ↳ `surname` | string | Last name |
| ↳ `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 |
### `microsoft_ad_create_user`
Create a new user in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `displayName` | string | Yes | Display name for the user |
| `mailNickname` | string | Yes | Mail alias for the user |
| `userPrincipalName` | string | Yes | User principal name \(e.g., "user@example.com"\) |
| `password` | string | Yes | Initial password for the user |
| `accountEnabled` | boolean | Yes | Whether the account is enabled |
| `givenName` | string | No | First name |
| `surname` | string | No | Last name |
| `jobTitle` | string | No | Job title |
| `department` | string | No | Department |
| `officeLocation` | string | No | Office location |
| `mobilePhone` | string | No | Mobile phone number |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `user` | object | Created user details |
| ↳ `id` | string | User ID |
| ↳ `displayName` | string | Display name |
| ↳ `givenName` | string | First name |
| ↳ `surname` | string | Last name |
| ↳ `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 |
### `microsoft_ad_update_user`
Update user properties in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name |
| `displayName` | string | No | Display name |
| `givenName` | string | No | First name |
| `surname` | string | No | Last name |
| `jobTitle` | string | No | Job title |
| `department` | string | No | Department |
| `officeLocation` | string | No | Office location |
| `mobilePhone` | string | No | Mobile phone number |
| `accountEnabled` | boolean | No | Whether the account is enabled |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `updated` | boolean | Whether the update was successful |
| `userId` | string | ID of the updated user |
### `microsoft_ad_delete_user`
Delete a user from Azure AD (Microsoft Entra ID). The user is moved to a temporary container and can be restored within 30 days.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `userId` | string | Yes | User ID or user principal name |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the deletion was successful |
| `userId` | string | ID of the deleted user |
### `microsoft_ad_list_groups`
List groups in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `top` | number | No | Maximum number of groups to return \(default 100, max 999\) |
| `filter` | string | No | OData filter expression \(e.g., "securityEnabled eq true"\) |
| `search` | string | No | Search string to filter groups by displayName or description |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `groups` | array | List of groups |
| `groupCount` | number | Number of groups returned |
### `microsoft_ad_get_group`
Get a group by ID from Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupId` | string | Yes | Group ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `group` | object | Group details |
| ↳ `id` | string | Group ID |
| ↳ `displayName` | string | Display name |
| ↳ `description` | string | Group description |
| ↳ `mail` | string | Email address |
| ↳ `mailEnabled` | boolean | Whether mail is enabled |
| ↳ `mailNickname` | string | Mail nickname |
| ↳ `securityEnabled` | boolean | Whether security is enabled |
| ↳ `groupTypes` | array | Group types |
| ↳ `visibility` | string | Group visibility |
| ↳ `createdDateTime` | string | Creation date |
### `microsoft_ad_create_group`
Create a new group in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `displayName` | string | Yes | Display name for the group |
| `mailNickname` | string | Yes | Mail alias for the group \(ASCII only, max 64 characters\) |
| `description` | string | No | Group description |
| `mailEnabled` | boolean | Yes | Whether mail is enabled \(true for Microsoft 365 groups\) |
| `securityEnabled` | boolean | Yes | Whether security is enabled \(true for security groups\) |
| `groupTypes` | string | No | Group type: "Unified" for Microsoft 365 group, leave empty for security group |
| `visibility` | string | No | Group visibility: "Private" or "Public" |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `group` | object | Created group details |
| ↳ `id` | string | Group ID |
| ↳ `displayName` | string | Display name |
| ↳ `description` | string | Group description |
| ↳ `mail` | string | Email address |
| ↳ `mailEnabled` | boolean | Whether mail is enabled |
| ↳ `mailNickname` | string | Mail nickname |
| ↳ `securityEnabled` | boolean | Whether security is enabled |
| ↳ `groupTypes` | array | Group types |
| ↳ `visibility` | string | Group visibility |
| ↳ `createdDateTime` | string | Creation date |
### `microsoft_ad_update_group`
Update group properties in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupId` | string | Yes | Group ID |
| `displayName` | string | No | Display name |
| `description` | string | No | Group description |
| `mailNickname` | string | No | Mail alias |
| `visibility` | string | No | Group visibility: "Private" or "Public" |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `updated` | boolean | Whether the update was successful |
| `groupId` | string | ID of the updated group |
### `microsoft_ad_delete_group`
Delete a group from Azure AD (Microsoft Entra ID). Microsoft 365 and security groups can be restored within 30 days.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupId` | string | Yes | Group ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the deletion was successful |
| `groupId` | string | ID of the deleted group |
### `microsoft_ad_list_group_members`
List members of a group in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupId` | string | Yes | Group ID |
| `top` | number | No | Maximum number of members to return \(default 100, max 999\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `members` | array | List of group members |
| `memberCount` | number | Number of members returned |
### `microsoft_ad_add_group_member`
Add a member to a group in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupId` | string | Yes | Group ID |
| `memberId` | string | Yes | User ID of the member to add |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `added` | boolean | Whether the member was added successfully |
| `groupId` | string | Group ID |
| `memberId` | string | Member ID that was added |
### `microsoft_ad_remove_group_member`
Remove a member from a group in Azure AD (Microsoft Entra ID)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupId` | string | Yes | Group ID |
| `memberId` | string | Yes | User ID of the member to remove |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `removed` | boolean | Whether the member was removed successfully |
| `groupId` | string | Group ID |
| `memberId` | string | Member ID that was removed |
+395
View File
@@ -0,0 +1,395 @@
import { AzureIcon } from '@/components/icons'
import { getScopesForService } from '@/lib/oauth/utils'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { MicrosoftAdResponse } from '@/tools/microsoft_ad/types'
export const MicrosoftAdBlock: BlockConfig<MicrosoftAdResponse> = {
type: 'microsoft_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.',
docsLink: 'https://docs.sim.ai/tools/microsoft_ad',
category: 'tools',
bgColor: '#0078D4',
icon: AzureIcon,
authMode: AuthMode.OAuth,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'List Users', id: 'list_users' },
{ label: 'Get User', id: 'get_user' },
{ label: 'Create User', id: 'create_user' },
{ label: 'Update User', id: 'update_user' },
{ label: 'Delete User', id: 'delete_user' },
{ label: 'List Groups', id: 'list_groups' },
{ label: 'Get Group', id: 'get_group' },
{ label: 'Create Group', id: 'create_group' },
{ label: 'Update Group', id: 'update_group' },
{ label: 'Delete Group', id: 'delete_group' },
{ label: 'List Group Members', id: 'list_group_members' },
{ label: 'Add Group Member', id: 'add_group_member' },
{ label: 'Remove Group Member', id: 'remove_group_member' },
],
value: () => 'list_users',
},
{
id: 'credential',
title: 'Microsoft Account',
type: 'oauth-input',
serviceId: 'microsoft-ad',
requiredScopes: getScopesForService('microsoft-ad'),
required: true,
},
// User ID field (for get, update, delete user)
{
id: 'userId',
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'] },
},
// Create user fields
{
id: 'displayName',
title: 'Display Name',
type: 'short-input',
placeholder: 'e.g., John Doe',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
required: { field: 'operation', value: 'create_user' },
},
{
id: 'mailNickname',
title: 'Mail Nickname',
type: 'short-input',
placeholder: 'e.g., johndoe',
condition: { field: 'operation', value: 'create_user' },
required: { field: 'operation', value: 'create_user' },
},
{
id: 'userPrincipalName',
title: 'User Principal Name',
type: 'short-input',
placeholder: 'e.g., johndoe@example.com',
condition: { field: 'operation', value: 'create_user' },
required: { field: 'operation', value: 'create_user' },
},
{
id: 'password',
title: 'Password',
type: 'short-input',
placeholder: 'Initial password',
condition: { field: 'operation', value: 'create_user' },
required: { field: 'operation', value: 'create_user' },
password: true,
},
{
id: 'accountEnabled',
title: 'Account Enabled',
type: 'dropdown',
options: [
{ label: 'No Change', id: '' },
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => '',
condition: { field: 'operation', value: 'update_user' },
},
{
id: 'accountEnabledCreate',
title: 'Account Enabled',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'true',
condition: { field: 'operation', value: 'create_user' },
},
// Update user optional fields
{
id: 'givenName',
title: 'First Name',
type: 'short-input',
placeholder: 'e.g., John',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
mode: 'advanced',
},
{
id: 'surname',
title: 'Last Name',
type: 'short-input',
placeholder: 'e.g., Doe',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
mode: 'advanced',
},
{
id: 'jobTitle',
title: 'Job Title',
type: 'short-input',
placeholder: 'e.g., Software Engineer',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
mode: 'advanced',
},
{
id: 'department',
title: 'Department',
type: 'short-input',
placeholder: 'e.g., Engineering',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
mode: 'advanced',
},
{
id: 'officeLocation',
title: 'Office Location',
type: 'short-input',
placeholder: 'e.g., Building A, Room 101',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
mode: 'advanced',
},
{
id: 'mobilePhone',
title: 'Mobile Phone',
type: 'short-input',
placeholder: 'e.g., +1-555-555-5555',
condition: { field: 'operation', value: ['create_user', 'update_user'] },
mode: 'advanced',
},
// List users/groups optional filters
{
id: 'top',
title: 'Max Results',
type: 'short-input',
placeholder: 'e.g., 100 (max 999)',
condition: {
field: 'operation',
value: ['list_users', 'list_groups', 'list_group_members'],
},
mode: 'advanced',
},
{
id: 'filter',
title: 'Filter',
type: 'short-input',
placeholder: "e.g., department eq 'Sales'",
condition: { field: 'operation', value: ['list_users', 'list_groups'] },
mode: 'advanced',
},
{
id: 'search',
title: 'Search',
type: 'short-input',
placeholder: 'Search by name or email',
condition: { field: 'operation', value: ['list_users', 'list_groups'] },
mode: 'advanced',
},
// Group ID field
{
id: 'groupId',
title: 'Group ID',
type: 'short-input',
placeholder: 'Group ID (GUID)',
condition: {
field: 'operation',
value: [
'get_group',
'update_group',
'delete_group',
'list_group_members',
'add_group_member',
'remove_group_member',
],
},
required: {
field: 'operation',
value: [
'get_group',
'update_group',
'delete_group',
'list_group_members',
'add_group_member',
'remove_group_member',
],
},
},
// Create group fields
{
id: 'groupDisplayName',
title: 'Display Name',
type: 'short-input',
placeholder: 'e.g., Engineering Team',
condition: { field: 'operation', value: ['create_group', 'update_group'] },
required: { field: 'operation', value: 'create_group' },
},
{
id: 'groupMailNickname',
title: 'Mail Nickname',
type: 'short-input',
placeholder: 'e.g., engineering-team',
condition: { field: 'operation', value: ['create_group', 'update_group'] },
required: { field: 'operation', value: 'create_group' },
},
{
id: 'groupDescription',
title: 'Description',
type: 'long-input',
placeholder: 'Group description',
condition: { field: 'operation', value: ['create_group', 'update_group'] },
},
{
id: 'mailEnabled',
title: 'Mail Enabled',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'false',
condition: { field: 'operation', value: 'create_group' },
},
{
id: 'securityEnabled',
title: 'Security Enabled',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'true',
condition: { field: 'operation', value: 'create_group' },
},
{
id: 'groupTypes',
title: 'Group Type',
type: 'dropdown',
options: [
{ label: 'Security Group', id: '' },
{ label: 'Microsoft 365 Group', id: 'Unified' },
],
value: () => '',
condition: { field: 'operation', value: 'create_group' },
mode: 'advanced',
},
{
id: 'visibility',
title: 'Visibility',
type: 'dropdown',
options: [
{ label: 'No Change', id: '' },
{ label: 'Private', id: 'Private' },
{ label: 'Public', id: 'Public' },
],
value: () => '',
condition: { field: 'operation', value: 'update_group' },
mode: 'advanced',
},
{
id: 'visibilityCreate',
title: 'Visibility',
type: 'dropdown',
options: [
{ label: 'Private', id: 'Private' },
{ label: 'Public', id: 'Public' },
],
value: () => 'Private',
condition: { field: 'operation', value: 'create_group' },
mode: 'advanced',
},
// Member ID (for add/remove member)
{
id: 'memberId',
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'] },
},
],
tools: {
access: [
'microsoft_ad_list_users',
'microsoft_ad_get_user',
'microsoft_ad_create_user',
'microsoft_ad_update_user',
'microsoft_ad_delete_user',
'microsoft_ad_list_groups',
'microsoft_ad_get_group',
'microsoft_ad_create_group',
'microsoft_ad_update_group',
'microsoft_ad_delete_group',
'microsoft_ad_list_group_members',
'microsoft_ad_add_group_member',
'microsoft_ad_remove_group_member',
],
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.operation === 'update_user') {
if (params.accountEnabled) result.accountEnabled = params.accountEnabled === 'true'
} else if (params.operation === 'create_user') {
if (params.accountEnabledCreate)
result.accountEnabled = params.accountEnabledCreate === 'true'
}
if (params.mailEnabled !== undefined) result.mailEnabled = params.mailEnabled === 'true'
if (params.securityEnabled !== undefined)
result.securityEnabled = params.securityEnabled === 'true'
// Map group-specific fields to tool param names
if (params.groupDisplayName) result.displayName = params.groupDisplayName
if (params.groupMailNickname) result.mailNickname = params.groupMailNickname
if (params.groupDescription) result.description = params.groupDescription
if (params.groupTypes !== undefined) result.groupTypes = params.groupTypes
if (params.operation === 'update_group') {
if (params.visibility) result.visibility = params.visibility
} else if (params.operation === 'create_group') {
if (params.visibilityCreate) result.visibility = params.visibilityCreate
}
return result
},
},
},
inputs: {
operation: { type: 'string' },
userId: { type: 'string' },
displayName: { type: 'string' },
mailNickname: { type: 'string' },
userPrincipalName: { type: 'string' },
password: { type: 'string' },
accountEnabled: { type: 'string' },
accountEnabledCreate: { type: 'string' },
givenName: { type: 'string' },
surname: { type: 'string' },
jobTitle: { type: 'string' },
department: { type: 'string' },
officeLocation: { type: 'string' },
mobilePhone: { type: 'string' },
top: { type: 'string' },
filter: { type: 'string' },
search: { type: 'string' },
groupId: { type: 'string' },
groupDisplayName: { type: 'string' },
groupMailNickname: { type: 'string' },
groupDescription: { type: 'string' },
mailEnabled: { type: 'string' },
securityEnabled: { type: 'string' },
groupTypes: { type: 'string' },
visibility: { type: 'string' },
visibilityCreate: { type: 'string' },
memberId: { 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.',
},
},
}
+2
View File
@@ -105,6 +105,7 @@ import { ManualTriggerBlock } from '@/blocks/blocks/manual_trigger'
import { McpBlock } from '@/blocks/blocks/mcp'
import { Mem0Block } from '@/blocks/blocks/mem0'
import { MemoryBlock } from '@/blocks/blocks/memory'
import { MicrosoftAdBlock } from '@/blocks/blocks/microsoft_ad'
import { MicrosoftDataverseBlock } from '@/blocks/blocks/microsoft_dataverse'
import { MicrosoftExcelBlock, MicrosoftExcelV2Block } from '@/blocks/blocks/microsoft_excel'
import { MicrosoftPlannerBlock } from '@/blocks/blocks/microsoft_planner'
@@ -322,6 +323,7 @@ export const registry: Record<string, BlockConfig> = {
mcp: McpBlock,
mem0: Mem0Block,
memory: MemoryBlock,
microsoft_ad: MicrosoftAdBlock,
microsoft_dataverse: MicrosoftDataverseBlock,
microsoft_excel: MicrosoftExcelBlock,
microsoft_excel_v2: MicrosoftExcelV2Block,
+41
View File
@@ -483,6 +483,7 @@ export const auth = betterAuth({
'google-tasks',
'vertex-ai',
'microsoft-ad',
'microsoft-dataverse',
'microsoft-teams',
'microsoft-excel',
@@ -1278,6 +1279,46 @@ export const auth = betterAuth({
},
},
{
providerId: 'microsoft-ad',
clientId: env.MICROSOFT_CLIENT_ID as string,
clientSecret: env.MICROSOFT_CLIENT_SECRET as string,
authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
userInfoUrl: 'https://graph.microsoft.com/v1.0/me',
scopes: getCanonicalScopesForProvider('microsoft-ad'),
responseType: 'code',
accessType: 'offline',
authentication: 'basic',
pkce: true,
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/microsoft-ad`,
getUserInfo: async (tokens) => {
try {
const response = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: { Authorization: `Bearer ${tokens.accessToken}` },
})
if (!response.ok) {
await response.text().catch(() => {})
logger.error('Failed to fetch Microsoft user info', { status: response.status })
throw new Error(`Failed to fetch Microsoft user info: ${response.statusText}`)
}
const profile = await response.json()
const now = new Date()
return {
id: `${profile.id}-${crypto.randomUUID()}`,
name: profile.displayName || 'Microsoft User',
email: profile.mail || profile.userPrincipalName,
emailVerified: true,
createdAt: now,
updatedAt: now,
}
} catch (error) {
logger.error('Error in Microsoft getUserInfo', { error })
throw error
}
},
},
{
providerId: 'microsoft-teams',
clientId: env.MICROSOFT_CLIENT_ID as string,
+1
View File
@@ -2,6 +2,7 @@ export const MICROSOFT_REFRESH_TOKEN_LIFETIME_DAYS = 90
export const PROACTIVE_REFRESH_THRESHOLD_DAYS = 7
export const MICROSOFT_PROVIDERS = new Set([
'microsoft-ad',
'microsoft-dataverse',
'microsoft-excel',
'microsoft-planner',
+19
View File
@@ -3,6 +3,7 @@ import {
AirtableIcon,
AsanaIcon,
AttioIcon,
AzureIcon,
BoxCompanyIcon,
CalComIcon,
ConfluenceIcon,
@@ -243,6 +244,24 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
name: 'Microsoft',
icon: MicrosoftIcon,
services: {
'microsoft-ad': {
name: 'Azure AD',
description: 'Connect to Azure AD (Microsoft Entra ID) and manage users and groups.',
providerId: 'microsoft-ad',
icon: AzureIcon,
baseProviderIcon: MicrosoftIcon,
scopes: [
'openid',
'profile',
'email',
'User.Read.All',
'User.ReadWrite.All',
'Group.ReadWrite.All',
'GroupMember.ReadWrite.All',
'Directory.Read.All',
'offline_access',
],
},
'microsoft-dataverse': {
name: 'Microsoft Dataverse',
description: 'Connect to Microsoft Dataverse and manage records.',
+2
View File
@@ -24,6 +24,7 @@ export type OAuthProvider =
| 'box'
| 'dropbox'
| 'microsoft'
| 'microsoft-ad'
| 'microsoft-dataverse'
| 'microsoft-excel'
| 'microsoft-planner'
@@ -73,6 +74,7 @@ export type OAuthService =
| 'jira'
| 'box'
| 'dropbox'
| 'microsoft-ad'
| 'microsoft-dataverse'
| 'microsoft-excel'
| 'microsoft-teams'
+5 -1
View File
@@ -213,7 +213,7 @@ export const SCOPE_DESCRIPTIONS: Record<string, string> = {
'ChannelMessage.ReadWrite': 'Read and write to Microsoft channels',
'ChannelMember.Read.All': 'Read team channel members',
'Group.Read.All': 'Read Microsoft groups',
'Group.ReadWrite.All': 'Write to Microsoft groups',
'Group.ReadWrite.All': 'Read and write all groups',
'Team.ReadBasic.All': 'Read Microsoft teams',
'TeamMember.Read.All': 'Read team members',
'Mail.ReadWrite': 'Write to Microsoft emails',
@@ -227,6 +227,10 @@ export const SCOPE_DESCRIPTIONS: Record<string, string> = {
'Sites.ReadWrite.All': 'Read and write Sharepoint sites',
'Sites.Manage.All': 'Manage Sharepoint sites',
'https://dynamics.microsoft.com/user_impersonation': 'Access Microsoft Dataverse on your behalf',
'User.Read.All': 'Read all user profiles',
'User.ReadWrite.All': 'Read and write all user profiles',
'GroupMember.ReadWrite.All': 'Read and write all group memberships',
'Directory.Read.All': 'Read directory data',
// Discord scopes
identify: 'Read Discord user',
@@ -0,0 +1,74 @@
import type {
MicrosoftAdAddGroupMemberParams,
MicrosoftAdAddGroupMemberResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const addGroupMemberTool: ToolConfig<
MicrosoftAdAddGroupMemberParams,
MicrosoftAdAddGroupMemberResponse
> = {
id: 'microsoft_ad_add_group_member',
name: 'Add Azure AD Group Member',
description: 'Add a member to a group in Azure AD (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',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID',
},
memberId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID of the member to add',
},
},
request: {
url: (params) => {
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
return `https://graph.microsoft.com/v1.0/groups/${encodeURIComponent(groupId)}/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?: MicrosoftAdAddGroupMemberParams) => {
return {
success: true,
output: {
added: true,
groupId: params?.groupId ?? '',
memberId: params?.memberId ?? '',
},
}
},
outputs: {
added: { type: 'boolean', description: 'Whether the member was added successfully' },
groupId: { type: 'string', description: 'Group ID' },
memberId: { type: 'string', description: 'Member ID that was added' },
},
}
+119
View File
@@ -0,0 +1,119 @@
import type {
MicrosoftAdCreateGroupParams,
MicrosoftAdCreateGroupResponse,
} from '@/tools/microsoft_ad/types'
import { GROUP_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const createGroupTool: ToolConfig<
MicrosoftAdCreateGroupParams,
MicrosoftAdCreateGroupResponse
> = {
id: 'microsoft_ad_create_group',
name: 'Create Azure AD Group',
description: 'Create a new group in Azure AD (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',
},
displayName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Display name for the group',
},
mailNickname: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Mail alias for the group (ASCII only, max 64 characters)',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Group description',
},
mailEnabled: {
type: 'boolean',
required: true,
visibility: 'user-or-llm',
description: 'Whether mail is enabled (true for Microsoft 365 groups)',
},
securityEnabled: {
type: 'boolean',
required: true,
visibility: 'user-or-llm',
description: 'Whether security is enabled (true for security groups)',
},
groupTypes: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Group type: "Unified" for Microsoft 365 group, leave empty for security group',
},
visibility: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Group visibility: "Private" or "Public"',
},
},
request: {
url: 'https://graph.microsoft.com/v1.0/groups',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
displayName: params.displayName,
mailNickname: params.mailNickname,
mailEnabled: params.mailEnabled,
securityEnabled: params.securityEnabled,
}
if (params.description) body.description = params.description
if (params.groupTypes) body.groupTypes = [params.groupTypes]
else body.groupTypes = []
if (params.visibility) body.visibility = params.visibility
return body
},
},
transformResponse: async (response: Response) => {
const group = await response.json()
return {
success: true,
output: {
group: {
id: group.id ?? null,
displayName: group.displayName ?? null,
description: group.description ?? null,
mail: group.mail ?? null,
mailEnabled: group.mailEnabled ?? null,
mailNickname: group.mailNickname ?? null,
securityEnabled: group.securityEnabled ?? null,
groupTypes: group.groupTypes ?? [],
visibility: group.visibility ?? null,
createdDateTime: group.createdDateTime ?? null,
},
},
}
},
outputs: {
group: {
type: 'object',
description: 'Created group details',
properties: GROUP_OUTPUT_PROPERTIES,
},
},
}
+150
View File
@@ -0,0 +1,150 @@
import type {
MicrosoftAdCreateUserParams,
MicrosoftAdCreateUserResponse,
} from '@/tools/microsoft_ad/types'
import { USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const createUserTool: ToolConfig<
MicrosoftAdCreateUserParams,
MicrosoftAdCreateUserResponse
> = {
id: 'microsoft_ad_create_user',
name: 'Create Azure AD User',
description: 'Create a new user in Azure AD (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',
},
displayName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Display name for the user',
},
mailNickname: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Mail alias for the user',
},
userPrincipalName: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User principal name (e.g., "user@example.com")',
},
password: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Initial password for the user',
},
accountEnabled: {
type: 'boolean',
required: true,
visibility: 'user-or-llm',
description: 'Whether the account is enabled',
},
givenName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'First name',
},
surname: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Last name',
},
jobTitle: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Job title',
},
department: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Department',
},
officeLocation: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Office location',
},
mobilePhone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Mobile phone number',
},
},
request: {
url: 'https://graph.microsoft.com/v1.0/users',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
accountEnabled: params.accountEnabled,
displayName: params.displayName,
mailNickname: params.mailNickname,
userPrincipalName: params.userPrincipalName,
passwordProfile: {
password: params.password,
forceChangePasswordNextSignIn: true,
},
}
if (params.givenName) body.givenName = params.givenName
if (params.surname) body.surname = params.surname
if (params.jobTitle) body.jobTitle = params.jobTitle
if (params.department) body.department = params.department
if (params.officeLocation) body.officeLocation = params.officeLocation
if (params.mobilePhone) body.mobilePhone = params.mobilePhone
return body
},
},
transformResponse: async (response: Response) => {
const user = await response.json()
return {
success: true,
output: {
user: {
id: user.id ?? null,
displayName: user.displayName ?? null,
givenName: user.givenName ?? null,
surname: user.surname ?? null,
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,
},
},
}
},
outputs: {
user: {
type: 'object',
description: 'Created user details',
properties: USER_OUTPUT_PROPERTIES,
},
},
}
@@ -0,0 +1,59 @@
import type {
MicrosoftAdDeleteGroupParams,
MicrosoftAdDeleteGroupResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const deleteGroupTool: ToolConfig<
MicrosoftAdDeleteGroupParams,
MicrosoftAdDeleteGroupResponse
> = {
id: 'microsoft_ad_delete_group',
name: 'Delete Azure AD Group',
description:
'Delete a group from Azure AD (Microsoft Entra ID). Microsoft 365 and security groups can be restored within 30 days.',
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',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID',
},
},
request: {
url: (params) => {
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
return `https://graph.microsoft.com/v1.0/groups/${encodeURIComponent(groupId)}`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (_response: Response, params?: MicrosoftAdDeleteGroupParams) => {
return {
success: true,
output: {
deleted: true,
groupId: params?.groupId ?? '',
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the deletion was successful' },
groupId: { type: 'string', description: 'ID of the deleted group' },
},
}
@@ -0,0 +1,59 @@
import type {
MicrosoftAdDeleteUserParams,
MicrosoftAdDeleteUserResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const deleteUserTool: ToolConfig<
MicrosoftAdDeleteUserParams,
MicrosoftAdDeleteUserResponse
> = {
id: 'microsoft_ad_delete_user',
name: 'Delete Azure AD User',
description:
'Delete a user from Azure AD (Microsoft Entra ID). The user is moved to a temporary container and can be restored within 30 days.',
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)}`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (_response: Response, params?: MicrosoftAdDeleteUserParams) => {
return {
success: true,
output: {
deleted: true,
userId: params?.userId ?? '',
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether the deletion was successful' },
userId: { type: 'string', description: 'ID of the deleted user' },
},
}
+70
View File
@@ -0,0 +1,70 @@
import type {
MicrosoftAdGetGroupParams,
MicrosoftAdGetGroupResponse,
} from '@/tools/microsoft_ad/types'
import { GROUP_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const getGroupTool: ToolConfig<MicrosoftAdGetGroupParams, MicrosoftAdGetGroupResponse> = {
id: 'microsoft_ad_get_group',
name: 'Get Azure AD Group',
description: 'Get a group by ID from Azure AD (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',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID',
},
},
request: {
url: (params) => {
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
return `https://graph.microsoft.com/v1.0/groups/${encodeURIComponent(groupId)}?$select=id,displayName,description,mail,mailEnabled,mailNickname,securityEnabled,groupTypes,visibility,createdDateTime`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const group = await response.json()
return {
success: true,
output: {
group: {
id: group.id ?? null,
displayName: group.displayName ?? null,
description: group.description ?? null,
mail: group.mail ?? null,
mailEnabled: group.mailEnabled ?? null,
mailNickname: group.mailNickname ?? null,
securityEnabled: group.securityEnabled ?? null,
groupTypes: group.groupTypes ?? [],
visibility: group.visibility ?? null,
createdDateTime: group.createdDateTime ?? null,
},
},
}
},
outputs: {
group: {
type: 'object',
description: 'Group details',
properties: GROUP_OUTPUT_PROPERTIES,
},
},
}
+71
View File
@@ -0,0 +1,71 @@
import type {
MicrosoftAdGetUserParams,
MicrosoftAdGetUserResponse,
} from '@/tools/microsoft_ad/types'
import { USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const getUserTool: ToolConfig<MicrosoftAdGetUserParams, MicrosoftAdGetUserResponse> = {
id: 'microsoft_ad_get_user',
name: 'Get Azure AD User',
description: 'Get a user by ID or user principal name from Azure AD',
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 (e.g., "user@example.com")',
},
},
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)}?$select=id,displayName,givenName,surname,userPrincipalName,mail,jobTitle,department,officeLocation,mobilePhone,accountEnabled`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const user = await response.json()
return {
success: true,
output: {
user: {
id: user.id ?? null,
displayName: user.displayName ?? null,
givenName: user.givenName ?? null,
surname: user.surname ?? null,
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,
},
},
}
},
outputs: {
user: {
type: 'object',
description: 'User details',
properties: USER_OUTPUT_PROPERTIES,
},
},
}
+27
View File
@@ -0,0 +1,27 @@
import { addGroupMemberTool } from '@/tools/microsoft_ad/add_group_member'
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 { getGroupTool } from '@/tools/microsoft_ad/get_group'
import { getUserTool } from '@/tools/microsoft_ad/get_user'
import { listGroupMembersTool } from '@/tools/microsoft_ad/list_group_members'
import { listGroupsTool } from '@/tools/microsoft_ad/list_groups'
import { listUsersTool } from '@/tools/microsoft_ad/list_users'
import { removeGroupMemberTool } from '@/tools/microsoft_ad/remove_group_member'
import { updateGroupTool } from '@/tools/microsoft_ad/update_group'
import { updateUserTool } from '@/tools/microsoft_ad/update_user'
export const microsoftAdListUsersTool = listUsersTool
export const microsoftAdGetUserTool = getUserTool
export const microsoftAdCreateUserTool = createUserTool
export const microsoftAdUpdateUserTool = updateUserTool
export const microsoftAdDeleteUserTool = deleteUserTool
export const microsoftAdListGroupsTool = listGroupsTool
export const microsoftAdGetGroupTool = getGroupTool
export const microsoftAdCreateGroupTool = createGroupTool
export const microsoftAdUpdateGroupTool = updateGroupTool
export const microsoftAdDeleteGroupTool = deleteGroupTool
export const microsoftAdListGroupMembersTool = listGroupMembersTool
export const microsoftAdAddGroupMemberTool = addGroupMemberTool
export const microsoftAdRemoveGroupMemberTool = removeGroupMemberTool
@@ -0,0 +1,78 @@
import type {
MicrosoftAdListGroupMembersParams,
MicrosoftAdListGroupMembersResponse,
} from '@/tools/microsoft_ad/types'
import { MEMBER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const listGroupMembersTool: ToolConfig<
MicrosoftAdListGroupMembersParams,
MicrosoftAdListGroupMembersResponse
> = {
id: 'microsoft_ad_list_group_members',
name: 'List Azure AD Group Members',
description: 'List members of a group in Azure AD (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',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID',
},
top: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of members to return (default 100, max 999)',
},
},
request: {
url: (params) => {
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
const queryParts = ['$select=id,displayName,mail']
if (params.top) queryParts.push(`$top=${params.top}`)
return `https://graph.microsoft.com/v1.0/groups/${encodeURIComponent(groupId)}/members?${queryParts.join('&')}`
},
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: 'List of group members',
properties: MEMBER_OUTPUT_PROPERTIES,
},
memberCount: { type: 'number', description: 'Number of members returned' },
},
}
+100
View File
@@ -0,0 +1,100 @@
import type {
MicrosoftAdListGroupsParams,
MicrosoftAdListGroupsResponse,
} from '@/tools/microsoft_ad/types'
import { GROUP_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const listGroupsTool: ToolConfig<
MicrosoftAdListGroupsParams,
MicrosoftAdListGroupsResponse
> = {
id: 'microsoft_ad_list_groups',
name: 'List Azure AD Groups',
description: 'List groups in Azure AD (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 groups to return (default 100, max 999)',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'OData filter expression (e.g., "securityEnabled eq true")',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search string to filter groups by displayName or description',
},
},
request: {
url: (params) => {
const queryParts: string[] = []
queryParts.push(
'$select=id,displayName,description,mail,mailEnabled,mailNickname,securityEnabled,groupTypes,visibility,createdDateTime'
)
if (params.top) queryParts.push(`$top=${params.top}`)
if (params.search && params.filter) {
throw new Error('$search and $filter cannot be used together in Microsoft Graph API')
}
if (params.filter) queryParts.push(`$filter=${encodeURIComponent(params.filter)}`)
if (params.search) {
queryParts.push(`$search="${encodeURIComponent(params.search)}"`)
queryParts.push('$count=true')
}
return `https://graph.microsoft.com/v1.0/groups?${queryParts.join('&')}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
ConsistencyLevel: 'eventual',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const groups = (data.value ?? []).map((group: Record<string, unknown>) => ({
id: group.id ?? null,
displayName: group.displayName ?? null,
description: group.description ?? null,
mail: group.mail ?? null,
mailEnabled: group.mailEnabled ?? null,
mailNickname: group.mailNickname ?? null,
securityEnabled: group.securityEnabled ?? null,
groupTypes: group.groupTypes ?? [],
visibility: group.visibility ?? null,
createdDateTime: group.createdDateTime ?? null,
}))
return {
success: true,
output: {
groups,
groupCount: groups.length,
},
}
},
outputs: {
groups: {
type: 'array',
description: 'List of groups',
properties: GROUP_OUTPUT_PROPERTIES,
},
groupCount: { type: 'number', description: 'Number of groups returned' },
},
}
+98
View File
@@ -0,0 +1,98 @@
import type {
MicrosoftAdListUsersParams,
MicrosoftAdListUsersResponse,
} from '@/tools/microsoft_ad/types'
import { USER_OUTPUT_PROPERTIES } from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const listUsersTool: ToolConfig<MicrosoftAdListUsersParams, MicrosoftAdListUsersResponse> = {
id: 'microsoft_ad_list_users',
name: 'List Azure AD Users',
description: 'List users in Azure AD (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 users to return (default 100, max 999)',
},
filter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'OData filter expression (e.g., "department eq \'Sales\'")',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search string to filter users by displayName or mail',
},
},
request: {
url: (params) => {
const queryParts: string[] = []
queryParts.push(
'$select=id,displayName,givenName,surname,userPrincipalName,mail,jobTitle,department,officeLocation,mobilePhone,accountEnabled'
)
if (params.top) queryParts.push(`$top=${params.top}`)
if (params.search && params.filter) {
throw new Error('$search and $filter cannot be used together in Microsoft Graph API')
}
if (params.filter) queryParts.push(`$filter=${encodeURIComponent(params.filter)}`)
if (params.search) {
queryParts.push(`$search="${encodeURIComponent(params.search)}"`)
queryParts.push('$count=true')
}
return `https://graph.microsoft.com/v1.0/users?${queryParts.join('&')}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
ConsistencyLevel: 'eventual',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
const users = (data.value ?? []).map((user: Record<string, unknown>) => ({
id: user.id ?? null,
displayName: user.displayName ?? null,
givenName: user.givenName ?? null,
surname: user.surname ?? null,
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,
}))
return {
success: true,
output: {
users,
userCount: users.length,
},
}
},
outputs: {
users: {
type: 'array',
description: 'List of users',
properties: USER_OUTPUT_PROPERTIES,
},
userCount: { type: 'number', description: 'Number of users returned' },
},
}
@@ -0,0 +1,68 @@
import type {
MicrosoftAdRemoveGroupMemberParams,
MicrosoftAdRemoveGroupMemberResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const removeGroupMemberTool: ToolConfig<
MicrosoftAdRemoveGroupMemberParams,
MicrosoftAdRemoveGroupMemberResponse
> = {
id: 'microsoft_ad_remove_group_member',
name: 'Remove Azure AD Group Member',
description: 'Remove a member from a group in Azure AD (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',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID',
},
memberId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'User ID of the member to remove',
},
},
request: {
url: (params) => {
const groupId = params.groupId?.trim()
const memberId = params.memberId?.trim()
if (!groupId) throw new Error('Group ID is required')
if (!memberId) throw new Error('Member ID is required')
return `https://graph.microsoft.com/v1.0/groups/${encodeURIComponent(groupId)}/members/${encodeURIComponent(memberId)}/$ref`
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (_response: Response, params?: MicrosoftAdRemoveGroupMemberParams) => {
return {
success: true,
output: {
removed: true,
groupId: params?.groupId ?? '',
memberId: params?.memberId ?? '',
},
}
},
outputs: {
removed: { type: 'boolean', description: 'Whether the member was removed successfully' },
groupId: { type: 'string', description: 'Group ID' },
memberId: { type: 'string', description: 'Member ID that was removed' },
},
}
+230
View File
@@ -0,0 +1,230 @@
import type { OutputProperty, ToolResponse } from '@/tools/types'
export interface MicrosoftAdBaseParams {
accessToken: string
}
export interface MicrosoftAdListUsersParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
search?: string
}
export interface MicrosoftAdGetUserParams extends MicrosoftAdBaseParams {
userId: string
}
export interface MicrosoftAdCreateUserParams extends MicrosoftAdBaseParams {
displayName: string
mailNickname: string
userPrincipalName: string
password: string
accountEnabled: boolean
givenName?: string
surname?: string
jobTitle?: string
department?: string
officeLocation?: string
mobilePhone?: string
}
export interface MicrosoftAdUpdateUserParams extends MicrosoftAdBaseParams {
userId: string
displayName?: string
givenName?: string
surname?: string
jobTitle?: string
department?: string
officeLocation?: string
mobilePhone?: string
accountEnabled?: boolean
}
export interface MicrosoftAdDeleteUserParams extends MicrosoftAdBaseParams {
userId: string
}
export interface MicrosoftAdListGroupsParams extends MicrosoftAdBaseParams {
top?: number
filter?: string
search?: string
}
export interface MicrosoftAdGetGroupParams extends MicrosoftAdBaseParams {
groupId: string
}
export interface MicrosoftAdCreateGroupParams extends MicrosoftAdBaseParams {
displayName: string
mailNickname: string
description?: string
mailEnabled: boolean
securityEnabled: boolean
groupTypes?: string
visibility?: string
}
export interface MicrosoftAdUpdateGroupParams extends MicrosoftAdBaseParams {
groupId: string
displayName?: string
description?: string
mailNickname?: string
visibility?: string
}
export interface MicrosoftAdDeleteGroupParams extends MicrosoftAdBaseParams {
groupId: string
}
export interface MicrosoftAdListGroupMembersParams extends MicrosoftAdBaseParams {
groupId: string
top?: number
}
export interface MicrosoftAdAddGroupMemberParams extends MicrosoftAdBaseParams {
groupId: string
memberId: string
}
export interface MicrosoftAdRemoveGroupMemberParams extends MicrosoftAdBaseParams {
groupId: string
memberId: string
}
export const 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' },
department: { type: 'string', description: 'Department' },
officeLocation: { type: 'string', description: 'Office location' },
mobilePhone: { type: 'string', description: 'Mobile phone number' },
accountEnabled: { type: 'boolean', description: 'Whether the account is enabled' },
} as const satisfies Record<string, OutputProperty>
export const GROUP_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Group ID' },
displayName: { type: 'string', description: 'Display name' },
description: { type: 'string', description: 'Group description' },
mail: { type: 'string', description: 'Email address' },
mailEnabled: { type: 'boolean', description: 'Whether mail is enabled' },
mailNickname: { type: 'string', description: 'Mail nickname' },
securityEnabled: { type: 'boolean', description: 'Whether security is enabled' },
groupTypes: { type: 'array', description: 'Group types' },
visibility: { type: 'string', description: 'Group visibility' },
createdDateTime: { type: 'string', description: 'Creation date' },
} as const satisfies Record<string, OutputProperty>
export const MEMBER_OUTPUT_PROPERTIES = {
id: { type: 'string', description: 'Member ID' },
displayName: { type: 'string', description: 'Display name' },
mail: { type: 'string', description: 'Email address' },
odataType: { type: 'string', description: 'Directory object type' },
} as const satisfies Record<string, OutputProperty>
export interface MicrosoftAdListUsersResponse extends ToolResponse {
output: {
users: Array<Record<string, unknown>>
userCount: number
}
}
export interface MicrosoftAdGetUserResponse extends ToolResponse {
output: {
user: Record<string, unknown>
}
}
export interface MicrosoftAdCreateUserResponse extends ToolResponse {
output: {
user: Record<string, unknown>
}
}
export interface MicrosoftAdUpdateUserResponse extends ToolResponse {
output: {
updated: boolean
userId: string
}
}
export interface MicrosoftAdDeleteUserResponse extends ToolResponse {
output: {
deleted: boolean
userId: string
}
}
export interface MicrosoftAdListGroupsResponse extends ToolResponse {
output: {
groups: Array<Record<string, unknown>>
groupCount: number
}
}
export interface MicrosoftAdGetGroupResponse extends ToolResponse {
output: {
group: Record<string, unknown>
}
}
export interface MicrosoftAdCreateGroupResponse extends ToolResponse {
output: {
group: Record<string, unknown>
}
}
export interface MicrosoftAdUpdateGroupResponse extends ToolResponse {
output: {
updated: boolean
groupId: string
}
}
export interface MicrosoftAdDeleteGroupResponse extends ToolResponse {
output: {
deleted: boolean
groupId: string
}
}
export interface MicrosoftAdListGroupMembersResponse extends ToolResponse {
output: {
members: Array<Record<string, unknown>>
memberCount: number
}
}
export interface MicrosoftAdAddGroupMemberResponse extends ToolResponse {
output: {
added: boolean
groupId: string
memberId: string
}
}
export interface MicrosoftAdRemoveGroupMemberResponse extends ToolResponse {
output: {
removed: boolean
groupId: string
memberId: string
}
}
export type MicrosoftAdResponse =
| MicrosoftAdListUsersResponse
| MicrosoftAdGetUserResponse
| MicrosoftAdCreateUserResponse
| MicrosoftAdUpdateUserResponse
| MicrosoftAdDeleteUserResponse
| MicrosoftAdListGroupsResponse
| MicrosoftAdGetGroupResponse
| MicrosoftAdCreateGroupResponse
| MicrosoftAdUpdateGroupResponse
| MicrosoftAdDeleteGroupResponse
| MicrosoftAdListGroupMembersResponse
| MicrosoftAdAddGroupMemberResponse
| MicrosoftAdRemoveGroupMemberResponse
@@ -0,0 +1,91 @@
import type {
MicrosoftAdUpdateGroupParams,
MicrosoftAdUpdateGroupResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const updateGroupTool: ToolConfig<
MicrosoftAdUpdateGroupParams,
MicrosoftAdUpdateGroupResponse
> = {
id: 'microsoft_ad_update_group',
name: 'Update Azure AD Group',
description: 'Update group properties in Azure AD (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',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Group ID',
},
displayName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Display name',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Group description',
},
mailNickname: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Mail alias',
},
visibility: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Group visibility: "Private" or "Public"',
},
},
request: {
url: (params) => {
const groupId = params.groupId?.trim()
if (!groupId) throw new Error('Group ID is required')
return `https://graph.microsoft.com/v1.0/groups/${encodeURIComponent(groupId)}`
},
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.displayName) body.displayName = params.displayName
if (params.description) body.description = params.description
if (params.mailNickname) body.mailNickname = params.mailNickname
if (params.visibility) body.visibility = params.visibility
return body
},
},
transformResponse: async (_response: Response, params?: MicrosoftAdUpdateGroupParams) => {
return {
success: true,
output: {
updated: true,
groupId: params?.groupId ?? '',
},
}
},
outputs: {
updated: { type: 'boolean', description: 'Whether the update was successful' },
groupId: { type: 'string', description: 'ID of the updated group' },
},
}
+119
View File
@@ -0,0 +1,119 @@
import type {
MicrosoftAdUpdateUserParams,
MicrosoftAdUpdateUserResponse,
} from '@/tools/microsoft_ad/types'
import type { ToolConfig } from '@/tools/types'
export const updateUserTool: ToolConfig<
MicrosoftAdUpdateUserParams,
MicrosoftAdUpdateUserResponse
> = {
id: 'microsoft_ad_update_user',
name: 'Update Azure AD User',
description: 'Update user properties in Azure AD (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',
},
displayName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Display name',
},
givenName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'First name',
},
surname: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Last name',
},
jobTitle: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Job title',
},
department: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Department',
},
officeLocation: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Office location',
},
mobilePhone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Mobile phone number',
},
accountEnabled: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the account is enabled',
},
},
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 body: Record<string, unknown> = {}
if (params.displayName) body.displayName = params.displayName
if (params.givenName) body.givenName = params.givenName
if (params.surname) body.surname = params.surname
if (params.jobTitle) body.jobTitle = params.jobTitle
if (params.department) body.department = params.department
if (params.officeLocation) body.officeLocation = params.officeLocation
if (params.mobilePhone) body.mobilePhone = params.mobilePhone
if (params.accountEnabled !== undefined) body.accountEnabled = params.accountEnabled
return body
},
},
transformResponse: async (_response: Response, params?: MicrosoftAdUpdateUserParams) => {
return {
success: true,
output: {
updated: true,
userId: params?.userId ?? '',
},
}
},
outputs: {
updated: { type: 'boolean', description: 'Whether the update was successful' },
userId: { type: 'string', description: 'ID of the updated user' },
},
}
+28
View File
@@ -1440,6 +1440,21 @@ import {
} from '@/tools/mailgun'
import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from '@/tools/mem0'
import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory'
import {
microsoftAdAddGroupMemberTool,
microsoftAdCreateGroupTool,
microsoftAdCreateUserTool,
microsoftAdDeleteGroupTool,
microsoftAdDeleteUserTool,
microsoftAdGetGroupTool,
microsoftAdGetUserTool,
microsoftAdListGroupMembersTool,
microsoftAdListGroupsTool,
microsoftAdListUsersTool,
microsoftAdRemoveGroupMemberTool,
microsoftAdUpdateGroupTool,
microsoftAdUpdateUserTool,
} from '@/tools/microsoft_ad'
import {
dataverseAssociateTool,
dataverseCreateMultipleTool,
@@ -3920,6 +3935,19 @@ export const tools: Record<string, ToolConfig> = {
datadog_list_downtimes: datadogListDowntimesTool,
datadog_cancel_downtime: datadogCancelDowntimeTool,
openai_image: openAIImageTool,
microsoft_ad_list_users: microsoftAdListUsersTool,
microsoft_ad_get_user: microsoftAdGetUserTool,
microsoft_ad_create_user: microsoftAdCreateUserTool,
microsoft_ad_update_user: microsoftAdUpdateUserTool,
microsoft_ad_delete_user: microsoftAdDeleteUserTool,
microsoft_ad_list_groups: microsoftAdListGroupsTool,
microsoft_ad_get_group: microsoftAdGetGroupTool,
microsoft_ad_create_group: microsoftAdCreateGroupTool,
microsoft_ad_update_group: microsoftAdUpdateGroupTool,
microsoft_ad_delete_group: microsoftAdDeleteGroupTool,
microsoft_ad_list_group_members: microsoftAdListGroupMembersTool,
microsoft_ad_add_group_member: microsoftAdAddGroupMemberTool,
microsoft_ad_remove_group_member: microsoftAdRemoveGroupMemberTool,
microsoft_teams_read_chat: microsoftTeamsReadChatTool,
microsoft_teams_write_chat: microsoftTeamsWriteChatTool,
microsoft_teams_read_channel: microsoftTeamsReadChannelTool,