Skip to content

Commit 01deb06

Browse files
committed
fix: conditionally disable Sentry MCP wrapper in test environments
- Prevent Sentry.wrapMcpServerWithSentry() from running during tests - Fixes 'module is already linked' VM errors in Vitest CI workers - Clean solution that preserves Sentry functionality in production - Based on research indicating Node.js VM module conflicts in test isolation
1 parent 124e721 commit 01deb06

3 files changed

Lines changed: 47 additions & 84 deletions

File tree

src/index.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,8 @@
1313
* - Handling server lifecycle events
1414
*/
1515

16-
// Import Sentry instrumentation only if not in test environment
17-
const isTestEnvironment =
18-
process.env.NODE_ENV === 'test' ||
19-
process.env.VITEST === 'true' ||
20-
process.env.CI === 'true' ||
21-
process.env.GITHUB_ACTIONS === 'true' ||
22-
process.argv.some((arg) => arg.includes('vitest')) ||
23-
(typeof global !== 'undefined' && 'vitest' in global) ||
24-
(typeof process !== 'undefined' && process.env.npm_lifecycle_event === 'test');
25-
26-
// Initialize Sentry asynchronously only in production environments
27-
if (!isTestEnvironment) {
28-
(async (): Promise<void> => {
29-
await import('./utils/sentry.js');
30-
})();
31-
}
16+
// Import Sentry instrumentation
17+
import './utils/sentry.js';
3218

3319
// Import server components
3420
import { createServer, startServer } from './server/server.js';

src/server/server.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,13 @@ export function createServer(): McpServer {
4343
},
4444
);
4545

46-
// Check if we're in a test environment to avoid Sentry module conflicts
47-
const isTestEnvironment =
48-
process.env.NODE_ENV === 'test' ||
49-
process.env.VITEST === 'true' ||
50-
process.env.CI === 'true' ||
51-
process.env.GITHUB_ACTIONS === 'true' ||
52-
process.argv.some((arg) => arg.includes('vitest')) ||
53-
(typeof global !== 'undefined' && 'vitest' in global) ||
54-
(typeof process !== 'undefined' && process.env.npm_lifecycle_event === 'test');
46+
// Wrap server with Sentry for MCP instrumentation (skip in test environments)
47+
const isTestEnvironment = Boolean(process.env.VITEST) || process.env.NODE_ENV === 'test';
5548

5649
if (isTestEnvironment) {
57-
// In test mode, return the base server without Sentry wrapping
5850
log('info', `Server initialized without Sentry (test mode) (version ${version})`);
5951
return baseServer;
6052
} else {
61-
// In production mode, wrap server with Sentry for MCP instrumentation
6253
const server = Sentry.wrapMcpServerWithSentry(baseServer);
6354
log('info', `Server initialized with Sentry MCP instrumentation (version ${version})`);
6455
return server;

src/utils/sentry.ts

Lines changed: 43 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -77,65 +77,51 @@ function checkBinaryAvailability(binary: string): { available: boolean; version?
7777
return { available: true, version };
7878
}
7979

80-
// Skip Sentry initialization during tests to prevent module loading conflicts
81-
const isTestEnvironment =
82-
process.env.NODE_ENV === 'test' ||
83-
process.env.VITEST === 'true' ||
84-
process.env.CI === 'true' ||
85-
process.env.GITHUB_ACTIONS === 'true' ||
86-
process.argv.some((arg) => arg.includes('vitest')) ||
87-
(typeof global !== 'undefined' && 'vitest' in global) ||
88-
(typeof process !== 'undefined' && process.env.npm_lifecycle_event === 'test');
89-
90-
if (!isTestEnvironment) {
91-
Sentry.init({
92-
dsn: 'https://798607831167c7b9fe2f2912f5d3c665@o4509258288332800.ingest.de.sentry.io/4509258293837904',
93-
94-
// Setting this option to true will send default PII data to Sentry
95-
// For example, automatic IP address collection on events
96-
sendDefaultPii: true,
97-
98-
// Set release version to match application version
99-
release: `xcodebuildmcp@${version}`,
100-
101-
// Set environment based on NODE_ENV
102-
environment: process.env.NODE_ENV ?? 'development',
103-
104-
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring
105-
// We recommend adjusting this value in production
106-
tracesSampleRate: 1.0,
107-
});
80+
Sentry.init({
81+
dsn: 'https://798607831167c7b9fe2f2912f5d3c665@o4509258288332800.ingest.de.sentry.io/4509258293837904',
82+
83+
// Setting this option to true will send default PII data to Sentry
84+
// For example, automatic IP address collection on events
85+
sendDefaultPii: true,
86+
87+
// Set release version to match application version
88+
release: `xcodebuildmcp@${version}`,
89+
90+
// Set environment based on NODE_ENV
91+
environment: process.env.NODE_ENV ?? 'development',
92+
93+
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring
94+
// We recommend adjusting this value in production
95+
tracesSampleRate: 1.0,
96+
});
97+
98+
// Add additional context that might be helpful for debugging
99+
const tags: Record<string, string> = {
100+
nodeVersion: process.version,
101+
platform: process.platform,
102+
arch: process.arch,
103+
};
104+
105+
// Only add Xcode Info if it's available
106+
const xcodeInfo = getXcodeInfo();
107+
if (!xcodeInfo.error) {
108+
tags.xcodeVersion = xcodeInfo.version;
109+
tags.xcodePath = xcodeInfo.path;
110+
} else {
111+
tags.xcodeVersion = 'Unknown';
112+
tags.xcodePath = 'Unknown';
108113
}
109114

110-
// Add additional context that might be helpful for debugging (only in production)
111-
if (!isTestEnvironment) {
112-
const tags: Record<string, string> = {
113-
nodeVersion: process.version,
114-
platform: process.platform,
115-
arch: process.arch,
116-
};
117-
118-
// Only add Xcode Info if it's available
119-
const xcodeInfo = getXcodeInfo();
120-
if (!xcodeInfo.error) {
121-
tags.xcodeVersion = xcodeInfo.version;
122-
tags.xcodePath = xcodeInfo.path;
123-
} else {
124-
tags.xcodeVersion = 'Unknown';
125-
tags.xcodePath = 'Unknown';
126-
}
127-
128-
const envVars = getEnvironmentVariables();
129-
tags.env_XCODEBUILDMCP_DEBUG = envVars['XCODEBUILDMCP_DEBUG'] ?? 'false';
130-
tags.env_XCODEMAKE_ENABLED = envVars['INCREMENTAL_BUILDS_ENABLED'] ?? 'false';
115+
const envVars = getEnvironmentVariables();
116+
tags.env_XCODEBUILDMCP_DEBUG = envVars['XCODEBUILDMCP_DEBUG'] ?? 'false';
117+
tags.env_XCODEMAKE_ENABLED = envVars['INCREMENTAL_BUILDS_ENABLED'] ?? 'false';
131118

132-
const miseAvailable = checkBinaryAvailability('mise');
133-
tags.miseAvailable = miseAvailable.available ? 'true' : 'false';
134-
tags.miseVersion = miseAvailable.version ?? 'Unknown';
119+
const miseAvailable = checkBinaryAvailability('mise');
120+
tags.miseAvailable = miseAvailable.available ? 'true' : 'false';
121+
tags.miseVersion = miseAvailable.version ?? 'Unknown';
135122

136-
const axeAvailable = checkBinaryAvailability('axe');
137-
tags.axeAvailable = axeAvailable.available ? 'true' : 'false';
138-
tags.axeVersion = axeAvailable.version ?? 'Unknown';
123+
const axeAvailable = checkBinaryAvailability('axe');
124+
tags.axeAvailable = axeAvailable.available ? 'true' : 'false';
125+
tags.axeVersion = axeAvailable.version ?? 'Unknown';
139126

140-
Sentry.setTags(tags);
141-
}
127+
Sentry.setTags(tags);

0 commit comments

Comments
 (0)