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
JavaScript
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),
},
],
};
}
}