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.
Files changed (99) hide show
  1. package/README.md +541 -0
  2. package/dist/auth/device-flow.d.ts +46 -0
  3. package/dist/auth/device-flow.d.ts.map +1 -0
  4. package/dist/auth/device-flow.js +141 -0
  5. package/dist/auth/device-flow.js.map +1 -0
  6. package/dist/auth/http-auth.d.ts +25 -0
  7. package/dist/auth/http-auth.d.ts.map +1 -0
  8. package/dist/auth/http-auth.js +101 -0
  9. package/dist/auth/http-auth.js.map +1 -0
  10. package/dist/auth/jwt-validator.d.ts +20 -0
  11. package/dist/auth/jwt-validator.d.ts.map +1 -0
  12. package/dist/auth/jwt-validator.js +83 -0
  13. package/dist/auth/jwt-validator.js.map +1 -0
  14. package/dist/auth/token-storage.d.ts +88 -0
  15. package/dist/auth/token-storage.d.ts.map +1 -0
  16. package/dist/auth/token-storage.js +273 -0
  17. package/dist/auth/token-storage.js.map +1 -0
  18. package/dist/clients/hasura-client.d.ts +33 -0
  19. package/dist/clients/hasura-client.d.ts.map +1 -0
  20. package/dist/clients/hasura-client.js +79 -0
  21. package/dist/clients/hasura-client.js.map +1 -0
  22. package/dist/config/environments.d.ts +51 -0
  23. package/dist/config/environments.d.ts.map +1 -0
  24. package/dist/config/environments.js +183 -0
  25. package/dist/config/environments.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +593 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/server/http-server.d.ts +14 -0
  31. package/dist/server/http-server.d.ts.map +1 -0
  32. package/dist/server/http-server.js +167 -0
  33. package/dist/server/http-server.js.map +1 -0
  34. package/dist/server/mcp-core.d.ts +12 -0
  35. package/dist/server/mcp-core.d.ts.map +1 -0
  36. package/dist/server/mcp-core.js +200 -0
  37. package/dist/server/mcp-core.js.map +1 -0
  38. package/dist/tools/acuity-init.d.ts +84 -0
  39. package/dist/tools/acuity-init.d.ts.map +1 -0
  40. package/dist/tools/acuity-init.js +239 -0
  41. package/dist/tools/acuity-init.js.map +1 -0
  42. package/dist/tools/get-dashboard-summary.d.ts +96 -0
  43. package/dist/tools/get-dashboard-summary.d.ts.map +1 -0
  44. package/dist/tools/get-dashboard-summary.js +264 -0
  45. package/dist/tools/get-dashboard-summary.js.map +1 -0
  46. package/dist/tools/get-issue.d.ts +62 -0
  47. package/dist/tools/get-issue.d.ts.map +1 -0
  48. package/dist/tools/get-issue.js +150 -0
  49. package/dist/tools/get-issue.js.map +1 -0
  50. package/dist/tools/get-lesson-learned.d.ts +53 -0
  51. package/dist/tools/get-lesson-learned.d.ts.map +1 -0
  52. package/dist/tools/get-lesson-learned.js +117 -0
  53. package/dist/tools/get-lesson-learned.js.map +1 -0
  54. package/dist/tools/get-lookup-values.d.ts +41 -0
  55. package/dist/tools/get-lookup-values.d.ts.map +1 -0
  56. package/dist/tools/get-lookup-values.js +127 -0
  57. package/dist/tools/get-lookup-values.js.map +1 -0
  58. package/dist/tools/get-project.d.ts +131 -0
  59. package/dist/tools/get-project.d.ts.map +1 -0
  60. package/dist/tools/get-project.js +340 -0
  61. package/dist/tools/get-project.js.map +1 -0
  62. package/dist/tools/get-risk.d.ts +65 -0
  63. package/dist/tools/get-risk.d.ts.map +1 -0
  64. package/dist/tools/get-risk.js +173 -0
  65. package/dist/tools/get-risk.js.map +1 -0
  66. package/dist/tools/get-status-reports.d.ts +46 -0
  67. package/dist/tools/get-status-reports.d.ts.map +1 -0
  68. package/dist/tools/get-status-reports.js +151 -0
  69. package/dist/tools/get-status-reports.js.map +1 -0
  70. package/dist/tools/init-auth.d.ts +47 -0
  71. package/dist/tools/init-auth.d.ts.map +1 -0
  72. package/dist/tools/init-auth.js +213 -0
  73. package/dist/tools/init-auth.js.map +1 -0
  74. package/dist/tools/list-issues.d.ts +134 -0
  75. package/dist/tools/list-issues.d.ts.map +1 -0
  76. package/dist/tools/list-issues.js +285 -0
  77. package/dist/tools/list-issues.js.map +1 -0
  78. package/dist/tools/list-lessons-learned.d.ts +79 -0
  79. package/dist/tools/list-lessons-learned.d.ts.map +1 -0
  80. package/dist/tools/list-lessons-learned.js +155 -0
  81. package/dist/tools/list-lessons-learned.js.map +1 -0
  82. package/dist/tools/list-projects.d.ts +200 -0
  83. package/dist/tools/list-projects.d.ts.map +1 -0
  84. package/dist/tools/list-projects.js +396 -0
  85. package/dist/tools/list-projects.js.map +1 -0
  86. package/dist/tools/list-risks.d.ts +166 -0
  87. package/dist/tools/list-risks.d.ts.map +1 -0
  88. package/dist/tools/list-risks.js +356 -0
  89. package/dist/tools/list-risks.js.map +1 -0
  90. package/dist/tools/search-projects.d.ts +90 -0
  91. package/dist/tools/search-projects.d.ts.map +1 -0
  92. package/dist/tools/search-projects.js +191 -0
  93. package/dist/tools/search-projects.js.map +1 -0
  94. package/dist/utils/formatters.d.ts +12 -0
  95. package/dist/utils/formatters.d.ts.map +1 -0
  96. package/dist/utils/formatters.js +28 -0
  97. package/dist/utils/formatters.js.map +1 -0
  98. package/openapi.yaml +194 -0
  99. 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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"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Acuity MCP Server
4
+ * Enables LLM access to Acuity project management data
5
+ */
6
+ import 'dotenv/config';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAGH,OAAO,eAAe,CAAC"}