Skip to main content
Glama
version-checker.js5.95 kB
/** * Version Checker Utility * Reads local version from __init__.py and checks PyPI for latest published version */ const fs = require('fs').promises; const path = require('path'); const https = require('https'); /** * Read version from __init__.py * @param {string} projectRoot - Path to project root directory * @returns {Promise<string|null>} Version string or null if not found */ async function readLocalVersion(projectRoot) { try { const initPath = path.join(projectRoot, 'src', 'mcp_memory_service', '__init__.py'); const content = await fs.readFile(initPath, 'utf8'); // Match __version__ = "X.Y.Z" or __version__ = 'X.Y.Z' const versionMatch = content.match(/__version__\s*=\s*['"]([\d.]+)['"]/); if (versionMatch && versionMatch[1]) { return versionMatch[1]; } return null; } catch (error) { return null; } } /** * Fetch latest version from PyPI * @param {string} packageName - Name of the package on PyPI * @param {number} timeout - Request timeout in ms (default: 2000) * @returns {Promise<string|null>} Latest version string or null if error */ async function fetchPyPIVersion(packageName = 'mcp-memory-service', timeout = 2000) { return new Promise((resolve) => { const url = `https://pypi.org/pypi/${packageName}/json`; const timeoutId = setTimeout(() => { resolve(null); }, timeout); https.get(url, { headers: { 'User-Agent': 'mcp-memory-service-hook' } }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { clearTimeout(timeoutId); try { const parsed = JSON.parse(data); const latestVersion = parsed?.info?.version; resolve(latestVersion || null); } catch (error) { resolve(null); } }); }).on('error', () => { clearTimeout(timeoutId); resolve(null); }); }); } /** * Compare two semantic versions * @param {string} local - Local version (e.g., "8.39.1") * @param {string} pypi - PyPI version (e.g., "8.38.0") * @returns {number} -1 if local < pypi, 0 if equal, 1 if local > pypi */ function compareVersions(local, pypi) { const localParts = local.split('.').map(Number); const pypiParts = pypi.split('.').map(Number); for (let i = 0; i < Math.max(localParts.length, pypiParts.length); i++) { const localPart = localParts[i] || 0; const pypiPart = pypiParts[i] || 0; if (localPart < pypiPart) return -1; if (localPart > pypiPart) return 1; } return 0; } /** * Get version information with local and PyPI comparison * @param {string} projectRoot - Path to project root directory * @param {Object} options - Options for version check * @param {boolean} options.checkPyPI - Whether to check PyPI (default: true) * @param {number} options.timeout - PyPI request timeout in ms (default: 2000) * @returns {Promise<Object>} Version info object */ async function getVersionInfo(projectRoot, options = {}) { const { checkPyPI = true, timeout = 2000 } = options; const localVersion = await readLocalVersion(projectRoot); const result = { local: localVersion, pypi: null, comparison: null, status: 'unknown' }; if (!localVersion) { result.status = 'error'; return result; } if (checkPyPI) { const pypiVersion = await fetchPyPIVersion('mcp-memory-service', timeout); result.pypi = pypiVersion; if (pypiVersion) { const comparison = compareVersions(localVersion, pypiVersion); result.comparison = comparison; if (comparison === 0) { result.status = 'published'; } else if (comparison > 0) { result.status = 'development'; } else { result.status = 'outdated'; } } else { result.status = 'local-only'; } } else { result.status = 'local-only'; } return result; } /** * Format version information for display * @param {Object} versionInfo - Version info from getVersionInfo() * @param {Object} colors - Console color codes * @returns {string} Formatted version string */ function formatVersionDisplay(versionInfo, colors) { const { local, pypi, status } = versionInfo; if (!local) { return `${colors.CYAN}📦 Version${colors.RESET} ${colors.DIM}→${colors.RESET} ${colors.GRAY}Unable to read version${colors.RESET}`; } let statusLabel = ''; let pypiDisplay = ''; switch (status) { case 'published': statusLabel = `${colors.GRAY}(published)${colors.RESET}`; break; case 'development': statusLabel = `${colors.GRAY}(local)${colors.RESET}`; pypiDisplay = pypi ? ` ${colors.DIM}•${colors.RESET} PyPI: ${colors.YELLOW}${pypi}${colors.RESET}` : ''; break; case 'outdated': statusLabel = `${colors.RED}(outdated)${colors.RESET}`; pypiDisplay = pypi ? ` ${colors.DIM}•${colors.RESET} PyPI: ${colors.GREEN}${pypi}${colors.RESET}` : ''; break; case 'local-only': statusLabel = `${colors.GRAY}(local)${colors.RESET}`; break; default: statusLabel = `${colors.GRAY}(unknown)${colors.RESET}`; } return `${colors.CYAN}📦 Version${colors.RESET} ${colors.DIM}→${colors.RESET} ${colors.BRIGHT}${local}${colors.RESET} ${statusLabel}${pypiDisplay}`; } module.exports = { readLocalVersion, fetchPyPIVersion, compareVersions, getVersionInfo, formatVersionDisplay };

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/doobidoo/mcp-memory-service'

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