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:

RequirementNotes
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_devices payload is heavy — scope the routine to a fixed set of sites. Each device record carries a udf block of 300 custom fields, almost all null. 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_device per device. The datto_list_devices record already contains a patchManagement object (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 patchStatus and pending/installed counts but no patch timestamp. Define drift as "not FullyPatched, or has approved patches pending," not as a number of days behind.
  • permitted_tools must be populated per connector. A routine with a connector attached but an empty permitted_tools list 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 FullyPatchedInstallError, 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.