Skip to main content
Glama

Workspace ONE UEM MCP Server

by XuyangZhang0
server.py20 kB
""" Workspace ONE UEM MCP Server This MCP server provides access to commonly used Workspace ONE UEM APIs for device management, user management, and system operations. Authentication supports both OAuth2 and Basic Auth methods. """ from fastmcp import FastMCP import httpx from typing import Optional, Literal import os from datetime import datetime, timedelta # Initialize FastMCP server mcp = FastMCP("Workspace ONE UEM") # Global configuration BASE_URL = os.getenv("WS1_UEM_BASE_URL", "") # e.g., https://cn1506.awmdm.com API_KEY = os.getenv("WS1_UEM_API_KEY", "") # Tenant code CLIENT_ID = os.getenv("WS1_UEM_CLIENT_ID", "") CLIENT_SECRET = os.getenv("WS1_UEM_CLIENT_SECRET", "") TOKEN_URL = os.getenv("WS1_UEM_TOKEN_URL", "") # e.g., https://na.uemauth.vmwservices.com/connect/token USERNAME = os.getenv("WS1_UEM_USERNAME", "") PASSWORD = os.getenv("WS1_UEM_PASSWORD", "") # Token cache _access_token = None _token_expiry = None async def get_oauth_token() -> str: """Get or refresh OAuth access token.""" global _access_token, _token_expiry # Return cached token if still valid if _access_token and _token_expiry and datetime.now() < _token_expiry: return _access_token # Get new token async with httpx.AsyncClient() as client: response = await client.post( TOKEN_URL, data={ "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, }, headers={"Content-Type": "application/x-www-form-urlencoded"}, ) response.raise_for_status() data = response.json() _access_token = data["access_token"] # Set expiry with 5 minute buffer expires_in = data.get("expires_in", 3600) _token_expiry = datetime.now() + timedelta(seconds=expires_in - 300) return _access_token async def make_request( method: str, endpoint: str, use_oauth: bool = True, api_version: str = "2", **kwargs ) -> dict: """ Make an authenticated request to Workspace ONE UEM API. Args: method: HTTP method (GET, POST, PUT, DELETE) endpoint: API endpoint path (e.g., '/api/mdm/devices/search') use_oauth: Use OAuth if True, Basic Auth if False api_version: API version (1 or 2) **kwargs: Additional arguments for httpx request """ url = f"{BASE_URL}{endpoint}" headers = { "Accept": f"application/json;version={api_version}", "Content-Type": "application/json", "aw-tenant-code": API_KEY, } if use_oauth and TOKEN_URL: token = await get_oauth_token() headers["Authorization"] = f"Bearer {token}" elif USERNAME and PASSWORD: # Use Basic Auth pass # httpx will handle this with auth parameter async with httpx.AsyncClient() as client: if use_oauth or not (USERNAME and PASSWORD): response = await client.request(method, url, headers=headers, **kwargs) else: response = await client.request( method, url, headers=headers, auth=(USERNAME, PASSWORD), **kwargs ) response.raise_for_status() # Some endpoints return empty responses if response.status_code == 204 or not response.content: return {"status": "success"} return response.json() # =========================== # Device Management (MDM) APIs # =========================== @mcp.tool() async def search_devices( search_by: Optional[Literal["Serialnumber", "Udid", "Macaddress", "ImeiNumber", "EasId"]] = None, search_value: Optional[str] = None, user: Optional[str] = None, model: Optional[str] = None, platform: Optional[str] = None, last_seen_days: Optional[int] = None, ownership: Optional[Literal["C", "E", "S"]] = None, lgid: Optional[int] = None, page: int = 0, page_size: int = 500, ) -> dict: """ Search for devices in Workspace ONE UEM. This is one of the most commonly used APIs for finding and listing devices. You can search by specific identifiers or use filters to find groups of devices. Args: search_by: Type of search (Serialnumber, Udid, Macaddress, ImeiNumber, EasId) search_value: Value to search for (required if search_by is specified) user: Filter by username model: Filter by device model platform: Filter by platform (e.g., Apple, Android, WindowsPC) last_seen_days: Filter devices seen within this many days ownership: Filter by ownership type (C=Corporate, E=Employee, S=Shared) lgid: Filter by Location Group ID (Organization Group) page: Page number for pagination page_size: Number of results per page (max 500) Returns: Device search results with device details Examples: # Search by serial number search_devices(search_by="Serialnumber", search_value="C02ABC123") # Find all Apple devices search_devices(platform="Apple") # Find devices not seen in 30 days search_devices(last_seen_days=30) """ params = {"page": page, "pagesize": page_size} if search_by and search_value: params["searchby"] = search_by params["id"] = search_value if user: params["user"] = user if model: params["model"] = model if platform: params["platform"] = platform if last_seen_days: params["lastseen"] = last_seen_days if ownership: params["ownership"] = ownership if lgid: params["lgid"] = lgid return await make_request("GET", "/api/mdm/devices/search", params=params) @mcp.tool() async def get_device_details( search_by: Literal["Serialnumber", "Udid", "Macaddress", "ImeiNumber", "EasId", "DeviceId"], search_value: str, ) -> dict: """ Get detailed information about a specific device. This returns comprehensive device information including hardware details, OS version, compliance status, enrollment info, network details, and more. Args: search_by: Type of identifier to search by search_value: The identifier value Returns: Detailed device information Examples: # Get device by serial number get_device_details(search_by="Serialnumber", search_value="C02ABC123") # Get device by UDID get_device_details(search_by="Udid", search_value="12345678-ABCD...") """ return await make_request( "GET", f"/api/mdm/devices", params={"searchby": search_by, "id": search_value} ) @mcp.tool() async def send_device_command( command: Literal[ "DeviceLock", "DeviceWipe", "EnterpriseWipe", "ClearPasscode", "DeviceQuery", "SyncDevice", "RestartDevice", "ShutDownDevice" ], search_by: Literal["Serialnumber", "Udid", "Macaddress", "ImeiNumber", "DeviceId"], search_value: str, ) -> dict: """ Send a command to a specific device. Common commands include lock, wipe, sync, restart, and device query. Use with caution for destructive commands like DeviceWipe. Args: command: The command to send search_by: Type of identifier search_value: The identifier value Returns: Command execution result Examples: # Lock a device send_device_command("DeviceLock", "Serialnumber", "C02ABC123") # Request device info sync send_device_command("DeviceQuery", "Serialnumber", "C02ABC123") """ return await make_request( "POST", f"/api/mdm/devices/commands", params={ "command": command, "searchby": search_by, "id": search_value } ) @mcp.tool() async def bulk_device_command( command: Literal[ "DeviceLock", "DeviceWipe", "EnterpriseWipe", "DeviceQuery", "SyncDevice" ], serial_numbers: list[str], ) -> dict: """ Send a command to multiple devices at once. This is much more efficient than sending individual commands when you need to perform the same action on multiple devices. Args: command: The command to send to all devices serial_numbers: List of device serial numbers Returns: Bulk command result with accepted and failed items Example: bulk_device_command("DeviceQuery", ["SN001", "SN002", "SN003"]) """ return await make_request( "POST", f"/api/mdm/devices/commands/bulk", params={"command": command, "searchby": "Serialnumber"}, json={"BulkValues": {"Value": serial_numbers}} ) @mcp.tool() async def get_device_compliance( search_by: Literal["Serialnumber", "Udid", "Macaddress", "ImeiNumber", "DeviceId"], search_value: str, ) -> dict: """ Get compliance status and details for a specific device. Returns information about whether the device meets compliance policies, which policies are violated, and compliance history. Args: search_by: Type of identifier search_value: The identifier value Returns: Device compliance information """ return await make_request( "GET", f"/api/mdm/devices/compliance", params={"searchby": search_by, "id": search_value} ) @mcp.tool() async def get_device_profiles(device_id: int) -> dict: """ Get all profiles assigned to a specific device. Shows configuration profiles, restrictions, and settings applied to the device. Args: device_id: The UEM device ID (obtained from device search/details) Returns: List of profiles assigned to the device """ return await make_request("GET", f"/api/mdm/devices/{device_id}/profiles") # =========================== # Organization Group (System) APIs # =========================== @mcp.tool() async def search_organization_groups( name: Optional[str] = None, group_id: Optional[str] = None, type: Optional[str] = None, ) -> dict: """ Search for organization groups (OGs) in Workspace ONE UEM. Organization groups are used to organize devices, users, and content in a hierarchical structure. Args: name: Search by organization group name group_id: Search by group ID type: Filter by group type Returns: Organization group search results Example: search_organization_groups(name="Sales Department") """ params = {} if name: params["name"] = name if group_id: params["groupid"] = group_id if type: params["type"] = type return await make_request("GET", "/api/system/groups/search", params=params) @mcp.tool() async def get_organization_group_details(og_id: int) -> dict: """ Get detailed information about a specific organization group. Args: og_id: The organization group ID Returns: Organization group details """ return await make_request("GET", f"/api/system/groups/{og_id}") # =========================== # User Management (System) APIs # =========================== @mcp.tool() async def search_users( firstname: Optional[str] = None, lastname: Optional[str] = None, email: Optional[str] = None, username: Optional[str] = None, organizationgroup_id: Optional[int] = None, page: int = 0, page_size: int = 500, ) -> dict: """ Search for users in Workspace ONE UEM. Args: firstname: Filter by first name lastname: Filter by last name email: Filter by email address username: Filter by username organizationgroup_id: Filter by organization group page: Page number for pagination page_size: Number of results per page Returns: User search results Example: search_users(email="john.doe@company.com") """ params = {"page": page, "pagesize": page_size} if firstname: params["firstname"] = firstname if lastname: params["lastname"] = lastname if email: params["email"] = email if username: params["username"] = username if organizationgroup_id: params["organizationgroupid"] = organizationgroup_id return await make_request("GET", "/api/system/users/search", params=params) @mcp.tool() async def get_user_details(user_id: int) -> dict: """ Get detailed information about a specific user. Args: user_id: The UEM user ID Returns: User details including enrollment info and assigned devices """ return await make_request("GET", f"/api/system/users/{user_id}") @mcp.tool() async def get_user_devices(user_id: int) -> dict: """ Get all devices enrolled by a specific user. Args: user_id: The UEM user ID Returns: List of devices enrolled by the user """ return await make_request("GET", f"/api/system/users/{user_id}/devices") # =========================== # Tag Management (System) APIs # =========================== @mcp.tool() async def get_tags(organization_group_id: int) -> dict: """ Get all tags available in a specific organization group. Tags are commonly used to categorize and organize devices for automation, assignment, and reporting purposes. Args: organization_group_id: The organization group ID Returns: List of available tags Example: get_tags(organization_group_id=123) """ return await make_request( "GET", f"/api/system/groups/{organization_group_id}/tags" ) @mcp.tool() async def add_device_tag(device_id: int, tag_id: int) -> dict: """ Add a tag to a specific device. Args: device_id: The UEM device ID tag_id: The tag ID to add Returns: Operation result """ return await make_request( "POST", f"/api/mdm/devices/{device_id}/tags/{tag_id}" ) @mcp.tool() async def remove_device_tag(device_id: int, tag_id: int) -> dict: """ Remove a tag from a specific device. Args: device_id: The UEM device ID tag_id: The tag ID to remove Returns: Operation result """ return await make_request( "DELETE", f"/api/mdm/devices/{device_id}/tags/{tag_id}" ) # =========================== # Application Management (MAM) APIs # =========================== @mcp.tool() async def search_apps( application_name: Optional[str] = None, bundle_id: Optional[str] = None, category: Optional[str] = None, platform: Optional[str] = None, status: Optional[str] = None, organization_group_id: Optional[int] = None, ) -> dict: """ Search for applications in Workspace ONE UEM. Args: application_name: Filter by application name bundle_id: Filter by bundle/package ID category: Filter by category platform: Filter by platform (Apple, Android, etc.) status: Filter by status (Active, Inactive, etc.) organization_group_id: Filter by organization group Returns: Application search results Example: search_apps(application_name="Microsoft Teams", platform="Apple") """ params = {} if application_name: params["applicationname"] = application_name if bundle_id: params["bundleid"] = bundle_id if category: params["category"] = category if platform: params["platform"] = platform if status: params["status"] = status if organization_group_id: params["organizationgroupid"] = organization_group_id return await make_request("GET", "/api/mam/apps/search", params=params) @mcp.tool() async def get_device_apps(device_id: int) -> dict: """ Get all applications installed on a specific device. Args: device_id: The UEM device ID Returns: List of installed applications """ return await make_request("GET", f"/api/mdm/devices/{device_id}/apps") # =========================== # Event and Audit (System) APIs # =========================== @mcp.tool() async def get_device_events( device_id: int, page: int = 0, page_size: int = 100, ) -> dict: """ Get event history for a specific device. Shows enrollment events, command history, compliance changes, and other important device lifecycle events. Args: device_id: The UEM device ID page: Page number for pagination page_size: Number of results per page Returns: Device event history """ return await make_request( "GET", f"/api/mdm/devices/{device_id}/events", params={"page": page, "pagesize": page_size} ) @mcp.tool() async def search_events( user: Optional[str] = None, module: Optional[str] = None, event_type: Optional[str] = None, start_date: Optional[str] = None, end_date: Optional[str] = None, page: int = 0, page_size: int = 500, ) -> dict: """ Search system-wide events and audit logs. Useful for tracking admin activities, system changes, and troubleshooting. Args: user: Filter by username module: Filter by module (e.g., Device, Application, Admin) event_type: Filter by event type start_date: Start date (YYYY-MM-DD format) end_date: End date (YYYY-MM-DD format) page: Page number page_size: Number of results per page Returns: Event search results Example: search_events(module="Device", start_date="2024-01-01", end_date="2024-01-31") """ params = {"page": page, "pagesize": page_size} if user: params["user"] = user if module: params["module"] = module if event_type: params["eventtype"] = event_type if start_date: params["startdate"] = start_date if end_date: params["enddate"] = end_date return await make_request("GET", "/api/system/events/search", params=params) # =========================== # Smart Groups APIs # =========================== @mcp.tool() async def get_smart_groups(organization_group_id: Optional[int] = None) -> dict: """ Get smart groups (assignment groups) from Workspace ONE UEM. Smart groups are dynamic groups of devices based on criteria like platform, ownership, tags, etc. They're used for targeted deployments. Args: organization_group_id: Filter by organization group (optional) Returns: List of smart groups """ params = {} if organization_group_id: params["organizationgroupid"] = organization_group_id return await make_request("GET", "/api/mdm/smartgroups/search", params=params) @mcp.tool() async def get_smart_group_devices(smart_group_id: int) -> dict: """ Get all devices in a specific smart group. Args: smart_group_id: The smart group ID Returns: List of devices in the smart group """ return await make_request( "GET", f"/api/mdm/smartgroups/{smart_group_id}/devices" ) # =========================== # Helper/Utility Tools # =========================== @mcp.tool() async def get_api_version() -> dict: """ Get Workspace ONE UEM API version information. Useful for verifying connectivity and API compatibility. Returns: API version details """ return await make_request("GET", "/api/system/info", api_version="1") if __name__ == "__main__": # Run the MCP server mcp.run()

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/XuyangZhang0/workspace-one-uem-mcp'

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