Patch Drift Reporter
A worked example of an advanced workflow: a Claude-managed scheduled agent that, once a week, lists the devices at a fixed set of Datto RMM sites, reads each device's patch status, flags everything that is not fully patched, and publishes a site-by-site report to Slack as a canvas with a one-line summary. There are no servers and no code to deploy: the agent is a saved prompt plus a schedule plus two MCP connectors, running in Claude's cloud.
What it builds
The finished workflow runs unattended on a weekly cron. Each run it lists the devices at each of its configured sites, reasons about which are behind on patching, groups the findings by site, and notifies your team, turning a scattered fleet into a single patch-drift report with a Slack trail.
Prerequisites
Before building, your Claude account needs all of the following:
| Requirement | Notes |
|---|---|
| WYRE MCP Gateway connector | Connected in claude.ai (https://mcp.wyre.ai/v1/mcp). Provides the datto_* tools. See the Gateway overview. |
| Datto RMM enabled in the gateway | The gateway's Datto RMM API user must be able to read sites and devices. |
| Slack connector | The first-party Slack connector connected in claude.ai (https://mcp.slack.com/mcp). Provides slack_create_canvas and slack_send_message. |
| Scheduled routines access | Created via the /schedule capability; managed at claude.ai/code/routines. |
| A destination Slack channel | e.g. #sw-dev, plus its channel ID. The Slack connector must have access to it. |
Known gotchas
These are the things that cost real time the first time through. Account for them up front and the build genuinely takes minutes.
- The
datto_list_devicespayload is heavy — scope the routine to a fixed set of sites. Each device record carries audfblock of 300 custom fields, almost allnull. Across a large fleet that bloat swamps a single routine run before it can finish — the run produces no output. Until the gateway slims that payload, bake a fixed set of roughly six site UIDs into the routine prompt rather than sweeping every site. - Read patch status from the list payload — don't call
datto_get_deviceper device. Thedatto_list_devicesrecord already contains apatchManagementobject (patchStatus,patchesApprovedPending, and counts). One list call per site is all the routine needs; a per-device call across the fleet risks the 60-second tool timeout. - There is no "last patched date" field — classify on status, not age.
The Datto payload exposes
patchStatusand pending/installed counts but no patch timestamp. Define drift as "notFullyPatched, or has approved patches pending," not as a number of days behind. -
permitted_toolsmust be populated per connector. A routine with a connector attached but an emptypermitted_toolslist runs with no tools and silently does nothing — no error, no output. List the exact tool names the routine needs. - A routine reaches only its attached connectors. The routine sandbox blocks arbitrary network egress, so notifications must go through the Slack connector's tools, not an outbound webhook. Attach every connector the workflow touches.
The one-shot build prompt
With the connectors above in place, paste this to Claude. It confirms the gateway, creates the routine, and verifies it end to end.
Build me a scheduled Patch Drift Reporter agent. Do all of this end to end:
1. Confirm the WYRE MCP Gateway works and Datto RMM is reachable: call
datto_list_sites and check it returns your managed sites.
2. Pick the sites this report should cover. Datto's datto_list_devices payload
carries a large per-device "udf" block (300 custom fields, mostly null), so
sweeping a big fleet in one routine run is not viable - the run drowns in
that payload. Choose a fixed set of about six sites and record each site's
siteUid. These siteUids get baked into the routine prompt.
3. Confirm a Slack connector is connected. Note the destination channel name
and ID (e.g. #sw-dev). If Slack does not show in the /schedule connector
list, read its connector_uuid and url from an existing routine that already
uses Slack (RemoteTrigger list -> get -> mcp_connections).
4. Create a Claude-managed scheduled routine named "Patch Drift Reporter":
- Schedule: weekly, cron "0 11 * * 1" (Monday 07:00 America/New_York =
11:00 UTC). Faster-than-hourly cadences are rejected.
- Attach TWO connectors, each with permitted_tools populated:
* WYRE MCP Gateway: datto_list_devices
* Slack: slack_create_canvas, slack_send_message
An empty permitted_tools list = the routine runs with no tools.
- Routine prompt: every run, list devices for each of the baked-in sites,
flag devices that are not fully patched, build a report grouped by site,
publish it as a Slack canvas, and post a one-line summary linking the
canvas. Use the exact routine prompt below, with your own siteUids.
5. Trigger a manual run and verify: a canvas titled "Patch Drift - <date>" was
created, a one-line summary landed in the destination channel, and the
report content matches the live fleet (spot-check one site). The resulting routine prompt
This is the lean prompt the build process installs into the scheduled routine itself. Substitute your own site UIDs and destination channel.
You are the Patch Drift Reporter. You run weekly. Keep it lean.
Use ONLY: datto_list_devices, slack_create_canvas, slack_send_message.
Do NOT call datto_list_sites - the sites are listed below.
Sites to check (siteUid - name):
- <site-uid-1> - <site name 1>
- <site-uid-2> - <site name 2>
- ... about six sites total ...
1. For each site above, call datto_list_devices with that siteUid and max 200.
2. For each device, read the patchManagement object. A device is DRIFTED if
patchManagement.patchStatus is anything other than "FullyPatched" (e.g.
InstallError, RebootRequired, NoPolicy, NoData), or
patchManagement.patchesApprovedPending is greater than 0. Ignore the large
udf field block on each device - it is not needed.
3. Build a report grouped by site: site name, total devices, drifted count,
and each drifted device's hostname, patchStatus, and patchesApprovedPending.
4. slack_create_canvas titled "Patch Drift - <today's date>" with the full
grouped report. Then slack_send_message to #sw-dev: a one-line summary
("Patch drift: N drifted devices across 6 sites - see canvas") linking it.
5. If a site cannot be read, list it in a "could not check" section instead of
skipping it silently. How it works
A weekly cadence, by design
Patch drift is a slow-moving signal — devices fall behind over days and weeks, not minutes. The routine runs every Monday morning so the report is waiting before the week's remediation work starts. A weekly cron is also kind to the API: one sweep per week, not one per hour.
Drift is defined on patch status, not age
A device is "drifted" if its patchStatus is anything other than
FullyPatched — InstallError, RebootRequired,
NoPolicy, NoData — or it has approved patches still pending
install. Datto's device record gives no last-patched timestamp, so status is the
signal. That single rule is the classification logic, which keeps the report
consistent week to week and the routine prompt short.
It covers a fixed set of sites, not the whole fleet
Each datto_list_devices record carries a 300-field udf
block that is almost entirely empty. Listing every device across a large fleet
floods a single routine run with that null data and the run never reaches the
report. So the routine is scoped to a fixed set of roughly six sites whose combined
device count a run can comfortably hold. Widening it to the whole fleet is a
gateway change — datto_list_devices needs to omit the udf
block — not a routine change.
Delivery is a Slack canvas plus a summary
The full site-by-site report can be long, so the routine publishes it as a Slack canvas, a durable document teammates can scroll and reference, and posts only a one-line summary to the channel linking it. The channel stays readable; the detail lives in the canvas. See delivery adapters for other ways to surface a routine's output.
Extending it
The natural next step is whole-fleet coverage: once the gateway's
datto_list_devices can return device records without the
udf block, the routine can drop the baked-in site list and call
datto_list_sites to sweep everything. The workflow is also RMM-agnostic.
To run the same patch check against a NinjaOne fleet, swap the
datto-rmm tools for the gateway's ninjaone tools; the
report-building and Slack-delivery body of the routine is identical.
Questions or a workflow you'd like documented?
Open an issue
in the msp-claude-plugins repository.