soti-mcp
v0.1.0
Published
Model Context Protocol server for SOTI MobiControl — operate Android/iOS/Windows/rugged MDM fleets with AI agents (Claude Code, Cursor, Cline, Continue).
Maintainers
Readme
soti-mcp
Operate your SOTI MobiControl MDM fleet from any AI agent — Claude Code, Cursor, Cline, Continue. A Model Context Protocol (MCP) server that wraps the SOTI MobiControl REST API as a clean tool surface so an LLM can list, search, command, and reshape your Android / iOS / Windows / rugged device estate via natural language.
30-second demo
Note: A scripted GIF will be added in v0.2. In the meantime, here is the kind of dialogue this server enables:
You: "Show me devices in the field group with battery below 30% that haven't checked in for 6 hours."
Claude (calls
soti_search_devices): Found 4 devices: device-a, device-b, …You: "Lock down device-a in kiosk mode."
Claude (calls
soti_lockdown_device): DispatchedEnableKioskModeto device-a.
Install
Per-project (recommended for MCP clients):
npm install --save-dev soti-mcpGlobally (for direct CLI use):
npm install -g soti-mcpThis package ships an executable soti-mcp binary that speaks MCP over stdio out of the box.
Configure your AI client
Claude Code
Add to your project's .mcp.json (or your user-level ~/.claude.json):
{
"mcpServers": {
"soti": {
"command": "soti-mcp",
"env": {
"SOTI_MC_BASE_URL": "https://mc.example.com/MobiControl",
"SOTI_MC_CLIENT_ID": "your-client-id",
"SOTI_MC_CLIENT_SECRET": "your-client-secret"
}
}
}
}Cursor
~/.cursor/mcp.json (or workspace-level .cursor/mcp.json):
{
"mcpServers": {
"soti": {
"command": "soti-mcp",
"env": {
"SOTI_MC_BASE_URL": "https://mc.example.com/MobiControl",
"SOTI_MC_CLIENT_ID": "your-client-id",
"SOTI_MC_CLIENT_SECRET": "your-client-secret"
}
}
}
}Cline
In your VS Code settings.json:
{
"cline.mcpServers": {
"soti": {
"command": "soti-mcp",
"env": {
"SOTI_MC_BASE_URL": "https://mc.example.com/MobiControl",
"SOTI_MC_CLIENT_ID": "your-client-id",
"SOTI_MC_CLIENT_SECRET": "your-client-secret"
}
}
}
}Continue
~/.continue/config.json:
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "soti-mcp",
"env": {
"SOTI_MC_BASE_URL": "https://mc.example.com/MobiControl",
"SOTI_MC_CLIENT_ID": "your-client-id",
"SOTI_MC_CLIENT_SECRET": "your-client-secret"
}
}
}
]
}
}HTTP / SSE transport (remote consumers)
Set SOTI_MCP_HTTP_HOST and SOTI_MCP_HTTP_PORT to expose the server over Streamable HTTP. Useful for hosting the MCP server in your platform stack.
SOTI_MCP_HTTP_HOST=0.0.0.0 SOTI_MCP_HTTP_PORT=8765 soti-mcpTool reference
| Tool | Kind | Purpose |
|---|---|---|
| soti_list_devices | Read | Paginated device list, optionally filtered by group path or property filter. |
| soti_get_device | Read | Full details for a single device by ID, MAC, or IMEI. |
| soti_search_devices | Read | Advanced filter search (CONTAINS, =, !=, >, <, AND, OR). |
| soti_get_device_location | Read | Last known GPS / cellular fix for a device. |
| soti_get_device_apps | Read | Installed application list, paginated and orderable. |
| soti_list_groups | Read | Device group hierarchy. |
| soti_get_group_devices | Read | Members of a device group, recursing into subgroups. |
| soti_list_policies | Read | Configuration profiles (SOTI calls these profiles). |
| soti_list_alerts | Read | Active compliance / alert policies. |
| soti_get_compliance_report | Read | Compliance status for a device, group, or fleet. |
| soti_send_command | Action | Generic device action (CheckIn, Lock, Locate, RemoteRing, …). |
| soti_install_app | Action | Install a managed application on a device. |
| soti_uninstall_app | Action | Uninstall a managed application from a device. |
| soti_lockdown_device | Action | Enable kiosk lockdown. |
| soti_release_device | Action | Release kiosk lockdown. |
| soti_wipe_device | DESTRUCTIVE | Factory-reset / wipe. Requires confirm: true. |
| soti_assign_policy | Action | Assign a profile to groups, devices, or a filter expression. |
| soti_move_device | Action | Move a device to another group. |
Resources
| URI Template | Returns |
|---|---|
| soti://device/{id} | Full JSON snapshot of one device. |
| soti://group/{path}/devices | Devices that belong to a group (recurses into subgroups). |
| soti://compliance/report/{date} | Compliance roll-up snapshot. |
Tool result envelope
Every tool returns a JSON object in this shape:
{
"ok": true,
"data": [ … ],
"nextCursor": "…optional opaque cursor…"
}…or, on failure:
{
"ok": false,
"error": {
"code": "DEVICE_NOT_FOUND",
"message": "Device not found (GET /devices/abc)",
"hint": "Verify the device ID, or use mac:/imei_meid_esn: prefix.",
"upstreamStatus": 404
}
}Error codes form a stable taxonomy: AUTH_FAILED, DEVICE_NOT_FOUND, RATE_LIMITED, PERMISSION_DENIED, INVALID_PARAMS, UPSTREAM_ERROR, NETWORK_ERROR, NOT_SUPPORTED, MISSING_CONFIRM, TIMEOUT.
Example prompts
- "Show me offline devices in the warehouse group."
- "Lock down device A1B2 in kiosk mode."
- "List the 5 most recent compliance violations on Android Plus devices."
- "Move all devices in
\\Stores\Old\Pilotinto\\Stores\Production." - "Which devices are running the
com.acme.posapp at version below 4.2?" - "Send a check-in command to every device that hasn't reported in 24 hours."
The LLM picks the right tools and chains them together; you stay in chat.
Security notes
- Credentials — Store
SOTI_MC_CLIENT_SECRETin your MCP client's secrets store, not in.mcp.jsonif you can avoid it. The client ID and secret should be created in the MobiControl console (Administration → API Clients) with the minimum group-scope permissions your workflows require. - Scope minimization — SOTI MC enforces permissions per device group. Create a dedicated API client for the LLM with read-only or limited-action rights, and grant write actions only on the groups you actually need to mutate.
- Destructive operations —
soti_wipe_devicerequires explicitconfirm: true. The tool description marks itDESTRUCTIVEso MCP clients can highlight it.soti_send_commandrejectsWipe/FactoryResetand forces callers throughsoti_wipe_device. - Audit logging — SOTI MC logs every API call against the API client identity. Use a dedicated client for the LLM so its activity is auditable separately from human operators.
- Token storage — OAuth2 access tokens are held only in the running process memory; never written to disk. Logs redact
Authorizationheaders and access tokens. - Rate limit — A token-bucket limiter (default 10 req/sec) prevents the LLM from accidentally hammering your MobiControl Server. Tune via
SOTI_MC_RATE_LIMIT_RPS. - Network — All traffic to MobiControl is over HTTPS. Use a private network or reverse proxy if your MobiControl Server is exposed to the internet.
Compatibility matrix
| MobiControl version | Status | Notes |
|---|---|---|
| 16.x (cloud + on-prem) | ✅ tested | Full tool surface available. |
| 15.x | ✅ expected | All endpoints used by this MCP exist since v14. |
| 14.x | ⚠️ partial | soti_search_devices and soti_move_device require v14+. |
| 13.x | ⚠️ legacy | Most read tools work; soti_search_devices falls back to soti_list_devices. |
| ≤ 12.x | ❌ unsupported | API surface differs significantly. |
When an endpoint is missing on an older version the server returns NOT_SUPPORTED rather than a raw 404 — your LLM can adapt its plan.
Environment variables
| Variable | Required | Default | Notes |
|---|---|---|---|
| SOTI_MC_BASE_URL | yes | — | e.g. https://mc.example.com/MobiControl. Token endpoint resolves to {BASE_URL}/api/token. |
| SOTI_MC_CLIENT_ID | yes | — | OAuth2 client ID. |
| SOTI_MC_CLIENT_SECRET | yes | — | OAuth2 client secret. |
| SOTI_MC_GRANT_TYPE | no | client_credentials | Or password. |
| SOTI_MC_USERNAME | when password | — | Resource Owner Password grant only. |
| SOTI_MC_PASSWORD | when password | — | Resource Owner Password grant only. |
| SOTI_MC_RATE_LIMIT_RPS | no | 10 | Per-server-instance request budget. |
| SOTI_MC_LOG_LEVEL | no | info | trace / debug / info / warn / error. Logs go to stderr only. |
| SOTI_MCP_HTTP_HOST | no | — | If set with SOTI_MCP_HTTP_PORT, switches to Streamable HTTP transport. |
| SOTI_MCP_HTTP_PORT | no | — | TCP port for HTTP transport. |
Local development
git clone https://github.com/tomer-bdbd/soti-mcp.git
cd soti-mcp
npm install
cp .env.example .env # then edit with your sandbox credentials
npm run dev # runs from src/ via tsx
npm test # 40+ unit tests, no SOTI sandbox required
npm run test:coverage # enforces ≥80% linesA live smoke test against a real SOTI MobiControl deployment is gated behind SOTI_MC_INTEGRATION=1 and is not run in CI. To exercise it locally:
SOTI_MC_INTEGRATION=1 npm testRoadmap
- v0.2 — bulk operations (
/devices/actions), webhook receiver for SOTI events, iOS APNS-aware command set, scripted demo GIF, OpenAPI-driven type generation. - v0.3 — multi-tenant config (multiple SOTI servers per process), retry budget surfaced as MCP progress events.
Contributing
Issues and PRs welcome. Please open an issue first for non-trivial changes so we can align on scope. Run npm run lint && npm run typecheck && npm test before opening the PR.
License
MIT — see LICENSE.
Trademark note. SOTI and MobiControl are trademarks of SOTI Inc. This project is not affiliated with or endorsed by SOTI Inc. The maintainer is an independent SOTI MobiControl operator who built this so AI agents could drive their fleet without re-implementing OAuth2 + REST glue.
