Skip to main content
Glama
mspstack

mcp-connectwise-psa

by mspstack

mcp-connectwise-psa

一个用于 ConnectWise PSA (Manage) 的 MCP (Model Context Protocol) 服务器 — 覆盖 8 个工具集的精选工具,涵盖技术人员、调度员和计费,外加一个用于其余 API 的逃生舱和一个用于本地部署的只读 SQL 工具集,使 AI 助理能够像每个角色一样使用 PSA:

  • 工单 — 搜索 / 我的工单 / 含备注的完整详情,创建,更新状态/优先级/负责人,添加讨论/内部备注,外加看板·状态·优先级发现以及每张工单的时间和任务

  • 时间 — 针对工单记录时间,查看自己的时间,工作角色查询,以及列出并提交时间表

  • 公司与联系人 — 快速查找,联系人详情(电话/电子邮件),公司站点

  • 配置 — 带序列号、IP、操作系统、保修信息的设备/资产(只读)

  • 调度 (schedule) — 计划条目(列表/我的/创建/重新安排/取消),以及成员及其时区、工作时间和空闲/已预订可用性

  • 开票 (finance, 只读) — 发票、协议,以及随时可开票的未开票计费时间

  • SQL (仅限本地部署) — 直接针对 cwwebapp_* Manage 数据库执行只读 T-SQL,用于 REST 无法表达的跨表报表,附带可搜索的架构目录和一个助手可以不断扩充的已保存查询库。通过配置 CW_DB_* 启用;一旦启用,每个未收窄工具集的会话都会拥有它

  • 工具集与角色预设 — 通过 x-cw-toolsets 标头(或 CW_TOOLSETS)仅启用会话所需的功能;预设 tech / dispatch / invoicing / all。默认是 all — 当需要更小的工具面时,可按会话收窄。每个工具还会将其工具集报告为 _meta.group,因此聚合器(MSPStack 网关)可以按能力对工具进行分组和切换

  • 按成员 API 密钥 (BYOK) — 每个用户提供自己的 ConnectWise 成员密钥;ConnectWise 强制执行该成员的安全角色,并且每次写入都归属于实际人员

  • 传输方式 — 本地使用 stdio,共享部署使用 streamable HTTP;包含 Docker 镜像

快速开始(本地,stdio)

npm install && npm run build
CW_SITE=na.myconnectwise.net \
CW_COMPANY_ID=yourcompany \
CW_CLIENT_ID=<integration clientId> \
CW_PUBLIC_KEY=xxxx CW_PRIVATE_KEY=yyyy \
CW_MEMBER_IDENTIFIER=jdoe \
node dist/index.js

Claude Desktop / Claude Code 配置:

{
  "mcpServers": {
    "connectwise": {
      "command": "node",
      "args": ["/path/to/mcp-connectwise-psa/dist/index.js"],
      "env": {
        "CW_SITE": "na.myconnectwise.net",
        "CW_COMPANY_ID": "yourcompany",
        "CW_CLIENT_ID": "<clientId>",
        "CW_PUBLIC_KEY": "xxxx",
        "CW_PRIVATE_KEY": "yyyy",
        "CW_MEMBER_IDENTIFIER": "jdoe"
      }
    }
  }
}

ConnectWise API 要求提供 clientId — 在 developer.connectwise.com 注册一个(免费)集成。API 成员密钥在 ConnectWise 中创建,路径为 我的账户 → API 密钥(按成员)或 系统 → 成员 → API 成员(集成账户)。

Related MCP server: superops-mcp

HTTP 部署

CW_SITE=… CW_COMPANY_ID=… CW_CLIENT_ID=… \
node dist/index.js --transport http --port 3000

或使用 Docker:docker build -t mcp-connectwise-psa . && docker run -p 3000:3000 -e CW_SITE -e CW_COMPANY_ID -e CW_CLIENT_ID mcp-connectwise-psa

路由

用途

POST/GET/DELETE /mcp

MCP streamable-http 端点

GET /health

存活探针

会话保存在内存中 — 请运行单个实例(或使用粘性会话)。

访问控制 — 自带密钥 (BYOK)

在 HTTP 上没有 MCP 级别的角色系统。每个会话出示自己的 ConnectWise 成员 API 密钥,ConnectWise 本身就是访问控制:成员的安全角色决定哪些操作成功,每条备注和时间条目都归属于该成员。

在 initialize 请求中发送你的密钥(以及会话中的每个后续请求):

x-cw-public-key:  <public key>
x-cw-private-key: <private key>
x-cw-member-id:   <your member identifier>   (optional — enables "my tickets"/"my time")
  • 没有密钥的请求将被拒绝并返回 401;两个密钥标头必须同时提供。

  • 密钥永远不会被记录。会话绑定到密钥对的 SHA-256 哈希;在同一会话 ID 上出示不同的密钥对 → 403。

  • 在 ConnectWise 中通过 我的账户 → API 密钥 创建成员 API 密钥。每个技术人员使用自己的密钥。

本地 stdio 是单用户的,使用环境变量 CW_PUBLIC_KEY/CW_PRIVATE_KEY 而不是标头。

工具集

工具被分组到工具集中,这样会话只能看到它所需的能力 — 调度员不需要开票工具,小的工具面可以让助手保持专注(并且其上下文成本更低)。写入是否真正成功仍然由成员的 ConnectWise 安全角色决定。

工具集键

工具

tickets

cw_search_tickets, cw_my_tickets, cw_get_ticket, cw_create_ticket, cw_update_ticket, cw_add_ticket_note, cw_list_boards, cw_get_board, cw_list_priorities, cw_list_ticket_time, cw_list_ticket_tasks

time

cw_create_time_entry, cw_update_time_entry, cw_list_my_time, cw_list_work_roles, cw_list_my_timesheets, cw_submit_timesheet

companies

cw_search_companies, cw_get_company, cw_search_contacts, cw_get_contact, cw_list_company_sites

configurations

cw_list_configurations, cw_get_configuration

schedule

cw_list_schedule_entries, cw_my_schedule, cw_schedule_ticket, cw_update_schedule_entry, cw_delete_schedule_entry, cw_member_availability, cw_list_members, cw_get_member

finance

cw_list_invoices, cw_get_invoice, cw_list_agreements, cw_get_agreement, cw_list_unbilled_time

advanced

cw_find_endpoint(搜索完整的 CW API — ~1,150 个端点)、cw_get(对任意路径执行只读 GET)

sql (仅本地部署,需要 CW_DB_*)

cw_db_query(只读 T-SQL)、cw_db_find_table(架构目录)、cw_db_find_query / cw_db_save_query(已保存查询库)

预设按角色捆绑键:tech = tickets + time + companies + configurations · dispatch = tickets + schedule + companies + configurations · invoicing = finance + time + companies · all = 所有键。角色预设刻意排除 sql — 技术人员工具面不是数据库工具面。

advanced 工具集是逃生舱(在 all 中,但不在任何角色预设中):cw_find_endpoint 搜索捆绑的完整 ConnectWise API 目录,cw_get 对任意路径执行只读 GET — 因此助手可以触及精选工具未包裹的长尾(采购、销售、项目、系统…)。要移除它,请改为指定键或角色预设(x-cw-toolsets: tech)。

使用逗号列表选择工具集,可混合键和预设:

  • HTTP — 每个会话的 x-cw-toolsets 标头:x-cw-toolsets: dispatch 或 x-cw-toolsets: tech,finance。

  • stdio — CW_TOOLSETS 环境变量或 --toolsets 标志:CW_TOOLSETS=invoicing。

默认是 all 预设 — 服务器配置了的所有能力;想要更小工具面的客户端会指定它需要的键或角色。CW_TOOLSETS/--toolsets 中的未知键会快速失败;x-cw-toolsets 标头中的未知令牌会被忽略。唯一的破坏性工具是 cw_delete_schedule_entry(dispatch);finance 是只读的。cw_db_save_query 会写入,但只写入查询库文件 — 数据库访问本身通过授权仅为 SELECT。

例外:sql 工具集

其他每个工具集都使用调用者自己的 ConnectWise 密钥运行,因此 ConnectWise 会过滤返回的内容。sql 则不然:它通过服务器范围的只读登录读取数据库,因此其结果不归属于任何成员,也不会被该成员的安全角色、看板限制或记录权限过滤。

因此,配置 CW_DB_* 才是关键决定。一旦服务器配置了数据库,sql 就是一个普通键:它在 all 中,在默认选择中,并且每个未收窄工具集的会话都可以读取整个 PSA 数据库。没有 CW_DB_* 的服务器会静默地移除它,因此从不想要它的部署不会出任何问题。

如果你需要让某些调用者而不是其他调用者访问数据库,请按会话执行(x-cw-toolsets: tech)或在服务器前面执行 — 聚合网关可以分别对 cw_db_* 工具分层。在服务器端限制损害的是登录:请参阅下面的运行手册,并将其保持为 db_datareader,同时拒绝凭据列。

SQL 工具集(本地部署数据库)

云托管的 ConnectWise 不提供数据库访问权限,因此此工具集仅适用于本地部署。请使用专门为此目的创建的登录,将其指向 Manage 数据库:

CW_DB_HOST=sqlhost CW_DB_NAME=cwwebapp_acme \
CW_DB_USER=cw_mcp_ro CW_DB_PASSWORD=… \
CW_DB_QUERY_LIBRARY=/data/cw-queries.json \
node dist/index.js

这就是全部:配置了数据库后,sql 工具集就成为默认选择的一部分。如果没有 CW_DB_* 却显式指定 sql,启动时会失败(而像 all 这样仅包含它的选择会被剪除)。在会话真正使用工具之前,不会连接到数据库。

从报表视图开始。 ConnectWise 附带反规范化(denormalized)的 v_rpt_* 视图,这些视图已经将看板、状态、公司和联系人联接到记录上 — v_rpt_service、v_rpt_time、v_rpt_company、v_rpt_invoices、v_rpt_agreementlist。cw_db_find_table 知道这些视图及其背后的基础表;它只携带关键列,因为确切的列列表只需一次 INFORMATION_SCHEMA 查询即可获得,并且对于你的版本来说始终是正确的。

已保存查询库是已提交的核心加上位于 CW_DB_QUERY_LIBRARY 的可写覆盖层(JSON,{ version, queries[] })。覆盖层条目按 slug 优先,cw_db_save_query 会追加到其中,scripts/import-queries.mjs 从现有的 BrightGauge 导出中填充它:

node scripts/import-queries.mjs /path/to/brightgauge-export

导入的查询保留在此存储库之外 — 它们是你的报表,可能包含公司名称和费率。在容器上,将 CW_DB_QUERY_LIBRARY 指向挂载的存储,否则已保存的查询会随容器一起消亡。

登录就是安全边界

没有语句验证:服务器按原样将模型的 SQL 发送给 SQL Server,因此登录被允许做什么,就恰好可能发生什么。两个脚本负责设置并证明这一点。

创建它 — 编辑顶部的四个变量,以 sysadmin 身份运行。@WhatIf 默认为 1,因此第一次运行只打印计划:

sqlcmd -S SQLHOST\CWPROD -d master -i scripts/create-readonly-login.sql

它创建的登录不归属任何服务器角色,将其添加到某个数据库的 db_datareader,DENY 其他所有权限(EXECUTE、所有写入、DDL、BACKUP),并对其发现的每个看似凭据的列 DENY SELECT — 这些列名会随 Manage 版本变化,而且每个 MSP 都会添加自己的列,因此通过发现而非硬编码来找到它们。重新运行是安全的,也是在升级添加表后重新应用 DENY 的方式。它会报告必须关闭但从不更改的实例级设置:禁用 xp_cmdshell 可能破坏其他应用程序,因此这仍然是一个决策。

验证它 — 使用新的登录身份,而不是管理员身份:

sqlcmd -S SQLHOST\CWPROD -d cwwebapp_acme -U cw_mcp_ro -P '<password>' -i scripts/verify-readonly-login.sql

每项检查都会输出 PASS 或 FAIL:SELECT 可正常使用,UPDATE/CREATE TABLE 被拒绝(在一个始终回滚的事务中执行,以防因缺少 DENY 而导致实际写操作发生),xp_cmdshell/sp_OACreate/OPENROWSET(BULK …) 不可达,凭据列不可读,且登录名未处于任何高风险角色中。只要是 FAIL 就代表当下不应启用该工具集。

先了解两个重要后果:

  • SELECT * 在含被拒列的任意表上都会失败,而不是返回其余列。这正是有意为之;工具的错误信息会提示模型指定所需的列名。

  • EXECUTE 权限才是关键权限。 拥有该权限后,“只读 SQL”会变成以 SQL Server 服务账户身份执行的远程代码执行——xp_cmdshell、sp_OACreate、sp_send_dbmail,以及用于 NTLM 捕获的 xp_dirtree。OPENROWSET/BULK INSERT 在完全不需要 EXECUTE 权限的情况下即可读取文件,因此 Ad Hoc Distributed Queries(Ad Hoc 分布式查询)也必须关闭。

操作层面建议:优先使用可读的 AG 辅助数据库副本或当前生产环境已恢复的报表副本;将 SQL 端口访问限制为仅 MCP 主机可访问;并针对此登录名启用 SQL Audit 或 Extended Events 会话。

配置参考

Variable

Default

Purpose

CW_SITE

—

ConnectWise 主机(云或本地部署;接受完整 URL)

CW_COMPANY_ID

—

登录公司 ID

CW_CLIENT_ID

—

集成 clientId

CW_PUBLIC_KEY / CW_PRIVATE_KEY

—

API 成员密钥 — stdio 必需;HTTP(自带证书)不使用

CW_MEMBER_IDENTIFIER

—

stdio 密钥所属的成员(my-tickets/my-time)

TRANSPORT / PORT

stdio / 3000

传输选择

CW_TOOLSETS

all

启用的工具集(keys/预设);HTTP 可通过 x-cw-toolsets 按会话覆盖

CW_DB_HOST

—

ConnectWise SQL Server 主机,或 host\INSTANCE — 即启用 sql 工具集

CW_DB_NAME / CW_DB_USER / CW_DB_PASSWORD

—

数据库及其专用只读登录(四项必须同时提供)

CW_DB_PORT

1433

TCP 端口;与无实例名同时使用时不生效

CW_DB_ENCRYPT / CW_DB_TRUST_SERVER_CERT

true / true

TLS,以及接受本地站点常见的自签名证书

CW_DB_READ_UNCOMMITTED

true

以 READ UNCOMMITTED 读取,避免报表扫描阻塞生产库的写操作

CW_DB_QUERY_TIMEOUT_MS / CW_DB_MAX_ROWS

30000 / 200

每次查询的截止时间和行数上限

CW_DB_QUERY_LIBRARY

—

可写已保存查询文件路径;未设置 ⇒ 仅内置查询,不提供保存工具

说明与限制

  • 工单搜索默认为开放工单;状态 / 看板名称需要精确匹配,基于文本的过滤器使用子串匹配。

  • 时间戳必须带整秒——服务器会进行格式化(ConnectWise 不接受含小数的秒)。

  • 时间条目要求在 ConnectWise 中,该日期必须对应一个处于打开状态的时间报告周期;若不存在该周期,API 的提示信息会被原样透传。

  • 部分本地版本未提供 /system/myAccount——对于“my tickets” / “my time”,需要显式提供成员标识符(CW_MEMBER_IDENTIFIER 或 x-cw-member-id)。

  • 讨论备注对客户同步可见;内部备注对客户不可见——工具本身会明确区分这一点。

  • cw_db_query 在达到 max_rows(默认 200)或约 20,000 字符预算限制时终止,并会从服务端取消该查询;响应中会说明当前命中哪个限制。单次查询默认超时时间为 30 秒,最长不超过 120 秒。

  • 数据库连接使用了 READ UNCOMMITTED 读级别,以避免报表扫描阻塞验证人员保存工单。代价是可能读到脏数据:在并发写入时,计数结果会不准。如需精确报表,请将 CW_DB_READ_UNCOMMITTED 设为 false。

  • SELECT * 在任意包含 DENY 列的表上都会失败——请明确列出所需列名。

  • 云端托管的 ConnectWise 实例没有数据库访问权限;sql 工具集仅限本地部署。

开发

npm install
npm run dev          # stdio via tsx
npm run dev:http     # http via tsx
npm test             # vitest
npm run build        # tsc → dist/

语 .

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    An MCP server for SuperOps PSA/RMM, enabling MSPs to manage tickets, assets, clients, and field technician operations through SuperOps's API.
    22
    3
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for Kaseya BMS PSA — tickets, accounts, time entries, and contracts. Enables AI assistants to manage service desk operations via the Kaseya BMS API.
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    MCP server for SolarWinds Service Desk (SWSD/Samanage) enabling reading and modifying tickets, comments, knowledge-base articles, and more via each user's own API token.
    37
    557 npm
    4
    MIT