acuity-mcp-server 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Acuity MCP Server
4
- * Enables LLM access to Acuity project management data
3
+ * Acuity MCP Server - Entry Point
4
+ *
5
+ * This file checks Node.js version before loading the main server.
6
+ * Required because ESM imports fail with cryptic errors on older Node versions.
5
7
  */
6
- import 'dotenv/config';
8
+ export {};
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAGH,OAAO,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG"}
package/dist/index.js CHANGED
@@ -1,593 +1,70 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Acuity MCP Server
4
- * Enables LLM access to Acuity project management data
3
+ * Acuity MCP Server - Entry Point
4
+ *
5
+ * This file checks Node.js version before loading the main server.
6
+ * Required because ESM imports fail with cryptic errors on older Node versions.
5
7
  */
6
- // Load .env.development for local development
7
- import 'dotenv/config';
8
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
- import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
11
- import { validateJWT } from './auth/jwt-validator.js';
12
- import { runDeviceFlow, refreshAccessToken } from './auth/device-flow.js';
13
- import { saveCredentials, loadCredentials, clearCredentials, getValidIdToken, tokenResponseToCredentials } from './auth/token-storage.js';
14
- import { HasuraClient } from './clients/hasura-client.js';
15
- import { acuityInit, acuityInitTool } from './tools/acuity-init.js';
16
- import { listProjects, listProjectsTool } from './tools/list-projects.js';
17
- import { getProject, getProjectTool } from './tools/get-project.js';
18
- import { getStatusReports, getStatusReportsTool } from './tools/get-status-reports.js';
19
- import { listRisks, listRisksTool } from './tools/list-risks.js';
20
- import { getRisk, getRiskTool } from './tools/get-risk.js';
21
- import { listIssues, listIssuesTool } from './tools/list-issues.js';
22
- import { getIssue, getIssueTool } from './tools/get-issue.js';
23
- import { listLessonsLearned, listLessonsLearnedTool } from './tools/list-lessons-learned.js';
24
- import { getLessonLearned, getLessonLearnedTool } from './tools/get-lesson-learned.js';
25
- import { getLookupValues, getLookupValuesTool } from './tools/get-lookup-values.js';
26
- import { searchProjects, searchProjectsTool } from './tools/search-projects.js';
27
- import { getDashboardSummary, getDashboardSummaryTool } from './tools/get-dashboard-summary.js';
28
- import { startHttpServer } from './server/http-server.js';
29
- // Session state - persists while MCP server is running
30
- let sessionInitData = null;
31
- import { initAuthTool, logoutTool, authStatusTool, initAuth, logout, getAuthStatus } from './tools/init-auth.js';
32
- import { getEnvironment, listEnvironments } from './config/environments.js';
33
- /**
34
- * Get Auth0 configuration from current environment
35
- * Uses ACUITY_ENV to determine which environment to use
36
- */
37
- function getAuth0Config() {
38
- const env = getEnvironment();
39
- return {
40
- domain: env.auth0Domain,
41
- clientId: env.auth0ClientId,
42
- audience: process.env.AUTH0_AUDIENCE,
43
- scope: 'openid profile email offline_access',
44
- };
45
- }
46
- /**
47
- * Handle 'init' command - authenticate via Device Flow
48
- */
49
- async function handleInit() {
50
- console.log('\n=== Acuity MCP Server - Authentication Setup ===\n');
51
- try {
52
- const config = getAuth0Config();
53
- const token = await runDeviceFlow(config);
54
- // Extract email from id_token if available
55
- let email;
56
- if (token.id_token) {
57
- try {
58
- const payload = JSON.parse(Buffer.from(token.id_token.split('.')[1], 'base64').toString());
59
- email = payload.email || payload['https://acuity.com/email'] || payload['https://acuityppm.com/email'];
60
- }
61
- catch {
62
- // Ignore parsing errors
63
- }
64
- }
65
- // Save credentials to keychain
66
- const credentials = tokenResponseToCredentials(token, email);
67
- await saveCredentials(credentials);
68
- console.log('='.repeat(50));
69
- console.log('Authentication successful!');
70
- if (email) {
71
- console.log(`Logged in as: ${email}`);
72
- }
73
- console.log('Credentials stored securely in system keychain.');
74
- console.log('='.repeat(50));
75
- console.log('\nYou can now use the MCP server with Claude Desktop or other clients.');
76
- console.log('No need to configure USER_JWT anymore!\n');
77
- }
78
- catch (error) {
79
- console.error('\nAuthentication failed:', error instanceof Error ? error.message : String(error));
80
- process.exit(1);
81
- }
82
- }
83
- /**
84
- * Handle 'logout' command - clear stored credentials
85
- */
86
- async function handleLogout() {
87
- console.log('\n=== Acuity MCP Server - Logout ===\n');
88
- const deleted = await clearCredentials();
89
- if (deleted) {
90
- console.log('Credentials removed from system keychain.');
91
- console.log('Run "init" to authenticate again.\n');
8
+ const major = parseInt(process.versions.node.split('.')[0], 10);
9
+ if (major < 20) {
10
+ const platform = process.platform;
11
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '~';
12
+ console.error('\n\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m');
13
+ console.error('\x1b[31m❌ ERROR: Node.js 20 or higher is required to run Acuity MCP Server\x1b[0m');
14
+ console.error('\x1b[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b[0m\n');
15
+ console.error(` Your version: \x1b[33m${process.version}\x1b[0m`);
16
+ console.error(' Required: \x1b[32m>= 20.0.0\x1b[0m\n');
17
+ console.error('\x1b[36m━━━ How to install Node.js 20+ ━━━\x1b[0m\n');
18
+ if (platform === 'darwin') {
19
+ // macOS
20
+ console.error('\x1b[33m📦 macOS:\x1b[0m\n');
21
+ console.error(' Option 1 - Homebrew (recommended):');
22
+ console.error(' \x1b[32m$ brew install node@22\x1b[0m\n');
23
+ console.error(' Option 2 - Official installer:');
24
+ console.error(' \x1b[4mhttps://nodejs.org/en/download/\x1b[0m\n');
25
+ }
26
+ else if (platform === 'win32') {
27
+ // Windows
28
+ console.error('\x1b[33m📦 Windows:\x1b[0m\n');
29
+ console.error(' Option 1 - winget:');
30
+ console.error(' \x1b[32m> winget install OpenJS.NodeJS.LTS\x1b[0m\n');
31
+ console.error(' Option 2 - Official installer:');
32
+ console.error(' \x1b[4mhttps://nodejs.org/en/download/\x1b[0m\n');
92
33
  }
93
34
  else {
94
- console.log('No credentials found to remove.\n');
35
+ // Linux/Ubuntu
36
+ console.error('\x1b[33m📦 Ubuntu/Linux:\x1b[0m\n');
37
+ console.error(' Option 1 - NodeSource (recommended):');
38
+ console.error(' \x1b[32m$ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -\x1b[0m');
39
+ console.error(' \x1b[32m$ sudo apt-get install -y nodejs\x1b[0m\n');
40
+ console.error(' Option 2 - Official binaries:');
41
+ console.error(' \x1b[4mhttps://nodejs.org/en/download/\x1b[0m\n');
42
+ }
43
+ console.error('\x1b[36m━━━ Using nvm? ━━━\x1b[0m\n');
44
+ console.error(' Claude Desktop (GUI app) does not load shell config where nvm is initialized.');
45
+ console.error(' Add PATH to your Claude Desktop config:\n');
46
+ if (platform === 'darwin') {
47
+ console.error(` \x1b[33mFile:\x1b[0m ~/Library/Application Support/Claude/claude_desktop_config.json\n`);
48
+ console.error(' \x1b[32m"env": {');
49
+ console.error(` "PATH": "${homeDir}/.nvm/versions/node/v22.x.x/bin:/usr/bin:/bin"`);
50
+ console.error(' }\x1b[0m\n');
51
+ }
52
+ else if (platform === 'win32') {
53
+ console.error(' \x1b[33mFile:\x1b[0m %APPDATA%\\Claude\\claude_desktop_config.json\n');
54
+ console.error(' \x1b[32m"env": {');
55
+ console.error(` "PATH": "${homeDir}\\\\.nvm\\\\versions\\\\node\\\\v22.x.x;C:\\\\Windows\\\\System32"`);
56
+ console.error(' }\x1b[0m\n');
95
57
  }
96
- }
97
- /**
98
- * Handle 'status' command - show current authentication status
99
- */
100
- async function handleStatus() {
101
- console.log('\n=== Acuity MCP Server - Status ===\n');
102
- const creds = await loadCredentials();
103
- if (!creds) {
104
- console.log('Status: Not authenticated');
105
- console.log('Run "init" to authenticate.\n');
106
- return;
107
- }
108
- const now = Math.floor(Date.now() / 1000);
109
- const isExpired = creds.expiresAt <= now;
110
- const expiresIn = creds.expiresAt - now;
111
- console.log(`Status: Authenticated`);
112
- if (creds.email) {
113
- console.log(`Email: ${creds.email}`);
114
- }
115
- console.log(`Token expires: ${isExpired ? 'EXPIRED' : `in ${Math.floor(expiresIn / 60)} minutes`}`);
116
- console.log(`Refresh token: ${creds.refreshToken ? 'Available' : 'Not available'}`);
117
- console.log('');
118
- }
119
- /**
120
- * Get access token for MCP operations
121
- * Supports both legacy USER_JWT env var and new Device Flow
122
- */
123
- async function getAccessToken() {
124
- // Legacy: Check for USER_JWT environment variable first
125
- const legacyToken = process.env.USER_JWT || process.env.JWT_TOKEN;
126
- if (legacyToken) {
127
- const { email } = await validateJWT(legacyToken);
128
- return { token: legacyToken, email };
129
- }
130
- // New: Use stored credentials from keychain
131
- const config = getAuth0Config();
132
- const refreshFn = async (refreshToken) => {
133
- return refreshAccessToken(config, refreshToken);
134
- };
135
- const idToken = await getValidIdToken(refreshFn);
136
- // Get email from stored credentials
137
- const creds = await loadCredentials();
138
- const email = creds?.email;
139
- if (!email) {
140
- // Try to extract from id token or validate to get email
141
- try {
142
- const { email: validatedEmail } = await validateJWT(idToken);
143
- return { token: idToken, email: validatedEmail };
144
- }
145
- catch {
146
- throw new Error('Could not determine user email from credentials');
147
- }
148
- }
149
- return { token: idToken, email };
150
- }
151
- /**
152
- * MCP Server instance
153
- */
154
- const server = new Server({
155
- name: 'acuity-mcp-server',
156
- version: '1.0.0'
157
- }, {
158
- capabilities: {
159
- tools: {}
160
- }
161
- });
162
- /**
163
- * List available tools
164
- */
165
- server.setRequestHandler(ListToolsRequestSchema, async () => {
166
- return {
167
- tools: [
168
- initAuthTool,
169
- authStatusTool,
170
- logoutTool,
171
- acuityInitTool,
172
- listProjectsTool,
173
- getProjectTool,
174
- searchProjectsTool,
175
- getStatusReportsTool,
176
- listRisksTool,
177
- getRiskTool,
178
- listIssuesTool,
179
- getIssueTool,
180
- listLessonsLearnedTool,
181
- getLessonLearnedTool,
182
- getLookupValuesTool,
183
- getDashboardSummaryTool
184
- ]
185
- };
186
- });
187
- /**
188
- * Handle tool execution
189
- */
190
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
191
- const { name } = request.params;
192
- try {
193
- // Auth tools don't require authentication
194
- switch (name) {
195
- case 'acuity_login': {
196
- const result = await initAuth();
197
- // If login was successful, auto-initialize context
198
- if (result.includes('Login Successful')) {
199
- try {
200
- const { token, email } = await getAccessToken();
201
- const hasuraClient = HasuraClient.forUser(email, token);
202
- sessionInitData = await acuityInit(hasuraClient);
203
- // Append initialization summary to login result
204
- const initSummary = `
205
-
206
- ---
207
- 📊 **Acuity Data Initialized**
208
-
209
- Company: ${sessionInitData.company}
210
- Projects: ${sessionInitData.entities.projects.count} | Proposals: ${sessionInitData.entities.proposals.count}
211
- Risks: ${sessionInitData.entities.project_risks.count} (project) + ${sessionInitData.entities.proposal_risks.count} (proposal)
212
- Issues: ${sessionInitData.entities.project_issues.count} (project) + ${sessionInitData.entities.proposal_issues.count} (proposal)
213
- Status Reports: ${sessionInitData.entities.status_reports.count}
214
-
215
- Ready to help! Try: "Show me active projects" or "List projects with red health status"`;
216
- return {
217
- content: [{ type: 'text', text: result + initSummary }]
218
- };
219
- }
220
- catch (initError) {
221
- // Login succeeded but init failed - still return login success
222
- const errorMsg = initError instanceof Error ? initError.message : String(initError);
223
- return {
224
- content: [{ type: 'text', text: result + `\n\n⚠️ Auto-initialization failed: ${errorMsg}. Use acuity_init to retry.` }]
225
- };
226
- }
227
- }
228
- return {
229
- content: [{ type: 'text', text: result }]
230
- };
231
- }
232
- case 'acuity_logout': {
233
- const result = await logout();
234
- return {
235
- content: [{ type: 'text', text: result }]
236
- };
237
- }
238
- case 'acuity_auth_status': {
239
- const result = await getAuthStatus();
240
- return {
241
- content: [{ type: 'text', text: result }]
242
- };
243
- }
244
- }
245
- // Data tools require authentication
246
- const { token, email } = await getAccessToken();
247
- const hasuraClient = HasuraClient.forUser(email, token);
248
- // Helper: auto-initialize if needed and return init context for response
249
- async function ensureInitialized() {
250
- if (!sessionInitData) {
251
- sessionInitData = await acuityInit(hasuraClient);
252
- }
253
- return sessionInitData;
254
- }
255
- switch (name) {
256
- case 'acuity_init': {
257
- // Always refresh on explicit call
258
- sessionInitData = await acuityInit(hasuraClient);
259
- // Return concise summary - enums are documented in tool descriptions
260
- const d = sessionInitData;
261
- const summary = `✅ Acuity initialized
262
-
263
- Company: ${d.company}
264
- Currency: ${d.currency_code} (${d.currency_locale})
265
-
266
- Data available:
267
- • Projects: ${d.entities.projects.count} | Proposals: ${d.entities.proposals.count}
268
- • Risks: ${d.entities.project_risks.count} (project) + ${d.entities.proposal_risks.count} (proposal)
269
- • Issues: ${d.entities.project_issues.count} (project) + ${d.entities.proposal_issues.count} (proposal)
270
- • Lessons: ${d.entities.lessons_learned.count}
271
- • Status Reports: ${d.entities.status_reports.count}
272
-
273
- Lookups: ${d.lookup_tables.departments} departments, ${d.lookup_tables.categories} categories, ${d.lookup_tables.lifecycles} lifecycles`;
274
- return {
275
- content: [{
276
- type: 'text',
277
- text: summary
278
- }]
279
- };
280
- }
281
- case 'list_projects': {
282
- // Auto-initialize if needed
283
- const initData = await ensureInitialized();
284
- const listResult = await listProjects(request.params.arguments, hasuraClient);
285
- // If this was auto-initialized, include context in response
286
- const response = sessionInitData === initData && !request.params.arguments
287
- ? { _context: initData, ...listResult }
288
- : listResult;
289
- return {
290
- content: [{
291
- type: 'text',
292
- text: JSON.stringify(response, null, 2)
293
- }]
294
- };
295
- }
296
- case 'get_project': {
297
- // Auto-initialize if needed
298
- await ensureInitialized();
299
- const projectResult = await getProject(request.params.arguments, hasuraClient);
300
- return {
301
- content: [{
302
- type: 'text',
303
- text: JSON.stringify(projectResult, null, 2)
304
- }]
305
- };
306
- }
307
- case 'get_status_reports': {
308
- // Auto-initialize if needed
309
- await ensureInitialized();
310
- const statusReportsText = await getStatusReports(request.params.arguments, hasuraClient);
311
- return {
312
- content: [{
313
- type: 'text',
314
- text: statusReportsText
315
- }]
316
- };
317
- }
318
- case 'list_risks': {
319
- // Auto-initialize if needed
320
- await ensureInitialized();
321
- const risksResult = await listRisks(request.params.arguments, hasuraClient);
322
- return {
323
- content: [{
324
- type: 'text',
325
- text: JSON.stringify(risksResult, null, 2)
326
- }]
327
- };
328
- }
329
- case 'get_risk': {
330
- // Auto-initialize if needed
331
- await ensureInitialized();
332
- const riskResult = await getRisk(request.params.arguments, hasuraClient);
333
- return {
334
- content: [{
335
- type: 'text',
336
- text: JSON.stringify(riskResult, null, 2)
337
- }]
338
- };
339
- }
340
- case 'list_issues': {
341
- // Auto-initialize if needed
342
- await ensureInitialized();
343
- const issuesResult = await listIssues(request.params.arguments, hasuraClient);
344
- return {
345
- content: [{
346
- type: 'text',
347
- text: JSON.stringify(issuesResult, null, 2)
348
- }]
349
- };
350
- }
351
- case 'get_issue': {
352
- // Auto-initialize if needed
353
- await ensureInitialized();
354
- const issueResult = await getIssue(request.params.arguments, hasuraClient);
355
- return {
356
- content: [{
357
- type: 'text',
358
- text: JSON.stringify(issueResult, null, 2)
359
- }]
360
- };
361
- }
362
- case 'list_lessons_learned': {
363
- // Auto-initialize if needed
364
- await ensureInitialized();
365
- const lessonsResult = await listLessonsLearned(request.params.arguments, hasuraClient);
366
- return {
367
- content: [{
368
- type: 'text',
369
- text: JSON.stringify(lessonsResult, null, 2)
370
- }]
371
- };
372
- }
373
- case 'get_lesson_learned': {
374
- // Auto-initialize if needed
375
- await ensureInitialized();
376
- const lessonResult = await getLessonLearned(request.params.arguments, hasuraClient);
377
- return {
378
- content: [{
379
- type: 'text',
380
- text: JSON.stringify(lessonResult, null, 2)
381
- }]
382
- };
383
- }
384
- case 'get_lookup_values': {
385
- // Auto-initialize if needed
386
- await ensureInitialized();
387
- const lookupResult = await getLookupValues(request.params.arguments, hasuraClient);
388
- return {
389
- content: [{
390
- type: 'text',
391
- text: JSON.stringify(lookupResult, null, 2)
392
- }]
393
- };
394
- }
395
- case 'search_projects': {
396
- // Auto-initialize if needed
397
- await ensureInitialized();
398
- const searchResult = await searchProjects(request.params.arguments, hasuraClient);
399
- return {
400
- content: [{
401
- type: 'text',
402
- text: JSON.stringify(searchResult, null, 2)
403
- }]
404
- };
405
- }
406
- case 'get_dashboard_summary': {
407
- // Auto-initialize if needed
408
- await ensureInitialized();
409
- const dashboardResult = await getDashboardSummary(request.params.arguments, hasuraClient);
410
- return {
411
- content: [{
412
- type: 'text',
413
- text: JSON.stringify(dashboardResult, null, 2)
414
- }]
415
- };
416
- }
417
- default:
418
- throw new Error(`Unknown tool: ${name}`);
419
- }
420
- }
421
- catch (error) {
422
- const errorMessage = error instanceof Error ? error.message : String(error);
423
- return {
424
- content: [{
425
- type: 'text',
426
- text: `Error: ${errorMessage}`
427
- }],
428
- isError: true
429
- };
430
- }
431
- });
432
- /**
433
- * Start the MCP server
434
- */
435
- async function runServer() {
436
- // Only log in debug mode to avoid interfering with MCP protocol
437
- const debug = process.env.LOG_LEVEL === 'debug';
438
- // Get environment configuration (will throw if ACUITY_ENV not set)
439
- const env = getEnvironment();
440
- if (debug) {
441
- console.error('[MCP] Starting Acuity MCP Server...');
442
- console.error('[MCP] Configuration:');
443
- console.error(`[MCP] Environment: ${env.id} (${env.name})`);
444
- console.error(`[MCP] Hasura endpoint: ${env.hasuraEndpoint}`);
445
- console.error(`[MCP] Auth0 domain: ${env.auth0Domain}`);
446
- }
447
- // Create stdio transport
448
- const transport = new StdioServerTransport();
449
- // Connect server to transport
450
- await server.connect(transport);
451
- if (debug) {
452
- console.error('[MCP] Server running on stdio');
453
- console.error('[MCP] Available tools: list_projects, get_status_reports');
454
- console.error('[MCP] Ready to receive requests...');
455
- }
456
- }
457
- /**
458
- * Parse command line arguments
459
- */
460
- function parseArgs(args) {
461
- let command = null;
462
- let port = 3000; // default port
463
- for (let i = 0; i < args.length; i++) {
464
- const arg = args[i];
465
- if (arg === '--port' || arg === '-p') {
466
- const portArg = args[i + 1];
467
- if (portArg) {
468
- port = parseInt(portArg, 10);
469
- i++; // skip next arg
470
- }
471
- }
472
- else if (arg.startsWith('--port=')) {
473
- port = parseInt(arg.split('=')[1], 10);
474
- }
475
- else if (!arg.startsWith('-')) {
476
- command = arg;
477
- }
478
- else if (arg === '--http' || arg === '-H') {
479
- command = 'http';
480
- }
481
- }
482
- return { command, port };
483
- }
484
- /**
485
- * Main entry point
486
- */
487
- async function main() {
488
- const args = process.argv.slice(2);
489
- const { command, port } = parseArgs(args);
490
- switch (command) {
491
- case 'init':
492
- await handleInit();
493
- break;
494
- case 'logout':
495
- await handleLogout();
496
- break;
497
- case 'status':
498
- await handleStatus();
499
- break;
500
- case 'http':
501
- // Start HTTP server for remote clients (Copilot, remote Claude, etc.)
502
- await startHttpServer(port);
503
- break;
504
- case 'help': {
505
- const envList = listEnvironments()
506
- .map(e => ` ${e.id.padEnd(12)} ${e.name}${e.auth0ClientId === 'TODO' ? ' (not configured)' : ''}`)
507
- .join('\n');
508
- console.log(`
509
- Acuity MCP Server - AI-powered access to project management data
510
-
511
- Usage:
512
- ACUITY_ENV=<env> npx acuity-mcp-server [command] [options]
513
-
514
- IMPORTANT: ACUITY_ENV is required. Set it to specify which Acuity instance to connect to.
515
-
516
- Available Environments:
517
- ${envList}
518
-
519
- Commands:
520
- (none) Start MCP server on stdio (for Claude Desktop, Cursor)
521
- http Start HTTP server for remote clients (Copilot Studio, remote Claude)
522
- init Authenticate via browser (Device Authorization Flow)
523
- logout Clear stored credentials
524
- status Show current authentication status
525
- help Show this help message
526
-
527
- Options:
528
- --port, -p <port> Port for HTTP server (default: 3000)
529
- --http, -H Start in HTTP mode (same as 'http' command)
530
-
531
- Examples:
532
- # Authenticate with US Production
533
- ACUITY_ENV=US-1 npx acuity-mcp-server init
534
-
535
- # Start stdio server for Claude Desktop
536
- ACUITY_ENV=US-1 npx acuity-mcp-server
537
-
538
- # Start HTTP server on custom port
539
- ACUITY_ENV=US-1 npx acuity-mcp-server http --port 8080
540
-
541
- Claude Desktop Configuration:
542
- Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
543
-
544
- {
545
- "mcpServers": {
546
- "acuity": {
547
- "command": "npx",
548
- "args": ["acuity-mcp-server"],
549
- "env": {
550
- "ACUITY_ENV": "US-1"
551
- }
552
- }
553
- }
554
- }
555
-
556
- Authentication:
557
- 1. Set ACUITY_ENV to your environment (e.g., US-1)
558
- 2. Run 'init' to authenticate via browser
559
- 3. Credentials are stored securely in system keychain
560
- 4. Each environment stores credentials separately
561
-
562
- Environment Variables:
563
- ACUITY_ENV Required: Environment to connect to (US-1, EU-1, etc.)
564
- LOG_LEVEL Set to 'debug' for verbose logging
565
- USER_JWT Legacy: JWT token (if keychain not available)
566
- MCP_API_KEY API key for HTTP mode
567
- MCP_SERVICE_EMAIL Service account email for API key auth
568
- `);
569
- break;
570
- }
571
- default:
572
- // No command or unknown command - run as MCP server (stdio)
573
- await runServer();
574
- break;
58
+ else {
59
+ console.error(' \x1b[33mFile:\x1b[0m ~/.config/Claude/claude_desktop_config.json\n');
60
+ console.error(' \x1b[32m"env": {');
61
+ console.error(` "PATH": "${homeDir}/.nvm/versions/node/v22.x.x/bin:/usr/bin:/bin"`);
62
+ console.error(' }\x1b[0m\n');
575
63
  }
576
- }
577
- // Handle graceful shutdown
578
- process.on('SIGINT', async () => {
579
- console.error('[MCP] Shutting down gracefully...');
580
- await server.close();
581
- process.exit(0);
582
- });
583
- process.on('SIGTERM', async () => {
584
- console.error('[MCP] Shutting down gracefully...');
585
- await server.close();
586
- process.exit(0);
587
- });
588
- // Start the application
589
- main().catch((error) => {
590
- console.error('[MCP] Fatal error:', error);
591
64
  process.exit(1);
592
- });
65
+ }
66
+ // Version OK - dynamically import the main server
67
+ // Dynamic import ensures Node 16 never tries to parse ESM syntax in main.js
68
+ import('./main.js');
69
+ export {};
593
70
  //# sourceMappingURL=index.js.map