@goxtechnologies/connectwise-psa-mcp 1.3.0 → 1.4.2

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.
Files changed (58) hide show
  1. package/data/reports.json +29 -0
  2. package/dist/index.js +4 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/operations/analytics-extended.d.ts.map +1 -1
  5. package/dist/operations/analytics-extended.js +15 -4
  6. package/dist/operations/analytics-extended.js.map +1 -1
  7. package/dist/operations/analytics-msp-schedule.js +12 -2
  8. package/dist/operations/analytics-msp-schedule.js.map +1 -1
  9. package/dist/operations/analytics-msp-time-entry.d.ts +68 -0
  10. package/dist/operations/analytics-msp-time-entry.d.ts.map +1 -0
  11. package/dist/operations/analytics-msp-time-entry.js +235 -0
  12. package/dist/operations/analytics-msp-time-entry.js.map +1 -0
  13. package/dist/operations/analytics-msp-time.js +27 -8
  14. package/dist/operations/analytics-msp-time.js.map +1 -1
  15. package/dist/operations/analytics.d.ts.map +1 -1
  16. package/dist/operations/analytics.js +2 -0
  17. package/dist/operations/analytics.js.map +1 -1
  18. package/dist/operations/registry.d.ts.map +1 -1
  19. package/dist/operations/registry.js +1 -0
  20. package/dist/operations/registry.js.map +1 -1
  21. package/dist/scrapers/index.d.ts +4 -0
  22. package/dist/scrapers/index.d.ts.map +1 -0
  23. package/dist/scrapers/index.js +10 -0
  24. package/dist/scrapers/index.js.map +1 -0
  25. package/dist/scrapers/reports.d.ts +3 -0
  26. package/dist/scrapers/reports.d.ts.map +1 -0
  27. package/dist/scrapers/reports.js +443 -0
  28. package/dist/scrapers/reports.js.map +1 -0
  29. package/dist/services/load-env.d.ts.map +1 -1
  30. package/dist/services/load-env.js +11 -1
  31. package/dist/services/load-env.js.map +1 -1
  32. package/dist/services/report-cache.d.ts +16 -0
  33. package/dist/services/report-cache.d.ts.map +1 -0
  34. package/dist/services/report-cache.js +230 -0
  35. package/dist/services/report-cache.js.map +1 -0
  36. package/dist/services/report-config.d.ts +28 -0
  37. package/dist/services/report-config.d.ts.map +1 -0
  38. package/dist/services/report-config.js +127 -0
  39. package/dist/services/report-config.js.map +1 -0
  40. package/dist/services/totp.d.ts +27 -0
  41. package/dist/services/totp.d.ts.map +1 -0
  42. package/dist/services/totp.js +76 -0
  43. package/dist/services/totp.js.map +1 -0
  44. package/dist/services/web-scraper.d.ts +76 -0
  45. package/dist/services/web-scraper.d.ts.map +1 -0
  46. package/dist/services/web-scraper.js +435 -0
  47. package/dist/services/web-scraper.js.map +1 -0
  48. package/dist/tools/reports.d.ts +9 -0
  49. package/dist/tools/reports.d.ts.map +1 -0
  50. package/dist/tools/reports.js +388 -0
  51. package/dist/tools/reports.js.map +1 -0
  52. package/dist/tools/validation.js +2 -2
  53. package/dist/tools/validation.js.map +1 -1
  54. package/dist/types/reports.d.ts +87 -0
  55. package/dist/types/reports.d.ts.map +1 -0
  56. package/dist/types/reports.js +3 -0
  57. package/dist/types/reports.js.map +1 -0
  58. package/package.json +18 -4
@@ -0,0 +1,435 @@
1
+ // ConnectWise PSA MCP Server — General-purpose browser automation service
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync } from 'node:fs';
3
+ import { join, dirname } from 'node:path';
4
+ import { generateTOTPSafe } from './totp.js';
5
+ // ---------------------------------------------------------------------------
6
+ // Constants
7
+ // ---------------------------------------------------------------------------
8
+ const STORAGE_STATE_PATH = join(process.env['HOME'] ?? process.env['USERPROFILE'] ?? '', '.config', 'connectwise-psa', 'browser-state.json');
9
+ const DOWNLOADS_PATH = join(process.env['HOME'] ?? process.env['USERPROFILE'] ?? '', '.config', 'connectwise-psa', 'browser-downloads');
10
+ const MAX_REQUESTS_BEFORE_RECYCLE = 50;
11
+ const CONTEXT_MAX_AGE_MS = 30 * 60 * 1000; // 30 minutes
12
+ const PAGE_TIMEOUT_MS = 60_000;
13
+ const SESSION_STALE_WINDOW_MS = 5 * 60 * 1000; // 5 minutes before cookie expiry
14
+ /** Default login steps — checked in order during authentication. */
15
+ const DEFAULT_LOGIN_STEPS = [
16
+ {
17
+ name: 'credentials',
18
+ detect: 'input[type="text"][name*="user"], input#username, input[name="email"]',
19
+ },
20
+ {
21
+ name: 'totp',
22
+ detect: 'input[name*="otp"], input[name*="code"], input[name*="totp"], input#mfaCode',
23
+ },
24
+ {
25
+ name: 'device_trust',
26
+ detect: '.trust-device, [data-testid="trust-device"], button:has-text("Trust")',
27
+ },
28
+ ];
29
+ // ---------------------------------------------------------------------------
30
+ // Semaphore — limits concurrent pages
31
+ // ---------------------------------------------------------------------------
32
+ class Semaphore {
33
+ max;
34
+ queue = [];
35
+ active = 0;
36
+ constructor(max) {
37
+ this.max = max;
38
+ }
39
+ async acquire() {
40
+ if (this.active < this.max) {
41
+ this.active++;
42
+ return;
43
+ }
44
+ await new Promise((resolve) => {
45
+ this.queue.push(resolve);
46
+ });
47
+ this.active++;
48
+ }
49
+ release() {
50
+ this.active = Math.max(0, this.active - 1);
51
+ const next = this.queue.shift();
52
+ if (next)
53
+ next();
54
+ }
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Playwright availability check (lazy, cached)
58
+ // ---------------------------------------------------------------------------
59
+ let _playwrightAvailable = null;
60
+ /**
61
+ * Returns true if the `playwright` package is installed and importable.
62
+ * Result is cached after the first call.
63
+ */
64
+ export async function isPlaywrightAvailable() {
65
+ if (_playwrightAvailable !== null)
66
+ return _playwrightAvailable;
67
+ try {
68
+ await import('playwright');
69
+ _playwrightAvailable = true;
70
+ }
71
+ catch {
72
+ _playwrightAvailable = false;
73
+ }
74
+ return _playwrightAvailable;
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Singleton accessor
78
+ // ---------------------------------------------------------------------------
79
+ let _instance = null;
80
+ /** Returns the singleton WebScraper instance (creates it on first call). */
81
+ export function getWebScraper() {
82
+ if (!_instance)
83
+ _instance = new WebScraper();
84
+ return _instance;
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // WebScraper
88
+ // ---------------------------------------------------------------------------
89
+ export class WebScraper {
90
+ browser = null;
91
+ context = null;
92
+ initialized = false;
93
+ semaphore;
94
+ requestCount = 0;
95
+ contextCreatedAt = 0;
96
+ cleanupRegistered = false;
97
+ // Circuit breaker — disable auth retries after repeated failures
98
+ authFailCount = 0;
99
+ AUTH_FAIL_LIMIT = 3;
100
+ constructor() {
101
+ const maxPages = parseInt(process.env['CW_WEB_MAX_PAGES'] ?? '1', 10) || 1;
102
+ this.semaphore = new Semaphore(maxPages);
103
+ }
104
+ // ---------------------------------------------------------------------------
105
+ // Lifecycle
106
+ // ---------------------------------------------------------------------------
107
+ /**
108
+ * Launch the browser, restore storageState if available, and register
109
+ * process cleanup hooks. Safe to call multiple times — subsequent calls are
110
+ * no-ops if already initialized.
111
+ */
112
+ async init() {
113
+ if (this.initialized)
114
+ return;
115
+ const pw = await import('playwright');
116
+ const headless = process.env['CW_WEB_HEADLESS'] !== 'false';
117
+ const locale = process.env['CW_LOCALE'] ?? 'en-CA';
118
+ this.browser = await pw.chromium.launch({ headless });
119
+ await this.createContext();
120
+ // Ensure downloads directory exists
121
+ mkdirSync(DOWNLOADS_PATH, { recursive: true });
122
+ this.initialized = true;
123
+ this.registerCleanupHooks();
124
+ }
125
+ /** Close the browser context and browser cleanly. */
126
+ async shutdown() {
127
+ try {
128
+ if (this.context) {
129
+ await this.context.close();
130
+ this.context = null;
131
+ }
132
+ }
133
+ catch {
134
+ // ignore close errors
135
+ }
136
+ try {
137
+ if (this.browser) {
138
+ await this.browser.close();
139
+ this.browser = null;
140
+ }
141
+ }
142
+ catch {
143
+ // ignore close errors
144
+ }
145
+ this.initialized = false;
146
+ }
147
+ /** Returns true if the browser has been launched and not yet shut down. */
148
+ isInitialized() {
149
+ return this.initialized;
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // Authentication
153
+ // ---------------------------------------------------------------------------
154
+ /**
155
+ * Ensure the browser session is authenticated.
156
+ * Fast path: checks cookie expiry in storageState.
157
+ * Slow path: navigates to CW and runs the login flow.
158
+ */
159
+ async ensureAuth() {
160
+ // Circuit breaker — stop retrying after too many consecutive failures
161
+ if (this.authFailCount >= this.AUTH_FAIL_LIMIT) {
162
+ throw new Error(`WebScraper: authentication circuit breaker open after ${this.authFailCount} failures. ` +
163
+ 'Check CW_WEB_USERNAME / CW_WEB_PASSWORD / CW_TOTP_SECRET.');
164
+ }
165
+ // Fast path: valid session cookies still present
166
+ if (this.hasValidSession())
167
+ return;
168
+ // Slow path: navigate and authenticate
169
+ const page = await this.context.newPage();
170
+ try {
171
+ const base = this.getCWBase();
172
+ await page.goto(base, { waitUntil: 'domcontentloaded' });
173
+ if (await this.isOnLoginPage(page)) {
174
+ await this.authenticate(page);
175
+ }
176
+ // Persist session state
177
+ const state = await this.context.storageState();
178
+ const stateJson = JSON.stringify(state, null, 2);
179
+ mkdirSync(dirname(STORAGE_STATE_PATH), { recursive: true });
180
+ writeFileSync(STORAGE_STATE_PATH, stateJson, { mode: 0o600 });
181
+ this.authFailCount = 0; // reset on success
182
+ }
183
+ catch (err) {
184
+ this.authFailCount++;
185
+ throw err;
186
+ }
187
+ finally {
188
+ await page.close();
189
+ }
190
+ }
191
+ /**
192
+ * Returns true if storageState contains at least one non-expired cookie
193
+ * (with more than SESSION_STALE_WINDOW_MS remaining).
194
+ */
195
+ hasValidSession() {
196
+ if (!existsSync(STORAGE_STATE_PATH))
197
+ return false;
198
+ try {
199
+ const raw = readFileSync(STORAGE_STATE_PATH, 'utf-8');
200
+ const state = JSON.parse(raw);
201
+ if (!state.cookies || state.cookies.length === 0)
202
+ return false;
203
+ const now = Date.now();
204
+ const validCookie = state.cookies.some((c) => {
205
+ if (c.expires == null || c.expires === -1)
206
+ return true; // session cookie
207
+ return c.expires * 1000 > now + SESSION_STALE_WINDOW_MS;
208
+ });
209
+ return validCookie;
210
+ }
211
+ catch {
212
+ return false;
213
+ }
214
+ }
215
+ /**
216
+ * Returns true if the current page is showing a login form.
217
+ */
218
+ async isOnLoginPage(page) {
219
+ const loginSelector = DEFAULT_LOGIN_STEPS[0]?.detect ?? 'input[type="password"]';
220
+ try {
221
+ await page.waitForSelector(loginSelector, { timeout: 3_000 });
222
+ return true;
223
+ }
224
+ catch {
225
+ // Also check for password field as fallback
226
+ try {
227
+ await page.waitForSelector('input[type="password"]', { timeout: 2_000 });
228
+ return true;
229
+ }
230
+ catch {
231
+ return false;
232
+ }
233
+ }
234
+ }
235
+ /**
236
+ * Walk through the modular login steps, filling in credentials and TOTP as needed.
237
+ */
238
+ async authenticate(page) {
239
+ const username = process.env['CW_WEB_USERNAME'] ?? '';
240
+ const password = process.env['CW_WEB_PASSWORD'] ?? '';
241
+ const totpSecret = process.env['CW_TOTP_SECRET'];
242
+ for (const step of DEFAULT_LOGIN_STEPS) {
243
+ let selector;
244
+ try {
245
+ await page.waitForSelector(step.detect, { timeout: step.timeout ?? 4_000 });
246
+ // Find the first matching selector
247
+ const parts = step.detect.split(',').map((s) => s.trim());
248
+ let found = '';
249
+ for (const part of parts) {
250
+ const el = await page.$(part);
251
+ if (el) {
252
+ found = part;
253
+ break;
254
+ }
255
+ }
256
+ if (!found)
257
+ continue;
258
+ selector = found;
259
+ }
260
+ catch {
261
+ // Step not present on this page — skip
262
+ continue;
263
+ }
264
+ if (step.name === 'credentials') {
265
+ // Fill username into the detected field
266
+ await page.fill(selector, username);
267
+ // Find and fill password field
268
+ const pwField = await page.$('input[type="password"]');
269
+ if (pwField)
270
+ await page.fill('input[type="password"]', password);
271
+ // Submit
272
+ await page.keyboard.press('Enter');
273
+ // Wait briefly for the page to transition
274
+ await page.waitForLoadState('domcontentloaded').catch(() => { });
275
+ }
276
+ else if (step.name === 'totp') {
277
+ if (!totpSecret) {
278
+ throw new Error('CW_TOTP_SECRET is required for TOTP authentication');
279
+ }
280
+ const code = await generateTOTPSafe(totpSecret);
281
+ await page.fill(selector, code);
282
+ await page.keyboard.press('Enter');
283
+ await page.waitForLoadState('domcontentloaded').catch(() => { });
284
+ }
285
+ else if (step.name === 'device_trust') {
286
+ // Click the trust button if present
287
+ const btn = await page.$(selector);
288
+ if (btn) {
289
+ await btn.click();
290
+ await page.waitForLoadState('domcontentloaded').catch(() => { });
291
+ }
292
+ }
293
+ }
294
+ }
295
+ // ---------------------------------------------------------------------------
296
+ // Page management
297
+ // ---------------------------------------------------------------------------
298
+ /**
299
+ * Acquire an authenticated page.
300
+ * Recycles context if needed, ensures auth, waits for semaphore, creates page.
301
+ */
302
+ async acquirePage() {
303
+ if (!this.initialized)
304
+ await this.init();
305
+ await this.recycleIfNeeded();
306
+ await this.ensureAuth();
307
+ await this.semaphore.acquire();
308
+ const page = await this.context.newPage();
309
+ page.setDefaultTimeout(PAGE_TIMEOUT_MS);
310
+ this.requestCount++;
311
+ return page;
312
+ }
313
+ /**
314
+ * Release a page acquired via acquirePage().
315
+ * Closes the page, releases the semaphore, and cleans the downloads folder.
316
+ */
317
+ releasePage(page) {
318
+ page.close().catch(() => { });
319
+ this.semaphore.release();
320
+ this.cleanDownloads();
321
+ }
322
+ // ---------------------------------------------------------------------------
323
+ // Network capture
324
+ // ---------------------------------------------------------------------------
325
+ /**
326
+ * Run `action` while listening for network responses matching `urlPattern`.
327
+ * Resolves with all matched responses once the pattern is satisfied or
328
+ * `timeoutMs` elapses.
329
+ */
330
+ async captureResponses(page, action, urlPattern, timeoutMs = 15_000) {
331
+ const matched = [];
332
+ const onResponse = (response) => {
333
+ const url = response.url();
334
+ if (typeof urlPattern === 'string'
335
+ ? url.includes(urlPattern)
336
+ : urlPattern.test(url)) {
337
+ matched.push(response);
338
+ }
339
+ };
340
+ page.on('response', onResponse);
341
+ try {
342
+ await Promise.race([
343
+ action(),
344
+ new Promise((_, reject) => setTimeout(() => reject(new Error('captureResponses timeout')), timeoutMs)),
345
+ ]);
346
+ }
347
+ finally {
348
+ page.off('response', onResponse);
349
+ }
350
+ return matched;
351
+ }
352
+ // ---------------------------------------------------------------------------
353
+ // Internal helpers
354
+ // ---------------------------------------------------------------------------
355
+ /** Close the existing context and create a fresh one, restoring storageState if present. */
356
+ async createContext() {
357
+ if (this.context) {
358
+ try {
359
+ await this.context.close();
360
+ }
361
+ catch { /* ignore */ }
362
+ this.context = null;
363
+ }
364
+ const locale = process.env['CW_LOCALE'] ?? 'en-CA';
365
+ const contextOptions = {
366
+ locale,
367
+ acceptDownloads: true,
368
+ };
369
+ if (existsSync(STORAGE_STATE_PATH)) {
370
+ contextOptions['storageState'] = STORAGE_STATE_PATH;
371
+ }
372
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
373
+ this.context = await this.browser.newContext(contextOptions);
374
+ this.contextCreatedAt = Date.now();
375
+ this.requestCount = 0;
376
+ }
377
+ /** Recycle the browser context if it has been used too much or too long. */
378
+ async recycleIfNeeded() {
379
+ const aged = Date.now() - this.contextCreatedAt > CONTEXT_MAX_AGE_MS;
380
+ const overLimit = this.requestCount >= MAX_REQUESTS_BEFORE_RECYCLE;
381
+ if (aged || overLimit) {
382
+ await this.createContext();
383
+ }
384
+ }
385
+ /** Register process-level cleanup hooks (once). */
386
+ registerCleanupHooks() {
387
+ if (this.cleanupRegistered)
388
+ return;
389
+ this.cleanupRegistered = true;
390
+ const cleanup = () => {
391
+ this.shutdown().catch(() => { });
392
+ };
393
+ process.once('SIGTERM', cleanup);
394
+ process.once('SIGINT', cleanup);
395
+ process.once('exit', cleanup);
396
+ // Detect stdin close (piped mode — parent process gone)
397
+ if (process.stdin && !process.stdin.destroyed) {
398
+ process.stdin.once('close', cleanup);
399
+ }
400
+ }
401
+ /** Delete all files in the downloads directory. */
402
+ cleanDownloads() {
403
+ try {
404
+ if (!existsSync(DOWNLOADS_PATH))
405
+ return;
406
+ const entries = readdirSync(DOWNLOADS_PATH, { withFileTypes: true });
407
+ for (const entry of entries) {
408
+ try {
409
+ rmSync(join(DOWNLOADS_PATH, entry.name), { recursive: true, force: true });
410
+ }
411
+ catch {
412
+ // best-effort
413
+ }
414
+ }
415
+ }
416
+ catch {
417
+ // ignore
418
+ }
419
+ }
420
+ /** Derive the CW web base URL from CW_API_URL. */
421
+ getCWBase() {
422
+ const apiUrl = process.env['CW_API_URL'] ?? '';
423
+ // Strip the path after the domain+port
424
+ try {
425
+ const parsed = new URL(apiUrl);
426
+ return `${parsed.protocol}//${parsed.host}`;
427
+ }
428
+ catch {
429
+ // Fallback: strip everything after the third slash
430
+ const parts = apiUrl.split('/');
431
+ return parts.slice(0, 3).join('/');
432
+ }
433
+ }
434
+ }
435
+ //# sourceMappingURL=web-scraper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-scraper.js","sourceRoot":"","sources":["../../src/services/web-scraper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ export declare const reportTools: Tool[];
3
+ export declare function handleReportTool(name: string, args: Record<string, unknown>): Promise<{
4
+ content: Array<{
5
+ type: 'text';
6
+ text: string;
7
+ }>;
8
+ }>;
9
+ //# sourceMappingURL=reports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reports.d.ts","sourceRoot":"","sources":["../../src/tools/reports.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAqB1D,eAAO,MAAM,WAAW,EAAE,IAAI,EAqE7B,CAAC;AAMF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAW7D"}