Skip to main content
Glama
intune-macos-handler.ts30.6 kB
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import { Client } from '@microsoft/microsoft-graph-client'; import { IntuneMacOSDeviceArgs, IntuneMacOSPolicyArgs, IntuneMacOSAppArgs, IntuneMacOSComplianceArgs } from '../types/intune-types.js'; // Intune macOS Device Management Handler export async function handleIntuneMacOSDevices( graphClient: Client, args: IntuneMacOSDeviceArgs ): Promise<{ content: { type: string; text: string }[] }> { let apiPath = ''; let result: any; switch (args.action) { case 'list': // List all macOS devices managed by Intune apiPath = '/deviceManagement/managedDevices'; const queryOptions: string[] = []; // Filter for macOS devices queryOptions.push(`$filter=operatingSystem eq 'macOS'`); if (args.filter) { queryOptions.push(`and ${args.filter}`); } if (queryOptions.length > 0) { apiPath += `?${queryOptions.join('')}`; } result = await graphClient.api(apiPath).get(); break; case 'get': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for get action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}`; result = await graphClient.api(apiPath).get(); break; case 'enroll': // Create enrollment invitation apiPath = '/deviceManagement/deviceEnrollmentConfigurations'; const enrollmentPayload = { displayName: 'macOS Device Enrollment', description: 'Automated macOS device enrollment', deviceEnrollmentConfigurationType: 'appleDeviceEnrollmentProgram', enableAuthenticationViaCompanyPortal: true, requireUserAuthentication: true, assignmentTarget: args.assignmentTarget }; result = await graphClient.api(apiPath).post(enrollmentPayload); break; case 'retire': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for retire action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}/retire`; result = await graphClient.api(apiPath).post({}); break; case 'wipe': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for wipe action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}/wipe`; const wipePayload = { keepEnrollmentData: false, keepUserData: false, macOsUnlockCode: '', // Optional unlock code for macOS persistEsimDataPlan: false }; result = await graphClient.api(apiPath).post(wipePayload); break; case 'restart': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for restart action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}/rebootNow`; result = await graphClient.api(apiPath).post({}); break; case 'sync': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for sync action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}/syncDevice`; result = await graphClient.api(apiPath).post({}); break; case 'remote_lock': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for remote_lock action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}/remoteLock`; result = await graphClient.api(apiPath).post({}); break; case 'collect_logs': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for collect_logs action'); } apiPath = `/deviceManagement/managedDevices/${args.deviceId}/createDeviceLogCollectionRequest`; const logCollectionPayload = { templateType: 'predefined' // or 'custom' }; result = await graphClient.api(apiPath).post(logCollectionPayload); break; default: throw new McpError(ErrorCode.InvalidParams, `Invalid action: ${args.action}`); } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } // Intune macOS Policy Management Handler export async function handleIntuneMacOSPolicies( graphClient: Client, args: IntuneMacOSPolicyArgs ): Promise<{ content: { type: string; text: string }[] }> { let apiPath = ''; let result: any; switch (args.action) { case 'list': // List policies based on type - using correct endpoints without invalid filters switch (args.policyType) { case 'Configuration': apiPath = '/deviceManagement/deviceConfigurations'; try { // First try with beta endpoint for more comprehensive results try { const allConfigs = await graphClient.api(apiPath).version('beta').get(); // Improved client-side filtering for macOS configuration policies const macOSPolicies = allConfigs.value?.filter((config: any) => { // Check for known macOS odata types if (config['@odata.type'] && ( config['@odata.type'].includes('macOS') || config['@odata.type'] === '#microsoft.graph.macOSCustomConfiguration' || config['@odata.type'] === '#microsoft.graph.macOSGeneralDeviceConfiguration' || config['@odata.type'] === '#microsoft.graph.macOSDeviceFeaturesConfiguration' || config['@odata.type'] === '#microsoft.graph.macOSEndpointProtectionConfiguration' )) { return true; } // Check display name and description for macOS indicators if ((config.displayName && ( config.displayName.toLowerCase().includes('mac') || config.displayName.toLowerCase().includes('macos'))) || (config.description && ( config.description.toLowerCase().includes('mac') || config.description.toLowerCase().includes('macos')))) { return true; } return false; }) || []; result = { ...allConfigs, value: macOSPolicies }; } catch (betaError) { // Fall back to v1.0 endpoint if beta fails console.log(`Beta API failed, falling back to v1.0: ${betaError}`); const allConfigs = await graphClient.api(apiPath).get(); // Same improved filtering logic for v1.0 const macOSPolicies = allConfigs.value?.filter((config: any) => { // Check for known macOS odata types if (config['@odata.type'] && ( config['@odata.type'].includes('macOS') || config['@odata.type'] === '#microsoft.graph.macOSCustomConfiguration' || config['@odata.type'] === '#microsoft.graph.macOSGeneralDeviceConfiguration' || config['@odata.type'] === '#microsoft.graph.macOSDeviceFeaturesConfiguration' )) { return true; } // Check display name and description for macOS indicators if ((config.displayName && ( config.displayName.toLowerCase().includes('mac') || config.displayName.toLowerCase().includes('macos'))) || (config.description && ( config.description.toLowerCase().includes('mac') || config.description.toLowerCase().includes('macos')))) { return true; } return false; }) || []; result = { ...allConfigs, value: macOSPolicies }; } // If no policies found, check configuration profiles endpoint as fallback if (result.value.length === 0) { try { apiPath = '/deviceManagement/configurationPolicies'; const configProfiles = await graphClient.api(apiPath).version('beta').get(); const macOSProfiles = configProfiles.value?.filter((profile: any) => profile.platforms?.includes('macOS') || profile.platforms?.includes('Mac') || (profile.name && ( profile.name.toLowerCase().includes('mac') || profile.name.toLowerCase().includes('macos'))) ) || []; result = { ...configProfiles, value: macOSProfiles }; } catch (profilesError) { console.log(`Configuration profiles endpoint failed: ${profilesError}`); // Keep the empty result from deviceConfigurations } } } catch (error) { throw new McpError(ErrorCode.InternalError, `Failed to fetch device configurations: ${error}`); } break; case 'Compliance': apiPath = '/deviceManagement/deviceCompliancePolicies'; try { // First try with beta endpoint for more comprehensive results try { const allPolicies = await graphClient.api(apiPath).version('beta').get(); // Improved client-side filtering for macOS compliance policies const macOSPolicies = allPolicies.value?.filter((policy: any) => { // Check for known macOS odata types if (policy['@odata.type'] && ( policy['@odata.type'].includes('macOS') || policy['@odata.type'] === '#microsoft.graph.macOSCompliancePolicy' )) { return true; } // Check display name and description for macOS indicators if ((policy.displayName && ( policy.displayName.toLowerCase().includes('mac') || policy.displayName.toLowerCase().includes('macos'))) || (policy.description && ( policy.description.toLowerCase().includes('mac') || policy.description.toLowerCase().includes('macos')))) { return true; } return false; }) || []; result = { ...allPolicies, value: macOSPolicies }; } catch (betaError) { // Fall back to v1.0 endpoint if beta fails console.log(`Beta API failed, falling back to v1.0: ${betaError}`); const allPolicies = await graphClient.api(apiPath).get(); // Same improved filtering logic for v1.0 const macOSPolicies = allPolicies.value?.filter((policy: any) => { // Check for known macOS odata types if (policy['@odata.type'] && ( policy['@odata.type'].includes('macOS') || policy['@odata.type'] === '#microsoft.graph.macOSCompliancePolicy' )) { return true; } // Check display name and description for macOS indicators if ((policy.displayName && ( policy.displayName.toLowerCase().includes('mac') || policy.displayName.toLowerCase().includes('macos'))) || (policy.description && ( policy.description.toLowerCase().includes('mac') || policy.description.toLowerCase().includes('macos')))) { return true; } return false; }) || []; result = { ...allPolicies, value: macOSPolicies }; } } catch (error) { throw new McpError(ErrorCode.InternalError, `Failed to fetch compliance policies: ${error}`); } break; case 'Security': // Use configuration policies for security settings apiPath = '/deviceManagement/configurationPolicies'; try { const allSecurityPolicies = await graphClient.api(apiPath).get(); // Client-side filtering for macOS security policies result = { ...allSecurityPolicies, value: allSecurityPolicies.value?.filter((policy: any) => policy.platforms?.includes('macOS') || policy.platforms?.includes('Mac') || policy.name?.toLowerCase().includes('macos') || policy.name?.toLowerCase().includes('mac') ) || [] }; } catch (error) { // Fallback to device configurations if configurationPolicies doesn't exist try { apiPath = '/deviceManagement/deviceConfigurations'; const allConfigs = await graphClient.api(apiPath).get(); result = { ...allConfigs, value: allConfigs.value?.filter((config: any) => config['@odata.type']?.includes('macOS') && (config.displayName?.toLowerCase().includes('security') || config.description?.toLowerCase().includes('security')) ) || [] }; } catch (fallbackError) { throw new McpError(ErrorCode.InternalError, `Failed to fetch security policies: ${fallbackError}`); } } break; case 'Update': // Try multiple potential endpoints for update policies try { // First try the beta endpoint for software update policies apiPath = '/deviceManagement/deviceConfigurations'; const allConfigs = await graphClient.api(apiPath).version('beta').get(); result = { ...allConfigs, value: allConfigs.value?.filter((config: any) => config['@odata.type'] === 'microsoft.graph.macOSSoftwareUpdateConfiguration' || config['@odata.type']?.includes('SoftwareUpdate') && config['@odata.type']?.includes('macOS') ) || [] }; } catch (error) { // Fallback to regular configurations try { apiPath = '/deviceManagement/deviceConfigurations'; const allConfigs = await graphClient.api(apiPath).get(); result = { ...allConfigs, value: allConfigs.value?.filter((config: any) => config.displayName?.toLowerCase().includes('update') && (config['@odata.type']?.includes('macOS') || config.displayName?.toLowerCase().includes('macos')) ) || [] }; } catch (fallbackError) { throw new McpError(ErrorCode.InternalError, `Failed to fetch update policies: ${fallbackError}`); } } break; case 'AppProtection': // Try app protection policies try { apiPath = '/deviceAppManagement/managedAppPolicies'; const allAppPolicies = await graphClient.api(apiPath).get(); result = { ...allAppPolicies, value: allAppPolicies.value?.filter((policy: any) => policy['@odata.type'] === 'microsoft.graph.macOSManagedAppProtection' || (policy.displayName?.toLowerCase().includes('macos') || policy.displayName?.toLowerCase().includes('mac')) ) || [] }; } catch (error) { throw new McpError(ErrorCode.InternalError, `Failed to fetch app protection policies: ${error}`); } break; default: throw new McpError(ErrorCode.InvalidParams, `Invalid policyType: ${args.policyType}`); } break; case 'get': if (!args.policyId) { throw new McpError(ErrorCode.InvalidParams, 'policyId is required for get action'); } switch (args.policyType) { case 'Configuration': apiPath = `/deviceManagement/deviceConfigurations/${args.policyId}`; break; case 'Compliance': apiPath = `/deviceManagement/deviceCompliancePolicies/${args.policyId}`; break; case 'Security': apiPath = `/deviceManagement/intents/${args.policyId}`; break; case 'Update': apiPath = `/deviceManagement/macOSSoftwareUpdatePolicies/${args.policyId}`; break; case 'AppProtection': apiPath = `/deviceAppManagement/macOSManagedAppProtections/${args.policyId}`; break; default: throw new McpError(ErrorCode.InvalidParams, `Get operation not supported for policyType: ${args.policyType}`); } result = await graphClient.api(apiPath).get(); break; case 'create': if (!args.name) { throw new McpError(ErrorCode.InvalidParams, 'name is required for create action'); } switch (args.policyType) { case 'Configuration': apiPath = '/deviceManagement/deviceConfigurations'; const configPayload = { '@odata.type': '#microsoft.graph.macOSCustomConfiguration', displayName: args.name, description: args.description || '', payloadFileName: args.settings?.customConfiguration?.payloadFileName || 'config.mobileconfig', payload: args.settings?.customConfiguration?.payload || '', platformType: 'macOS', version: 1 }; result = await graphClient.api(apiPath).post(configPayload); break; case 'Compliance': apiPath = '/deviceManagement/deviceCompliancePolicies'; const compliancePayload = { '@odata.type': '#microsoft.graph.macOSCompliancePolicy', displayName: args.name, description: args.description || '', platformType: 'macOS', passwordRequired: args.settings?.compliance?.passwordRequired ?? false, passwordMinimumLength: args.settings?.compliance?.passwordMinimumLength ?? 4, passwordMinutesOfInactivityBeforeLock: args.settings?.compliance?.passwordMinutesOfInactivityBeforeLock ?? 15, storageRequireEncryption: args.settings?.compliance?.storageRequireEncryption ?? true, osMinimumVersion: args.settings?.compliance?.osMinimumVersion, osMaximumVersion: args.settings?.compliance?.osMaximumVersion, systemIntegrityProtectionEnabled: args.settings?.compliance?.systemIntegrityProtectionEnabled ?? true, firewallEnabled: args.settings?.compliance?.firewallEnabled ?? true, gatekeeperAllowedAppSource: args.settings?.compliance?.gatekeeperAllowedAppSource ?? 'macAppStoreAndIdentifiedDevelopers' }; result = await graphClient.api(apiPath).post(compliancePayload); break; default: throw new McpError(ErrorCode.InvalidParams, `Create operation not supported for policyType: ${args.policyType}`); } break; case 'update': if (!args.policyId) { throw new McpError(ErrorCode.InvalidParams, 'policyId is required for update action'); } switch (args.policyType) { case 'Configuration': apiPath = `/deviceManagement/deviceConfigurations/${args.policyId}`; const updateConfigPayload = { displayName: args.name, description: args.description, payloadFileName: args.settings?.customConfiguration?.payloadFileName, payload: args.settings?.customConfiguration?.payload }; result = await graphClient.api(apiPath).patch(updateConfigPayload); break; case 'Compliance': apiPath = `/deviceManagement/deviceCompliancePolicies/${args.policyId}`; const updateCompliancePayload = { displayName: args.name, description: args.description, passwordRequired: args.settings?.compliance?.passwordRequired, passwordMinimumLength: args.settings?.compliance?.passwordMinimumLength, storageRequireEncryption: args.settings?.compliance?.storageRequireEncryption, osMinimumVersion: args.settings?.compliance?.osMinimumVersion, osMaximumVersion: args.settings?.compliance?.osMaximumVersion }; result = await graphClient.api(apiPath).patch(updateCompliancePayload); break; default: throw new McpError(ErrorCode.InvalidParams, `Update operation not supported for policyType: ${args.policyType}`); } break; case 'delete': if (!args.policyId) { throw new McpError(ErrorCode.InvalidParams, 'policyId is required for delete action'); } switch (args.policyType) { case 'Configuration': apiPath = `/deviceManagement/deviceConfigurations/${args.policyId}`; break; case 'Compliance': apiPath = `/deviceManagement/deviceCompliancePolicies/${args.policyId}`; break; default: throw new McpError(ErrorCode.InvalidParams, `Delete operation not supported for policyType: ${args.policyType}`); } await graphClient.api(apiPath).delete(); result = { message: `${args.policyType} policy deleted successfully` }; break; case 'assign': if (!args.policyId || !args.assignments) { throw new McpError(ErrorCode.InvalidParams, 'policyId and assignments are required for assign action'); } switch (args.policyType) { case 'Configuration': apiPath = `/deviceManagement/deviceConfigurations/${args.policyId}/assignments`; break; case 'Compliance': apiPath = `/deviceManagement/deviceCompliancePolicies/${args.policyId}/assignments`; break; default: throw new McpError(ErrorCode.InvalidParams, `Assign operation not supported for policyType: ${args.policyType}`); } const assignmentPayload = { assignments: args.assignments.map(assignment => ({ target: { '@odata.type': assignment.target.groupId ? '#microsoft.graph.groupAssignmentTarget' : '#microsoft.graph.allDevicesAssignmentTarget', groupId: assignment.target.groupId }, intent: assignment.intent || 'apply' })) }; result = await graphClient.api(apiPath).post(assignmentPayload); break; case 'deploy': if (!args.policyId) { throw new McpError(ErrorCode.InvalidParams, 'policyId is required for deploy action'); } // For deploy action, we'll assign to all devices or specified groups const deployApiPath = args.policyType === 'Configuration' ? `/deviceManagement/deviceConfigurations/${args.policyId}/assignments` : `/deviceManagement/deviceCompliancePolicies/${args.policyId}/assignments`; const deployPayload = { assignments: [{ target: { '@odata.type': '#microsoft.graph.allDevicesAssignmentTarget' }, intent: 'apply' }] }; result = await graphClient.api(deployApiPath).post(deployPayload); break; default: throw new McpError(ErrorCode.InvalidParams, `Invalid action: ${args.action}`); } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } // Intune macOS App Management Handler export async function handleIntuneMacOSApps( graphClient: Client, args: IntuneMacOSAppArgs ): Promise<{ content: { type: string; text: string }[] }> { let apiPath = ''; let result: any; switch (args.action) { case 'list': // List macOS applications apiPath = '/deviceAppManagement/mobileApps'; apiPath += `?$filter=deviceType eq 'macOS'`; if (args.appType) { apiPath += ` and microsoft.graph.${args.appType}`; } result = await graphClient.api(apiPath).get(); break; case 'get': if (!args.appId) { throw new McpError(ErrorCode.InvalidParams, 'appId is required for get action'); } apiPath = `/deviceAppManagement/mobileApps/${args.appId}`; result = await graphClient.api(apiPath).get(); break; case 'deploy': if (!args.appId || !args.assignment) { throw new McpError(ErrorCode.InvalidParams, 'appId and assignment are required for deploy action'); } apiPath = `/deviceAppManagement/mobileApps/${args.appId}/assignments`; const deploymentPayload = { mobileAppAssignments: args.assignment.groupIds.map(groupId => ({ target: { '@odata.type': '#microsoft.graph.groupAssignmentTarget', groupId: groupId }, intent: args.assignment?.installIntent || 'available', settings: { '@odata.type': '#microsoft.graph.macOSLobAppAssignmentSettings', uninstallOnDeviceRemoval: true } })) }; result = await graphClient.api(apiPath).post(deploymentPayload); break; case 'update': if (!args.appId) { throw new McpError(ErrorCode.InvalidParams, 'appId is required for update action'); } apiPath = `/deviceAppManagement/mobileApps/${args.appId}`; const updatePayload = { displayName: args.appInfo?.displayName, description: args.appInfo?.description, publisher: args.appInfo?.publisher, bundleId: args.appInfo?.bundleId, buildNumber: args.appInfo?.buildNumber, versionNumber: args.appInfo?.versionNumber, minimumSupportedOperatingSystem: args.appInfo?.minimumSupportedOperatingSystem }; result = await graphClient.api(apiPath).patch(updatePayload); break; case 'remove': if (!args.appId) { throw new McpError(ErrorCode.InvalidParams, 'appId is required for remove action'); } apiPath = `/deviceAppManagement/mobileApps/${args.appId}`; await graphClient.api(apiPath).delete(); result = { message: 'App removed successfully' }; break; case 'sync_status': if (!args.appId) { throw new McpError(ErrorCode.InvalidParams, 'appId is required for sync_status action'); } // Get app installation status across devices apiPath = `/deviceAppManagement/mobileApps/${args.appId}/deviceStatuses`; result = await graphClient.api(apiPath).get(); break; default: throw new McpError(ErrorCode.InvalidParams, `Invalid action: ${args.action}`); } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } // Intune macOS Compliance Monitoring Handler export async function handleIntuneMacOSCompliance( graphClient: Client, args: IntuneMacOSComplianceArgs ): Promise<{ content: { type: string; text: string }[] }> { let apiPath = ''; let result: any; switch (args.action) { case 'get_status': if (args.deviceId) { // Get compliance status for specific device apiPath = `/deviceManagement/managedDevices/${args.deviceId}/deviceCompliancePolicyStates`; } else { // Get overall compliance status for macOS devices apiPath = '/deviceManagement/deviceCompliancePolicyDeviceStateSummary'; apiPath += `?$filter=platformType eq 'macOS'`; } result = await graphClient.api(apiPath).get(); break; case 'get_details': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for get_details action'); } // Get detailed compliance information for device apiPath = `/deviceManagement/managedDevices/${args.deviceId}/deviceCompliancePolicyStates`; const complianceStates = await graphClient.api(apiPath).get(); // Get device configuration states const configApiPath = `/deviceManagement/managedDevices/${args.deviceId}/deviceConfigurationStates`; const configStates = await graphClient.api(configApiPath).get(); result = { deviceId: args.deviceId, compliancePolicyStates: complianceStates, configurationStates: configStates }; break; case 'update_policy': if (!args.policyId) { throw new McpError(ErrorCode.InvalidParams, 'policyId is required for update_policy action'); } apiPath = `/deviceManagement/deviceCompliancePolicies/${args.policyId}`; const updatePayload = { passwordRequired: args.complianceData?.passwordCompliant, storageRequireEncryption: args.complianceData?.encryptionCompliant, systemIntegrityProtectionEnabled: args.complianceData?.systemIntegrityCompliant, firewallEnabled: args.complianceData?.firewallCompliant }; result = await graphClient.api(apiPath).patch(updatePayload); break; case 'force_evaluation': if (!args.deviceId) { throw new McpError(ErrorCode.InvalidParams, 'deviceId is required for force_evaluation action'); } // Trigger compliance evaluation on device apiPath = `/deviceManagement/managedDevices/${args.deviceId}/syncDevice`; await graphClient.api(apiPath).post({}); // Also trigger compliance policy evaluation const evalApiPath = `/deviceManagement/managedDevices/${args.deviceId}/triggerConfigurationManagerAction`; await graphClient.api(evalApiPath).post({ action: { actionType: 'evaluateCompliance' } }); result = { message: 'Compliance evaluation triggered successfully' }; break; default: throw new McpError(ErrorCode.InvalidParams, `Invalid action: ${args.action}`); } return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DynamicEndpoints/m365-core-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server