acuity-mcp-server 1.0.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 +541 -0
- package/dist/auth/device-flow.d.ts +46 -0
- package/dist/auth/device-flow.d.ts.map +1 -0
- package/dist/auth/device-flow.js +141 -0
- package/dist/auth/device-flow.js.map +1 -0
- package/dist/auth/http-auth.d.ts +25 -0
- package/dist/auth/http-auth.d.ts.map +1 -0
- package/dist/auth/http-auth.js +101 -0
- package/dist/auth/http-auth.js.map +1 -0
- package/dist/auth/jwt-validator.d.ts +20 -0
- package/dist/auth/jwt-validator.d.ts.map +1 -0
- package/dist/auth/jwt-validator.js +83 -0
- package/dist/auth/jwt-validator.js.map +1 -0
- package/dist/auth/token-storage.d.ts +88 -0
- package/dist/auth/token-storage.d.ts.map +1 -0
- package/dist/auth/token-storage.js +273 -0
- package/dist/auth/token-storage.js.map +1 -0
- package/dist/clients/hasura-client.d.ts +33 -0
- package/dist/clients/hasura-client.d.ts.map +1 -0
- package/dist/clients/hasura-client.js +79 -0
- package/dist/clients/hasura-client.js.map +1 -0
- package/dist/config/environments.d.ts +51 -0
- package/dist/config/environments.d.ts.map +1 -0
- package/dist/config/environments.js +183 -0
- package/dist/config/environments.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +593 -0
- package/dist/index.js.map +1 -0
- package/dist/server/http-server.d.ts +14 -0
- package/dist/server/http-server.d.ts.map +1 -0
- package/dist/server/http-server.js +167 -0
- package/dist/server/http-server.js.map +1 -0
- package/dist/server/mcp-core.d.ts +12 -0
- package/dist/server/mcp-core.d.ts.map +1 -0
- package/dist/server/mcp-core.js +200 -0
- package/dist/server/mcp-core.js.map +1 -0
- package/dist/tools/acuity-init.d.ts +84 -0
- package/dist/tools/acuity-init.d.ts.map +1 -0
- package/dist/tools/acuity-init.js +239 -0
- package/dist/tools/acuity-init.js.map +1 -0
- package/dist/tools/get-dashboard-summary.d.ts +96 -0
- package/dist/tools/get-dashboard-summary.d.ts.map +1 -0
- package/dist/tools/get-dashboard-summary.js +264 -0
- package/dist/tools/get-dashboard-summary.js.map +1 -0
- package/dist/tools/get-issue.d.ts +62 -0
- package/dist/tools/get-issue.d.ts.map +1 -0
- package/dist/tools/get-issue.js +150 -0
- package/dist/tools/get-issue.js.map +1 -0
- package/dist/tools/get-lesson-learned.d.ts +53 -0
- package/dist/tools/get-lesson-learned.d.ts.map +1 -0
- package/dist/tools/get-lesson-learned.js +117 -0
- package/dist/tools/get-lesson-learned.js.map +1 -0
- package/dist/tools/get-lookup-values.d.ts +41 -0
- package/dist/tools/get-lookup-values.d.ts.map +1 -0
- package/dist/tools/get-lookup-values.js +127 -0
- package/dist/tools/get-lookup-values.js.map +1 -0
- package/dist/tools/get-project.d.ts +131 -0
- package/dist/tools/get-project.d.ts.map +1 -0
- package/dist/tools/get-project.js +340 -0
- package/dist/tools/get-project.js.map +1 -0
- package/dist/tools/get-risk.d.ts +65 -0
- package/dist/tools/get-risk.d.ts.map +1 -0
- package/dist/tools/get-risk.js +173 -0
- package/dist/tools/get-risk.js.map +1 -0
- package/dist/tools/get-status-reports.d.ts +46 -0
- package/dist/tools/get-status-reports.d.ts.map +1 -0
- package/dist/tools/get-status-reports.js +151 -0
- package/dist/tools/get-status-reports.js.map +1 -0
- package/dist/tools/init-auth.d.ts +47 -0
- package/dist/tools/init-auth.d.ts.map +1 -0
- package/dist/tools/init-auth.js +213 -0
- package/dist/tools/init-auth.js.map +1 -0
- package/dist/tools/list-issues.d.ts +134 -0
- package/dist/tools/list-issues.d.ts.map +1 -0
- package/dist/tools/list-issues.js +285 -0
- package/dist/tools/list-issues.js.map +1 -0
- package/dist/tools/list-lessons-learned.d.ts +79 -0
- package/dist/tools/list-lessons-learned.d.ts.map +1 -0
- package/dist/tools/list-lessons-learned.js +155 -0
- package/dist/tools/list-lessons-learned.js.map +1 -0
- package/dist/tools/list-projects.d.ts +200 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +396 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-risks.d.ts +166 -0
- package/dist/tools/list-risks.d.ts.map +1 -0
- package/dist/tools/list-risks.js +356 -0
- package/dist/tools/list-risks.js.map +1 -0
- package/dist/tools/search-projects.d.ts +90 -0
- package/dist/tools/search-projects.d.ts.map +1 -0
- package/dist/tools/search-projects.js +191 -0
- package/dist/tools/search-projects.js.map +1 -0
- package/dist/utils/formatters.d.ts +12 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +28 -0
- package/dist/utils/formatters.js.map +1 -0
- package/openapi.yaml +194 -0
- package/package.json +68 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure Token Storage using System Keychain
|
|
3
|
+
*
|
|
4
|
+
* Uses keytar for cross-platform keychain access:
|
|
5
|
+
* - macOS: Keychain Access
|
|
6
|
+
* - Windows: Credential Manager
|
|
7
|
+
* - Linux: libsecret
|
|
8
|
+
*
|
|
9
|
+
* If keytar is not available (e.g., no native build tools),
|
|
10
|
+
* falls back to no-op storage and users must use USER_JWT env var.
|
|
11
|
+
*/
|
|
12
|
+
import { getKeychainServiceName } from '../config/environments.js';
|
|
13
|
+
const ACCOUNT_NAME = 'default';
|
|
14
|
+
const PENDING_ACCOUNT_NAME = 'pending-device-code';
|
|
15
|
+
// Cached keytar module (or null if not available)
|
|
16
|
+
let keytarModule = undefined;
|
|
17
|
+
let keytarLoadError = null;
|
|
18
|
+
/**
|
|
19
|
+
* Dynamically load keytar module
|
|
20
|
+
* Returns null if keytar is not available (no native build tools, etc.)
|
|
21
|
+
*/
|
|
22
|
+
async function getKeytar() {
|
|
23
|
+
if (keytarModule !== undefined) {
|
|
24
|
+
return keytarModule;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// Dynamic import to make keytar optional
|
|
28
|
+
const keytar = await import('keytar');
|
|
29
|
+
keytarModule = keytar.default || keytar;
|
|
30
|
+
return keytarModule;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
keytarModule = null;
|
|
34
|
+
keytarLoadError = error instanceof Error ? error.message : String(error);
|
|
35
|
+
// Only log once
|
|
36
|
+
console.error('[Auth] Keytar not available - credential storage disabled.');
|
|
37
|
+
console.error('[Auth] To enable: install build tools (Xcode CLT on macOS, Visual Studio Build Tools on Windows)');
|
|
38
|
+
console.error('[Auth] Fallback: use USER_JWT environment variable for authentication.');
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if keytar is available
|
|
44
|
+
*/
|
|
45
|
+
export async function isKeytarAvailable() {
|
|
46
|
+
const keytar = await getKeytar();
|
|
47
|
+
return keytar !== null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the error message if keytar failed to load
|
|
51
|
+
*/
|
|
52
|
+
export function getKeytarLoadError() {
|
|
53
|
+
return keytarLoadError;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the service name for keychain storage
|
|
57
|
+
* Includes environment name: acuity-mcp-{env}
|
|
58
|
+
*/
|
|
59
|
+
function getServiceName() {
|
|
60
|
+
return getKeychainServiceName();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Save credentials to the system keychain
|
|
64
|
+
* Returns false if keytar is not available
|
|
65
|
+
*/
|
|
66
|
+
export async function saveCredentials(credentials) {
|
|
67
|
+
const keytar = await getKeytar();
|
|
68
|
+
if (!keytar) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const data = JSON.stringify(credentials);
|
|
72
|
+
await keytar.setPassword(getServiceName(), ACCOUNT_NAME, data);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Load credentials from the system keychain
|
|
77
|
+
* Returns null if keytar is not available or no credentials stored
|
|
78
|
+
*/
|
|
79
|
+
export async function loadCredentials() {
|
|
80
|
+
const keytar = await getKeytar();
|
|
81
|
+
if (!keytar) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const data = await keytar.getPassword(getServiceName(), ACCOUNT_NAME);
|
|
86
|
+
if (!data) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return JSON.parse(data);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Clear credentials from the system keychain
|
|
97
|
+
* Returns false if keytar is not available
|
|
98
|
+
*/
|
|
99
|
+
export async function clearCredentials() {
|
|
100
|
+
const keytar = await getKeytar();
|
|
101
|
+
if (!keytar) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
return keytar.deletePassword(getServiceName(), ACCOUNT_NAME);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if credentials exist in the keychain
|
|
108
|
+
*/
|
|
109
|
+
export async function hasCredentials() {
|
|
110
|
+
const creds = await loadCredentials();
|
|
111
|
+
return creds !== null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if the stored access token is expired
|
|
115
|
+
* Returns true if expired or no credentials exist
|
|
116
|
+
*/
|
|
117
|
+
export async function isTokenExpired(bufferSeconds = 60) {
|
|
118
|
+
const creds = await loadCredentials();
|
|
119
|
+
if (!creds) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
const now = Math.floor(Date.now() / 1000);
|
|
123
|
+
return creds.expiresAt - bufferSeconds <= now;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Convert TokenResponse from Device Flow to StoredCredentials
|
|
127
|
+
*/
|
|
128
|
+
export function tokenResponseToCredentials(token, email) {
|
|
129
|
+
const now = Math.floor(Date.now() / 1000);
|
|
130
|
+
return {
|
|
131
|
+
accessToken: token.access_token,
|
|
132
|
+
refreshToken: token.refresh_token,
|
|
133
|
+
idToken: token.id_token,
|
|
134
|
+
expiresAt: now + token.expires_in,
|
|
135
|
+
email,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the access token, refreshing if necessary
|
|
140
|
+
* Throws if no valid token available and refresh fails
|
|
141
|
+
*/
|
|
142
|
+
export async function getValidAccessToken(refreshFn) {
|
|
143
|
+
const creds = await loadCredentials();
|
|
144
|
+
if (!creds) {
|
|
145
|
+
throw new Error('No credentials stored. Run "init" to authenticate.');
|
|
146
|
+
}
|
|
147
|
+
// Check if token is expired
|
|
148
|
+
const now = Math.floor(Date.now() / 1000);
|
|
149
|
+
const isExpired = creds.expiresAt - 60 <= now; // 60 second buffer
|
|
150
|
+
if (!isExpired) {
|
|
151
|
+
return creds.accessToken;
|
|
152
|
+
}
|
|
153
|
+
// Token is expired, try to refresh
|
|
154
|
+
if (!creds.refreshToken) {
|
|
155
|
+
throw new Error('Token expired and no refresh token available. Run "init" to re-authenticate.');
|
|
156
|
+
}
|
|
157
|
+
if (!refreshFn) {
|
|
158
|
+
throw new Error('Token expired and no refresh function provided.');
|
|
159
|
+
}
|
|
160
|
+
console.error('[Auth] Access token expired, refreshing...');
|
|
161
|
+
try {
|
|
162
|
+
const newToken = await refreshFn(creds.refreshToken);
|
|
163
|
+
// Save new credentials
|
|
164
|
+
const newCreds = tokenResponseToCredentials(newToken, creds.email);
|
|
165
|
+
// Keep the original refresh token if a new one wasn't provided
|
|
166
|
+
if (!newCreds.refreshToken && creds.refreshToken) {
|
|
167
|
+
newCreds.refreshToken = creds.refreshToken;
|
|
168
|
+
}
|
|
169
|
+
await saveCredentials(newCreds);
|
|
170
|
+
console.error('[Auth] Token refreshed successfully.');
|
|
171
|
+
return newCreds.accessToken;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
throw new Error(`Failed to refresh token: ${error instanceof Error ? error.message : String(error)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Save pending device code during two-phase login
|
|
179
|
+
* Returns false if keytar is not available
|
|
180
|
+
*/
|
|
181
|
+
export async function savePendingDeviceCode(pending) {
|
|
182
|
+
const keytar = await getKeytar();
|
|
183
|
+
if (!keytar) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const data = JSON.stringify(pending);
|
|
187
|
+
await keytar.setPassword(getServiceName(), PENDING_ACCOUNT_NAME, data);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Load pending device code
|
|
192
|
+
* Returns null if keytar is not available or no pending code
|
|
193
|
+
*/
|
|
194
|
+
export async function loadPendingDeviceCode() {
|
|
195
|
+
const keytar = await getKeytar();
|
|
196
|
+
if (!keytar) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const data = await keytar.getPassword(getServiceName(), PENDING_ACCOUNT_NAME);
|
|
201
|
+
if (!data) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const pending = JSON.parse(data);
|
|
205
|
+
// Check if expired
|
|
206
|
+
const now = Math.floor(Date.now() / 1000);
|
|
207
|
+
if (pending.expiresAt <= now) {
|
|
208
|
+
await clearPendingDeviceCode();
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
return pending;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Clear pending device code
|
|
219
|
+
* Returns false if keytar is not available
|
|
220
|
+
*/
|
|
221
|
+
export async function clearPendingDeviceCode() {
|
|
222
|
+
const keytar = await getKeytar();
|
|
223
|
+
if (!keytar) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return keytar.deletePassword(getServiceName(), PENDING_ACCOUNT_NAME);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get the ID token (JWT) for Hasura authentication, refreshing if necessary
|
|
230
|
+
* Hasura requires a JWT, not an opaque access token
|
|
231
|
+
*/
|
|
232
|
+
export async function getValidIdToken(refreshFn) {
|
|
233
|
+
const creds = await loadCredentials();
|
|
234
|
+
if (!creds) {
|
|
235
|
+
throw new Error('No credentials stored. Run "init" to authenticate.');
|
|
236
|
+
}
|
|
237
|
+
if (!creds.idToken) {
|
|
238
|
+
throw new Error('No ID token available. Run "init" to re-authenticate.');
|
|
239
|
+
}
|
|
240
|
+
// Check if token is expired
|
|
241
|
+
const now = Math.floor(Date.now() / 1000);
|
|
242
|
+
const isExpired = creds.expiresAt - 60 <= now; // 60 second buffer
|
|
243
|
+
if (!isExpired) {
|
|
244
|
+
return creds.idToken;
|
|
245
|
+
}
|
|
246
|
+
// Token is expired, try to refresh
|
|
247
|
+
if (!creds.refreshToken) {
|
|
248
|
+
throw new Error('Token expired and no refresh token available. Run "init" to re-authenticate.');
|
|
249
|
+
}
|
|
250
|
+
if (!refreshFn) {
|
|
251
|
+
throw new Error('Token expired and no refresh function provided.');
|
|
252
|
+
}
|
|
253
|
+
console.error('[Auth] ID token expired, refreshing...');
|
|
254
|
+
try {
|
|
255
|
+
const newToken = await refreshFn(creds.refreshToken);
|
|
256
|
+
// Save new credentials
|
|
257
|
+
const newCreds = tokenResponseToCredentials(newToken, creds.email);
|
|
258
|
+
// Keep the original refresh token if a new one wasn't provided
|
|
259
|
+
if (!newCreds.refreshToken && creds.refreshToken) {
|
|
260
|
+
newCreds.refreshToken = creds.refreshToken;
|
|
261
|
+
}
|
|
262
|
+
await saveCredentials(newCreds);
|
|
263
|
+
if (!newCreds.idToken) {
|
|
264
|
+
throw new Error('Refresh did not return ID token. Run "init" to re-authenticate.');
|
|
265
|
+
}
|
|
266
|
+
console.error('[Auth] Token refreshed successfully.');
|
|
267
|
+
return newCreds.idToken;
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
throw new Error(`Failed to refresh token: ${error instanceof Error ? error.message : String(error)}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=token-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-storage.js","sourceRoot":"","sources":["../../src/auth/token-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hasura GraphQL Client
|
|
3
|
+
* Provides authenticated GraphQL queries with automatic row-level security
|
|
4
|
+
*/
|
|
5
|
+
export declare class HasuraClient {
|
|
6
|
+
private client;
|
|
7
|
+
private userEmail;
|
|
8
|
+
private jwtToken?;
|
|
9
|
+
/**
|
|
10
|
+
* Creates a new Hasura client for a specific user
|
|
11
|
+
*
|
|
12
|
+
* @param userEmail - Email address for row-level security filtering
|
|
13
|
+
* @param jwtToken - Optional JWT token for authorization
|
|
14
|
+
*/
|
|
15
|
+
constructor(userEmail: string, jwtToken?: string);
|
|
16
|
+
/**
|
|
17
|
+
* Execute a GraphQL query
|
|
18
|
+
*
|
|
19
|
+
* @param query - GraphQL query string
|
|
20
|
+
* @param variables - Query variables
|
|
21
|
+
* @returns Query result
|
|
22
|
+
*/
|
|
23
|
+
query<T = any>(query: string, variables?: Record<string, any>): Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Get the user email this client is scoped to
|
|
26
|
+
*/
|
|
27
|
+
getUserEmail(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Create a new client for a different user
|
|
30
|
+
*/
|
|
31
|
+
static forUser(userEmail: string, jwtToken?: string): HasuraClient;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=hasura-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasura-client.d.ts","sourceRoot":"","sources":["../../src/clients/hasura-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAS;IAE1B;;;;;OAKG;gBACS,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IAuChD;;;;;;OAMG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAUhF;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY;CAGnE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hasura GraphQL Client
|
|
3
|
+
* Provides authenticated GraphQL queries with automatic row-level security
|
|
4
|
+
*/
|
|
5
|
+
import { GraphQLClient } from 'graphql-request';
|
|
6
|
+
import { getEnvironment } from '../config/environments.js';
|
|
7
|
+
export class HasuraClient {
|
|
8
|
+
client;
|
|
9
|
+
userEmail;
|
|
10
|
+
jwtToken;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new Hasura client for a specific user
|
|
13
|
+
*
|
|
14
|
+
* @param userEmail - Email address for row-level security filtering
|
|
15
|
+
* @param jwtToken - Optional JWT token for authorization
|
|
16
|
+
*/
|
|
17
|
+
constructor(userEmail, jwtToken) {
|
|
18
|
+
const env = getEnvironment();
|
|
19
|
+
const endpoint = process.env.HASURA_GRAPHQL_ENDPOINT || env.hasuraEndpoint;
|
|
20
|
+
this.userEmail = userEmail;
|
|
21
|
+
this.jwtToken = jwtToken;
|
|
22
|
+
// Build headers
|
|
23
|
+
const headers = {
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'X-Hasura-Role': 'user',
|
|
26
|
+
'X-Hasura-Email': userEmail
|
|
27
|
+
};
|
|
28
|
+
// JWT token is REQUIRED for security - never fallback to admin secret
|
|
29
|
+
if (!jwtToken) {
|
|
30
|
+
throw new Error('JWT token is required for Hasura authentication. Cannot use admin secret for user queries.');
|
|
31
|
+
}
|
|
32
|
+
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
33
|
+
// Debug: Log headers being sent (in debug mode)
|
|
34
|
+
if (process.env.LOG_LEVEL === 'debug') {
|
|
35
|
+
console.error('[HasuraClient] Creating client for user:', userEmail);
|
|
36
|
+
console.error('[HasuraClient] Headers:', {
|
|
37
|
+
'X-Hasura-Role': headers['X-Hasura-Role'],
|
|
38
|
+
'X-Hasura-Email': headers['X-Hasura-Email'],
|
|
39
|
+
'Authorization': `Bearer ${jwtToken.substring(0, 20)}...`
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Create GraphQL client with Hasura session variables
|
|
43
|
+
// Use native fetch from Node.js 18+
|
|
44
|
+
this.client = new GraphQLClient(endpoint, {
|
|
45
|
+
headers,
|
|
46
|
+
fetch: fetch // Use global fetch from Node.js
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Execute a GraphQL query
|
|
51
|
+
*
|
|
52
|
+
* @param query - GraphQL query string
|
|
53
|
+
* @param variables - Query variables
|
|
54
|
+
* @returns Query result
|
|
55
|
+
*/
|
|
56
|
+
async query(query, variables) {
|
|
57
|
+
try {
|
|
58
|
+
return await this.client.request(query, variables);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// Enhance error message with context
|
|
62
|
+
const message = error.response?.errors?.[0]?.message || error.message;
|
|
63
|
+
throw new Error(`GraphQL query failed: ${message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the user email this client is scoped to
|
|
68
|
+
*/
|
|
69
|
+
getUserEmail() {
|
|
70
|
+
return this.userEmail;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a new client for a different user
|
|
74
|
+
*/
|
|
75
|
+
static forUser(userEmail, jwtToken) {
|
|
76
|
+
return new HasuraClient(userEmail, jwtToken);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=hasura-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasura-client.js","sourceRoot":"","sources":["../../src/clients/hasura-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,OAAO,YAAY;IACf,MAAM,CAAgB;IACtB,SAAS,CAAS;IAClB,QAAQ,CAAU;IAE1B;;;;;OAKG;IACH,YAAY,SAAiB,EAAE,QAAiB;QAC9C,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,GAAG,CAAC,cAAc,CAAC;QAE3E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAEzB,gBAAgB;QAChB,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,MAAM;YACvB,gBAAgB,EAAE,SAAS;SAC5B,CAAC;QAEF,sEAAsE;QACtE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;QAChH,CAAC;QAED,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,QAAQ,EAAE,CAAC;QAEhD,gDAAgD;QAChD,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,SAAS,CAAC,CAAC;YACrE,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE;gBACvC,eAAe,EAAE,OAAO,CAAC,eAAe,CAAC;gBACzC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAAC;gBAC3C,eAAe,EAAE,UAAU,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK;aAC1D,CAAC,CAAC;QACL,CAAC;QAED,sDAAsD;QACtD,oCAAoC;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,aAAa,CAAC,QAAQ,EAAE;YACxC,OAAO;YACP,KAAK,EAAE,KAAK,CAAC,gCAAgC;SAC9C,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,CAAU,KAAa,EAAE,SAA+B;QACjE,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAI,KAAK,EAAE,SAAS,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,qCAAqC;YACrC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,SAAiB,EAAE,QAAiB;QACjD,OAAO,IAAI,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Configuration
|
|
3
|
+
*
|
|
4
|
+
* Supports multiple Acuity environments via ACUITY_ENV environment variable.
|
|
5
|
+
* Each environment has its own Hasura endpoint, Auth0 tenant, and keychain storage.
|
|
6
|
+
*
|
|
7
|
+
* ACUITY_ENV is REQUIRED - there is no default environment.
|
|
8
|
+
* Users must specify which environment to connect to.
|
|
9
|
+
*/
|
|
10
|
+
export interface EnvironmentConfig {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
hasuraEndpoint: string;
|
|
14
|
+
auth0Domain: string;
|
|
15
|
+
auth0ClientId: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* All available Acuity environments
|
|
19
|
+
*
|
|
20
|
+
* MCP Client IDs marked as TODO need to be created in Auth0:
|
|
21
|
+
* 1. Go to Auth0 Dashboard → Applications → Create Application
|
|
22
|
+
* 2. Name: "Acuity MCP Server"
|
|
23
|
+
* 3. Type: Native
|
|
24
|
+
* 4. Enable Grant Types: Device Code, Refresh Token
|
|
25
|
+
* 5. Copy Client ID here
|
|
26
|
+
*/
|
|
27
|
+
export declare const ENVIRONMENTS: Record<string, EnvironmentConfig>;
|
|
28
|
+
/**
|
|
29
|
+
* Get the current environment name from ACUITY_ENV
|
|
30
|
+
* Throws if ACUITY_ENV is not set - environment selection is required
|
|
31
|
+
*/
|
|
32
|
+
export declare function getEnvironmentName(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Get the current environment configuration
|
|
35
|
+
* Throws if environment is not found
|
|
36
|
+
*/
|
|
37
|
+
export declare function getEnvironment(): EnvironmentConfig;
|
|
38
|
+
/**
|
|
39
|
+
* Get the keychain service name for the current environment
|
|
40
|
+
* Format: acuity-mcp-{env}
|
|
41
|
+
*/
|
|
42
|
+
export declare function getKeychainServiceName(): string;
|
|
43
|
+
/**
|
|
44
|
+
* List all available environments (for help/status commands)
|
|
45
|
+
*/
|
|
46
|
+
export declare function listEnvironments(): EnvironmentConfig[];
|
|
47
|
+
/**
|
|
48
|
+
* Check if an environment is fully configured (has Auth0 Client ID)
|
|
49
|
+
*/
|
|
50
|
+
export declare function isEnvironmentConfigured(envId: string): boolean;
|
|
51
|
+
//# sourceMappingURL=environments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environments.d.ts","sourceRoot":"","sources":["../../src/config/environments.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CA2G1D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAgB3C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAuBlD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,EAAE,CAEtD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG9D"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Configuration
|
|
3
|
+
*
|
|
4
|
+
* Supports multiple Acuity environments via ACUITY_ENV environment variable.
|
|
5
|
+
* Each environment has its own Hasura endpoint, Auth0 tenant, and keychain storage.
|
|
6
|
+
*
|
|
7
|
+
* ACUITY_ENV is REQUIRED - there is no default environment.
|
|
8
|
+
* Users must specify which environment to connect to.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* All available Acuity environments
|
|
12
|
+
*
|
|
13
|
+
* MCP Client IDs marked as TODO need to be created in Auth0:
|
|
14
|
+
* 1. Go to Auth0 Dashboard → Applications → Create Application
|
|
15
|
+
* 2. Name: "Acuity MCP Server"
|
|
16
|
+
* 3. Type: Native
|
|
17
|
+
* 4. Enable Grant Types: Device Code, Refresh Token
|
|
18
|
+
* 5. Copy Client ID here
|
|
19
|
+
*/
|
|
20
|
+
export const ENVIRONMENTS = {
|
|
21
|
+
// ============================================
|
|
22
|
+
// US Region
|
|
23
|
+
// ============================================
|
|
24
|
+
'US-1': {
|
|
25
|
+
id: 'US-1',
|
|
26
|
+
name: 'Production US',
|
|
27
|
+
hasuraEndpoint: 'https://hasura-prod.acuityppm.link/v1/graphql',
|
|
28
|
+
auth0Domain: 'acuityppm.auth0.com',
|
|
29
|
+
auth0ClientId: 'w1nhNpUEdSybNrJaHfeKNlLv6yQ5UuTA',
|
|
30
|
+
},
|
|
31
|
+
'US-staging': {
|
|
32
|
+
id: 'US-staging',
|
|
33
|
+
name: 'Staging US',
|
|
34
|
+
hasuraEndpoint: 'https://hasura-staging.acuityppm.link/v1/graphql',
|
|
35
|
+
auth0Domain: 'acuityppm.auth0.com',
|
|
36
|
+
auth0ClientId: 'w1nhNpUEdSybNrJaHfeKNlLv6yQ5UuTA',
|
|
37
|
+
},
|
|
38
|
+
'US-2': {
|
|
39
|
+
id: 'US-2',
|
|
40
|
+
name: 'CAAT',
|
|
41
|
+
hasuraEndpoint: 'https://hasura-caat.acuityppm.link/v1/graphql',
|
|
42
|
+
auth0Domain: 'caat-ppm.us.auth0.com',
|
|
43
|
+
auth0ClientId: 'jLC3FAQebNzDcaUZmBRtCxw4xJOmgo25',
|
|
44
|
+
},
|
|
45
|
+
'US-3': {
|
|
46
|
+
id: 'US-3',
|
|
47
|
+
name: 'Members 1st',
|
|
48
|
+
hasuraEndpoint: 'https://hasura-members1st.acuityppm.link/v1/graphql',
|
|
49
|
+
auth0Domain: 'acuity-members1st.us.auth0.com',
|
|
50
|
+
auth0ClientId: 'vNYlDJL85gk379NzcMI2dfw4d6sPOngQ',
|
|
51
|
+
},
|
|
52
|
+
'US-5': {
|
|
53
|
+
id: 'US-5',
|
|
54
|
+
name: 'Geelong Port',
|
|
55
|
+
hasuraEndpoint: 'https://hasura-geelongport.acuityppm.link/v1/graphql',
|
|
56
|
+
auth0Domain: 'acuity-geelongport.us.auth0.com',
|
|
57
|
+
auth0ClientId: 'JF7MR6diZRpoR70xYNqZsQHptP4GJB49',
|
|
58
|
+
},
|
|
59
|
+
'US-6': {
|
|
60
|
+
id: 'US-6',
|
|
61
|
+
name: 'Hypertherm',
|
|
62
|
+
hasuraEndpoint: 'https://hasura-hypertherm.acuityppm.link/v1/graphql',
|
|
63
|
+
auth0Domain: 'acuity-hypertherm.us.auth0.com',
|
|
64
|
+
auth0ClientId: 'S8ZSktKYrtl8pZ8N0H1toXRRMDDapYLN',
|
|
65
|
+
},
|
|
66
|
+
'US-7': {
|
|
67
|
+
id: 'US-7',
|
|
68
|
+
name: 'Wannon Water',
|
|
69
|
+
hasuraEndpoint: 'https://hasura-wannonwater.acuityppm.link/v1/graphql',
|
|
70
|
+
auth0Domain: 'acuityppm-wannonwater.us.auth0.com',
|
|
71
|
+
auth0ClientId: '2vdIA8X7q3oWqT1eFnozOO06Vt3Qi5OZ',
|
|
72
|
+
},
|
|
73
|
+
// ============================================
|
|
74
|
+
// EU Region
|
|
75
|
+
// ============================================
|
|
76
|
+
'EU-1': {
|
|
77
|
+
id: 'EU-1',
|
|
78
|
+
name: 'Production EU',
|
|
79
|
+
hasuraEndpoint: 'https://hasura-eu.acuityppm.link/v1/graphql',
|
|
80
|
+
auth0Domain: 'acuityppm-eu.eu.auth0.com',
|
|
81
|
+
auth0ClientId: 'MIwZGLZHThyBGcRuMseAkmo5O6ZxwCBq',
|
|
82
|
+
},
|
|
83
|
+
'EU-2': {
|
|
84
|
+
id: 'EU-2',
|
|
85
|
+
name: 'MAI',
|
|
86
|
+
hasuraEndpoint: 'https://hasura-mai.acuityppm.link/v1/graphql',
|
|
87
|
+
auth0Domain: 'acuityppm-mai.eu.auth0.com',
|
|
88
|
+
auth0ClientId: 'ygp8UJhG16vm1dGx4uChlS0mBqGyO6YP',
|
|
89
|
+
},
|
|
90
|
+
'EU-3': {
|
|
91
|
+
id: 'EU-3',
|
|
92
|
+
name: 'Westfund',
|
|
93
|
+
hasuraEndpoint: 'https://hasura-westfund.acuityppm.link/v1/graphql',
|
|
94
|
+
auth0Domain: 'acuity-westfund.us.auth0.com',
|
|
95
|
+
auth0ClientId: 'q3QdV4YOVATWnK728eSNEknExOiVlbhP',
|
|
96
|
+
},
|
|
97
|
+
'EU-4': {
|
|
98
|
+
id: 'EU-4',
|
|
99
|
+
name: 'SMBC Group',
|
|
100
|
+
hasuraEndpoint: 'https://hasura-smbcgroup.acuityppm.link/v1/graphql',
|
|
101
|
+
auth0Domain: 'acuityppm-smbc.eu.auth0.com',
|
|
102
|
+
auth0ClientId: 'J031PmZXSFDVFhO6CuJxowQy5YfcLOHe',
|
|
103
|
+
},
|
|
104
|
+
// ============================================
|
|
105
|
+
// UAE Region
|
|
106
|
+
// ============================================
|
|
107
|
+
'UAE-1': {
|
|
108
|
+
id: 'UAE-1',
|
|
109
|
+
name: 'Production UAE',
|
|
110
|
+
hasuraEndpoint: 'https://hasura-uae.acuityppm.link/v1/graphql',
|
|
111
|
+
auth0Domain: 'acuity-uae.eu.auth0.com',
|
|
112
|
+
auth0ClientId: 'L9PvXQa31UYzWQe2bU2f2mnRuAvDtfb2',
|
|
113
|
+
},
|
|
114
|
+
// ============================================
|
|
115
|
+
// Development (local)
|
|
116
|
+
// ============================================
|
|
117
|
+
'dev': {
|
|
118
|
+
id: 'dev',
|
|
119
|
+
name: 'Local Development',
|
|
120
|
+
hasuraEndpoint: 'http://localhost:8080/v1/graphql',
|
|
121
|
+
auth0Domain: 'acuityppm.auth0.com',
|
|
122
|
+
auth0ClientId: 'w1nhNpUEdSybNrJaHfeKNlLv6yQ5UuTA',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Get the current environment name from ACUITY_ENV
|
|
127
|
+
* Throws if ACUITY_ENV is not set - environment selection is required
|
|
128
|
+
*/
|
|
129
|
+
export function getEnvironmentName() {
|
|
130
|
+
const envName = process.env.ACUITY_ENV;
|
|
131
|
+
if (!envName) {
|
|
132
|
+
const available = Object.keys(ENVIRONMENTS).join(', ');
|
|
133
|
+
throw new Error(`ACUITY_ENV environment variable is required.\n` +
|
|
134
|
+
`Available environments: ${available}\n\n` +
|
|
135
|
+
`Set it in your MCP client config:\n` +
|
|
136
|
+
` "env": { "ACUITY_ENV": "US-1" }\n\n` +
|
|
137
|
+
`Or via command line:\n` +
|
|
138
|
+
` ACUITY_ENV=US-1 npx acuity-mcp-server`);
|
|
139
|
+
}
|
|
140
|
+
return envName;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the current environment configuration
|
|
144
|
+
* Throws if environment is not found
|
|
145
|
+
*/
|
|
146
|
+
export function getEnvironment() {
|
|
147
|
+
const envName = getEnvironmentName();
|
|
148
|
+
const config = ENVIRONMENTS[envName];
|
|
149
|
+
if (!config) {
|
|
150
|
+
const available = Object.keys(ENVIRONMENTS).join(', ');
|
|
151
|
+
throw new Error(`Unknown environment: "${envName}".\n` +
|
|
152
|
+
`Available environments: ${available}`);
|
|
153
|
+
}
|
|
154
|
+
// Check if Auth0 Client ID is configured
|
|
155
|
+
if (config.auth0ClientId === 'TODO') {
|
|
156
|
+
throw new Error(`Environment "${envName}" is not fully configured.\n` +
|
|
157
|
+
`Missing Auth0 MCP Client ID for tenant: ${config.auth0Domain}\n\n` +
|
|
158
|
+
`To fix: Create a Native Application in Auth0 with Device Code grant enabled,\n` +
|
|
159
|
+
`then add the Client ID to environments.ts`);
|
|
160
|
+
}
|
|
161
|
+
return config;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get the keychain service name for the current environment
|
|
165
|
+
* Format: acuity-mcp-{env}
|
|
166
|
+
*/
|
|
167
|
+
export function getKeychainServiceName() {
|
|
168
|
+
return `acuity-mcp-${getEnvironmentName()}`;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* List all available environments (for help/status commands)
|
|
172
|
+
*/
|
|
173
|
+
export function listEnvironments() {
|
|
174
|
+
return Object.values(ENVIRONMENTS);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if an environment is fully configured (has Auth0 Client ID)
|
|
178
|
+
*/
|
|
179
|
+
export function isEnvironmentConfigured(envId) {
|
|
180
|
+
const config = ENVIRONMENTS[envId];
|
|
181
|
+
return config !== undefined && config.auth0ClientId !== 'TODO';
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=environments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environments.js","sourceRoot":"","sources":["../../src/config/environments.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAGH,OAAO,eAAe,CAAC"}
|