UNPKG

@razorpay/blade-mcp

Version:

Model Context Protocol server for Blade

139 lines 4.67 kB
import os from 'os'; import crypto from 'crypto'; import { basename } from 'path'; import * as Sentry from '@sentry/node'; import { Analytics } from '@segment/analytics-node'; import { getPackageJSONVersion } from './generalUtils.js'; import { getUserName } from './getUserName.js'; let cachedMachineId = null; // Context to track if the current call is from MCP SSE let mcpSseAnalyticsContext = { protocol: 'stdio', }; const setMcpSseAnalyticsContext = ({ protocol }) => { mcpSseAnalyticsContext = { protocol, }; }; const handleError = ({ toolName, errorObject, mcpErrorMessage = '', }) => { if (errorObject) { Sentry.captureException(errorObject); } return { isError: true, content: [ { type: 'text', text: errorObject ? `Error in ${toolName}: ${errorObject instanceof Error ? errorObject.message : String(errorObject)}` : mcpErrorMessage, }, ], }; }; /** * Get MAC addresses from network interfaces */ const getMacAddresses = () => { try { const interfaces = os.networkInterfaces(); const macAddresses = []; // Collect all non-internal MAC addresses Object.values(interfaces).forEach((networkInterface) => { if (networkInterface) { networkInterface.forEach((details) => { if (!details.internal && details.mac && details.mac !== '00:00:00:00:00:00') { macAddresses.push(details.mac); } }); } }); return macAddresses; } catch (error) { Sentry.captureException(error); return []; } }; /** * Create a hash from a string */ const createHashFromString = (input) => { const hash = crypto.createHash('sha256'); hash.update(input); return hash.digest('hex').substring(0, 12); }; /** * Generates a consistent machine ID based primarily on MAC addresses */ export const getUniqueIdentifier = () => { // Return cached ID if available if (cachedMachineId) { return cachedMachineId; } try { // Get MAC addresses from network interfaces const macAddresses = getMacAddresses(); // If we have MAC addresses, use them to generate the ID if (macAddresses.length > 0) { // Create a hash of the MAC addresses const hash = crypto.createHash('sha256'); hash.update(macAddresses.join('-')); // Generate deterministic machine ID const machineId = `blade-${hash.digest('hex').substring(0, 16)}`; cachedMachineId = machineId; return machineId; } // Fallback to hostname if no MAC addresses available const fallbackId = `blade-host-${createHashFromString(os.hostname())}`; cachedMachineId = fallbackId; return fallbackId; } catch (error) { Sentry.captureException(error); // Ultimate fallback if any errors occur const fallbackId = `blade-fallback-${Date.now()}`; cachedMachineId = fallbackId; return fallbackId; } }; const sendAnalytics = ({ eventName, properties, }) => { try { const analytics = new Analytics({ writeKey: 'FRC1wLlDOhHQPLkFSvPeU0f1apsxtPFY' ?? '' }); // Get or create machine ID const projectRootDirectory = properties ?.currentProjectRootDirectory; const oldUserId = getUniqueIdentifier(); const userId = getUserName({ currentProjectRootDirectory: projectRootDirectory, }); analytics.track({ userId, event: eventName, properties: { osType: os.type(), nodeVersion: process.version, serverVersion: getPackageJSONVersion(), userName: userId, rootDirectoryName: basename(projectRootDirectory), ...mcpSseAnalyticsContext, ...properties, }, }); analytics.alias({ userId, previousId: oldUserId, }); } catch (error) { // Use console.error (stderr) to avoid interfering with MCP protocol (stdout) console.error('[Analytics Error]', { eventName, error: error instanceof Error ? error.message : String(error), properties: JSON.stringify(properties), }); Sentry.captureException(error); } }; export { handleError, sendAnalytics, setMcpSseAnalyticsContext }; //# sourceMappingURL=analyticsUtils.js.map