Content
# ConnectWise MCP
Local stdio MCP server exposing **23 read-only ConnectWise Manage tools** to Claude. It wraps the
[`xssentials`](https://github.com/XssentialsAI/xssentials) ConnectWise client and speaks MCP over
stdio. Credentials live in 1Password and are resolved at launch via `op run` — plaintext secrets
never touch disk. Read-only by default, with one audited write — member creation (see [Write Surface](#write-surface-read-only-by-default--one-audited-write)).
These instructions target **Claude Desktop on macOS** (used inside the **Cowork** tab).
> **In a hurry?** The [ELI5 quickstart](#-install-it-like-im-5) below is all most people need. The
> numbered reference sections come after it.
---
## 🧸 Install it like I'm 5
Do these seven steps in order. Copy-paste the gray boxes into the **Terminal** app
(press `⌘ + Space`, type "Terminal", hit Enter).
### Step 1 — Install the two helper apps
This server needs **1Password** (to hold your secret keys safely) and **Homebrew** (a tool
installer). If you don't have Homebrew yet, paste this and follow the prompts:
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
Then install 1Password and its command-line helper:
```bash
brew install --cask 1password
brew install 1password-cli
```
Open the **1Password app → Settings → Developer** and turn on **"Integrate with 1Password CLI."**
(This is what lets the server unlock secrets with your fingerprint instead of asking you to log in.)
### Step 2 — Download the code
```bash
mkdir -p ~/mcp && cd ~/mcp
git clone https://github.com/XssentialsAI/xssentials.git
git clone https://github.com/XssentialsAI/connectwise-mcp.git
```
### Step 3 — Set it up (copy this whole block)
```bash
cd ~/mcp/connectwise-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ../xssentials
pip install -e .
```
### Step 4 — Make your secret-keys file
You need a ConnectWise **API Member** with a public/private key pair (see
[Credentials](#4-credentials-your-own-connectwise-api-keys) for where to click). Put those in a
1Password item, then:
```bash
cp cw.env.example cw.env
open -e cw.env
```
In the file that opens, point each `op://...` line at your 1Password item, save, and close. Don't
paste raw keys — use the `op://` references. (`cw.env` is git-ignored, so it never gets uploaded.)
### Step 5 — Get the two paths Claude needs
Run this and **keep the two lines it prints** — you'll paste them in Step 6:
```bash
echo "op path: $(which op)"
echo "python path: $PWD/.venv/bin/python"
```
### Step 6 — Tell Claude Desktop about the server
In Claude Desktop: **Settings → Developer → Edit Config**. That opens a file called
`claude_desktop_config.json`. Make it look like this, swapping in **your** two paths from Step 5 and
your real home folder:
```json
{
"mcpServers": {
"connectwise": {
"command": "/opt/homebrew/bin/op",
"args": [
"run",
"--env-file=/Users/YOU/mcp/connectwise-mcp/cw.env",
"--",
"/Users/YOU/mcp/connectwise-mcp/.venv/bin/python",
"-m",
"connectwise_mcp"
]
}
}
}
```
Save the file, make sure the **1Password app is unlocked**, then **fully quit and reopen Claude
Desktop** (⌘Q, then open it again).
> ⚠️ **Don't** use the **"+ Add custom connector"** button in Cowork for this. That button is for
> *web* servers on the internet. This server runs **on your Mac**, so it goes in **Edit Config**
> (above) instead.
### Step 7 — Turn it on in Cowork and test it
1. Open the **Cowork** tab in Claude Desktop.
2. Go to **Customize → Connectors**. You'll see **connectwise** in the list — flip its switch **On**.
3. (Optional) Click **Tool permissions** to choose Allow / Ask / Block per tool. ConnectWise is
read-only, so **Allow** is safe for everything.
4. In a Cowork chat, type: **"Run cw_ping"**. You should get a card with the ConnectWise version and
region in a couple of seconds. 🎉
If something breaks, see [Troubleshooting](#troubleshooting).
---
## Reference
### 1. Prerequisites
- **macOS** with **Python 3.10+** (`python3 --version`)
- **1Password app** (desktop) + **1Password CLI** (`op`) with *Developer → Integrate with 1Password
CLI* enabled
- **Claude Desktop** — https://claude.ai/download
### 2. Get the code
Clone all three repos as siblings (the server depends on `xssentials`):
```bash
mkdir -p ~/mcp && cd ~/mcp
git clone https://github.com/XssentialsAI/xssentials.git
git clone https://github.com/XssentialsAI/connectwise-mcp.git
```
### 3. Install
```bash
cd ~/mcp/connectwise-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ../xssentials # satisfies the xssentials dependency, no network fetch
pip install -e .
```
### 4. Credentials (your own ConnectWise API keys)
1. In ConnectWise Manage: **System → Members → API Members** → create an API Member.
2. On that member, generate a **public/private key pair** (the private key is shown once — save it).
3. Your **clientId** is your developer-registration UUID from https://developer.connectwise.com.
Store these in 1Password, then `cp cw.env.example cw.env` and point each `op://vault/item/field`
reference at your item. The five required fields and the auth note are documented inline in
`cw.env.example`. Verify the references resolve:
```bash
op run --env-file=cw.env -- printenv CW_COMPANY_ID # should print your company id
```
### 5. Install into Claude Desktop (Edit Config)
**Settings → Developer → Edit Config** opens `~/Library/Application Support/Claude/claude_desktop_config.json`.
Add the `connectwise` entry shown in [Step 6](#step-6--tell-claude-desktop-about-the-server).
**Use absolute paths** — Claude Desktop launches servers with a minimal `PATH` that excludes
Homebrew, so a bare `"op"` or `"python"` will fail. Find your `op` with `which op` (Apple Silicon →
`/opt/homebrew/bin/op`, Intel → `/usr/local/bin/op`). Unlock 1Password, then quit and reopen Claude
Desktop.
### 6. Enable in Cowork (Customize tab)
Local servers are *installed* via Edit Config but *toggled on* in **Cowork → Customize →
Connectors**, alongside web and custom connectors. Flip **connectwise** on. Use **Tool permissions**
(Allow / Ask / Block) to control individual tools — all read-only here, so Allow is safe.
### 7. Verify
In a Cowork chat: **"Run cw_ping"** → a markdown card with CW version, region (e.g. `NA`), and
`isCloud: True`, in under ~2s.
## Troubleshooting
- **Server won't start / "command not found"** — you used a bare `op` or `python`. Both must be
**absolute paths** in the config (Claude Desktop doesn't inherit your shell `PATH`).
- **Secrets don't resolve / `op` prompts** — open and unlock the 1Password desktop app, and confirm
*Developer → Integrate with 1Password CLI* is enabled.
- **Server isn't in the Customize list** — confirm the JSON is valid and you fully quit (⌘Q) and
reopened Claude Desktop. Check **Settings → Developer** for the server's status/logs.
- **`pip install -e .` can't find xssentials** — run `pip install -e ../xssentials` first.
- **401 from ConnectWise** — re-check the five fields in 1Password; the Basic header is
`<company_id>+<public_key>:<private_key>` and all five fields are required.
## Tool Index
24 tools registered in `connectwise_mcp/__main__.py` (23 read-only + 1 audited write):
| Tool | CW Resource | Use |
|---|---|---|
| `cw_ping` | `/system/info` | Auth canary |
| `cw_find` | parallel cross-resource | Fuzzy lookup across companies, projects, tickets, opps, invoices, POs |
| `cw_company` | `/company/companies` | Single company card by id or name |
| `cw_project_list` | `/project/projects` | Project search with filters |
| `cw_project_status` | `/project/projects/{id}` + AR + open tickets | Single-project rollup |
| `cw_invoice_lookup` | `/finance/invoices/{id}` + product lines | Single-invoice consolidator |
| `cw_invoice_list` | `/finance/invoices` | Invoice search with filters |
| `cw_ar_aging` | `/finance/invoices` | AR aging buckets per company/project |
| `cw_service_tickets` | `/service/tickets` | Service ticket search across boards |
| `cw_ticket_detail` | `/service/tickets/{id}` + notes + time | Single-ticket consolidator |
| `cw_time_entries` | `/time/entries` | Time entry search with filters |
| `cw_opportunity_status` | `/sales/opportunities/{id}` | Single opportunity rollup |
| `cw_purchasing_status` | `/procurement/purchaseorders` | PO header + line items, or per-vendor PO list |
| `cw_pick_ship_status` | `/procurement/products` | Procurement-readiness flags |
| `cw_sales_orders` | `/sales/salesOrders` | Sales order search |
| `cw_purchase_approvals` | n/a | Structured gap message (no approvals endpoint) |
| `cw_inventory` | `/procurement/warehouseBins` | Per-warehouse-bin on-hand quantities |
| `cw_product_catalog` | `/procurement/catalog` | Catalog search by query / manufacturer |
| `cw_configurations` | `/company/configurations` | Configuration items |
| `cw_expense_reports` | `/expense/entries` | Expense entry search |
| `cw_query` | arbitrary GET | Universal escape hatch; nested-scalar projection via `fields="parent/child"` |
| `cw_report` | `/system/reports/{name}` | Run a CW system report as a flat Markdown table |
| `cw_schedule_entries` | `/schedule/entries` | Schedule entry search (assigned-tech bookings) |
| `cw_create_member` ✍️ | `POST /system/members` | **The only write tool** — employee onboarding; dry-run by default, `confirm=true` to create |
## Write Surface (read-only by default + one audited write)
The server is read-only by default. Reads are GET-only and enforced at two layers:
1. **Client-level** — `xssentials.connectwise.client._request` raises `NotImplementedError` on any
non-GET HTTP method.
2. **Validators-level** — `connectwise_mcp._validators.enforce_get_only` guards the `cw_query` path.
The single sanctioned write is **member creation** (`cw_create_member`), which goes through a separate
`client._write_request` that is hard-allowlisted to `/system/members` (`client.WRITE_ALLOWLIST`) and
accepts POST/PATCH only. It forces `employeeIdentifer="XX"` (payroll safety) and `adminFlag=false`, and
defaults to a dry-run preview — it POSTs only when `confirm=true`.
Verify on any commit:
```bash
# No ad-hoc writes leak into tools — every write must route through client._write_request:
grep -nE '_request\("(POST|PUT|PATCH|DELETE)"' connectwise_mcp/__main__.py # expect zero hits
# The write allowlist stays scoped to members:
grep -n 'WRITE_ALLOWLIST' ../xssentials/xssentials/connectwise/client.py # expect /system/members only
```
## License
Internal Xssentials tooling. ConnectWise is a trademark of ConnectWise, LLC.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available