Skip to content

Tools Reference

Rod Christiansen edited this page Oct 8, 2026 · 3 revisions

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.

Summary

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.

search_devices

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" }
    }
  }
]

get_device

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" }
    }
  }
}

get_device_module

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 }
  }
}

module_report

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"
  }
]

fleet_summary

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"
}

application_usage

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"
}

recent_events

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
}

api_health

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"
}

Authentication and scope

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.

Clone this wiki locally