@goxtechnologies/connectwise-psa-mcp 1.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/data/connectwise_api.db +0 -0
- package/data/manage.json +298179 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/analytics-extended.d.ts +6 -0
- package/dist/operations/analytics-extended.d.ts.map +1 -0
- package/dist/operations/analytics-extended.js +825 -0
- package/dist/operations/analytics-extended.js.map +1 -0
- package/dist/operations/analytics-msp-assets.d.ts +3 -0
- package/dist/operations/analytics-msp-assets.d.ts.map +1 -0
- package/dist/operations/analytics-msp-assets.js +180 -0
- package/dist/operations/analytics-msp-assets.js.map +1 -0
- package/dist/operations/analytics-msp-clients.d.ts +3 -0
- package/dist/operations/analytics-msp-clients.d.ts.map +1 -0
- package/dist/operations/analytics-msp-clients.js +198 -0
- package/dist/operations/analytics-msp-clients.js.map +1 -0
- package/dist/operations/analytics-msp-comms.d.ts +3 -0
- package/dist/operations/analytics-msp-comms.d.ts.map +1 -0
- package/dist/operations/analytics-msp-comms.js +127 -0
- package/dist/operations/analytics-msp-comms.js.map +1 -0
- package/dist/operations/analytics-msp-contracts.d.ts +3 -0
- package/dist/operations/analytics-msp-contracts.d.ts.map +1 -0
- package/dist/operations/analytics-msp-contracts.js +91 -0
- package/dist/operations/analytics-msp-contracts.js.map +1 -0
- package/dist/operations/analytics-msp-financial.d.ts +3 -0
- package/dist/operations/analytics-msp-financial.d.ts.map +1 -0
- package/dist/operations/analytics-msp-financial.js +300 -0
- package/dist/operations/analytics-msp-financial.js.map +1 -0
- package/dist/operations/analytics-msp-procurement.d.ts +3 -0
- package/dist/operations/analytics-msp-procurement.d.ts.map +1 -0
- package/dist/operations/analytics-msp-procurement.js +78 -0
- package/dist/operations/analytics-msp-procurement.js.map +1 -0
- package/dist/operations/analytics-msp-projects.d.ts +3 -0
- package/dist/operations/analytics-msp-projects.d.ts.map +1 -0
- package/dist/operations/analytics-msp-projects.js +190 -0
- package/dist/operations/analytics-msp-projects.js.map +1 -0
- package/dist/operations/analytics-msp-sales.d.ts +3 -0
- package/dist/operations/analytics-msp-sales.d.ts.map +1 -0
- package/dist/operations/analytics-msp-sales.js +99 -0
- package/dist/operations/analytics-msp-sales.js.map +1 -0
- package/dist/operations/analytics-msp-schedule.d.ts +3 -0
- package/dist/operations/analytics-msp-schedule.d.ts.map +1 -0
- package/dist/operations/analytics-msp-schedule.js +339 -0
- package/dist/operations/analytics-msp-schedule.js.map +1 -0
- package/dist/operations/analytics-msp-team.d.ts +3 -0
- package/dist/operations/analytics-msp-team.d.ts.map +1 -0
- package/dist/operations/analytics-msp-team.js +195 -0
- package/dist/operations/analytics-msp-team.js.map +1 -0
- package/dist/operations/analytics-msp-tickets.d.ts +3 -0
- package/dist/operations/analytics-msp-tickets.d.ts.map +1 -0
- package/dist/operations/analytics-msp-tickets.js +578 -0
- package/dist/operations/analytics-msp-tickets.js.map +1 -0
- package/dist/operations/analytics-msp-time.d.ts +3 -0
- package/dist/operations/analytics-msp-time.d.ts.map +1 -0
- package/dist/operations/analytics-msp-time.js +485 -0
- package/dist/operations/analytics-msp-time.js.map +1 -0
- package/dist/operations/analytics-msp-utils.d.ts +49 -0
- package/dist/operations/analytics-msp-utils.d.ts.map +1 -0
- package/dist/operations/analytics-msp-utils.js +157 -0
- package/dist/operations/analytics-msp-utils.js.map +1 -0
- package/dist/operations/analytics.d.ts +9 -0
- package/dist/operations/analytics.d.ts.map +1 -0
- package/dist/operations/analytics.js +742 -0
- package/dist/operations/analytics.js.map +1 -0
- package/dist/operations/executor.d.ts +10 -0
- package/dist/operations/executor.d.ts.map +1 -0
- package/dist/operations/executor.js +243 -0
- package/dist/operations/executor.js.map +1 -0
- package/dist/operations/registry.d.ts +16 -0
- package/dist/operations/registry.d.ts.map +1 -0
- package/dist/operations/registry.js +847 -0
- package/dist/operations/registry.js.map +1 -0
- package/dist/services/api-database.d.ts +38 -0
- package/dist/services/api-database.d.ts.map +1 -0
- package/dist/services/api-database.js +191 -0
- package/dist/services/api-database.js.map +1 -0
- package/dist/services/cache.d.ts +12 -0
- package/dist/services/cache.d.ts.map +1 -0
- package/dist/services/cache.js +32 -0
- package/dist/services/cache.js.map +1 -0
- package/dist/services/connectwise-api.d.ts +43 -0
- package/dist/services/connectwise-api.d.ts.map +1 -0
- package/dist/services/connectwise-api.js +198 -0
- package/dist/services/connectwise-api.js.map +1 -0
- package/dist/services/db-builder.d.ts +11 -0
- package/dist/services/db-builder.d.ts.map +1 -0
- package/dist/services/db-builder.js +237 -0
- package/dist/services/db-builder.js.map +1 -0
- package/dist/services/fast-memory.d.ts +39 -0
- package/dist/services/fast-memory.d.ts.map +1 -0
- package/dist/services/fast-memory.js +147 -0
- package/dist/services/fast-memory.js.map +1 -0
- package/dist/services/load-env.d.ts +15 -0
- package/dist/services/load-env.d.ts.map +1 -0
- package/dist/services/load-env.js +59 -0
- package/dist/services/load-env.js.map +1 -0
- package/dist/tools/batch.d.ts +9 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +159 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/composite.d.ts +9 -0
- package/dist/tools/composite.d.ts.map +1 -0
- package/dist/tools/composite.js +353 -0
- package/dist/tools/composite.js.map +1 -0
- package/dist/tools/discovery.d.ts +9 -0
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/discovery.js +245 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/execution.d.ts +9 -0
- package/dist/tools/execution.d.ts.map +1 -0
- package/dist/tools/execution.js +130 -0
- package/dist/tools/execution.js.map +1 -0
- package/dist/tools/memory.d.ts +9 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +152 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/operations.d.ts +9 -0
- package/dist/tools/operations.d.ts.map +1 -0
- package/dist/tools/operations.js +214 -0
- package/dist/tools/operations.js.map +1 -0
- package/dist/tools/pagination.d.ts +9 -0
- package/dist/tools/pagination.d.ts.map +1 -0
- package/dist/tools/pagination.js +133 -0
- package/dist/tools/pagination.js.map +1 -0
- package/dist/tools/validation.d.ts +9 -0
- package/dist/tools/validation.d.ts.map +1 -0
- package/dist/tools/validation.js +705 -0
- package/dist/tools/validation.js.map +1 -0
- package/dist/types/index.d.ts +145 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/operations.d.ts +30 -0
- package/dist/types/operations.d.ts.map +1 -0
- package/dist/types/operations.js +3 -0
- package/dist/types/operations.js.map +1 -0
- package/dist/utils/conditions.d.ts +20 -0
- package/dist/utils/conditions.d.ts.map +1 -0
- package/dist/utils/conditions.js +78 -0
- package/dist/utils/conditions.js.map +1 -0
- package/dist/utils/formatters.d.ts +35 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +337 -0
- package/dist/utils/formatters.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// ConnectWise PSA MCP Server — ConnectWise Manage REST API HTTP Client
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
3
|
+
const PAGE_SIZE = 250;
|
|
4
|
+
const DEFAULT_MAX_ITEMS = 5_000;
|
|
5
|
+
const MAX_RETRIES = 3;
|
|
6
|
+
const RATE_LIMIT_WARN_THRESHOLD = 10;
|
|
7
|
+
// Delays in ms for retry attempts: 1s, 3s, 9s (exponential backoff)
|
|
8
|
+
const RETRY_DELAYS_MS = [1_000, 3_000, 9_000];
|
|
9
|
+
// HTTP status codes that warrant a retry
|
|
10
|
+
const RETRYABLE_STATUSES = new Set([409, 429, 500]);
|
|
11
|
+
export class ConnectWiseAPI {
|
|
12
|
+
config;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the Basic auth header and required ConnectWise headers.
|
|
18
|
+
* Credentials: base64(authPrefix+publicKey:privateKey)
|
|
19
|
+
*/
|
|
20
|
+
getAuthHeaders() {
|
|
21
|
+
const username = `${this.config.authPrefix}${this.config.publicKey}`;
|
|
22
|
+
const credentials = Buffer.from(`${username}:${this.config.privateKey}`).toString('base64');
|
|
23
|
+
return {
|
|
24
|
+
Authorization: `Basic ${credentials}`,
|
|
25
|
+
clientId: this.config.companyId,
|
|
26
|
+
'Content-Type': 'application/json',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Core HTTP request using native fetch (Node 18+).
|
|
31
|
+
* Builds the full URL from config.apiUrl + path. Query params are always
|
|
32
|
+
* appended to the URL as a query string. The request body is JSON-encoded
|
|
33
|
+
* for POST/PUT/PATCH. Emits a rate-limit warning when
|
|
34
|
+
* X-RateLimit-Remaining drops below 10. Does NOT truncate response data.
|
|
35
|
+
*/
|
|
36
|
+
async request(options) {
|
|
37
|
+
const { path, method, params, body, timeout = DEFAULT_TIMEOUT_MS } = options;
|
|
38
|
+
const queryString = params
|
|
39
|
+
? new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString()
|
|
40
|
+
: '';
|
|
41
|
+
const url = queryString
|
|
42
|
+
? `${this.config.apiUrl}${path}?${queryString}`
|
|
43
|
+
: `${this.config.apiUrl}${path}`;
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
46
|
+
let response;
|
|
47
|
+
try {
|
|
48
|
+
response = await fetch(url, {
|
|
49
|
+
method,
|
|
50
|
+
headers: this.getAuthHeaders(),
|
|
51
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
52
|
+
signal: controller.signal,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
clearTimeout(timeoutId);
|
|
57
|
+
}
|
|
58
|
+
// Rate-limit warning before body consumption
|
|
59
|
+
const remaining = response.headers.get('X-RateLimit-Remaining');
|
|
60
|
+
if (remaining !== null && Number(remaining) < RATE_LIMIT_WARN_THRESHOLD) {
|
|
61
|
+
console.warn(`[ConnectWiseAPI] Rate limit warning: X-RateLimit-Remaining=${remaining}`);
|
|
62
|
+
}
|
|
63
|
+
// Collect all response headers into a plain object
|
|
64
|
+
const headers = {};
|
|
65
|
+
response.headers.forEach((value, key) => {
|
|
66
|
+
headers[key] = value;
|
|
67
|
+
});
|
|
68
|
+
// Parse body — JSON when Content-Type indicates it, otherwise raw text
|
|
69
|
+
let data;
|
|
70
|
+
const contentType = response.headers.get('Content-Type') ?? '';
|
|
71
|
+
if (contentType.includes('application/json')) {
|
|
72
|
+
data = (await response.json());
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
data = (await response.text());
|
|
76
|
+
}
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(`[ConnectWiseAPI] HTTP ${response.status} ${response.statusText} — ${url}\n${JSON.stringify(data)}`);
|
|
79
|
+
}
|
|
80
|
+
return { data, status: response.status, headers };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Retry wrapper around `request`. Retries up to MAX_RETRIES (3) times on
|
|
84
|
+
* HTTP 409, 429, or 500 with exponential backoff (1 s -> 3 s -> 9 s).
|
|
85
|
+
*/
|
|
86
|
+
async requestWithRetry(options) {
|
|
87
|
+
let lastError;
|
|
88
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
89
|
+
try {
|
|
90
|
+
return await this.request(options);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
lastError = err;
|
|
94
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
95
|
+
const statusMatch = /HTTP (\d{3})/.exec(message);
|
|
96
|
+
const status = statusMatch ? Number(statusMatch[1]) : null;
|
|
97
|
+
const isRetryable = status !== null && RETRYABLE_STATUSES.has(status);
|
|
98
|
+
if (!isRetryable || attempt === MAX_RETRIES) {
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
const delayMs = RETRY_DELAYS_MS[attempt] ?? RETRY_DELAYS_MS[RETRY_DELAYS_MS.length - 1];
|
|
102
|
+
console.warn(`[ConnectWiseAPI] Retryable error (HTTP ${status}), attempt ${attempt + 1}/${MAX_RETRIES}. Waiting ${delayMs}ms...`);
|
|
103
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw lastError;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Auto-paginate through all pages of a collection endpoint.
|
|
110
|
+
* Pages are fetched with pageSize=250. Stops when a page returns fewer
|
|
111
|
+
* than 250 items. If accumulated items exceed maxItems (default 5000),
|
|
112
|
+
* stops early and sets truncated=true.
|
|
113
|
+
*/
|
|
114
|
+
async paginatedFetch(path, conditions, fields, maxItems = DEFAULT_MAX_ITEMS) {
|
|
115
|
+
const items = [];
|
|
116
|
+
let page = 1;
|
|
117
|
+
let pagesFetched = 0;
|
|
118
|
+
let truncated = false;
|
|
119
|
+
while (true) {
|
|
120
|
+
const params = {
|
|
121
|
+
page,
|
|
122
|
+
pageSize: PAGE_SIZE,
|
|
123
|
+
};
|
|
124
|
+
if (conditions)
|
|
125
|
+
params['conditions'] = conditions;
|
|
126
|
+
if (fields)
|
|
127
|
+
params['fields'] = fields;
|
|
128
|
+
const response = await this.requestWithRetry({ path, method: 'GET', params });
|
|
129
|
+
const pageItems = response.data ?? [];
|
|
130
|
+
pagesFetched += 1;
|
|
131
|
+
for (const item of pageItems) {
|
|
132
|
+
if (items.length >= maxItems) {
|
|
133
|
+
truncated = true;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
items.push(item);
|
|
137
|
+
}
|
|
138
|
+
if (truncated || pageItems.length < PAGE_SIZE) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
page += 1;
|
|
142
|
+
}
|
|
143
|
+
return { items, totalCount: items.length, pagesFetched, truncated };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Retrieve the total record count for a given path.
|
|
147
|
+
* Tries `${path}/count` first. Falls back to `${path}count` on 404.
|
|
148
|
+
* Expects the count endpoint to return { count: number }.
|
|
149
|
+
*/
|
|
150
|
+
async count(path, conditions) {
|
|
151
|
+
const params = {};
|
|
152
|
+
if (conditions)
|
|
153
|
+
params['conditions'] = conditions;
|
|
154
|
+
const base = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
155
|
+
try {
|
|
156
|
+
const res = await this.request({
|
|
157
|
+
path: `${base}/count`,
|
|
158
|
+
method: 'GET',
|
|
159
|
+
params,
|
|
160
|
+
});
|
|
161
|
+
return res.data.count;
|
|
162
|
+
}
|
|
163
|
+
catch (primaryErr) {
|
|
164
|
+
const message = primaryErr instanceof Error ? primaryErr.message : String(primaryErr);
|
|
165
|
+
if (/HTTP 404/.test(message)) {
|
|
166
|
+
const res = await this.request({
|
|
167
|
+
path: `${base}count`,
|
|
168
|
+
method: 'GET',
|
|
169
|
+
params,
|
|
170
|
+
});
|
|
171
|
+
return res.data.count;
|
|
172
|
+
}
|
|
173
|
+
throw primaryErr;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// Singleton factory
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
let instance = null;
|
|
181
|
+
/**
|
|
182
|
+
* Returns the shared ConnectWiseAPI singleton, initialised from environment
|
|
183
|
+
* variables on first call:
|
|
184
|
+
* CW_API_URL, CW_COMPANY_ID, CW_PUBLIC_KEY, CW_PRIVATE_KEY, CW_AUTH_PREFIX
|
|
185
|
+
*/
|
|
186
|
+
export function getAPI() {
|
|
187
|
+
if (!instance) {
|
|
188
|
+
instance = new ConnectWiseAPI({
|
|
189
|
+
apiUrl: process.env['CW_API_URL'] ?? '',
|
|
190
|
+
companyId: process.env['CW_COMPANY_ID'] ?? '',
|
|
191
|
+
publicKey: process.env['CW_PUBLIC_KEY'] ?? '',
|
|
192
|
+
privateKey: process.env['CW_PRIVATE_KEY'] ?? '',
|
|
193
|
+
authPrefix: process.env['CW_AUTH_PREFIX'] ?? '',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return instance;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=connectwise-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectwise-api.js","sourceRoot":"","sources":["../../src/services/connectwise-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db-builder.ts
|
|
3
|
+
*
|
|
4
|
+
* Parses the ConnectWise OpenAPI spec (manage.json) and builds a SQLite
|
|
5
|
+
* database used at runtime for endpoint discovery.
|
|
6
|
+
*
|
|
7
|
+
* Run via: node dist/services/db-builder.js
|
|
8
|
+
* Invoked automatically by the `build:db` npm script after `tsc`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildDatabase(): void;
|
|
11
|
+
//# sourceMappingURL=db-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-builder.d.ts","sourceRoot":"","sources":["../../src/services/db-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA8MH,wBAAgB,aAAa,IAAI,IAAI,CAoKpC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db-builder.ts
|
|
3
|
+
*
|
|
4
|
+
* Parses the ConnectWise OpenAPI spec (manage.json) and builds a SQLite
|
|
5
|
+
* database used at runtime for endpoint discovery.
|
|
6
|
+
*
|
|
7
|
+
* Run via: node dist/services/db-builder.js
|
|
8
|
+
* Invoked automatically by the `build:db` npm script after `tsc`.
|
|
9
|
+
*/
|
|
10
|
+
import Database from 'better-sqlite3';
|
|
11
|
+
import { existsSync, readFileSync, unlinkSync } from 'fs';
|
|
12
|
+
import { dirname, join } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// ESM __dirname shim
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete'];
|
|
23
|
+
/**
|
|
24
|
+
* Safely JSON-stringify a value, returning an empty string on failure.
|
|
25
|
+
* Avoids crashing on circular references or non-serialisable values.
|
|
26
|
+
*/
|
|
27
|
+
function safeStringify(value) {
|
|
28
|
+
if (value === undefined || value === null)
|
|
29
|
+
return '';
|
|
30
|
+
try {
|
|
31
|
+
return JSON.stringify(value);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Pick the first media-type entry from a content map that carries a schema or
|
|
39
|
+
* example, preferring well-known types but accepting any key present.
|
|
40
|
+
*/
|
|
41
|
+
function pickMediaType(content) {
|
|
42
|
+
if (!content)
|
|
43
|
+
return undefined;
|
|
44
|
+
// Prefer standard JSON content types first.
|
|
45
|
+
const preferredKeys = [
|
|
46
|
+
'application/json',
|
|
47
|
+
'application/json; charset=utf-8',
|
|
48
|
+
];
|
|
49
|
+
for (const key of preferredKeys) {
|
|
50
|
+
if (content[key])
|
|
51
|
+
return content[key];
|
|
52
|
+
}
|
|
53
|
+
// Fall back to the first available key (ConnectWise uses a versioned media
|
|
54
|
+
// type: "application/vnd.connectwise.com+json; version=2025.1").
|
|
55
|
+
const firstKey = Object.keys(content)[0];
|
|
56
|
+
return firstKey !== undefined ? content[firstKey] : undefined;
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// DDL — split into individual statements so each can be prepared and run
|
|
60
|
+
// without needing the multi-statement exec() shortcut
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
const DDL_STATEMENTS = [
|
|
63
|
+
`CREATE TABLE endpoints (
|
|
64
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
65
|
+
path TEXT NOT NULL,
|
|
66
|
+
method TEXT NOT NULL,
|
|
67
|
+
description TEXT,
|
|
68
|
+
category TEXT,
|
|
69
|
+
summary TEXT,
|
|
70
|
+
tags TEXT,
|
|
71
|
+
UNIQUE(path, method)
|
|
72
|
+
)`,
|
|
73
|
+
`CREATE TABLE parameters (
|
|
74
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
+
endpoint_id INTEGER NOT NULL,
|
|
76
|
+
name TEXT NOT NULL,
|
|
77
|
+
location TEXT NOT NULL,
|
|
78
|
+
required INTEGER NOT NULL DEFAULT 0,
|
|
79
|
+
type TEXT,
|
|
80
|
+
description TEXT,
|
|
81
|
+
FOREIGN KEY (endpoint_id) REFERENCES endpoints(id)
|
|
82
|
+
)`,
|
|
83
|
+
`CREATE TABLE request_bodies (
|
|
84
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
85
|
+
endpoint_id INTEGER NOT NULL,
|
|
86
|
+
schema TEXT,
|
|
87
|
+
example TEXT,
|
|
88
|
+
FOREIGN KEY (endpoint_id) REFERENCES endpoints(id)
|
|
89
|
+
)`,
|
|
90
|
+
`CREATE TABLE response_bodies (
|
|
91
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
92
|
+
endpoint_id INTEGER NOT NULL,
|
|
93
|
+
status_code TEXT NOT NULL,
|
|
94
|
+
description TEXT,
|
|
95
|
+
schema TEXT,
|
|
96
|
+
example TEXT,
|
|
97
|
+
FOREIGN KEY (endpoint_id) REFERENCES endpoints(id)
|
|
98
|
+
)`,
|
|
99
|
+
'CREATE INDEX idx_endpoints_path ON endpoints(path)',
|
|
100
|
+
'CREATE INDEX idx_endpoints_method ON endpoints(method)',
|
|
101
|
+
'CREATE INDEX idx_endpoints_tags ON endpoints(tags)',
|
|
102
|
+
'CREATE INDEX idx_endpoints_category ON endpoints(category)',
|
|
103
|
+
];
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Core builder
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
export function buildDatabase() {
|
|
108
|
+
const manageJsonPath = join(__dirname, '../../data/manage.json');
|
|
109
|
+
const dbPath = join(__dirname, '../../data/connectwise_api.db');
|
|
110
|
+
// ---- Load spec ----------------------------------------------------------
|
|
111
|
+
if (!existsSync(manageJsonPath)) {
|
|
112
|
+
throw new Error(`manage.json not found at: ${manageJsonPath}`);
|
|
113
|
+
}
|
|
114
|
+
console.log(`Reading spec from: ${manageJsonPath}`);
|
|
115
|
+
const raw = readFileSync(manageJsonPath, 'utf-8');
|
|
116
|
+
const spec = JSON.parse(raw);
|
|
117
|
+
// ---- Destroy existing DB so we always rebuild cleanly -------------------
|
|
118
|
+
if (existsSync(dbPath)) {
|
|
119
|
+
console.log('Existing database found — deleting for fresh rebuild.');
|
|
120
|
+
unlinkSync(dbPath);
|
|
121
|
+
}
|
|
122
|
+
// ---- Create database & apply schema -------------------------------------
|
|
123
|
+
const db = new Database(dbPath);
|
|
124
|
+
// WAL mode for better write throughput during the bulk insert.
|
|
125
|
+
db.pragma('journal_mode = WAL');
|
|
126
|
+
db.pragma('synchronous = NORMAL');
|
|
127
|
+
// Run each DDL statement individually via prepare + run so that we avoid
|
|
128
|
+
// calling the multi-statement shorthand method.
|
|
129
|
+
for (const stmt of DDL_STATEMENTS) {
|
|
130
|
+
db.prepare(stmt).run();
|
|
131
|
+
}
|
|
132
|
+
// ---- Prepared statements ------------------------------------------------
|
|
133
|
+
const insertEndpoint = db.prepare(`
|
|
134
|
+
INSERT OR IGNORE INTO endpoints (path, method, description, category, summary, tags)
|
|
135
|
+
VALUES (@path, @method, @description, @category, @summary, @tags)
|
|
136
|
+
`);
|
|
137
|
+
const selectEndpointId = db.prepare(`
|
|
138
|
+
SELECT id FROM endpoints WHERE path = @path AND method = @method
|
|
139
|
+
`);
|
|
140
|
+
const insertParameter = db.prepare(`
|
|
141
|
+
INSERT INTO parameters (endpoint_id, name, location, required, type, description)
|
|
142
|
+
VALUES (@endpoint_id, @name, @location, @required, @type, @description)
|
|
143
|
+
`);
|
|
144
|
+
const insertRequestBody = db.prepare(`
|
|
145
|
+
INSERT INTO request_bodies (endpoint_id, schema, example)
|
|
146
|
+
VALUES (@endpoint_id, @schema, @example)
|
|
147
|
+
`);
|
|
148
|
+
const insertResponseBody = db.prepare(`
|
|
149
|
+
INSERT INTO response_bodies (endpoint_id, status_code, description, schema, example)
|
|
150
|
+
VALUES (@endpoint_id, @status_code, @description, @schema, @example)
|
|
151
|
+
`);
|
|
152
|
+
// ---- Counters -----------------------------------------------------------
|
|
153
|
+
let totalEndpoints = 0;
|
|
154
|
+
let totalParameters = 0;
|
|
155
|
+
// ---- Bulk insert wrapped in a single transaction for speed --------------
|
|
156
|
+
const runImport = db.transaction(() => {
|
|
157
|
+
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
158
|
+
for (const method of HTTP_METHODS) {
|
|
159
|
+
const operation = pathItem[method];
|
|
160
|
+
if (!operation)
|
|
161
|
+
continue;
|
|
162
|
+
// -- Endpoint fields ------------------------------------------------
|
|
163
|
+
const category = operation.tags?.[0] ?? '';
|
|
164
|
+
const summary = operation.summary ?? '';
|
|
165
|
+
const tags = operation.tags ? operation.tags.join(',') : '';
|
|
166
|
+
// Use operationId or summary as a fallback when description is absent.
|
|
167
|
+
const description = operation.description?.trim() ||
|
|
168
|
+
operation.operationId?.trim() ||
|
|
169
|
+
summary;
|
|
170
|
+
insertEndpoint.run({
|
|
171
|
+
path,
|
|
172
|
+
method: method.toUpperCase(),
|
|
173
|
+
description,
|
|
174
|
+
category,
|
|
175
|
+
summary,
|
|
176
|
+
tags,
|
|
177
|
+
});
|
|
178
|
+
// Retrieve the auto-assigned id (INSERT OR IGNORE means a duplicate
|
|
179
|
+
// silently skips; the SELECT still returns the existing row's id).
|
|
180
|
+
const row = selectEndpointId.get({ path, method: method.toUpperCase() });
|
|
181
|
+
// Guard: row should always exist after the insert-or-ignore above.
|
|
182
|
+
if (!row)
|
|
183
|
+
continue;
|
|
184
|
+
const endpointId = row.id;
|
|
185
|
+
totalEndpoints++;
|
|
186
|
+
// -- Parameters -----------------------------------------------------
|
|
187
|
+
if (Array.isArray(operation.parameters)) {
|
|
188
|
+
for (const param of operation.parameters) {
|
|
189
|
+
const paramType = param.schema?.type ?? '';
|
|
190
|
+
insertParameter.run({
|
|
191
|
+
endpoint_id: endpointId,
|
|
192
|
+
name: param.name ?? '',
|
|
193
|
+
location: param.in ?? '',
|
|
194
|
+
required: param.required === true ? 1 : 0,
|
|
195
|
+
type: paramType,
|
|
196
|
+
description: param.description ?? '',
|
|
197
|
+
});
|
|
198
|
+
totalParameters++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// -- Request body ---------------------------------------------------
|
|
202
|
+
if (operation.requestBody) {
|
|
203
|
+
const mediaType = pickMediaType(operation.requestBody.content);
|
|
204
|
+
insertRequestBody.run({
|
|
205
|
+
endpoint_id: endpointId,
|
|
206
|
+
schema: safeStringify(mediaType?.schema),
|
|
207
|
+
example: safeStringify(mediaType?.example),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// -- Response bodies ------------------------------------------------
|
|
211
|
+
if (operation.responses) {
|
|
212
|
+
for (const [statusCode, response] of Object.entries(operation.responses)) {
|
|
213
|
+
const mediaType = pickMediaType(response.content);
|
|
214
|
+
insertResponseBody.run({
|
|
215
|
+
endpoint_id: endpointId,
|
|
216
|
+
status_code: statusCode,
|
|
217
|
+
description: response.description ?? '',
|
|
218
|
+
schema: safeStringify(mediaType?.schema),
|
|
219
|
+
example: safeStringify(mediaType?.example),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
runImport();
|
|
227
|
+
// ---- Housekeeping -------------------------------------------------------
|
|
228
|
+
db.pragma('wal_checkpoint(TRUNCATE)');
|
|
229
|
+
db.close();
|
|
230
|
+
console.log(`Built database: ${totalEndpoints} endpoints, ${totalParameters} parameters`);
|
|
231
|
+
console.log(`Database written to: ${dbPath}`);
|
|
232
|
+
}
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// Entry point — always runs when invoked as a script
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
buildDatabase();
|
|
237
|
+
//# sourceMappingURL=db-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-builder.js","sourceRoot":"","sources":["../../src/services/db-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SavedQuery } from '../types/index.js';
|
|
2
|
+
export declare class FastMemory {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(dbPath: string);
|
|
5
|
+
/**
|
|
6
|
+
* Ensure the saved_queries table exists. Safe to call multiple times.
|
|
7
|
+
*/
|
|
8
|
+
private initialize;
|
|
9
|
+
/**
|
|
10
|
+
* Persist a query. Uses INSERT OR REPLACE (upsert) keyed on the
|
|
11
|
+
* UNIQUE constraint (path, method). Sets timestamp to Date.now().
|
|
12
|
+
*
|
|
13
|
+
* Returns the row id of the inserted or replaced record.
|
|
14
|
+
*/
|
|
15
|
+
saveQuery(description: string, path: string, method: string, params?: string, data?: string): number;
|
|
16
|
+
/**
|
|
17
|
+
* Find a saved query by exact path and method match. If found, increments
|
|
18
|
+
* usage_count and updates timestamp to Date.now(), then returns the
|
|
19
|
+
* updated record. Returns null when no match exists.
|
|
20
|
+
*/
|
|
21
|
+
findQuery(path: string, method: string): SavedQuery | null;
|
|
22
|
+
/**
|
|
23
|
+
* Search saved queries by keyword. When searchTerm is provided, performs a
|
|
24
|
+
* LIKE search on both description and path. Returns all records when
|
|
25
|
+
* searchTerm is absent or empty. Results are ordered by usage_count DESC.
|
|
26
|
+
*/
|
|
27
|
+
searchQueries(searchTerm?: string): SavedQuery[];
|
|
28
|
+
/**
|
|
29
|
+
* Delete a saved query by its integer id.
|
|
30
|
+
* Returns true when a row was deleted, false when no row matched.
|
|
31
|
+
*/
|
|
32
|
+
deleteQuery(id: number): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Delete all saved queries. Returns the number of rows removed.
|
|
35
|
+
*/
|
|
36
|
+
clearAll(): number;
|
|
37
|
+
close(): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=fast-memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fast-memory.d.ts","sourceRoot":"","sources":["../../src/services/fast-memory.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA+C/C,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAO1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,SAAS,CACP,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,GACZ,MAAM;IAuBT;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IA8B1D;;;;OAIG;IACH,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAmChD;;;OAGG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAOhC;;OAEG;IACH,QAAQ,IAAI,MAAM;IAKlB,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// ConnectWise PSA MCP Server — SQLite Saved Query System
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import Database from 'better-sqlite3';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Helper — resolve DB path relative to this compiled module
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function resolveDbPath(dbPath) {
|
|
9
|
+
if (dbPath)
|
|
10
|
+
return dbPath;
|
|
11
|
+
// Default: same data directory used by APIDatabase
|
|
12
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
return join(scriptDir, '..', '..', 'data', 'fast-memory.db');
|
|
14
|
+
}
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// DDL — schema guaranteed by initialize()
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const CREATE_TABLE_SQL = `
|
|
19
|
+
CREATE TABLE IF NOT EXISTS saved_queries (
|
|
20
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
|
+
description TEXT NOT NULL,
|
|
22
|
+
path TEXT NOT NULL,
|
|
23
|
+
method TEXT NOT NULL,
|
|
24
|
+
params TEXT,
|
|
25
|
+
data TEXT,
|
|
26
|
+
timestamp INTEGER NOT NULL,
|
|
27
|
+
usage_count INTEGER NOT NULL DEFAULT 0,
|
|
28
|
+
UNIQUE (path, method)
|
|
29
|
+
)
|
|
30
|
+
`;
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// FastMemory
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
export class FastMemory {
|
|
35
|
+
db;
|
|
36
|
+
constructor(dbPath) {
|
|
37
|
+
const resolved = resolveDbPath(dbPath);
|
|
38
|
+
// Opened read-write so we can create the table and mutate rows
|
|
39
|
+
this.db = new Database(resolved);
|
|
40
|
+
this.initialize();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Ensure the saved_queries table exists. Safe to call multiple times.
|
|
44
|
+
*/
|
|
45
|
+
initialize() {
|
|
46
|
+
this.db.prepare(CREATE_TABLE_SQL).run();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Persist a query. Uses INSERT OR REPLACE (upsert) keyed on the
|
|
50
|
+
* UNIQUE constraint (path, method). Sets timestamp to Date.now().
|
|
51
|
+
*
|
|
52
|
+
* Returns the row id of the inserted or replaced record.
|
|
53
|
+
*/
|
|
54
|
+
saveQuery(description, path, method, params, data) {
|
|
55
|
+
const stmt = this.db.prepare(`
|
|
56
|
+
INSERT OR REPLACE INTO saved_queries (description, path, method, params, data, timestamp, usage_count)
|
|
57
|
+
VALUES (?, ?, ?, ?, ?, ?, COALESCE(
|
|
58
|
+
(SELECT usage_count FROM saved_queries WHERE path = ? AND method = ?), 0
|
|
59
|
+
))
|
|
60
|
+
`);
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
const result = stmt.run(description, path, method.toUpperCase(), params ?? null, data ?? null, now, path, method.toUpperCase());
|
|
63
|
+
return result.lastInsertRowid;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Find a saved query by exact path and method match. If found, increments
|
|
67
|
+
* usage_count and updates timestamp to Date.now(), then returns the
|
|
68
|
+
* updated record. Returns null when no match exists.
|
|
69
|
+
*/
|
|
70
|
+
findQuery(path, method) {
|
|
71
|
+
const normalMethod = method.toUpperCase();
|
|
72
|
+
const row = this.db
|
|
73
|
+
.prepare('SELECT id, description, path, method, params, data, timestamp, usage_count FROM saved_queries WHERE path = ? AND method = ?')
|
|
74
|
+
.get(path, normalMethod);
|
|
75
|
+
if (!row)
|
|
76
|
+
return null;
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
this.db
|
|
79
|
+
.prepare('UPDATE saved_queries SET usage_count = usage_count + 1, timestamp = ? WHERE id = ?')
|
|
80
|
+
.run(now, row.id);
|
|
81
|
+
return {
|
|
82
|
+
id: row.id,
|
|
83
|
+
description: row.description,
|
|
84
|
+
path: row.path,
|
|
85
|
+
method: row.method,
|
|
86
|
+
params: row.params,
|
|
87
|
+
data: row.data,
|
|
88
|
+
timestamp: now,
|
|
89
|
+
usage_count: row.usage_count + 1,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Search saved queries by keyword. When searchTerm is provided, performs a
|
|
94
|
+
* LIKE search on both description and path. Returns all records when
|
|
95
|
+
* searchTerm is absent or empty. Results are ordered by usage_count DESC.
|
|
96
|
+
*/
|
|
97
|
+
searchQueries(searchTerm) {
|
|
98
|
+
let rows;
|
|
99
|
+
if (searchTerm && searchTerm.trim().length > 0) {
|
|
100
|
+
const pattern = `%${searchTerm.trim()}%`;
|
|
101
|
+
rows = this.db
|
|
102
|
+
.prepare(`SELECT id, description, path, method, params, data, timestamp, usage_count
|
|
103
|
+
FROM saved_queries
|
|
104
|
+
WHERE description LIKE ? OR path LIKE ?
|
|
105
|
+
ORDER BY usage_count DESC`)
|
|
106
|
+
.all(pattern, pattern);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
rows = this.db
|
|
110
|
+
.prepare(`SELECT id, description, path, method, params, data, timestamp, usage_count
|
|
111
|
+
FROM saved_queries
|
|
112
|
+
ORDER BY usage_count DESC`)
|
|
113
|
+
.all();
|
|
114
|
+
}
|
|
115
|
+
return rows.map((row) => ({
|
|
116
|
+
id: row.id,
|
|
117
|
+
description: row.description,
|
|
118
|
+
path: row.path,
|
|
119
|
+
method: row.method,
|
|
120
|
+
params: row.params,
|
|
121
|
+
data: row.data,
|
|
122
|
+
timestamp: row.timestamp,
|
|
123
|
+
usage_count: row.usage_count,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Delete a saved query by its integer id.
|
|
128
|
+
* Returns true when a row was deleted, false when no row matched.
|
|
129
|
+
*/
|
|
130
|
+
deleteQuery(id) {
|
|
131
|
+
const result = this.db
|
|
132
|
+
.prepare('DELETE FROM saved_queries WHERE id = ?')
|
|
133
|
+
.run(id);
|
|
134
|
+
return result.changes > 0;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Delete all saved queries. Returns the number of rows removed.
|
|
138
|
+
*/
|
|
139
|
+
clearAll() {
|
|
140
|
+
const result = this.db.prepare('DELETE FROM saved_queries').run();
|
|
141
|
+
return result.changes;
|
|
142
|
+
}
|
|
143
|
+
close() {
|
|
144
|
+
this.db.close();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=fast-memory.js.map
|