Skip to main content
Glama
npm-version-checker.tsβ€’5.85 kB
/** * NPM Version Checker Utility * * Checks if the current n8n-mcp version is outdated by comparing * against the latest version published on npm. */ import { logger } from './logger'; /** * NPM Registry Response structure * Based on npm registry JSON format for package metadata */ interface NpmRegistryResponse { version: string; [key: string]: unknown; } export interface VersionCheckResult { currentVersion: string; latestVersion: string | null; isOutdated: boolean; updateAvailable: boolean; error: string | null; checkedAt: Date; updateCommand?: string; } // Cache for version check to avoid excessive npm requests let versionCheckCache: VersionCheckResult | null = null; let lastCheckTime: number = 0; const CACHE_TTL_MS = 1 * 60 * 60 * 1000; // 1 hour cache /** * Check if current version is outdated compared to npm registry * Uses caching to avoid excessive npm API calls * * @param forceRefresh - Force a fresh check, bypassing cache * @returns Version check result */ export async function checkNpmVersion(forceRefresh: boolean = false): Promise<VersionCheckResult> { const now = Date.now(); // Return cached result if available and not expired if (!forceRefresh && versionCheckCache && (now - lastCheckTime) < CACHE_TTL_MS) { logger.debug('Returning cached npm version check result'); return versionCheckCache; } // Get current version from package.json const packageJson = require('../../package.json'); const currentVersion = packageJson.version; try { // Fetch latest version from npm registry const response = await fetch('https://registry.npmjs.org/n8n-mcp/latest', { headers: { 'Accept': 'application/json', }, signal: AbortSignal.timeout(5000) // 5 second timeout }); if (!response.ok) { logger.warn('Failed to fetch npm version info', { status: response.status, statusText: response.statusText }); const result: VersionCheckResult = { currentVersion, latestVersion: null, isOutdated: false, updateAvailable: false, error: `npm registry returned ${response.status}`, checkedAt: new Date() }; versionCheckCache = result; lastCheckTime = now; return result; } // Parse and validate JSON response let data: unknown; try { data = await response.json(); } catch (error) { throw new Error('Failed to parse npm registry response as JSON'); } // Validate response structure if (!data || typeof data !== 'object' || !('version' in data)) { throw new Error('Invalid response format from npm registry'); } const registryData = data as NpmRegistryResponse; const latestVersion = registryData.version; // Validate version format (semver: x.y.z or x.y.z-prerelease) if (!latestVersion || !/^\d+\.\d+\.\d+/.test(latestVersion)) { throw new Error(`Invalid version format from npm registry: ${latestVersion}`); } // Compare versions const isOutdated = compareVersions(currentVersion, latestVersion) < 0; const result: VersionCheckResult = { currentVersion, latestVersion, isOutdated, updateAvailable: isOutdated, error: null, checkedAt: new Date(), updateCommand: isOutdated ? `npm install -g n8n-mcp@${latestVersion}` : undefined }; // Cache the result versionCheckCache = result; lastCheckTime = now; logger.debug('npm version check completed', { current: currentVersion, latest: latestVersion, outdated: isOutdated }); return result; } catch (error) { logger.warn('Error checking npm version', { error: error instanceof Error ? error.message : String(error) }); const result: VersionCheckResult = { currentVersion, latestVersion: null, isOutdated: false, updateAvailable: false, error: error instanceof Error ? error.message : 'Unknown error', checkedAt: new Date() }; // Cache error result to avoid rapid retry versionCheckCache = result; lastCheckTime = now; return result; } } /** * Compare two semantic version strings * Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2 * * @param v1 - First version (e.g., "1.2.3") * @param v2 - Second version (e.g., "1.3.0") * @returns Comparison result */ export function compareVersions(v1: string, v2: string): number { // Remove 'v' prefix if present const clean1 = v1.replace(/^v/, ''); const clean2 = v2.replace(/^v/, ''); // Split into parts and convert to numbers const parts1 = clean1.split('.').map(n => parseInt(n, 10) || 0); const parts2 = clean2.split('.').map(n => parseInt(n, 10) || 0); // Compare each part for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { const p1 = parts1[i] || 0; const p2 = parts2[i] || 0; if (p1 < p2) return -1; if (p1 > p2) return 1; } return 0; // Versions are equal } /** * Clear the version check cache (useful for testing) */ export function clearVersionCheckCache(): void { versionCheckCache = null; lastCheckTime = 0; } /** * Format version check result as a user-friendly message * * @param result - Version check result * @returns Formatted message */ export function formatVersionMessage(result: VersionCheckResult): string { if (result.error) { return `Version check failed: ${result.error}. Current version: ${result.currentVersion}`; } if (!result.latestVersion) { return `Current version: ${result.currentVersion} (latest version unknown)`; } if (result.isOutdated) { return `⚠️ Update available! Current: ${result.currentVersion} β†’ Latest: ${result.latestVersion}`; } return `βœ“ You're up to date! Current version: ${result.currentVersion}`; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/czlonkowski/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server