内容
# ConnectWise MCP
本地标准输入/输出MCP服务器,向Claude暴露**23个只读ConnectWise Manage工具**。它封装了[`xssentials`](https://github.com/XssentialsAI/xssentials) ConnectWise客户端,并通过标准输入/输出进行MCP通信。凭证存储在1Password中,并在启动时通过`op run`解析 —— 明文密钥从未触及磁盘。默认情况下为只读,允许一个经过审计的写入 —— 成员创建(参见[写入表面](#写入表面-默认只读-一个经过审计的写入))。
这些说明针对**macOS上的Claude Desktop**(在**Cowork**选项卡中使用)。
> **急忙?** 下面的[ELI5快速入门](#-像我5岁一样安装)是大多数人需要的。编号参考部分在它之后。
---
## 🧸 像我5岁一样安装
按顺序执行以下七个步骤。将灰色框复制粘贴到**终端**应用程序中(按`⌘ + Space`,输入"终端",然后按Enter)。
### 步骤 1 — 安装两个辅助应用程序
该服务器需要**1Password**(安全存储您的密钥)和**Homebrew**(一个工具安装程序)。如果您还没有Homebrew,请粘贴以下内容并按照提示进行:
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
然后安装1Password及其命令行助手:
```bash
brew install --cask 1password
brew install 1password-cli
```
打开**1Password应用程序 → 设置 → 开发者**,并启用**"与1Password CLI集成。"** (这样服务器就可以使用您的指纹解锁密钥,而不是要求您登录。)
### 步骤 2 — 下载代码
```bash
mkdir -p ~/mcp && cd ~/mcp
git clone https://github.com/XssentialsAI/xssentials.git
git clone https://github.com/XssentialsAI/connectwise-mcp.git
```
### 步骤 3 — 设置(复制整个块)
```bash
cd ~/mcp/connectwise-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ../xssentials
pip install -e .
```
### 步骤 4 — 创建您的密钥文件
您需要一个带有公钥/私钥对的ConnectWise **API成员**(参见[凭证](#4-凭证-您自己的connectwise-api密钥)以获取点击位置)。将它们放在1Password项目中,然后:
```bash
cp cw.env.example cw.env
open -e cw.env
```
在打开的文件中,将每行`op://...`指向您的1Password项目,保存并关闭。不要粘贴原始密钥 — 使用`op://`引用。(`cw.env`被git忽略,因此它永远不会被上传。)
### 步骤 5 — 获取Claude需要的两个路径
运行此命令并**保留它打印的两个行** — 您将在步骤 6 中粘贴它们:
```bash
echo "op path: $(which op)"
echo "python path: $PWD/.venv/bin/python"
```
### 步骤 6 — 告诉Claude Desktop关于服务器的信息
在Claude Desktop中:**设置 → 开发者 → 编辑配置**。这将打开一个名为`claude_desktop_config.json`的文件。使其如下所示,替换为您在步骤 5 中的两个路径和您的实际主文件夹路径:
```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"
]
}
}
}
```
保存文件,确保**1Password应用已解锁**,然后**完全退出并重新打开Claude Desktop**(⌘Q,然后再次打开)。
> ⚠️ **不要**在Cowork中使用**"+ 添加自定义连接器"**按钮。这按钮用于互联网上的网络服务器。此服务器在您的**Mac上运行**,因此它放置在**编辑配置**(上面)中。
### 步骤 7 — 在 Cowork 中启用并测试
1. 在Claude Desktop中打开**Cowork**选项卡。
2. 进入**自定义 → 连接器**。您将在列表中看到**connectwise** — 切换其开关为**开启**。
3. (可选)点击**工具权限**,选择允许/询问/阻止每个工具。ConnectWise是只读的,因此**允许**对所有内容都是安全的。
4. 在Cowork聊天中,输入:**"运行cw_ping"**。您应该在几秒钟内收到一张包含ConnectWise版本和区域的卡片。 🎉
如果出现问题,请参见[故障排除](#故障排除)。
---
## 参考
### 1. 先决条件
- 带有 **Python 3.10+** (`python3 --version`)的 **macOS**
- **1Password 应用**(桌面)+ **1Password CLI** (`op`)启用了*开发者 → 与1Password CLI集成*
- **Claude Desktop** — https://claude.ai/download
### 2. 获取代码
将所有三个仓库克隆为兄弟目录(服务器依赖于`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. 安装
```bash
cd ~/mcp/connectwise-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e ../xssentials # 满足xssentials依赖,无需网络获取
pip install -e .
```
### 4. 凭证(您自己的ConnectWise API密钥)
1. 在ConnectWise Manage中:**系统 → 成员 → API成员** → 创建API成员。
2. 在该成员上,生成一个**公钥/私钥对**(私钥仅显示一次 — 保存它)。
3. 您的**clientId**是来自https://developer.connectwise.com的开发者注册UUID。
将这些存储在1Password中,然后`cp cw.env.example cw.env`并将每个`op://vault/item/field`引用指向您的项目。五个必需字段和身份验证注释在`cw.env.example`中内联记录。验证引用是否解析:
```bash
op run --env-file=cw.env -- printenv CW_COMPANY_ID # 应该打印您的公司ID
```
### 5. 安装到Claude Desktop(编辑配置)
**设置 → 开发者 → 编辑配置** 打开 `~/Library/Application Support/Claude/claude_desktop_config.json`。添加在[步骤 6](#步骤-6-告诉-claude-desktop-关于服务器)中显示的`connectwise`条目。**使用绝对路径** — Claude Desktop使用最小的`PATH`启动服务器,不包括Homebrew,因此裸`"op"`或`"python"`将失败。使用`which op`查找您的`op`(Apple Silicon → `/opt/homebrew/bin/op`,Intel → `/usr/local/bin/op`)。解锁1Password,然后退出并重新打开Claude Desktop。
### 6. 在 Cowork 中启用(自定义选项卡)
本地服务器通过**编辑配置**安装,但在**Cowork → 自定义 → 连接器**中启用。切换**connectwise**为开启。使用**工具权限**(允许/询问/阻止)控制各个工具 — 这里都是只读的,因此允许是安全的。
### 7. 验证
在 Cowork 聊天中:**"运行 cw_ping"** → 带有 CW 版本、区域(例如 `NA`)和 `isCloud: True` 的 Markdown 卡片,时间少于 ~2 秒。
## 故障排除
- **服务器无法启动 / "命令未找到"** — 您使用了裸露的 `op` 或 `python`。两者在配置中都必须是**绝对路径**(Claude Desktop 不继承您的 shell `PATH`)。
- **机密无法解析 / `op` 提示** — 打开并解锁 1Password 桌面应用程序,并确认已启用*开发者 → 与 1Password CLI 集成*。
- **服务器未在自定义列表中** — 确认 JSON 有效,并且您完全退出(⌘Q)并重新打开了 Claude Desktop。在**设置 → 开发者**中查看服务器的状态/日志。
- **`pip install -e .` 找不到 xssentials** — 先运行 `pip install -e ../xssentials`。
- **来自 ConnectWise 的 401** — 重新检查 1Password 中的五个字段;基本头部是 `<company_id>+<public_key>:<private_key>`,并且所有五个字段都是必需的。
## 工具索引
`connectwise_mcp/__main__.py`中注册了24种工具(23种只读 + 1种经审计的写入):
| 工具 | CW 资源 | 用途 |
|---|---|---|
| `cw_ping` | `/system/info` | 身份验证测试 |
| `cw_find` | 并行跨资源 | 跨公司、项目、工单、机会、发票、采购订单的模糊查找 |
| `cw_company` | `/company/companies` | 按ID或名称查找单个公司卡片 |
| `cw_project_list` | `/project/projects` | 带筛选器的项目搜索 |
| `cw_project_status` | `/project/projects/{id}` + AR + 开放工单 | 单项目汇总 |
| `cw_invoice_lookup` | `/finance/invoices/{id}` + 产品线 | 单发票整合器 |
| `cw_invoice_list` | `/finance/invoices` | 带筛选器的发票搜索 |
| `cw_ar_aging` | `/finance/invoices` | 按公司/项目划分的应收账款时段 |
| `cw_service_tickets` | `/service/tickets` | 跨面板的服务工单搜索 |
| `cw_ticket_detail` | `/service/tickets/{id}` + 备注 + 时间 | 单工单整合器 |
| `cw_time_entries` | `/time/entries` | 带筛选器的时间条目搜索 |
| `cw_opportunity_status` | `/sales/opportunities/{id}` | 单机会汇总 |
| `cw_purchasing_status` | `/procurement/purchaseorders` | 采购订单标题 + 行项目,或按供应商的采购订单列表 |
| `cw_pick_ship_status` | `/procurement/products` | 采购准备标志 |
| `cw_sales_orders` | `/sales/salesOrders` | 销售订单搜索 |
| `cw_purchase_approvals` | n/a | 结构化间隙消息(无审批端点) |
| `cw_inventory` | `/procurement/warehouseBins` | 按仓库仓位划分的库存数量 |
| `cw_product_catalog` | `/procurement/catalog` | 按查询/制造商搜索目录 |
| `cw_configurations` | `/company/configurations` | 配置项 |
| `cw_expense_reports` | `/expense/entries` | 费用条目搜索 |
| `cw_query` | 任意GET | 通用逃生阀;通过`fields="parent/child"`进行嵌套标量投影 |
| `cw_report` | `/system/reports/{name}` | 以扁平Markdown表格运行CW系统报告 |
| `cw_schedule_entries` | `/schedule/entries` | 调度条目搜索(分配技术人员预订) |
| `cw_create_member` ✍️ | `POST /system/members` | **唯一写入工具** — 员工入职;默认为干运行,`confirm=true`创建 |
## 写入表面(默认只读 + 一个经审计的写入)
服务器默认情况下为只读。读取是仅限GET的,并在两个层面上强制执行:
1. **客户端级别** — `xssentials.connectwise.client._request`对任何非GET HTTP方法都引发`NotImplementedError`。
2. **验证器级别** — `connectwise_mcp._validators.enforce_get_only`保护`cw_query`路径。
单个批准的写入是**成员创建**(`cw_create_member`),它通过单独的`client._write_request`进行,该请求被硬允许列入`/system/members`(`client.WRITE_ALLOWLIST`)并仅接受POST/PATCH。它强制执行`employeeIdentifer="XX"`(工资安全)和`adminFlag=false`,并默认为干运行预览 — 仅当`confirm=true`时才POST。
在任何提交上验证:
```bash
# 没有临时写入会泄露到工具中 — 每个写入都必须通过client._write_request路由:
grep -nE '_request\("(POST|PUT|PATCH|DELETE)"' connectwise_mcp/__main__.py # 预期为零命中
# 写入允许列表保持作用于成员:
grep -n 'WRITE_ALLOWLIST' ../xssentials/xssentials/connectwise/client.py # 预期仅限/system/members
```
## 许可证
Xssentials内部工具。ConnectWise是ConnectWise, LLC的商标。