connectwise-platform
README.md
# connectwise-platform-mcp
**MCP server for the ConnectWise Platform (formerly known as Asio).**
An MCP server that lets Claude (Claude Desktop, Claude Code or another MCP
client) work with the ConnectWise Platform partner API, using your own
API credentials. You build it from source and run it yourself.
> **This is not an official ConnectWise product.** It is an independent,
> open-source project. It is not affiliated with, endorsed by or supported by
> ConnectWise. "ConnectWise" and "Asio" are used only to say which API this
> talks to.
There is no shared hosted service: each MSP runs its own copy, with its own
credentials. Most of this README covers running it on your own machine, where
it reads your credentials from a local `.env` file. You can also host your own
copy behind Microsoft Entra sign-in (see
[The HTTP server](#the-http-server-self-hosting)). Either way it talks to the
ConnectWise API directly and never sends your credentials or data anywhere
else.
## Contents
- [Before you start](#before-you-start)
- [Install from source](#install-from-source)
- [Create an API key in ConnectWise Platform](#create-an-api-key-in-connectwise-platform)
- [Configure](#configure)
- [Connect it to Claude](#connect-it-to-claude)
- [Modes: what Claude can see](#modes-what-claude-can-see)
- [The confirm step for high-impact operations](#the-confirm-step-for-high-impact-operations)
- [Per-tool permissions in Claude](#per-tool-permissions-in-claude)
- [Sensitive operations (opt-in)](#sensitive-operations-opt-in)
- [Scope checklist](#scope-checklist)
- [What the error messages mean](#what-the-error-messages-mean)
- [Known limits](#known-limits)
- [Development](#development)
## Before you start
You need:
- **Node.js 22.12 or later.** Check with `node --version`.
- **git.**
- **A ConnectWise developer portal account** (developer.connectwise.com), to
download the ConnectWise Platform API spec. The spec is not included in this
repository (see below).
- **Rights to create API access in ConnectWise Platform:** Primary Admin or
Primary Super Admin, or an application role with both "API Access – Update"
and "API Access – View".
- **Claude Desktop or Claude Code.**
The shell commands below are for macOS and Linux. On Windows, use PowerShell
equivalents (for example `Copy-Item` for `cp`, `New-Item -ItemType Directory`
for `mkdir -p`); the `npm` commands are the same.
## Install from source
### 1. Get the code
```bash
git clone https://github.com/steelbrooktech/connectwise-platform-mcp.git
```
```bash
cd connectwise-platform-mcp
```
```bash
npm ci
```
On npm 12 or later, `npm ci` may warn that install scripts for `esbuild` and
`fsevents` were blocked. That is expected and safe to ignore: both are
development tools, and everything in this README works without those scripts.
### 2. Add the ConnectWise API spec
The server's tools are generated from ConnectWise's OpenAPI spec. That file is
ConnectWise's, so it is not in this repository: you download it yourself.
1. Sign in to the ConnectWise developer portal at developer.connectwise.com.
2. Go to **ConnectWise Platform > Platform Partners > REST API > Current API**.
3. Download the OpenAPI spec file, `currentPartnerAPI.yaml`.
4. Save it in this folder as `docs/connectwise-platform/currentPartnerAPI.yaml`:
```bash
mkdir -p docs/connectwise-platform
```
Then move the downloaded file there. For example, on macOS:
```bash
mv ~/Downloads/currentPartnerAPI.yaml docs/connectwise-platform/
```
`docs/` is listed in `.gitignore`, so the spec is never committed.
This project was built against spec version **3.2.0**. If ConnectWise has
published a newer version, the next step may stop and list the operations that
differ from `config/scope-map.json`. Nothing is guessed: open an issue, or
update the scope map yourself.
### 3. Generate the tools and build
```bash
npm run generate
```
This reads the spec plus `config/scope-map.json`, `config/tool-groups.json` and
`config/api-quirks.json`, and writes the tool catalogue to `src/core/generated/`. It
should end with `Warnings: 0` and `Wrote .../src/core/generated/operations.json`.
```bash
npm run build
```
Optionally, run the tests (no network, no credentials needed):
```bash
npm test
```
## Create an API key in ConnectWise Platform
**Start with read scopes only.** You can add write scopes later if you decide to
use full mode.
1. In ConnectWise Platform, go to **Integrations > API Access**.
2. Click **+ Generate API Access**.
3. Enter an **API Key Name** (for example "Claude MCP, read only") and a
**Description**.
4. From **Scopes**, select the read scopes for the areas you want Claude to see.
The [scope checklist](#scope-checklist) lists them per tool group. For a
first try, Companies, Sites, Devices and Tickets Read are enough.
5. Tick **Consent**, then click **Generate API Access**.
6. Copy the **Client ID** and **Client Secret** straight into your `.env` file
(next section) or a password manager. **The Client Secret is shown only
once.** If you lose it, use **Regenerate**, which replaces both values.
Note: with read scopes only, ConnectWise itself refuses any write, whatever
this server's mode. The scopes on the credential are the real limit; this
server's modes are an extra layer on top.
## Configure
```bash
cp .env.example .env
```
Edit `.env`:
| Setting | Required | Values | Default |
|---|---|---|---|
| `CWP_CLIENT_ID` | yes | from API Access | |
| `CWP_CLIENT_SECRET` | yes | from API Access | |
| `CWP_REGION` | no | `us`, `eu`, `au` | `us` |
| `CWP_MODE` | no | `read_only`, `full` | `read_only` |
| `CWP_ENABLE_SENSITIVE` | no | see [Sensitive operations](#sensitive-operations-opt-in) | empty |
| `CWP_TOOL_GROUPS` | no | comma-separated, for example `devices,tickets` | all groups |
`.env` is listed in `.gitignore`. Never commit it or paste its contents anywhere.
Check that the credential works (one token request and one rate-limit request,
both read-only; prints statuses and scope names, never the token):
```bash
npm run live-check
```
## Connect it to Claude
Find the full path of this folder (you need it below):
```bash
pwd
```
### Claude Code
**Option A: only inside this folder.** The repository has a `.mcp.json` that
starts the server from `dist/`. Run `claude` in this folder. The first time,
Claude Code asks whether you trust the folder (accept), then asks you to
approve the project's MCP server: approve `connectwise-platform`. Until you do,
`claude mcp list` shows it as "Pending approval".
**Option B: in every project.** Register it for your user, with absolute
paths. Replace `/path/to/connectwise-platform-mcp` with the output of `pwd`:
```bash
claude mcp add --scope user --transport stdio connectwise-platform -- node --env-file=/path/to/connectwise-platform-mcp/.env /path/to/connectwise-platform-mcp/dist/index.js
```
Options come before the name `connectwise-platform`; everything after `--` is the command
that starts the server. This reads the credentials from your `.env` file, so the
secret is not copied into Claude Code's own settings. (You could pass them with
`-e KEY=value` instead, but then they are stored in `~/.claude.json`.)
Check it:
```bash
claude mcp list
```
It should show `connectwise-platform` as connected. Inside a Claude Code session, `/mcp`
shows the same.
### Claude Desktop
1. Find the full path of `node`. Claude Desktop does not always see the same
`PATH` as your terminal, so use the full path:
```bash
which node
```
2. Open the config file: in Claude Desktop, **Settings > Developer > Edit
Config**. It is at:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
3. Add this server under `mcpServers` (keep any servers already there). Use
absolute paths only:
```json
{
"mcpServers": {
"connectwise-platform": {
"command": "/full/path/to/node",
"args": [
"--env-file=/path/to/connectwise-platform-mcp/.env",
"/path/to/connectwise-platform-mcp/dist/index.js"
]
}
}
}
```
On Windows, write paths with forward slashes or doubled backslashes, for
example `"C:/Users/you/connectwise-platform-mcp/dist/index.js"`.
4. Quit Claude Desktop completely and start it again.
## Modes: what Claude can see
Set with `CWP_MODE` in `.env`. What each mode exposes comes from
`config/scope-map.json`, not from a hand-kept list. After changing `.env`,
restart Claude Desktop, or in Claude Code run `/mcp` and reconnect
`connectwise-platform`, so the server reads it again.
| Mode | Tools | Operations |
|---|---|---|
| `read_only` (default) | 11 read tools (`cwp_devices`, `cwp_tickets`, ...) plus `cwp_describe_operation` | 105 reads. No writes. The two sensitive reads stay hidden unless opted in. |
| `full` | the same 11 read tools, 7 write tools (`cwp_devices_write`, `cwp_tickets_write`, ...), `cwp_confirm_write` plus `cwp_describe_operation` | 105 reads and 37 writes. Ten writes need a confirm step. |
- Each tool covers one area. Claude picks an `operation` and passes its
parameters in `params`; `cwp_describe_operation` shows any operation's
parameters, paging and required scope.
- `CWP_TOOL_GROUPS` narrows either mode to some areas, for example
`devices,tickets`. Groups: automation, backup_dashboard, cloud_backup,
companies, custom_fields, devices, patching, platform, policies,
security_cases, tickets.
- Large lists are paged. When more results exist, a result includes
`next_page`; Claude passes those values back to get the next page.
## The confirm step for high-impact operations
In `full` mode, ten operations never run through their own tool. Their
`_write` tool only returns a preview, and a separate tool, `cwp_confirm_write`,
carries them out:
| Operation | Tool | What it does |
|---|---|---|
| `schedule_task` | `cwp_automation_write` | Runs a script or task on client endpoints. Highest-impact operation. |
| `delete_device_endpoints_uninstall` | `cwp_devices_write` | Uninstalls the agent from endpoints. |
| `post_api_platform_v2_device_endpoints_move` | `cwp_devices_write` | Moves devices to another company and site, which can change which policies and deployments apply to them. The move can take 15 to 30 minutes to show. |
| `cancel_backup_subscription` | `cwp_cloud_backup_write` | Cancels a cloud backup subscription. |
| `suspend_backup_subscription` | `cwp_cloud_backup_write` | Suspends a cloud backup subscription. |
| `revoke_customer_access` | `cwp_cloud_backup_write` | Revokes a customer user's access to a backup subscription. |
| `delete_api_platform_v1_service_ticketing_tags_id` | `cwp_tickets_write` | Deletes a ticket tag. |
| `put_api_platform_v1_company_companies_company_id_custom_fields` | `cwp_custom_fields_write` | Sets custom field values on a company. May trigger automatic deployment to every device in the company if the field drives a monitor. |
| `put_api_platform_v1_company_sites_site_id_custom_fields` | `cwp_custom_fields_write` | Sets custom field values on a site. May trigger automatic deployment to every device at the site if the field drives a monitor. |
| `put_api_platform_v2_device_endpoints_endpoint_id_custom_fields` | `cwp_custom_fields_write` | Sets custom field values on a device. May trigger automatic deployment to this device if the field drives a monitor. |
Why custom field values: a common setup is a company field such as
"BlackPoint Enabled" that a monitor watches, so ticking it installs software
on every device in that company. The API cannot show which fields drive
monitors, so every custom field value write is treated as possibly
high-impact.
How it works:
1. Calling the operation through its `_write` tool returns a **preview** and
changes nothing. It never does more, even if Claude passes `confirm`. The preview makes
no network call at all (not even for a token). It has a plain-English
headline (for example "This will schedule a script on 2 endpoints"), target
counts, the exact request, the region, the console scope needed, and the
read tools Claude should use to look up names for the IDs.
For custom field writes, the headline lists each field (by ID) with its
new value, and any value that looks like a token, key or opaque ID
(16 or more characters, no spaces, letters and digits) is masked to its
first 4 characters, in the request shown as well. The real value is still
sent on confirm.
2. The preview tells Claude to stop there. It says that an instruction given
before the preview existed is not approval, even one asking for this exact
change. Claude should look up the device, company and script names (and,
for custom field writes, the field names and their current values), show
them to you with the preview, end its turn, and wait for you.
3. Only after you reply with an explicit yes, Claude calls `cwp_confirm_write`
with the same tool, operation and parameters, within **10 minutes** of the
preview.
4. Each preview can be confirmed **once**. Any change to the parameters gets a
fresh preview instead. Previews are kept in memory only, so restarting the
server means a new preview.
Step 2 is an instruction to Claude, and Claude can still get it wrong. What
actually stops it depends on your app; see
[Making sure a person approves](#making-sure-a-person-approves).
The other 27 writes run when called (in `full` mode only). No write is ever
retried after a timeout or a server error: the message says the outcome is
unknown and to check in ConnectWise before trying again.
## Making sure a person approves
The confirm step stops a change from running on the first call. On its own,
it cannot tell whether you or Claude approved the preview. Two settings close
that gap in Claude Code.
**The confirm tool always asks, in Claude Code.** `cwp_confirm_write` is
marked with `_meta["anthropic/requiresUserInteraction"]: true`. Claude Code
then asks you before every call to it, in every permission mode, including
auto mode and bypass mode, and an allow rule cannot skip it. The prompt shows
the exact tool, operation and parameters about to run. See
[Require approval for a specific tool](https://code.claude.com/docs/en/mcp).
This is a Claude Code feature, not part of the MCP standard. Claude Desktop
and other MCP clients ignore it, so there the preview instructions and the
app's own approval settings are all you have.
**Recommended: an ask rule for every write tool.** Add this to
`~/.claude/settings.json` (all your projects) or `.claude/settings.local.json`
(this project, just you):
```json
{
"permissions": {
"ask": ["mcp__connectwise-platform__cwp_*_write"]
}
}
```
Claude Code then asks before any write, including the 27 that have no
preview, such as renaming a device. Ask rules apply in auto mode as well, and
an ask rule wins over an allow rule that matches the same tool. If your server
has a different name in your MCP config, use that name in place of
`connectwise-platform`. To check the rule works, ask Claude for a harmless write and
look for the prompt.
What each layer can and cannot do:
| Layer | Where it works | What it guarantees |
|---|---|---|
| Credential scopes | Everywhere | The API refuses anything the credential has no scope for. |
| Preview, then `cwp_confirm_write` | Every client | A confirm operation never runs on its first call, and only runs with the exact preview. Approval is an instruction to Claude, not enforced. |
| `requiresUserInteraction` on `cwp_confirm_write` | Claude Code only | You are asked before every confirm operation runs, whatever the mode. In `dontAsk` mode the call is denied. |
| Ask rule for `cwp_*_write` | Claude Code, on the machine where you set it | You are asked before every write, previews included. |
## Per-tool permissions in Claude
Safety comes in layers, strongest first:
1. **The scopes on your ConnectWise credential.** This is the real
enforcement: without a scope, the API refuses the call.
2. **This server's mode and confirm step.** `read_only` hides every write
tool. In `full` mode, high-impact operations return a preview first.
3. **Your Claude app's own permission prompts and rules**, described below.
Every tool tells Claude what kind it is. Read tools are marked
`readOnlyHint: true`; `_write` tools are marked `destructiveHint: true` and
`readOnlyHint: false`. Under the MCP specification these are hints, and
clients must treat them as untrusted, so do not rely on them as a control.
Neither Claude app documents using them to approve or block calls.
**Claude Code.** In the default mode, Claude Code asks before the first use
of each tool. You can set rules in `.claude/settings.json` (shared with the
project), `.claude/settings.local.json` (just you, this project) or
`~/.claude/settings.json` (just you, everywhere), and see them all with
`/permissions`. MCP tools are named `mcp__<server>__<tool>`, so with the
server named `connectwise-platform`:
```json
{
"permissions": {
"allow": ["mcp__connectwise-platform__cwp_devices", "mcp__connectwise-platform__cwp_describe_operation"],
"ask": ["mcp__connectwise-platform__cwp_*_write"]
}
}
```
`mcp__connectwise-platform` (or `mcp__connectwise-platform__*`) covers every tool on the server.
Rules match whole tools, not the operation inside one, so the confirm step
is what separates high-impact operations from routine ones within a write
tool. See [Claude Code permissions](https://code.claude.com/docs/en/permissions)
and [Claude Code MCP](https://code.claude.com/docs/en/mcp).
**Claude Desktop.** For connectors, Claude Desktop lets you set each tool to
Always allow, Needs approval or Blocked (see
[Use connectors](https://support.claude.com/en/articles/11176164-use-connectors-to-extend-claude-s-capabilities)).
Anthropic's pages on local servers added through `claude_desktop_config.json`
or as desktop extensions do not currently describe per-tool settings for
them, so check what your version of the app shows. Keep write tools on
approval rather than Always allow.
## Sensitive operations (opt-in)
Two reads are hidden in **both** modes unless you name them in
`CWP_ENABLE_SENSITIVE` (comma-separated):
```
CWP_ENABLE_SENSITIVE=get_endpoint_session_details,post_device_installation_token
```
- `get_endpoint_session_details` returns remote-access session details for
endpoints. Needs "Platform - Remote Access - Read".
- `post_device_installation_token` generates or returns an **agent install
token**, which can enroll new devices into your tenant. ConnectWise lets any
credential with **"Platform - Devices - Read"** call it, so an ordinary
read-only credential could create install tokens. That is why this server
hides it by default, even in `read_only` mode.
Opt in only to the one you need, and only if you understand what it exposes.
## Scope checklist
Scopes as named under **Integrations > API Access**, per tool group. "Used by"
says whether read or write operations need the scope (write tools exist only in
`full` mode).
Status:
- **verified**: tested on a live tenant. The operation returned 200 with the
scope on the credential and 403 after the scope was removed. Ten scopes were
removed together in one run.
- **documented**: ConnectWise's documentation names this scope for the
operation. Not tested by removing it.
- **inferred**: the documentation names no scope. The scope here is a best
guess from the API area and method.
- **unknown**: a guess was tested and was wrong. The message on a 403 says what
was tried.
A mixed status, such as "verified (5), inferred (2)", means the scope's
operations differ. Run `cwp_describe_operation` for the detail on each one.
<!-- scope-checklist:start -->
**automation** (`cwp_automation`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - Automation - Create | write | 1 | inferred |
| Platform - Automation - Read | read | 7 | verified (5), inferred (2) |
**backup_dashboard** (`cwp_backup_dashboard`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Backup Dashboard - Read alarms | read | 1 | documented |
| Backup Dashboard - Read backup jobs | read | 1 | documented |
| Backup Dashboard - Read DR readiness | read | 1 | documented |
**cloud_backup** (`cwp_cloud_backup`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - Backup - Create | write | 6 | documented |
| Platform - Backup - Delete | write | 3 | documented |
| Platform - Backup - Read | read | 8 | documented |
**companies** (`cwp_companies`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - Companies - Read | read | 2 | verified |
| Platform - Companies - Write | write | 3 | inferred |
| Platform - Contacts - Write | write | 1 | inferred |
| Platform - DataMapping - Read | read | 1 | verified |
| Platform - Sites - Read | read | 4 | verified |
| Platform - Sites - Write | write | 3 | inferred |
**custom_fields** (`cwp_custom_fields`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - Custom Fields Definitions - Read | read | 1 | verified |
| Platform - Custom Fields Definitions - Write | write | 2 | inferred |
| Platform - Custom Fields Values - Read | read | 3 | verified |
| Platform - Custom Fields Values - Write | write | 3 | inferred |
| (not known) | read | 1 (get_api_platform_v1_custom_field_definitions_definition_id) | unknown |
**devices** (`cwp_devices`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - DeviceGroups - Read | read | 3 | documented |
| Platform - Devices - Delete | write | 1 | documented |
| Platform - Devices - Read | read (includes opt-in post_device_installation_token) | 20 | documented (19), inferred (1) |
| Platform - Devices - Write | write | 2 | documented (1), inferred (1) |
| Platform - Remote Access - Read | read (includes opt-in get_endpoint_session_details) | 1 | documented |
| Platform - Suspension - Read | read | 2 | verified |
**patching** (`cwp_patching`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - OS Patching - Read | read | 3 | documented |
| Platform - Os Patching Endpoint Patches (read) | read | 1 | verified |
| Platform - Third Party Patching - Read | read | 2 | documented |
| Security - Vulnerabilities - Read | read | 1 | verified |
**platform** (`cwp_platform`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| (none needed) | read | 1 (get_current_rate_limit) | none needed |
**policies** (`cwp_policies`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - Policy - Read | read | 12 | documented |
**security_cases** (`cwp_security_cases`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Security - Cases - Read | read | 17 | documented |
| Security - Cases - Write | write | 2 | documented |
**tickets** (`cwp_tickets`)
| Console scope | Used by | Operations | Status |
|---|---|---|---|
| Platform - Tickets - Create | write | 3 | inferred |
| Platform - Tickets - Read | read | 14 | verified |
| Platform - Tickets - Update | write | 7 | inferred |
<!-- scope-checklist:end -->
This table is generated from `config/scope-map.json`
(`npm run scope-checklist -- --write`). A test checks that it is up to date.
## What the error messages mean
The server turns HTTP errors into plain messages. It never puts the token or
secret in a message.
| Status | When | What to do |
|---|---|---|
| **401** | The token request was refused, or the API refused the token even after a fresh one. | Check `CWP_CLIENT_ID`, `CWP_CLIENT_SECRET` and `CWP_REGION`, and that the API access is still enabled under Integrations > API Access. |
| **403** | The credential lacks the scope for this operation. | The message names the console scope from the scope checklist and says how sure that is (verified, documented, best guess or unknown). Add the scope under Integrations > API Access. |
| **423** | ConnectWise locked token requests because new tokens were requested too often. | Wait a few minutes. The server caches its token and keeps using it until shortly before it expires, so this is rare. |
| **429** | Rate limit reached. ConnectWise documents 500 requests per 5 minutes per token; in testing the remaining count was reported separately per route. | The server waits and retries (up to 60 seconds, at most twice). If the wait would be longer, it stops and says when to try again. Results warn when a route is down to its last 10%. |
| **503** | "Service not implemented": the service is not available to this account or region. | The message says the service may not be enabled for your account or region. Seen for the Cloud Backup operations. Not retried. Other 503s on reads are retried once. |
Some operations answer **404** when there is simply nothing to return (for
example no open threats, or no metrics stored for an endpoint). For those, the
server returns an empty result with a note quoting ConnectWise's message
instead of an error. The list, with the evidence for each, is in
`config/api-quirks.json`. A 404 from any other operation is an error.
## Known limits
Tested against one live tenant, read-only. Not everything could be exercised:
- **No write operation (37) has been called against a live tenant.** They are
tested against a mock server only, for method, path and body.
- **The two sensitive reads** have not been called live.
- **Never returned 200 live** (so their response shapes are untested):
- Cloud Backup: all 8 reads. `get_subscriptions`, `get_alerts_health` and
`get_data_centers` answered 503 "Service not implemented"; the 5 detail
reads need a subscription ID from `get_subscriptions`.
- Security cases: the 13 per-threat reads (there were no open threats to read)
and the 3 summaries plus `list_cases` (only seen as "no data").
- The 2 task-instance reads (`task_instance_summary`,
`task_instance_execution_results`): the task list returns no task IDs.
- `get_api_platform_v1_custom_field_definitions_definition_id`: 403 with the
scope tried; the right scope is unknown.
- Seen only as "no data" (404) for the one endpoint tested: CPU, memory and
disk usage, the memory and processor time series, endpoint relations,
effective policy override, and hypervisor hosts.
- `post_api_platform_v1_mapping_mappings_lookup` was only called with an
empty request body.
- Only the `us` region has been tested.
- The legacy ConnectWise API is out of scope.
## Development
```bash
npm test
```
Runs the unit tests against mocks. Tests that need the ConnectWise spec are
skipped if it is not present.
These scripts call **your own tenant**, read-only, with the credentials in
`.env`. They print statuses, counts and field names, never record contents:
- `npm run live-check`: token and rate-limit check.
- `npm run build && npm run live-tools`: a few read tools through the server.
- `npm run build && npm run live-smoke`: every non-sensitive read once. Writes
a report to `test/live/reports/` (gitignored).
Other files worth knowing:
- `config/scope-map.json`: per-operation scope, read or write, confirm and
sensitive flags.
- `config/api-quirks.json`: corrections to the spec seen live, each with its
evidence.
- `config/tool-groups.json`: which API area goes in which tool.
### How the code is laid out
- `src/core/`: the library. Settings, the operation catalogue, mode
filtering, the API client and token, the confirm gate and previews, and
`store.ts`, which holds the token and previews either in memory or in a
key-value service (encrypted token, previews as fingerprints only).
- `src/index.ts`: the stdio server that Claude Desktop and Claude Code start.
- `src/remote.ts` and `src/remote/`: an MCP server over Streamable HTTP.
### The HTTP server (self-hosting)
```bash
npm run build && npm run start:remote
```
It listens on `http://127.0.0.1:8787/mcp` (change the port with
`CWP_HTTP_PORT`). Without sign-in (`CWP_AUTH=none`, the default) it listens on
this machine only. With `CWP_AUTH=entra`, every call needs a Microsoft Entra
token from an allowed user; see `src/remote/entra.ts` for its settings and
`plans/hosting-setup.md` for hosting it on Vercel (`api/mcp.ts`). It reads the same `CWP_*` settings, plus:
| Variable | Meaning |
|---|---|
| `CWP_STORE` | `memory` (default) or `redis-rest` |
| `CWP_STORE_URL`, `CWP_STORE_TOKEN` | For `redis-rest`: a Redis REST endpoint (Upstash-style) and its token |
| `CWP_STORE_ENCRYPTION_KEY` | For `redis-rest`: 32 random bytes, base64 (`openssl rand -base64 32`). Encrypts the cached ConnectWise token |
| `CWP_STORE_NAMESPACE` | Optional key prefix (default `connectwise-platform`) |
## Licence
MIT. See `LICENSE`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues