UNPKG

mcp-package-version

Version:

An MCP server to provide LLMs the latest (stable) version of packages in package.json and requirements.txt files

113 lines (112 loc) 4.5 kB
import axios from 'axios'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; export class PythonHandler { registry = 'https://pypi.org/pypi'; async getPackageVersion(packageName, currentVersion, label) { try { const response = await axios.get(`${this.registry}/${encodeURIComponent(packageName)}/json`); const latestVersion = response.data.info.version; if (!latestVersion) { throw new Error('Latest version not found'); } const result = { name: label ? `${packageName} (${label})` : packageName, latestVersion, registry: 'pypi', }; if (currentVersion) { // Remove any comparison operators from the current version const cleanCurrentVersion = currentVersion.replace(/^[=<>~!]+/, ''); result.currentVersion = cleanCurrentVersion; } return result; } catch (error) { console.error(`Error fetching PyPI package ${packageName}:`, error); throw new McpError(ErrorCode.InternalError, `Failed to fetch PyPI package ${packageName}`); } } async getLatestVersionFromRequirements(args) { if (!args.requirements || !Array.isArray(args.requirements)) { throw new McpError(ErrorCode.InvalidParams, 'Invalid requirements array'); } const results = []; for (const requirement of args.requirements) { if (typeof requirement !== 'string') continue; // Parse package name and version from requirement string const match = requirement.match(/^([a-zA-Z0-9-_.]+)([=<>~!]+.*)?$/); if (!match) continue; const [, name, version = '0.0.0'] = match; try { const result = await this.getPackageVersion(name, version); results.push(result); } catch (error) { console.error(`Error checking PyPI package ${name}:`, error); } } return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } async getLatestVersion(args) { if (!args.dependencies || typeof args.dependencies !== 'object') { throw new McpError(ErrorCode.InvalidParams, 'Invalid dependencies object from pyproject.toml'); } const results = []; const dependencies = args.dependencies; // Process main dependencies if (dependencies.dependencies) { for (const [name, version] of Object.entries(dependencies.dependencies)) { try { const result = await this.getPackageVersion(name, version); results.push(result); } catch (error) { console.error(`Error checking PyPI package ${name}:`, error); } } } // Process optional dependencies if (dependencies['optional-dependencies']) { for (const [group, deps] of Object.entries(dependencies['optional-dependencies'])) { for (const [name, version] of Object.entries(deps)) { try { const result = await this.getPackageVersion(name, version, `optional: ${group}`); results.push(result); } catch (error) { console.error(`Error checking PyPI package ${name}:`, error); } } } } // Process dev dependencies if (dependencies['dev-dependencies']) { for (const [name, version] of Object.entries(dependencies['dev-dependencies'])) { try { const result = await this.getPackageVersion(name, version, 'dev'); results.push(result); } catch (error) { console.error(`Error checking PyPI package ${name}:`, error); } } } return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } }