Repository navigation
Tools Reference
The server registers eight tools, all read-only, all defined in
src/reportmate_mcp/server.py. Each is a single authenticated GET against a
ReportMate API endpoint under /api/v1. Nothing is cached, retried, reshaped,
or paginated by the MCP server — the API's JSON is handed to the agent as it
arrives.
The JSON below is hand-authored and trimmed to show structure. Real responses carry many more fields, and fleet-wide tools return one entry per device.
| Tool | Parameters | Upstream endpoint | Returns |
|---|---|---|---|
search_devices |
query: str = "", limit: int = 0
|
GET /api/v1/devices |
Array of device summaries |
get_device |
serial_number: str |
GET /api/v1/device/{serial_number} |
Object with every module for one device |
get_device_module |
serial_number: str, module: str
|
GET /api/v1/device/{serial_number}/modules/{module} |
One module document |
module_report |
module: str |
GET /api/v1/{module} |
Array, one entry per device |
fleet_summary |
none | GET /api/v1/dashboard |
Rollup object |
application_usage |
none | GET /api/v1/applications/usage |
Usage aggregation object |
recent_events |
limit: int = 50 |
GET /api/v1/events?limit= |
Events object |
api_health |
none | GET /api/v1/health/ready |
Readiness object |
Note what is absent: no archive, no delete, no ingest. ReportMateClient has no
request method other than get, so no write endpoint is reachable even by accident.
Lists the fleet, optionally narrowed by a free-text query.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
query |
string | "" |
Case-insensitive substring filter. Empty returns everything. |
limit |
int | 0 |
Cap on results. 0 or negative means no cap. |
Both are applied in the MCP server, after the API call. The tool always
requests the full device list — it passes no limit or offset upstream, even
though the endpoint accepts them — then filters in Python:
devices = [d for d in devices if q in str(d).lower()]Because the match is against the whole device record stringified, a query hits any field: serial, name, hostname, OS, asset tag, location. That makes it forgiving for exploration and unsuitable for precision. There is no field-scoped or comparison syntax.
The endpoint returns an object with a devices array; the tool unwraps it and
returns the array, so the response's total, page and hasMore fields are
discarded. Archived devices are excluded by default upstream, and the list
entries carry no archived field.
[
{
"serialNumber": "WS-0001",
"deviceId": "0d9f1c2e-6b3a-4f21-9c88-1a2b3c4d5e6f",
"deviceName": "workstation-01",
"hostname": "workstation-01",
"platform": "windows",
"osName": "Windows 11 Enterprise",
"osVersion": "24H2",
"status": "online",
"lastSeen": "2026-09-03T14:22:10+00:00",
"assetTag": "A-1042",
"location": "Building A",
"modules": {
"system": { "operatingSystem": { "name": "Windows 11 Enterprise", "version": "24H2" } },
"network": { "hostname": "workstation-01" }
}
}
]The full record for one device: every module the device has reported, in one object.
| Parameter | Type | Meaning |
|---|---|---|
serial_number |
string | Serial number. Serial is ReportMate's primary device identifier. |
The device object also carries inventoryState — the device's state in an
asset inventory system, or null when none is recorded.
This is the largest single-device payload the server can produce. When you know
which module you want, use get_device_module instead. Two things are trimmed
upstream before you see them: the installs run log (fetched separately by the
dashboard because it can run to megabytes) and the per-tool log tails inside the
management module.
An unknown serial returns 404, which surfaces as a failed tool call.
{
"success": true,
"device": {
"serialNumber": "WS-0001",
"deviceId": "0d9f1c2e-6b3a-4f21-9c88-1a2b3c4d5e6f",
"name": "workstation-01",
"platform": "windows",
"clientVersion": "2026.08.19.1130",
"lastSeen": "2026-09-03T14:22:10+00:00",
"archived": false,
"modules": {
"system": { "operatingSystem": { "name": "Windows 11 Enterprise", "version": "24H2" } },
"hardware": { "manufacturer": "Contoso", "model": "Widget 5000" },
"installs": { "cimian": { "items": [] } },
"security": { "firewall": { "isEnabled": true } },
"inventory": { "assetTag": "A-1042", "location": "Building A" }
}
}
}One module document for one device.
| Parameter | Type | Meaning |
|---|---|---|
serial_number |
string | Serial number. |
module |
string | One of the ten module names. |
The ten valid names — the same list as the MODULES tuple in server.py and
the dashboard's tabs:
inventory installs applications system management
identity hardware peripherals security network
Nothing validates the name locally, but the API does: an unrecognized module
returns 400, not 404. A valid module with no data for that device returns
200 with data set to null. The document is the module as the device agent
reported it, nested by section, not the flattened projection that
module_report returns. Nested paths such as installs/full work against
module_report but not here — this route takes a single module segment.
Displays and printers are collected as part of peripherals; profile data is
part of management and security.
{
"success": true,
"module": "security",
"data": {
"antivirus": { "name": "Microsoft Defender", "isEnabled": true, "isUpToDate": true },
"firewall": { "isEnabled": true },
"encryption": { "bitLocker": { "isEnabled": true } },
"certificateSummary": { "totalCount": 47, "expiredCount": 2 },
"pendingReboot": { "required": false }
}
}The fleet-wide version: one module, every device.
| Parameter | Type | Meaning |
|---|---|---|
module |
string | Module name. Surrounding whitespace and slashes are stripped, so "installs", "/installs", and "/installs/" are equivalent. |
The tool interpolates the cleaned name straight into /api/v1/<module>. That
means the ten module names work, and so do the API's nested report routes, which
MODULES does not enumerate — installs/full for full per-device install
records, and security/certificates for a certificate inventory.
These endpoints return a bare JSON array, not an object with a devices key.
They accept limit and offset upstream, but this tool passes neither, so you
get the whole fleet in one response. On a large fleet that is the most likely
thing to exhaust an agent's context or hit the 30-second client timeout.
Each entry carries a common identity block — deviceId, deviceName,
serialNumber, assetTag, lastSeen, collectedAt, plus inventory scoping
fields such as usage, catalog, location, department — and then the fields
particular to that module. Where an asset inventory system has recorded a state
for the device, the entry also carries inventoryState; a device whose state is
storage is expected to be silent, so tell the agent not to read its old
lastSeen as a missing device.
installs and applications are the exceptions to the one-row-per-device rule:
installs flattens to one row per managed install item, and applications to
one row per installed application.
[
{
"deviceId": "0d9f1c2e-6b3a-4f21-9c88-1a2b3c4d5e6f",
"deviceName": "workstation-01",
"serialNumber": "WS-0001",
"assetTag": "A-1042",
"lastSeen": "2026-09-03T14:22:10+00:00",
"itemName": "AcmeWidgetPro",
"currentStatus": "Installed",
"latestVersion": "5.2.1",
"installedVersion": "5.2.1",
"installDate": "2026-08-14T09:03:00+00:00",
"updateAvailable": false,
"platform": "windows",
"source": "cimian"
}
]The dashboard rollup — device list with status, install error and warning counts, and a recent-events slice, in one call.
No parameters. The endpoint accepts eventsLimit and includeArchived; the
tool passes neither, so you get the defaults.
status on each device is derived from lastSeen: online, idle after an hour,
offline after a day. installStats splits errors and warnings by platform and
counts both items and devices, which are different numbers — one device with
five failing packages is one device and five items.
Devices an asset inventory reports as in storage stay in devices, each with
its inventoryState, but are left out of totalDevices and installStats and
counted in storageDevices.
{
"devices": [
{
"serialNumber": "WS-0001",
"name": "workstation-01",
"platform": "windows",
"osName": "Windows 11 Enterprise",
"osVersion": "24H2",
"status": "online",
"lastSeen": "2026-09-03T14:22:10+00:00",
"archived": false
}
],
"totalDevices": 412,
"storageDevices": 7,
"installStats": {
"totalErrorItems": 37,
"totalWarningItems": 118,
"devicesWithErrors": 12,
"devicesWithWarnings": 54,
"hasInstallData": true
},
"events": [
{
"id": "b1c2d3e4",
"device": "WS-0001",
"deviceName": "workstation-01",
"kind": "error",
"message": "Install failed: AcmeWidgetPro",
"ts": "2026-09-03T13:58:44+00:00"
}
],
"totalEvents": 200,
"lastUpdated": "2026-09-03T14:25:00+00:00"
}Fleet-wide application utilization, aggregated from usage history.
No parameters. The endpoint has an extensive filter set — days (default 30),
applicationNames, platforms, minHours, and inventory scoping — none of
which this tool exposes. You always get the unfiltered default window.
Which number to quote matters here, and the field names do not make it obvious:
| Field | What it counts |
|---|---|
activeHours |
Foreground time with user input. The figure to report. |
totalHours |
Summed process lifetime, no wall-clock ceiling. Diagnostic only. |
activeDeviceCount / activeUserCount
|
Devices and users with non-zero active time. Use these for seat and licence questions. |
deviceCount / userCount
|
Anything that produced a usage row at all, including pure background process time. |
installedDeviceCount |
In-scope devices whose applications inventory lists the app. The only install figure here. |
A background service will show a device count close to the whole fleet against almost no active hours. Both numbers are correct; they answer different questions.
{
"status": "ok",
"applications": [
{
"name": "Acme Widget Pro",
"activeHours": 512.4,
"totalHours": 2140.8,
"launchCount": 1893,
"activeDeviceCount": 46,
"deviceCount": 118,
"activeUserCount": 39,
"installedDeviceCount": 122,
"isSingleActiveUser": false,
"lastUsed": "2026-09-03T11:40:00+00:00"
}
],
"topUsers": [
{ "username": "user1", "totalHours": 88.5, "launchCount": 210, "appsUsed": 14 }
],
"summary": {
"totalAppsTracked": 213,
"totalActiveHours": 9184.2,
"totalUsageHours": 41022.7,
"uniqueUsers": 287,
"uniqueDevices": 396,
"noActiveUseAppCount": 61
},
"lastUpdated": "2026-09-03T14:25:00+00:00"
}The most recent fleet events. Event kinds are success, warning, error,
info and system.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
limit |
int | 50 |
Passed straight through as the limit query parameter. |
The upstream endpoint constrains limit to 1–1000. A value of 0 or
anything above 1000 is rejected by FastAPI validation as a 422 before any data
is fetched, so do not use 0 to mean "everything" the way you can with
search_devices.
Events come back newest first. The tool exposes only limit — the endpoint's
offset, startDate, endDate, and type filters are not wired up, so you
cannot ask for errors only; filter the returned list instead.
Event payloads are not included. Fetching a full payload needs an endpoint this server does not expose.
{
"success": true,
"events": [
{
"id": "b1c2d3e4",
"device": "WS-0001",
"deviceName": "workstation-01",
"assetTag": "A-1042",
"kind": "error",
"message": "Install failed: AcmeWidgetPro",
"ts": "2026-09-03T13:58:44+00:00",
"platform": "windows"
}
],
"total": 18422,
"count": 50,
"limit": 50,
"offset": 0
}The API's readiness probe, which includes a database-connectivity check.
No parameters. Useful as a first call when something is not working: it distinguishes "the API cannot reach its database" from "the fleet has no data".
One caveat worth knowing. /api/v1/health/ready is an unauthenticated
endpoint upstream, so a successful api_health proves your REPORTMATE_API_URL
is right and the API is up — it does not prove your credential works. To
test the credential, call any other tool.
When the database is unreachable the API answers 503, which the HTTP client
raises rather than returning, so a database outage surfaces as a tool error
rather than a not_ready body.
{
"status": "ready",
"timestamp": "2026-09-03T14:25:00+00:00",
"database": "connected"
}Every tool other than api_health hits an authenticated endpoint. The server
sends one header, chosen by Configuration: X-API-Key when a key is set,
otherwise X-Client-Passphrase.
The API derives the required scope from the HTTP method, and this server only
ever issues GET, which requires the read scope. A read-scoped API key is
therefore sufficient for the entire toolset — there is nothing here that a
broader key would unlock.
| Status | Meaning |
|---|---|
| 401 | No credential sent, or the key or passphrase is not accepted. |
| 403 | The API key is valid but lacks the read scope. |
| 400 | Invalid module name on get_device_module. |
| 404 | Unknown serial number, or a module path the API does not serve. |
| 422 | Out-of-range limit on recent_events. |
| 503 |
api_health only — the API cannot reach its database. |
AGPL-3.0-or-later — a commercial license is also available. reportmate.app