WWDC26 AnnouncementPulsatrix native actions are coming soon to the Shortcuts app

Documentation

The control plane for ConnectWise MSPs.

Your AI does the busywork across your whole ConnectWise stack — and never changes anything until a human approves. Here's how it fits together, how to set it up, and everything it can do.

4
ConnectWise connectors
447
Actions available
11
Capability areas
147
Gated behind approval

#Overview

Most automation tools bolt onto one product and stop there. Pulsatrix connects to the whole ConnectWise suite at once and exposes it as a single set of well-described actions your AI assistant can call — reading data freely, and proposing every change for your approval first.

Here's how the pieces fit together — you direct your LLM, it works through the Pulsatrix MCP into ConnectWise, and high-risk actions route to your phone for a signed approval, with the AI never in that path:

You & team IN CONTROL LLM YOUR CLAUDE Pulsatrix MCP GATEWAY CONNECTWISE STACK PSA RMM CPQ Remote ask calls acts Approval broker OUT-OF-BAND Your phone FACE / TOUCH ID High-risk actions pause here — you approve, out of band, before anything runs.

#Prerequisites & setup

Pulsatrix talks to ConnectWise with credentials you create. The golden rule: give it a dedicated, least-privilege identity per product — never your own personal login — so you can scope it tightly, audit it, and revoke it without disturbing anyone's day-to-day access.

PSA — ConnectWise Manage (required)

Create a dedicated API Member (an API-only account, not a real person) with its own Security Role, then generate an API key pair:

  1. Manage → System → Members → API Members → new API member; assign it a dedicated Security Role (see the table below).
  2. On that member: API Keys → new key → that's your CW_PUBLIC_KEY / CW_PRIVATE_KEY (the private key is shown once — store it as a secret).
  3. Your site URL and company id become CW_API_URL / CW_COMPANY_ID (add CW_AUTH_PREFIX only if your tenant uses an authorization prefix).
RMM — ConnectWise Asio (optional)

Create an OAuth2 API client (client ID + secret) scoped to the device reads and the patch/script operations you actually want. Maps to CW_RMM_CLIENT_ID / CW_RMM_CLIENT_SECRET (+ CW_RMM_API_URL).

CPQ — Quosal Sell (optional)

In Sell admin, generate an access key + API key pair. Maps to CW_CPQ_ACCESS_KEY / CW_CPQ_PUBLIC_KEY / CW_CPQ_PRIVATE_KEY.

ScreenConnect (optional — highest risk)

Install the REST API extension, set a CTRLAuthHeader secret on it, and note its extension GUID. Maps to CW_SC_URL / CW_SC_AUTH_SECRET / CW_SC_EXTENSION_ID. This connector can run commands on endpoints — treat its secret accordingly.

Exact menu paths shift between product versions — check current ConnectWise docs for the click-path. The variable mapping above is what Pulsatrix reads, and won't change.

Recommended security roles

Scope each identity to the least it needs. Whatever you grant, Pulsatrix adds a second layer — the riskiest writes are gated regardless of the role (see below) — but the role is your first and hardest boundary.

ProductCreateRecommended baselineIf over-scoped
PSA (Manage)Dedicated API member + custom Security RoleInquire across the modules you'll use; Add/Edit only where you want writes (tickets, time, notes); keep Delete off; grant Finance only if you'll billThe role is the AI's blast radius. Finance + Delete are the dangerous grants
RMM (Asio)OAuth2 API clientDevice read + only the patch/script scopes you needScript scope = remote code execution on endpoints
CPQ (Quosal)Sell API key pairQuote read/writeCan create and modify financial documents
ScreenConnectREST extension credentialsEnable only if you want remote sessions at allFull remote control of client machines — the largest blast radius of any connector
Risk levels & what's gated

Two independent guards sit in front of every action: the confirm gate (any write needs confirm:true, otherwise you get a dry-run preview), and — when the approval app is enabled — the hard-gated floor (the riskiest actions need a signed human verdict no matter what the role or ruleset allows).

HIGH · Remote exec · finance · deletes Human approval — hard-gated, no exceptions MEDIUM · Routine writes Notes, time, status — needs confirm:true LOW · Reads & inquire Tickets, devices, dashboards — runs freely
Three tiers, two guards: reads run free, routine writes need confirm, and the high-risk floor always needs a signed human approval.
Action classExamplesRiskGating
Read / inquireget ticket, list devices, dashboards, analyticsLowNever gated — runs freely
Routine writeadd note, log time, update ticket status or priorityMediumconfirm:true (dry-run preview otherwise)
Remote executionsc_run_command, sc_send_command, sc_create_session, pkg_uninstall, remote_session, run_remediationHighHard-gated — signed human approval
Finance writecreate_invoice, post_invoice, generate_batch, approve_expense, approve_poHighHard-gated
Destructive / rawevery cw_api DELETE, writes to blocked pathsHighHard-gated

Hard-gated means out-of-band and fail-closed: even a prompt-injected model that asks nicely can't get through, and if the approval broker is unreachable the action is denied. Details in Security & privacy.

#Install the MCP

Pulsatrix ships as a single MCP server — one stdio process your AI client launches on demand. There are two ways to install it.

Option A — MCPB bundle (easiest, Claude Desktop)

Download the pulsatrix-<version>.mcpb from the latest release and double-click it. Claude Desktop installs the server and prompts for each setting in its UI, storing secrets in the OS keychain. Build your own with npm run package:mcpb.

Option B — from source (any client)
# clone, install, build → dist/index.js
cd pulsatrix/mcp
nvm use && npm install
npm run build

The server then launches with node /abs/path/to/pulsatrix/mcp/dist/index.js — that command goes into your client's config below.

Connect to ConnectWise

Configuration is environment variables. PSA (Manage) is required; RMM, CPQ and ScreenConnect are optional and light up their tools only when set.

VariableReq.What it is
CW_API_URLe.g. https://na.myconnectwise.net/v4_6_release/apis/3.0
CW_COMPANY_IDCompany identifier (UUID, or legacy short code)
CW_PUBLIC_KEY / CW_PRIVATE_KEYManage API key pair (Members → API Keys)
CW_AUTH_PREFIXHeader prefix like ACME+, if your tenant uses one
CW_RMM_* / CW_CPQ_* / CW_SC_*Enable RMM (Asio), CPQ (Quosal) and ScreenConnect

The full variable reference (analytics, locale, resolver, tenant tuning) lives in the repo's .env.sample. Ask the running server cw_meta(view:"status") to see what loaded.

#Set up the broker

The approval broker is optional — you only need it for the out-of-band human-approval loop, where gated actions wait for a tap in the Pulsatrix app. Without it the MCP still reads freely and dry-run-previews every write.

It's a Docker Compose stack (broker + Postgres + a Caddy edge):

cd pulsatrix/broker
# put local TLS material + secrets under broker/.secrets/ (gitignored)
docker compose up --build

Then turn on the approval hook by setting these on the MCP server's environment:

PULSATRIX_APPROVAL_URL=https://your-broker.example   # https only
PULSATRIX_APPROVAL_ENROLL_TOKEN=…                  # or mTLS client cert + key
PULSATRIX_APPROVAL_BROKER_PUBLIC_KEY=…             # verifies signed grants
PULSATRIX_APPROVAL_MODE=enforce                    # enforce | observe | dry-run

The hook activates only when PULSATRIX_APPROVAL_URL (https) and one auth method are both set — anything half-configured refuses to start, loudly. If the broker is unreachable, gated actions deny (fail-closed). Full local dev loop: docs/specs/app/90-dev-loop-runbook.md.

#Add it to your AI client

Every MCP client uses the same contract: a stdio server launched with node dist/index.js and the env vars above. Only the config location changes. The base config:

{
  "mcpServers": {
    "pulsatrix": {
      "command": "node",
      "args": ["/abs/path/to/pulsatrix/mcp/dist/index.js"],
      "env": {
        "CW_API_URL": "https://na.myconnectwise.net/v4_6_release/apis/3.0",
        "CW_COMPANY_ID": "…",
        "CW_PUBLIC_KEY": "…",
        "CW_PRIVATE_KEY": "…"
      }
    }
  }
}
ClientWhere the config goes
Claude Desktopclaude_desktop_config.json (macOS: ~/Library/Application Support/Claude/) — or just double-click the .mcpb bundle.
Claude CoworkAdd through Cowork's connector / MCP settings, using the command + env above.
Claude Code.mcp.json at the project root, or claude mcp add pulsatrix node /abs/…/dist/index.js. Tools appear as mcp__pulsatrix__cw_*.
opencode~/.config/opencode/config.json → an "mcp" entry with "type":"local", command as an array, and environment (instead of env).
Hermes · Openclaw · PiStandard stdio MCP clients — point their MCP config at the same node dist/index.js command and env.
Cursor~/.cursor/mcp.json (global) or a workspace .cursor/mcp.json — same shape as Claude Desktop.

Any spec-compliant MCP client works — the contract is just stdio transport, the node dist/index.js command, and the ConnectWise env vars. Verify any client by asking it to call cw_meta with view: "status".

#Quickstart

Once the MCP is installed and added to your client, confirm it's wired up. Ask your assistant to run:

cw_meta(view: "status")

It reports which connectors loaded — PSA is required; RMM, CPQ and ScreenConnect light up only when their env vars are set. Then try a front door for your role:

RoleFirst call
Technician — your daycw_dashboards(action:"my_day", params:{member})
Dispatcher — the queuecw_dashboards(action:"dispatch", params:{scope:"today"})
Service managercw_dashboards(action:"daily_briefing")
vCIO — one clientcw_dashboards(action:"brief", params:{scope:<company_id>})

You don't have to phrase calls yourself — just talk to your assistant in plain English ("what's on my plate today?"). The calls above are what it runs under the hood. Every action's full help is one call away: cw_<category>(action:"help", for:"<name>").

#ConnectWise connectors

ConnectWise is where we started. Each connector is fully wired into the action catalog — no extra setup per module.

PSA
ConnectWise Manage

Service tickets, companies and contacts, agreements, time entries and billing — the operational core of the shop.

PSA · Service deskLive
RMM
ConnectWise Asio

Device inventory, monitoring and health, plus remote scripts to diagnose and fix endpoints without leaving the assistant.

RMM · EndpointsLive
CPQ
ConnectWise / Quosal Sell

Build quotes and proposals, manage line items, and spin up renewal quotes straight from expiring agreements.

CPQ · QuotingLive
ScreenConnect
ConnectWise Control

Launch and manage remote sessions to reach the endpoint directly — every connection a gated, approve-first action.

Remote · SessionsLive

#Capabilities

Every connector feeds one shared action catalog — 447 actions across 11 areas. Read-only actions (300) run on their own; the rest are state-changing and always wait for your approval.

Tickets130 actions

Create, triage, route, update and close service tickets — notes, statuses, SLAs and escalations.

Time56 actions

Log, review, edit and bill time entries against tickets and agreements; spot unbilled work.

Companies55 actions

Company and contact records, configurations, plus health, risk and retention analytics.

Finance51 actions

Invoices, accounts receivable, billing, revenue rollups and procurement insight.

Team41 actions

Members, schedules, workload and dispatch — see who's free and what's on their plate.

CPQ27 actions

Quotes and proposals, line items, and renewal quotes generated from agreements.

RMM23 actions

Device inventory, monitoring and remote scripts via the Asio connector.

Sales20 actions

Opportunities, sales activities and pipeline visibility across your accounts.

Agreements19 actions

Contracts and additions, MRR, renewals, utilization and coverage gaps.

Projects18 actions

Project phases, tickets and tracking for larger pieces of work.

Dashboards7 actions

Cross-module rollups and at-a-glance metrics for the whole tenant.

Counts reflect the current release catalog and grow with every update.

#Actions & chaining

Your assistant sees 14 tools — one per product area, plus a few fixed ones. Each tool exposes 25–45 actions through an action parameter — ~440 in total. You never memorize them: the model picks the action, and the MCP describes itself.

Tool(s)What they cover
cw_tickets · cw_time · cw_companies · cw_agreements · cw_projects · cw_sales · cw_finance · cw_team · cw_dashboardsPSA (Manage) daily workflows + analytics
cw_rmmAsio devices, patches, scripts + the ScreenConnect bridge (sc_*)
cw_cpqQuosal Sell quotes, lines, templates, catalog
cw_apiRaw API escape hatch (confirm-gated, path block-list)
cw_calcHardened math — closes the LLM decimal-arithmetic gap
cw_metaIntrospection: catalog, status, summary, improvements
One call instead of many

The win is that a multi-step ConnectWise workflow collapses into a single action. Triaging a ticket with its notes, tasks, time entries and the RMM health of the device behind it:

cw_tickets(action: "get", params: { id: 12345, depth: "full" })

One call instead of five raw API hits across PSA and RMM. Every write needs confirm: true — without it you get a dry-run preview, never a real change.

Chaining across products

Some actions span two products and resolve IDs across them for you — and when a match is ambiguous, it's surfaced rather than guessed:

WorkflowAction
Remote session from a ticket (PSA → RMM → ScreenConnect)cw_tickets(action:"remote_session", params:{ticket_id}, confirm:true)
Run remediation on the device behind a ticketcw_tickets(action:"run_remediation", params:{ticket_id, command}, confirm:true)
Find recurring issues on one devicecw_tickets(action:"recurring_on_device", params:{device_id})
PSA opportunity → CPQ quote shellcw_sales(action:"draft_quote", params:{opportunity_id}, confirm:true)
Expiring agreement → renewal quotecw_agreements(action:"renewal_quote", params:{agreement_id}, confirm:true)
Company fleet health summarycw_companies(action:"fleet_health", params:{id})

Beyond these built-in cross-product calls, the assistant chains actions itself across a conversation: read a ticket → pull the device → propose a script → log the time → update the ticket — pausing for your approval on anything that changes state.

"Fix ticket#123" LLMPLANS THE STEPS cw_tickets · getREAD cw_rmm · device_healthREAD cw_tickets · run_remediationGATED 📱 cw_time · add_entryWRITE
One plain-English ask fans into several calls across products. Reads run free; the gated remediation waits for your approval before it touches the endpoint.
Discovering everything

Pulsatrix is self-documenting — there's no external manual to keep in sync. Three calls reveal the whole surface in-session:

cw_meta(view: "catalog")                          # all 14 tools + workflows + gotchas
cw_<category>()                                  # the action table for one area
cw_<category>(action: "help", for: "<name>")     # schema + examples for one action

#Recipes

Real flows — each one is a single conversation with the assistant. Reads run freely; the 📱 gated step is the moment your phone buzzes with full redacted context for a Face/Touch-ID approval (when the approval loop is on).

ScenarioWhat the assistant does📱 Gated step
"Ticket #12345 says the server is slow — fix it."Reads ticket + notes + time + RMM health of the device, diagnoses a stuck service, proposes the fixRestart command — you see the exact command before approving
Overnight patch remediationLists failed patches across the fleet, picks the remediation script, queues per-device runsEach script run lands as an approval card — approve from bed, or deny
"This printer issue keeps coming back."Correlates tickets on the same endpoint, drafts a root-cause note, proposes a permanent fixThe script execution — the diagnosis itself is free
Billing day on autopilotFlags missing time, agreement drift, unbilled work; fixes records and assembles invoicesInvoice / batch posting — money never moves without a signed verdict
Renewal quote in one breathExpiring agreement → renewal quote with uplift → CPQ quote shell with catalog linesSending the quote — drafts are free, outbound is gated
Emergency CVE responseFinds every endpoint running the vulnerable package, builds the uninstall planEach uninstall — fleet-wide, one tap per device or batch-approve
Autonomous night shift, auditableA local model works tickets overnight in observe mode — every would-be-gated action logged, nothing blockedNext morning: review the activity feed, flip to enforce when you trust it

#Skills

You don't need skills to use Pulsatrix. The product works on its own — just talk to your assistant in plain English (see Quickstart). Skills are an optional layer on top: a way to codify a recurring business process — your morning triage, your billing review, your renewal sweep — so the assistant runs it the same way every time, the way your shop wants it done.

They're plain-Markdown playbooks you import into Claude (Code or Desktop) — starter examples, not bundled with the MCP, yours to edit or replace. Each follows the rule Pulsatrix enforces anyway: read freely, propose changes, let the human approve.

How to import

Download a skill's SKILL.md into a folder of the same name under your skills directory:

~/.claude/skills/<skill-name>/SKILL.md     # personal — available in every project
.claude/skills/<skill-name>/SKILL.md       # per-project — committed with the repo

Claude picks it up on the next session. It just needs the Pulsatrix MCP configured (see Add it to your AI client).

Starter skills
SkillWhat it doesGet it
msp-morning-triageReview your day + triage new tickets with full context, then propose changes you approveSKILL.md
msp-weekly-prebillingValidate the week — missing time, agreement drift, unbilled work — and prep invoices for approvalSKILL.md
msp-renewal-sweepFind expiring agreements and draft renewal quotes (gated), so nothing lapsesSKILL.md
Example: msp-morning-triage
---
name: msp-morning-triage
description: Use at the start of a service-desk shift to review the day
  and triage new ConnectWise tickets through the Pulsatrix MCP.
---

# MSP morning triage

You have the Pulsatrix MCP (ConnectWise) available. Gather context
generously, then propose changes the human approves.

1. Pull the day:  cw_dashboards(action:"my_day", params:{ member })
2. Show the queue: cw_dashboards(action:"dispatch", params:{ scope:"today" })
3. Read each ticket in full:
     cw_tickets(action:"get", params:{ id, depth:"full" })
4. Propose a category, priority and one-line plan per ticket. Wait.
5. Apply on approval only (confirm:true) — each write is gated.

Rules: reads are free; never change a ticket, post a note, or touch a
device without the technician's explicit OK.

Skills are plain Markdown — rename them, edit the steps, or write your own. They live on your machine; Pulsatrix never ships or requires them.

#Approvals & safety

Pulsatrix keeps the human in the loop. The LLM never touches ConnectWise directly — it goes through the MCP, and anything that changes state is gated behind your approval.

1
You ask

Prompt the LLM of your choice in plain language — "what's overdue for ACME?", "rebuild this mailbox", "quote a renewal".

2
It reads & plans

The assistant calls Pulsatrix to pull what it needs across your tools. Read-only actions run freely — no interruptions for looking things up.

3
It asks before it acts

Any state-changing action is gated: Pulsatrix asks you directly to approve — bypassing the AI entirely. Approve, and only then does it run. Decline, and nothing happens.

4
It reports back

Results flow back up to the assistant, which explains what it did — with the ticket, time entry or change already in ConnectWise.

Action proposed Risk gate Your phoneFACE / TOUCH ID Signed grantSINGLE-USE · 30s Action runs ● Reads & low-risk actions run immediately — never gated. ● Deny · timeout · bad signature · expired grant → blocked. Fail-closed.
A gated action's journey. The model proposes; the gate classifies; you approve on your phone; a single-use, signed grant lets it run. The LLM never holds the approval material.

#Security & privacy

Nothing writes without confirm

Every non-GET action requires confirm: true. Without it the dispatcher returns a dry-run preview and never builds the upstream request — enforced before any HTTP call is made, and locked by a release-gate test.

The hard-gated floor

When the approval hook is on, these are always gated, regardless of any ruleset:

  • ScreenConnect remote execution — sc_run_command, sc_send_command, sc_create_session, pkg_uninstall
  • Ticket-driven remote access — remote_session, run_remediation
  • Finance writes — post_invoice, create_invoice, generate_batch, approve_expense, approve_po
  • Every cw_api DELETE and any write to a blocked path — even if a prompt-injected model asks nicely
The LLM is never in the trust path

Approval requests carry PII-redacted context. Verdicts are ES256-signed by a Secure Enclave key that never leaves your device; grants are single-use with a 30-second TTL. Nothing the model says — or a prompt injection attempts — can forge an approval. Fail-closed: broker unreachable, timeout, bad signature or expired grant ⇒ the action does not run.

PII redaction, two layers

Every captured string is scanned twice before it's ever stored — a key-based drop and a value regex (email, phone, Luhn card numbers, SSN/SIN, IPv4, JWT, cloud-provider tokens).

Local, optional analytics

Usage is captured to a local SQLite database (90-day retention) that powers the self-improvement view — it never leaves your machine. Turn it off entirely with CW_ANALYTICS_DISABLED=true.

Reference

#Configuration

All configuration is environment variables, set in your client's MCP config (or via the MCPB UI). Set each variable in exactly one place. The full reference with defaults lives in the repo's .env.sample; the essentials:

PSA — Manage (required)
VariableNotes
CW_API_URLFull URL incl. /v4_6_release/apis/3.0
CW_COMPANY_IDCompany identifier (UUID or legacy short code)
CW_PUBLIC_KEY / CW_PRIVATE_KEYManage API key pair
CW_AUTH_PREFIXHeader prefix like ACME+ (if your tenant uses one)
CW_CLIENT_IDOnly for tenants that enforce the clientId header
CW_TIMEZONEIANA tz for display only (default America/New_York); CW stores UTC
Optional connectors
VariableEnables
CW_RMM_CLIENT_ID / CW_RMM_CLIENT_SECRET (+ CW_RMM_API_URL)cw_rmm — Asio devices, patches, scripts
CW_CPQ_ACCESS_KEY / CW_CPQ_PUBLIC_KEY / CW_CPQ_PRIVATE_KEYcw_cpq — Quosal Sell quoting
CW_SC_URL / CW_SC_AUTH_SECRET / CW_SC_EXTENSION_IDScreenConnect sc_* actions inside cw_rmm

A missing optional connector doesn't break anything — its tool is still registered but returns a structured "not configured" error; PSA keeps working.

Approval hook (optional)
VariableNotes
PULSATRIX_APPROVAL_URLBroker base URL — https only; absent = hook disabled
PULSATRIX_APPROVAL_ENROLL_TOKENEnrollment token (or use the mTLS cert/key pair instead)
PULSATRIX_APPROVAL_BROKER_PUBLIC_KEYES256 public key that verifies signed grants
PULSATRIX_APPROVAL_MODEenforce (default) · observe · dry-run
PULSATRIX_APPROVAL_FAIL_MODEclosed (default) — broker down ⇒ gated actions deny
PULSATRIX_APPROVAL_TIMEOUT_MSHow long a human has to answer (default 90 000)
Analytics & resolver
VariableNotes
CW_ANALYTICS_DISABLEDtrue skips all local capture
CW_ANALYTICS_PATHSQLite path (default ~/.config/pulsatrix/data/analytics.db)
CW_RESOLVER_DB_PATH / CW_RESOLVER_TTL_SECONDSCross-product ID cache (default 4 h)

#FAQ & troubleshooting

Which LLM does it use?

Whichever you point at it. Pulsatrix is a standard MCP server, so it works with the Claude you already pay for (Desktop, Code, Cowork), or any spec-compliant client — including local models. The tool surface is tuned to stay accurate even on local-class models (Qwen-35B target).

Does it write to my tenant without permission?

No. Every non-GET action needs confirm: true; without it you get a dry-run preview, never a real call. With the approval hook on, the riskiest actions also require a signed human verdict.

Why only 14 tools for 440+ actions?

Local-class models lose tool-selection accuracy past ~15 tools. Pulsatrix groups everything into 14 tools (one per product area) that each expose 25–45 actions via an action parameter — discoverable through cw_meta.

I'm getting a "not configured" error

That connector's env vars aren't set. RMM, CPQ and ScreenConnect are optional — set their variables (see Configuration) to light them up. PSA keeps working regardless.

The approval hook won't start

By design — it refuses any half-configured state. It activates only when PULSATRIX_APPROVAL_URL (https) and one auth method (enrollment token, or mTLS cert + key) are both set. Check the startup log line for what's missing.

How do I check it's connected?

Ask the assistant to run cw_meta(view:"status") — it lists what loaded and flags anything misconfigured.

Can I turn off analytics?

Yes — CW_ANALYTICS_DISABLED=true. The server still works; nothing is captured.

Is this an official ConnectWise product?

No. Pulsatrix is an independent project. ConnectWise, Manage, Asio, Quosal Sell and ScreenConnect are trademarks of ConnectWise, LLC, used here for descriptive interoperability only.

#Custom connectors

We're all-in on ConnectWise — the aim is to cover your PSA, RMM, CPQ and remote stack end to end, not to chase every tool on the market. That focus is exactly why the integration runs deep instead of skin-deep.

Need something ConnectWise-adjacent?

If a tool your shop can't work without plugs into your ConnectWise workflow, tell us what it should do — we build bespoke connectors case by case.

Request a connector