@alfe.ai/connectwise-automate-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # ConnectWise Automate MCP
2
+
3
+ Runtime-agnostic stdio MCP server for Automate client, location and endpoint
4
+ inventory, available scripts, computer monitors, and script/monitor history.
5
+ Every provider operation requires an explicit Alfe `connectionId`.
6
+
7
+ Create a **ConnectWise Automate** connection in Alfe using the public HTTPS
8
+ server origin and an integrator username/password. Alfe supplies the vendor
9
+ ClientID. This server reads Alfe configuration through `@alfe.ai/config` and
10
+ resolves fresh connection credentials through `AgentApiClient` on every call.
11
+ It requires no ConnectWise environment variables or copied secrets.
12
+
13
+ The integration manifest starts the first release with:
14
+
15
+ ```yaml
16
+ mcp_servers:
17
+ - id: connectwise-automate
18
+ command: npx
19
+ args: ["-y", "@alfe.ai/connectwise-automate-mcp@0.1.0"]
20
+ env: {}
21
+ ```
22
+
23
+ Call `automate_list_connections`, then pass the chosen `connectionId` to:
24
+
25
+ - `automate_check_connection`
26
+ - `automate_list_clients`
27
+ - `automate_list_locations`
28
+ - `automate_list_computers`
29
+ - `automate_list_scripts`
30
+ - `automate_list_monitor_history`
31
+ - `automate_list_internal_monitor_results`
32
+ - `automate_list_computer_monitors` (also requires `computerId`)
33
+ - `automate_list_computer_script_history` (also requires `computerId`)
34
+
35
+ List tools accept a 1-based `page`, `pageSize` (1–200, default 50), and an
36
+ Automate `condition` expression. They return `{ items, page, pageSize }` for
37
+ one page. Scripts are discovered and their execution history can be inspected;
38
+ these tools do not execute scripts, reboot computers, or change monitors.
39
+
40
+ The server calls `/cwa/api/v1/` on the selected origin. It caches bearer tokens
41
+ per connection, refreshes while they are still valid, and reauthenticates after
42
+ expiration or one failed GET. Credential rotation and revoked Alfe scope take
43
+ effect on the next tool call. Interactive/2FA login challenges return an
44
+ actionable connection error.
45
+
46
+ Private-network and loopback servers are unsupported. Each request resolves
47
+ the hostname afresh, rejects any non-public DNS answer, and pins the HTTPS
48
+ socket to a checked IP while retaining TLS hostname validation. Redirects are
49
+ never followed. Requests, DNS, responses and tool results have explicit bounds;
50
+ raw provider errors and credential-bearing fields are never forwarded.
51
+
52
+ ## API contract and verification
53
+
54
+ The primary API reference is the [ConnectWise Automate developer
55
+ portal](https://developer.connectwise.com/Products/Automate/Integrating_with_Automate/API),
56
+ which requires a ConnectWise developer login. The implementation was checked
57
+ against its exported **Automate API v1** OpenAPI contracts, preserved in the
58
+ [System](https://github.com/covenanttechnologysolutions/connectwise-rest/blob/d04740719c5a62008af4dc31287a621b2f07f860/generator/automate-json/System.json),
59
+ [Company](https://github.com/covenanttechnologysolutions/connectwise-rest/blob/d04740719c5a62008af4dc31287a621b2f07f860/generator/automate-json/Company.json),
60
+ [Computers](https://github.com/covenanttechnologysolutions/connectwise-rest/blob/d04740719c5a62008af4dc31287a621b2f07f860/generator/automate-json/Computers.json),
61
+ [Scripts](https://github.com/covenanttechnologysolutions/connectwise-rest/blob/d04740719c5a62008af4dc31287a621b2f07f860/generator/automate-json/Scripts.json)
62
+ and [Monitors](https://github.com/covenanttechnologysolutions/connectwise-rest/blob/d04740719c5a62008af4dc31287a621b2f07f860/generator/automate-json/Monitors.json)
63
+ files at that immutable mirror revision. The mirror is independently
64
+ maintained; it is not a live contract obtained from the customer's server.
65
+
66
+ Tests exercise the token wire format/lifecycle, endpoint and pagination
67
+ contracts, connection selection/rotation/revocation, DNS and response bounds,
68
+ error redaction, and real in-memory MCP protocol requests. No live customer
69
+ Automate server was available during implementation. Before declaring a
70
+ deployment operational, configure a real vendor ClientID and integrator
71
+ connection, run `automate_check_connection`, page each relevant inventory tool,
72
+ and verify refresh against the installed Automate version. An account must
73
+ have the corresponding Automate read permissions; permission failures are
74
+ reported without widening access.
package/dist/bin.cjs ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ const require_server = require("./server2.cjs");
3
+ //#region src/bin.ts
4
+ require_server.startServer().then(({ close }) => {
5
+ let stopping = false;
6
+ const shutdown = () => {
7
+ if (stopping) return;
8
+ stopping = true;
9
+ close().then(() => process.exit(0), () => process.exit(1));
10
+ };
11
+ process.once("SIGINT", shutdown);
12
+ process.once("SIGTERM", shutdown);
13
+ }).catch((error) => {
14
+ process.stderr.write(`[ERROR] connectwise-automate-mcp startup: ${require_server.safeError(error).code}\n`);
15
+ process.exitCode = 1;
16
+ });
17
+ //#endregion
package/dist/bin.d.cts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { a as startServer, s as safeError } from "./server2.js";
3
+ //#region src/bin.ts
4
+ startServer().then(({ close }) => {
5
+ let stopping = false;
6
+ const shutdown = () => {
7
+ if (stopping) return;
8
+ stopping = true;
9
+ close().then(() => process.exit(0), () => process.exit(1));
10
+ };
11
+ process.once("SIGINT", shutdown);
12
+ process.once("SIGTERM", shutdown);
13
+ }).catch((error) => {
14
+ process.stderr.write(`[ERROR] connectwise-automate-mcp startup: ${safeError(error).code}\n`);
15
+ process.exitCode = 1;
16
+ });
17
+ //#endregion
18
+ export {};
@@ -0,0 +1,8 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_server = require("./server2.cjs");
3
+ exports.AutomateClient = require_server.AutomateClient;
4
+ exports.AutomateRuntime = require_server.AutomateRuntime;
5
+ exports.SERVER_NAME = require_server.SERVER_NAME;
6
+ exports.SERVER_VERSION = require_server.SERVER_VERSION;
7
+ exports.createServer = require_server.createServer;
8
+ exports.startServer = require_server.startServer;
@@ -0,0 +1,95 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
+
4
+ //#region src/boundary.d.ts
5
+
6
+ interface AutomateCredentials {
7
+ serverUrl: string;
8
+ username: string;
9
+ password: string;
10
+ clientId: string;
11
+ }
12
+ interface Address {
13
+ address: string;
14
+ family: number;
15
+ }
16
+ type Resolver = (hostname: string) => Promise<Address[]>;
17
+ interface HttpRequest {
18
+ url: URL;
19
+ address: Address;
20
+ method: "GET" | "POST";
21
+ headers: Record<string, string>;
22
+ body?: string;
23
+ signal: AbortSignal;
24
+ }
25
+ interface HttpResponse {
26
+ status: number;
27
+ body: string;
28
+ }
29
+ type Transport$1 = (request: HttpRequest) => Promise<HttpResponse>;
30
+ /** TLS validates the original hostname while lookup pins the checked public IP. */
31
+ //#endregion
32
+ //#region src/client.d.ts
33
+ declare const LIST_RESOURCES: readonly ["clients", "locations", "computers", "scripts", "monitor_history", "internal_monitor_results"];
34
+ type ListResource = (typeof LIST_RESOURCES)[number];
35
+ interface ListOptions {
36
+ page?: number;
37
+ pageSize?: number;
38
+ condition?: string;
39
+ }
40
+ interface ClientOptions {
41
+ resolve?: Resolver;
42
+ transport?: Transport$1;
43
+ now?: () => number;
44
+ timeoutMs?: number;
45
+ }
46
+ declare class AutomateClient {
47
+ private readonly credentials;
48
+ private readonly resolve;
49
+ private readonly transport;
50
+ private readonly now;
51
+ private readonly timeoutMs;
52
+ private token;
53
+ private tokenRequest;
54
+ constructor(credentials: AutomateCredentials, options?: ClientOptions);
55
+ list(resource: ListResource, options?: ListOptions): Promise<unknown>;
56
+ listComputer(resource: "monitors" | "script_history", computerId: number, options?: ListOptions): Promise<unknown>;
57
+ checkConnection(): Promise<{
58
+ connected: true;
59
+ }>;
60
+ private readList;
61
+ private getToken;
62
+ private acquireToken;
63
+ private mintToken;
64
+ private request;
65
+ }
66
+ //#endregion
67
+ //#region src/server.d.ts
68
+ declare const SERVER_NAME = "connectwise-automate-mcp";
69
+ declare const SERVER_VERSION: string;
70
+ /** Raw responses remain unknown until the runtime validates their authority. */
71
+ interface ConnectionSource {
72
+ getConnectProviderAccounts(provider: string): Promise<unknown>;
73
+ getConnectionCredentials(connectionId: string): Promise<unknown>;
74
+ }
75
+ declare class AutomateRuntime {
76
+ private readonly source;
77
+ private readonly options;
78
+ private readonly clients;
79
+ constructor(source: ConnectionSource, options?: ClientOptions);
80
+ listConnections(): Promise<unknown>;
81
+ getClient(selector: string): Promise<AutomateClient>;
82
+ clear(): void;
83
+ }
84
+ declare function createServer(source: ConnectionSource, options?: ClientOptions & {
85
+ emit?: (line: string) => void;
86
+ }): {
87
+ server: McpServer;
88
+ runtime: AutomateRuntime;
89
+ };
90
+ declare function startServer(source?: ConnectionSource, transport?: Transport): Promise<{
91
+ server: McpServer;
92
+ close: () => Promise<void>;
93
+ }>;
94
+ //#endregion
95
+ export { AutomateClient, type AutomateCredentials, AutomateRuntime, type ClientOptions, ConnectionSource, type ListOptions, type ListResource, SERVER_NAME, SERVER_VERSION, createServer, startServer };
@@ -0,0 +1,95 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
3
+
4
+ //#region src/boundary.d.ts
5
+
6
+ interface AutomateCredentials {
7
+ serverUrl: string;
8
+ username: string;
9
+ password: string;
10
+ clientId: string;
11
+ }
12
+ interface Address {
13
+ address: string;
14
+ family: number;
15
+ }
16
+ type Resolver = (hostname: string) => Promise<Address[]>;
17
+ interface HttpRequest {
18
+ url: URL;
19
+ address: Address;
20
+ method: "GET" | "POST";
21
+ headers: Record<string, string>;
22
+ body?: string;
23
+ signal: AbortSignal;
24
+ }
25
+ interface HttpResponse {
26
+ status: number;
27
+ body: string;
28
+ }
29
+ type Transport$1 = (request: HttpRequest) => Promise<HttpResponse>;
30
+ /** TLS validates the original hostname while lookup pins the checked public IP. */
31
+ //#endregion
32
+ //#region src/client.d.ts
33
+ declare const LIST_RESOURCES: readonly ["clients", "locations", "computers", "scripts", "monitor_history", "internal_monitor_results"];
34
+ type ListResource = (typeof LIST_RESOURCES)[number];
35
+ interface ListOptions {
36
+ page?: number;
37
+ pageSize?: number;
38
+ condition?: string;
39
+ }
40
+ interface ClientOptions {
41
+ resolve?: Resolver;
42
+ transport?: Transport$1;
43
+ now?: () => number;
44
+ timeoutMs?: number;
45
+ }
46
+ declare class AutomateClient {
47
+ private readonly credentials;
48
+ private readonly resolve;
49
+ private readonly transport;
50
+ private readonly now;
51
+ private readonly timeoutMs;
52
+ private token;
53
+ private tokenRequest;
54
+ constructor(credentials: AutomateCredentials, options?: ClientOptions);
55
+ list(resource: ListResource, options?: ListOptions): Promise<unknown>;
56
+ listComputer(resource: "monitors" | "script_history", computerId: number, options?: ListOptions): Promise<unknown>;
57
+ checkConnection(): Promise<{
58
+ connected: true;
59
+ }>;
60
+ private readList;
61
+ private getToken;
62
+ private acquireToken;
63
+ private mintToken;
64
+ private request;
65
+ }
66
+ //#endregion
67
+ //#region src/server.d.ts
68
+ declare const SERVER_NAME = "connectwise-automate-mcp";
69
+ declare const SERVER_VERSION: string;
70
+ /** Raw responses remain unknown until the runtime validates their authority. */
71
+ interface ConnectionSource {
72
+ getConnectProviderAccounts(provider: string): Promise<unknown>;
73
+ getConnectionCredentials(connectionId: string): Promise<unknown>;
74
+ }
75
+ declare class AutomateRuntime {
76
+ private readonly source;
77
+ private readonly options;
78
+ private readonly clients;
79
+ constructor(source: ConnectionSource, options?: ClientOptions);
80
+ listConnections(): Promise<unknown>;
81
+ getClient(selector: string): Promise<AutomateClient>;
82
+ clear(): void;
83
+ }
84
+ declare function createServer(source: ConnectionSource, options?: ClientOptions & {
85
+ emit?: (line: string) => void;
86
+ }): {
87
+ server: McpServer;
88
+ runtime: AutomateRuntime;
89
+ };
90
+ declare function startServer(source?: ConnectionSource, transport?: Transport): Promise<{
91
+ server: McpServer;
92
+ close: () => Promise<void>;
93
+ }>;
94
+ //#endregion
95
+ export { AutomateClient, type AutomateCredentials, AutomateRuntime, type ClientOptions, ConnectionSource, type ListOptions, type ListResource, SERVER_NAME, SERVER_VERSION, createServer, startServer };
package/dist/server.js ADDED
@@ -0,0 +1,2 @@
1
+ import { a as startServer, i as createServer, n as SERVER_NAME, o as AutomateClient, r as SERVER_VERSION, t as AutomateRuntime } from "./server2.js";
2
+ export { AutomateClient, AutomateRuntime, SERVER_NAME, SERVER_VERSION, createServer, startServer };