{"id":"b7156697-052b-4bc3-87c3-75db37df47e3","slug":"connectwise-manage-mcp","kind":"mcp","name":"connectwise-manage-mcp","summary":"MCP server for ConnectWise Manage (PSA) — tickets, companies, contacts, projects, and time entry tools for AI assistants","owner":"WYRE-AI","repoUrl":"https://github.com/WYRE-AI/connectwise-manage-mcp","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":27,"match":null,"fewStars":false,"format":"mcp","createdAt":"2026-02-13T16:18:48.000Z","addedAt":"2026-10-14T05:02:43.592Z","logoUrl":"https://avatars.githubusercontent.com/u/288510302?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","memory-knowledge","utilities"],"tags":["ai-tools","claude","connectwise-manage","msp","typescript","wyre-technology","msp-mcp"],"runtime":"docker","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":true,"reviewed":false,"syncedAt":"2026-10-14T19:31:28.765Z","sample":false,"url":"https://conduits.dev/tools/connectwise-manage-mcp","language":"TypeScript","transports":["stdio"],"install":{"docker":"ghcr.io/wyre-technology/connectwise-manage-mcp:v1.8.6"},"requirements":{"env":[{"name":"CW_MANAGE_COMPANY_ID","secret":false,"required":true,"description":"ConnectWise company identifier (the short name used to log in)"},{"name":"CW_MANAGE_PUBLIC_KEY","secret":true,"required":true,"description":"ConnectWise API member public key (Members > API Members)"},{"name":"CW_MANAGE_PRIVATE_KEY","secret":true,"required":true,"description":"ConnectWise API member private key"},{"name":"CW_MANAGE_CLIENT_ID","secret":true,"required":true,"description":"Client ID from the ConnectWise Developer Portal (developer.connectwise.com)"},{"name":"CW_MANAGE_URL","secret":false,"required":false,"description":"ConnectWise API base URL — region-specific (api-na/eu/au.myconnectwise.net) or self-hosted"},{"name":"MCP_TRANSPORT","secret":false,"required":false,"description":"Transport mode for the server. Set to 'stdio' for local CLI use; the image defaults to 'http' for gateway hosting."},{"name":"AUTH_MODE","secret":false,"required":false,"description":"Credential source: 'env' reads vars locally, 'gateway' expects header injection from the WYRE MCP Gateway."},{"name":"LOG_LEVEL","secret":false,"required":false,"description":"Log verbosity: debug, info, warn, error"}],"upstreamMayCharge":false},"tools":null,"readme":"# ConnectWise Manage MCP Server\n\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen.svg)](https://nodejs.org/)\n\n**Let your AI assistant work directly with ConnectWise Manage.** Search tickets, log time, look up companies and contacts, manage projects — through natural conversation instead of clicking through the CWM interface.\n\nThis is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that gives Claude (or any MCP-compatible AI) 83 tools covering the daily operations ConnectWise Manage shops depend on. Works with both **cloud-hosted and self-hosted** CWM instances — just point it at your server.\n\n> **Part of the [MSP Claude Plugins](https://github.com/wyre-technology/msp-claude-plugins) ecosystem** — a growing suite of AI integrations for the MSP stack including [Autotask](https://github.com/wyre-technology/autotask-mcp), [Datto RMM](https://github.com/wyre-technology/datto-rmm-mcp), [IT Glue](https://github.com/wyre-technology/itglue-mcp), [HaloPSA](https://github.com/wyre-technology/halopsa-mcp), [NinjaOne](https://github.com/wyre-technology/ninjaone-mcp), [Huntress](https://github.com/wyre-technology/huntress-mcp), and more. Built by MSPs, for MSPs.\n\n## One-Click Deployment\n\n[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/WYRE-AI/connectwise-manage-mcp/tree/main)\n\n[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/WYRE-AI/connectwise-manage-mcp)\n\n> **Note on registry auth:** This server depends only on public npm packages, so the Cloudflare and DigitalOcean cloud builders install its dependencies anonymously — no token is required for one-click deploy. (If a future release adds a private `@wyre-ai/*` dependency, you would supply a GitHub PAT with `read:packages` as a build variable — `NODE_AUTH_TOKEN` for Cloudflare Workers, a build-time `GITHUB_TOKEN` secret for DigitalOcean.)\n>\n> **Installing the published package:** The released package is published to the [GitHub Packages](https://github.com/WYRE-AI/connectwise-manage-mcp/pkgs/npm/connectwise-manage-mcp) npm registry, which requires authentication on every install (even for public packages). To install it, authenticate npm to `npm.pkg.github.com` with a GitHub PAT that has `read:packages`:\n>\n> ```bash\n> export NODE_AUTH_TOKEN=$(gh auth token)\n> npm install @wyre-ai/connectwise-manage-mcp\n> ```\n\nFor deploying to **Azure Container Apps** with Entra ID OAuth 2.1, see [AZURE_ACA_DEPLOYMENT.md](AZURE_ACA_DEPLOYMENT.md).\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Required | Description |\n|----------|----------|-------------|\n| `CW_MANAGE_COMPANY_ID` | For stdio and `AUTH_MODE=env` | Your ConnectWise company identifier |\n| `CW_MANAGE_PUBLIC_KEY` | For stdio and `AUTH_MODE=env` | API member public key |\n| `CW_MANAGE_PRIVATE_KEY` | For stdio and `AUTH_MODE=env` | API member private key |\n| `CW_MANAGE_CLIENT_ID` | For stdio and `AUTH_MODE=env` | Client ID from [ConnectWise Developer Portal](https://developer.connectwise.com/) |\n| `CW_MANAGE_URL` | No | API base URL (see below) |\n| `CW_MANAGE_REJECT_UNAUTHORIZED` | No | Set to `false` for self-signed certs (default: `true`) |\n| `MCP_TRANSPORT` | No | `stdio` (default) or `http` |\n| `MCP_HTTP_PORT` | No | HTTP port (default: `8080`) |\n| `MCP_HTTP_HOST` | No | Bind address when `MCP_TRANSPORT=http`. Defaults to `127.0.0.1`. A configured `CONDUIT_S2S_SECRET` honors this value. The container image sets `0.0.0.0`. |\n| `CONDUIT_S2S_SECRET` | Yes for HTTP | Service-to-service secret. The HTTP server logs an error and exits non-zero when this is empty or a known placeholder (`changeme`, `secret`, `your-s2s-secret`, `replace-me`, and similar). Compose will not start when it is unset. Never commit a real value. Leave it empty in `.env.example`. |\n| `MCP_ALLOW_INSECURE_DEV` | No | Set to `1` only for local development to start HTTP without `CONDUIT_S2S_SECRET`. The process logs a warning, does not enforce `X-Gateway-S2S`, and listens on `127.0.0.1` only (a wider `MCP_HTTP_HOST` is overridden). A placeholder secret is still refused. |\n| `AUTH_MODE` | No | `env` (default) or `gateway`. Gateway mode requires `X-CW-Company-Id`, `X-CW-Public-Key`, `X-CW-Private-Key`, and `X-CW-Client-Id` on each `/mcp` request. `env` uses `CW_MANAGE_*` after S2S auth succeeds. |\n\n`/health` stays unauthenticated and does not read ConnectWise credentials. The stdio transport does not use `CONDUIT_S2S_SECRET`.\n\n### HTTP authentication\n\nWith `MCP_TRANSPORT=http` and `CONDUIT_S2S_SECRET` set, every `/mcp` request must include `X-Gateway-S2S`. The header value is:\n\n```text\nt=<unix seconds>,v1=<hex hmac-sha256>\n```\n\n`v1` is HMAC-SHA256 (hex, lowercase) of the ASCII string `t=<unix seconds>`, keyed with `CONDUIT_S2S_SECRET`. The timestamp must be within 300 seconds of the server clock. A missing or invalid header returns 401. The process never writes the secret to logs.\n\n```text\nX-Gateway-S2S: t=1710000000,v1=<hmac-sha256(\"t=1710000000\")>\n```\n\nIn `AUTH_MODE=gateway`, `/mcp` also requires `X-CW-Company-Id`, `X-CW-Public-Key`, `X-CW-Private-Key`, and `X-CW-Client-Id` (`X-CW-URL` is optional). Those headers are the credential source for that request. A request missing any required header returns 401.\n\nEntra OAuth discovery routes stay available when `MCP_OAUTH_ENABLED=true`. `POST /mcp` still requires a valid `X-Gateway-S2S` header when the secret is set.\n\n### API Base URL (`CW_MANAGE_URL`)\n\n| Instance Type | URL |\n|---------------|-----|\n| Cloud (North America) | `https://api-na.myconnectwise.net` (default) |\n| Cloud (Europe) | `https://api-eu.myconnectwise.net` |\n| Cloud (Australia) | `https://api-au.myconnectwise.net` |\n| **Self-hosted** | `https://cwm.yourcompany.com` |\n\nFor self-hosted instances, set `CW_MANAGE_URL` to your server's base URL. The server automatically appends `/v4_6_release/apis/3.0` unless the URL already contains that path.\n\nIf your self-hosted instance uses a self-signed certificate, also set `CW_MANAGE_REJECT_UNAUTHORIZED=false`.\n\n### Getting Your API Keys\n\n1. Log in to your ConnectWise Manage instance\n2. Navigate to **System > Members > API Members**\n3. Create a new API member with appropriate permissions\n4. Generate API keys for the member\n5. Get your Client ID from the [ConnectWise Developer Portal](https://developer.connectwise.com/)\n\n## Available Tools\n\n### Interactive Ticket Card (MCP Apps)\n\n`cw_get_ticket` renders as an interactive card in MCP Apps hosts\n(Claude Desktop/web) with an in-card \"Add note\" round-trip via\n`cw_add_ticket_note` that always posts internal-only (`internalAnalysisFlag`)\nnotes; plain-JSON behavior is unchanged in other hosts. The card is neutral by\ndefault and brandable via `window.__BRAND__` injection or `MCP_BRAND_*` env\nvars (`MCP_BRAND_NAME`, `MCP_BRAND_LOGO_URL`, `MCP_BRAND_PRIMARY_COLOR`,\n`MCP_BRAND_ACCENT_COLOR`, `MCP_BRAND_BG`, `MCP_BRAND_TEXT`) — no rebuild\nneeded.\n\n### Tickets\n- `cw_search_tickets` — Search service tickets with conditions\n- `cw_get_ticket` — Get a ticket by ID\n- `cw_get_ticket_configurations` — List configuration references associated with a service ticket (paged)\n- `cw_update_ticket_configurations` — Add/remove ticket configuration associations in an ordered batch, reporting every outcome\n- `cw_create_ticket` — Create a new service ticket. Optional `parentTicketId` (the same field `cw_get_ticket` returns) creates the ticket as a child of that parent.\n- `cw_update_ticket` — Update a ticket (JSON Patch)\n- `cw_get_ticket_notes` — Get all notes on a ticket (including child ticket notes)\n- `cw_add_ticket_note` — Add a note to a ticket (discussion, internal, or resolution). Optional `emailContactFlag`, `emailResourceFlag`, `emailCcFlag`, and `emailCc` control who is emailed. **Nothing is emailed unless one of those flags is set** (or you set `processNotifications`). Omitting them does not email the contact. The create response reports `internalFlag` / `externalFlag` from the note type that was stored, so an internal note is not also reported as external.\n\n### Companies\n- `cw_search_companies` — Search companies\n- `cw_get_company` — Get a company by ID\n- `cw_create_company` — Create a new company\n- `cw_update_company` — Update a company (JSON Patch)\n\n### Contacts\n- `cw_search_contacts` — Search contacts. Filter contact type with `typeName`, `typeId`, or `childConditions` (for example `types/name = \"Primary\"`). `types` / `types/name` in `conditions` is invalid and returns 400 `ApiFindCondition` because type is a child collection; the tool moves those clauses to `childConditions`. Contacts have `firstName` and `lastName`, not `name`. String values use double quotes (`firstName = \"Ada\"`); single quotes are accepted and rewritten.\n- `cw_get_contact` — Get a contact by ID\n- `cw_create_contact` — Create a new contact\n- `cw_update_contact` — Update a contact (JSON Patch on `PATCH /company/contacts/{id}`). Paths include `firstName`, `lastName`, `title`, `inactiveFlag`, `site`, `communicationItems` (email and phone), and `customFields`. Contact types are not on this patch.\n- `cw_update_contact_types` — Add or remove a contact type such as \"Decision Maker\" (`POST` / `DELETE /company/contacts/{id}/typeAssociations`). Adding a type the contact already has does not create a duplicate.\n\n### Projects\n- `cw_search_projects` — Search projects\n- `cw_get_project` — Get a project by ID\n- `cw_create_project` — Create a new project\n- `cw_search_project_tickets` — Search tickets under a project\n- `cw_get_project_ticket` — Get a specific project ticket by ID\n- `cw_get_project_ticket_notes` — Get all notes on a project ticket (including child ticket notes)\n- `cw_add_project_ticket_note` — Add a note to a project ticket (discussion, internal, or resolution)\n\n### Time Entries\n- `cw_search_time_entries` — Search time entries\n- `cw_get_time_entry` — Get a time entry by ID\n- `cw_create_time_entry` — Create a new time entry\n- `cw_update_time_entry` — Correct a time entry (JSON Patch on `PATCH /time/entries/{id}`). Use this to fix `actualHours` when a metered agreement rounds to 0.25.\n- `cw_delete_time_entry` — Delete a time entry (`DELETE /time/entries/{id}`). Manage rejects deletes of entries that are already billed.\n\n### Schedule Entries\n- `cw_search_schedule_entries` — Search booked resource time\n- `cw_list_schedule_types` — List schedule types (Service, Project, Sales, Meeting)\n- `cw_list_schedule_statuses` — List schedule statuses (Tentative, Firm)\n- `cw_create_schedule_entry` — Book resource time against a ticket, activity or project ticket\n- `cw_update_schedule_entry` — Update a schedule entry (JSON Patch)\n\n### Members\n- `cw_search_members` — Search members/technicians\n- `cw_get_member` — Get a member by ID\n\n### Configuration Items\n- `cw_search_configurations` — Search configuration items (assets)\n- `cw_get_configuration` — Get a configuration item by ID\n\n### Service Reference Data\n- `cw_list_boards` — List service boards\n- `cw_list_priorities` — List ticket priorities\n- `cw_list_statuses` — List statuses for a board\n\n### Activities\n- `cw_search_activities` — Search activities\n- `cw_get_activity` — Get an activity by ID\n- `cw_create_activity` — Create a new activity\n\n### Agreements\n- `cw_search_agreements` — Search agreements\n- `cw_get_agreement` — Get an agreement by ID\n- `cw_get_agreement_additions` — Get additions (line items) on an agreement\n- `cw_update_agreement_addition` — Update an addition with JSON Patch (quantity, effectiveDate, cancelledDate, billCustomer, etc.); supports a `dryRun` preview that makes no write\n- `cw_create_agreement_addition` — Create a new addition on an agreement\n- `cw_search_agreement_recaps` — Search agreement billing recaps (used, remaining, overrun, next invoice)\n- `cw_get_agreement_recap` — Get the billing recap for one agreement\n\n### Invoices\n- `cw_search_invoices` — Search invoices (CW `conditions`, `orderBy` e.g. `id desc`)\n- `cw_get_invoice` — Get an invoice by ID\n- `cw_update_invoice` — Update one invoice with JSON Patch (`PATCH /finance/invoices/{id}`); returns the updated invoice in the same shape as `cw_get_invoice`. Allowed paths (leading slash optional): `status/id` or `status` (`{ id }`), `attention`, `dueDate`, `date`, `reference`, `customerPO`, `internalNotes`, `billToCompany` and `billingSite` (each as `{ id }` or via `/id`; `billToSite` is accepted as an alias for Manage's `billingSite`). Manage invoices have no `billToContact` field — use `attention` for the contact name. Custom and payment fields are rejected before Manage is called. `dryRun: true` GETs the invoice, applies the patch in memory, and returns `{ dryRun: true, saved: false, preview }` with no write. No batch mode — loop over ids (concurrency 3–5) and report per invoice. Resolve billing status names to ids first; never guess an id. Manage 400/401/403/404/409 errors pass through; 429 is retried once, and 5xx is retried once only when every operation is replace (a PATCH that includes add or remove retries only on 429). Requires the API member's role to have **Finance › Invoices: Edit**.\n\n### Procurement\n- `cw_search_purchase_orders` — Search purchase orders\n- `cw_get_purchase_order` — Get a purchase order by ID\n- `cw_get_purchase_order_items` — Get the line items on a purchase order\n- `cw_search_procurement_products` — Search procurement product items (SKUs placed on a ticket, project, order or opportunity)\n- `cw_get_procurement_product` — Get a procurement product item by ID\n\n### Sales Orders\n- `cw_search_sales_orders` — Search sales orders\n- `cw_get_sales_order` — Get a sales order by ID\n- `cw_get_sales_order_products` — Get the product items on a sales order\n\n### Opportunities\n- `cw_search_opportunities` — Search opportunities\n- `cw_get_opportunity` — Get an opportunity by ID\n- `cw_search_opportunity_forecasts` — Search opportunity forecast lines\n- `cw_search_opportunity_notes` — Search notes on an opportunity\n- `cw_search_sales_stages` — List sales pipeline stages\n\n### Catalog (Products)\n- `cw_search_catalog_items` — Search product catalog items\n- `cw_get_catalog_item` — Get a catalog item by ID\n- `cw_create_catalog_item` — Create a new catalog item\n- `cw_update_catalog_item` — Update a catalog item (JSON Patch)\n- `cw_list_catalog_categories` — List catalog categories\n- `cw_list_catalog_subcategories` — List catalog subcategories\n- `cw_list_manufacturers` — List manufacturers\n\n### Procurement Inventory\nWarehouse stock and Inventory Adjustments. An adjustment is the only supported way to change on-hand quantities through the API: create the header, add the lines, then close it to post.\n- `cw_list_warehouses` — List inventory warehouses\n- `cw_list_warehouse_bins` — List warehouse bins, optionally for one warehouse\n- `cw_get_inventory_on_hand` — Report every item with non-zero on-hand per bin, negatives included, with unit cost and extended value\n- `cw_list_adjustment_types` — List inventory adjustment types\n- `cw_create_adjustment` — Create an adjustment header (moves no stock)\n- `cw_add_adjustment_detail` — Add one signed adjustment line, with serial numbers for serialised items\n- `cw_get_adjustment` — Get an adjustment header with all of its detail lines\n- `cw_close_adjustment` — Post the adjustment. **Not reversible except by a counter-adjustment**\n\nTwo limits of the ConnectWise API are worth knowing before using these. There is no `summary` field on an adjustment: the free-text fields are `reason` (max 100 characters) and `notes`. And no endpoint exposes an average cost, so the `unitCost` reported by `cw_get_inventory_on_hand` is the catalog item's own `cost` field, which is the standing cost rather than the weighted average cost ConnectWise values the stock at.\n\n### Health\n- `cw_test_connection` — Test connection (hits `/system/info`)\n\n## Usage\n\n### With Claude Desktop\n\nAdd to your `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"connectwise-manage\": {\n      \"command\": \"npx\",\n      \"args\": [\"@wyre-ai/connectwise-manage-mcp\"],\n      \"env\": {\n        \"CW_MANAGE_COMPANY_ID\": \"your-company-id\",\n        \"CW_MANAGE_PUBLIC_KEY\": \"your-public-key\",\n        \"CW_MANAGE_PRIVATE_KEY\": \"your-private-key\",\n        \"CW_MANAGE_CLIENT_ID\": \"your-client-id\"\n      }\n    }\n  }\n}\n```\n\nFor a self-hosted instance:\n\n```json\n{\n  \"mcpServers\": {\n    \"connectwise-manage\": {\n      \"command\": \"npx\",\n      \"args\": [\"@wyre-ai/connectwise-manage-mcp\"],\n      \"env\": {\n        \"CW_MANAGE_URL\": \"https://cwm.yourcompany.com\",\n        \"CW_MANAGE_COMPANY_ID\": \"your-company-id\",\n        \"CW_MANAGE_PUBLIC_KEY\": \"your-public-key\",\n        \"CW_MANAGE_PRIVATE_KEY\": \"your-private-key\",\n        \"CW_MANAGE_CLIENT_ID\": \"your-client-id\",\n        \"CW_MANAGE_REJECT_UNAUTHORIZED\": \"false\"\n      }\n    }\n  }\n}\n```\n\n### With Docker\n\nThe checked-in Compose service is gateway mode. It does not set `CW_MANAGE_*` and does not use `env_file`, so a project `.env` cannot inject static vendor credentials into the container. Compose still interpolates `CONDUIT_S2S_SECRET` and `LOG_LEVEL` from that file. The image listens on `0.0.0.0` inside the container; the published port is loopback.\n\n```bash\nCONDUIT_S2S_SECRET=\"$CONDUIT_S2S_SECRET\" docker compose up -d\n```\n\nGenerate the secret with `openssl rand -hex 32`. Compose refuses to start when `CONDUIT_S2S_SECRET` is unset. Every `/mcp` request must carry `X-Gateway-S2S` (see [HTTP authentication](#http-authentication)) plus the `X-CW-*` credential headers.\n\nSingle-tenant HTTP (`AUTH_MODE=env`) still requires the S2S secret, then uses `CW_MANAGE_*`:\n\n```bash\ndocker run \\\n  -e CONDUIT_S2S_SECRET=\"$CONDUIT_S2S_SECRET\" \\\n  -e AUTH_MODE=env \\\n  -e CW_MANAGE_COMPANY_ID=your-company-id \\\n  -e CW_MANAGE_PUBLIC_KEY=your-public-key \\\n  -e CW_MANAGE_PRIVATE_KEY=your-private-key \\\n  -e CW_MANAGE_CLIENT_ID=your-client-id \\\n  -p 127.0.0.1:8080:8080 \\\n  ghcr.io/wyre-ai/connectwise-manage-mcp:latest\n```\n\n`docker-compose.caddy.yml` is that single-tenant shape behind Caddy. It still requires `CONDUIT_S2S_SECRET`.\n\n### HTTP Transport (Gateway Mode)\n\nRun with HTTP transport for multi-tenant gateway deployments. The process exits until `CONDUIT_S2S_SECRET` is set:\n\n```bash\nCONDUIT_S2S_SECRET=\"$CONDUIT_S2S_SECRET\" MCP_TRANSPORT=http AUTH_MODE=gateway node dist/index.js\n```\n\nEach `/mcp` request needs a valid `X-Gateway-S2S` header plus `X-CW-Company-Id`, `X-CW-Public-Key`, `X-CW-Private-Key`, and `X-CW-Client-Id` (`X-CW-URL` is optional).\n\nLocal HTTP without a secret is development-only. It binds `127.0.0.1` and logs a warning:\n\n```bash\nMCP_TRANSPORT=http MCP_ALLOW_INSECURE_DEV=1 npm start\n```\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Run in development (stdio)\nnpm run dev\n\n# HTTP refuses to start until CONDUIT_S2S_SECRET is set\nCONDUIT_S2S_SECRET=\"$CONDUIT_S2S_SECRET\" MCP_TRANSPORT=http npm start\n\n# Local HTTP only, without a service-to-service secret (always binds 127.0.0.1)\nMCP_TRANSPORT=http MCP_ALLOW_INSECURE_DEV=1 npm start\n\n# Type check\nnpm run typecheck\n\n# Run tests\nnpm test\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nApache-2.0\n\n---\n\nBuilt by [WYRE Technology](https://github.com/wyre-technology) — part of the [MSP Claude Plugins](https://github.com/wyre-technology/msp-claude-plugins) ecosystem\n"}