miradore-mcp
# miradore-mcp
An [MCP](https://modelcontextprotocol.io) server that connects Claude (or any MCP client) to [Miradore Online](https://www.miradore.com) mobile device management.
Ask things like:
- "Which devices haven't checked in this week?"
- "Give me a health summary of the fleet."
- "What apps and profiles are on Jim's iPad?"
- "I just added serial C02XXXXXXXX to Apple Business Manager for jim@example.com. Sync it, pre-assign it to Jim, and tell me when it enrolls and whether it got its apps."
It uses both of Miradore's APIs: API v1 for rich device inventory and queries, and API v2 for actions like syncing from Apple, deploying apps and remote commands.
> Not affiliated with or endorsed by Miradore Ltd.
## Requirements
- Node.js 20 or newer
- A Miradore Online plan that includes API access (the Free plan does not)
- A Miradore API key: in the Miradore console go to **System > Infrastructure diagram**, hover the **API** icon and choose **Create key**. Create a dedicated key for this server so you can revoke it independently.
## Setup
You need two values:
| Variable | What it is |
| --- | --- |
| `MIRADORE_SITE` | Your site name: the `your-site` part of `https://online.miradore.com/your-site/` |
| `MIRADORE_API_KEY` | The API key you created |
### Claude Code
```sh
claude mcp add miradore \
-e MIRADORE_SITE=your-site \
-e MIRADORE_API_KEY=your-key \
-- npx -y github:ajthom90/miradore-mcp
```
### Claude Desktop
Add this to `claude_desktop_config.json` (Settings > Developer > Edit Config):
```json
{
"mcpServers": {
"miradore": {
"command": "npx",
"args": ["-y", "github:ajthom90/miradore-mcp"],
"env": {
"MIRADORE_SITE": "your-site",
"MIRADORE_API_KEY": "your-key"
}
}
}
}
```
### From a local clone
```sh
git clone https://github.com/ajthom90/miradore-mcp.git
cd miradore-mcp
npm install # also builds dist/
```
Then point your client at `node /path/to/miradore-mcp/dist/index.js` with the same environment variables.
## Safety modes
| Variable | Default | Effect |
| --- | --- | --- |
| `MIRADORE_READ_ONLY` | `false` | `true` exposes **only** read-only tools. Recommended for monitoring-only use. |
| `MIRADORE_ALLOW_DESTRUCTIVE` | `false` | `true` adds lock, reboot, Lost Mode, wipe, retire and delete-user tools. Each one also requires `confirm: true` on every call. |
Every tool carries MCP annotations (`readOnlyHint` / `destructiveHint`), so clients like Claude Code can ask before running anything that changes Miradore.
## Tools
**Monitoring (read-only)**
| Tool | Purpose |
| --- | --- |
| `list_devices` | Filter devices by platform, status, tag, user, name, model, serial, or days since last check-in |
| `get_device` | Full details for one device, plus optional apps, installed profiles, deployed config profiles, security, storage, pending OS updates, hardware, management, and custom attributes |
| `device_health_summary` | Fleet overview: counts, OS versions, and devices that are stale, unassigned, without a passcode, or unencrypted |
| `list_enrollments` | Recent enrollments, including Apple Automated Device Enrollment (`DEP`) |
| `list_users`, `get_user_device_identifiers` | Users, and serials pre-assigned to them |
| `list_tags`, `list_config_profiles`, `list_custom_attributes` | Lookups for IDs used by the setup tools |
| `get_application` | An app and its deployment methods |
| `get_device_locations`, `list_suspended_devices` | Location history and suspended devices |
| `query_miradore` | Advanced: any API v1 read with your own `select` / `filters` / `orderby` |
**Setup and changes** (hidden when `MIRADORE_READ_ONLY=true`)
`sync_apple_dep_devices`, `assign_device_identifiers_to_user`, `remove_device_identifier_from_user`, `add_device_tag`, `remove_device_tag`, `deploy_config_profile`, `remove_config_profile`, `deploy_application`, `update_device`, `set_device_custom_attribute`, `create_user`, `update_user`, `add_user_tags`, `remove_user_tag`, `activate_device`
**Destructive** (only when `MIRADORE_ALLOW_DESTRUCTIVE=true`)
`lock_device`, `reboot_device`, `enable_lost_mode`, `disable_lost_mode`, `wipe_device`, `retire_device`, `delete_user`
### What the Miradore API can't do
The API cannot list Apple Business Manager devices that haven't enrolled yet, assign an Automated Device Enrollment profile, or manage business policies. Do those in the Miradore console. A typical new-Apple-device flow:
1. Add the device to Apple Business Manager (e.g. with Apple Configurator) and assign the enrollment profile in Miradore.
2. Ask Claude to `sync_apple_dep_devices` and `assign_device_identifiers_to_user` with the serial.
3. Power the device on. Claude can watch `list_enrollments` / `list_devices` and then check its apps and profiles with `get_device`.
## Privacy and security
- The API key is read from environment variables only. It is never logged, and is scrubbed from every error message. API v1 sends the key in the URL query string, so URLs echoed by network errors are redacted too.
- Only `https://` base URLs are accepted.
- Device and user data returned by the tools goes to the AI model you're using, the same as anything else you show it. Use `MIRADORE_READ_ONLY` and a dedicated, revocable key if that's a concern.
- Your MCP client stores the environment variables in its config file (e.g. `~/.claude.json` or `claude_desktop_config.json`). Keep that file private.
## Checking it against your account
To confirm every read tool works with your Miradore site:
```sh
cp .env.example .env # fill in your values; .env is git-ignored
npm run live-check
```
The script runs only read-only tools. It prints pass/fail, counts and field names, never values, so the output is safe to share in a bug report.
## Development
```sh
npm install
npm test # unit tests with mocked Miradore responses
npm run typecheck
npm run build
```
Test fixtures use only invented data. Please never commit real site names, keys, serial numbers or user details.
API references: [API v1 specification](https://www.miradore.com/knowledge/integrations/programmers-guide-to-api-v1/) and the [API v2 Swagger](https://online.miradore.com/swagger/index.html).
## License
[MIT](LICENSE)
TDQS
Scored across 28 tools
Most tools target a distinct resource+action (devices, users, tags, config profiles, apps, enrollments, DEP), and descriptions explicitly cross-reference each other (e.g. list_custom_attributes -> set_device_custom_attribute). A few boundaries blur: list_suspended_devices is largely a filtered list_devices, device_health_summary overlaps with list_devices filters, get_device_locations duplicates a get_device detail area, and query_miradore is a catch-all that could tempt an agent away from the specific read tools. These are minor and the descriptions steer correctly.
The dominant pattern is consistent snake_case verb_noun (list_devices, get_device, add_device_tag, deploy_application, update_user), which is easy to predict. Deviations are minor but present: device_health_summary is noun-first with no verb, query_miradore uses a domain suffix rather than a resource noun, and tag verbs are inconsistently pluralized (add_user_tags vs remove_user_tag).
At 28 tools the surface sits above the comfortable band, though the domain is genuinely broad (devices, users, tags, config profiles, applications, enrollments, DEP sync) and no two tools are redundant. The count is defensible but heavy: several list_* reads could be consolidated as filters on a single listing tool.
Core MDM workflows are covered, but the lifecycle is asymmetric: activate_device exists to un-retire devices while no retire_device is offered, deploy_application and deploy_config_profile have removal counterparts only for profiles (no app removal), tags can be listed and attached but never created or deleted, and there is no list_applications (get_application requires an ID from the console URL). Users can be created and updated but not deleted or deleted-from-device.