pypi-query-mcp/VERSION_PARAMETER_FIX_SUMMARY.md
Ryan Malloy 0087573fc3 fix: enable version parameter functionality in get_package_dependencies
- Fix version parameter being ignored - now properly fetches specified versions
- Enhance PyPIClient with version-specific URL construction
- Add version format validation with regex patterns
- Improve error handling for non-existent versions
- Test with Django 4.2.0, FastAPI 0.100.0, NumPy 1.20.0
2025-08-15 11:53:41 -06:00

7.4 KiB

Version Parameter Fix Summary

Problem Description

The get_package_dependencies tool in the PyPI Query MCP Server had a critical issue where the version parameter was completely ignored. When users requested dependencies for a specific version of a package (e.g., Django 4.2.0), the tool would:

  1. Accept the version parameter but ignore it
  2. Always fetch the latest version of the package instead
  3. Return dependencies for the latest version, not the requested version
  4. Only log a warning message about the unimplemented functionality

This made the tool unreliable for users who needed to analyze dependencies for specific package versions.

Root Cause Analysis

The issue was located in the query_package_dependencies function in /tmp/a/fix-version-parameter/pypi_query_mcp/tools/package_query.py:

# Old problematic code (lines 250-261)
async with PyPIClient() as client:
    package_data = await client.get_package_info(package_name)  # No version passed!

    # TODO: In future, support querying specific version dependencies
    # For now, we return dependencies for the latest version
    if version and version != package_data.get("info", {}).get("version"):
        logger.warning(
            f"Specific version {version} requested but not implemented yet. "
            f"Returning dependencies for latest version."
        )

The underlying PyPI client's get_package_info method also did not support version-specific queries, always fetching the latest package information.

Implementation Changes

1. Enhanced PyPIClient to Support Version-Specific Queries

File: /tmp/a/fix-version-parameter/pypi_query_mcp/core/pypi_client.py

Changes:

  • Modified get_package_info method signature to accept optional version parameter
  • Added URL construction logic to query specific versions using PyPI's API pattern: https://pypi.org/pypi/{package}/{version}/json
  • Enhanced cache key generation to include version information
  • Improved error handling with specific messages for version-not-found cases

Before:

async def get_package_info(self, package_name: str, use_cache: bool = True) -> dict[str, Any]:
    # Always fetched latest version
    url = f"{self.base_url}/{quote(normalized_name)}/json"

After:

async def get_package_info(self, package_name: str, version: str | None = None, use_cache: bool = True) -> dict[str, Any]:
    # Version-aware URL construction
    if version:
        url = f"{self.base_url}/{quote(normalized_name)}/{quote(version)}/json"
    else:
        url = f"{self.base_url}/{quote(normalized_name)}/json"

2. Fixed query_package_dependencies Function

File: /tmp/a/fix-version-parameter/pypi_query_mcp/tools/package_query.py

Changes:

  • Removed TODO comment and warning about unimplemented functionality
  • Added version parameter validation using regex patterns
  • Simplified function logic to actually pass the version parameter to PyPIClient

Before:

async with PyPIClient() as client:
    package_data = await client.get_package_info(package_name)  # No version!
    
    # TODO: In future, support querying specific version dependencies
    if version and version != package_data.get("info", {}).get("version"):
        logger.warning("Specific version requested but not implemented yet...")

After:

async with PyPIClient() as client:
    # Pass the version parameter to get_package_info
    package_data = await client.get_package_info(package_name, version=version)
    return format_dependency_info(package_data)

3. Added Version Format Validation

File: /tmp/a/fix-version-parameter/pypi_query_mcp/tools/package_query.py

New Function:

def validate_version_format(version: str | None) -> bool:
    """Validate that a version string follows a reasonable format."""
    if version is None:
        return True
    
    # Supports: 1.0.0, 1.0, 1.0.0a1, 1.0.0b2, 1.0.0rc1, 1.0.0.dev1, etc.
    version_pattern = r"^[0-9]+(?:\.[0-9]+)*(?:[\.\-]?(?:a|b|rc|alpha|beta|dev|pre|post|final)[0-9]*)*$"
    return bool(re.match(version_pattern, version.strip(), re.IGNORECASE))

4. Enhanced Error Handling

  • Added proper validation that raises InvalidPackageNameError for malformed version strings
  • Improved PyPIClient error messages to distinguish between package-not-found and version-not-found errors
  • Maintained existing error handling patterns for consistency

Test Results

Comprehensive testing was performed to verify the fix works correctly:

Successful Test Cases

  1. Django 4.2.0: Successfully retrieved 4 runtime dependencies
  2. FastAPI 0.100.0: Successfully retrieved 3 runtime dependencies
  3. NumPy 1.20.0: Successfully retrieved 0 runtime dependencies (NumPy has minimal deps)
  4. Requests 2.25.1: Successfully retrieved 4 runtime dependencies
  5. Pre-release versions (Django 5.0a1): Successfully handled

Verification of Fix

  • Version-specific queries now return different results than latest version queries
  • Django 4.2.0 returns different dependencies than Django 5.2.5 (latest)
  • Dependency counts differ between versions (Django 4.2.0: 4 deps, Django latest: 3 deps)
  • Dependency specifications updated between versions (e.g., asgiref (<4,>=3.6.0) vs asgiref>=3.8.1)

Error Handling Test Cases

  1. Invalid version format (invalid.version!): Correctly rejected with InvalidPackageNameError
  2. Non-existent version (Django 999.999.999): Correctly rejected with PackageNotFoundError
  3. Non-existent package: Correctly handled with appropriate error

Version Validation Test Cases

  • 1.0.0, 2.1, 1.0.0a1, 1.0.0b2, 1.0.0rc1, 2.0.0.dev1: All valid
  • invalid.version!, empty string: Correctly rejected
  • None (latest version): Correctly accepted

Impact and Benefits

Before the Fix

  • Users requesting get_package_dependencies("django", "4.2.0") would get dependencies for Django 5.2.5 (latest)
  • No way to analyze dependencies for specific historical versions
  • Misleading results that could lead to incorrect dependency analysis
  • Function parameter was essentially non-functional

After the Fix

  • Users can reliably get dependencies for any specific version available on PyPI
  • Proper error handling for non-existent versions
  • Accurate dependency analysis for historical versions
  • Full compatibility with PyPI's version-specific API endpoints

Backward Compatibility

The fix maintains full backward compatibility:

  • Existing calls without version parameter continue to work identically
  • Error handling patterns remain consistent
  • Return value structure unchanged
  • All existing functionality preserved

Files Modified

  1. /tmp/a/fix-version-parameter/pypi_query_mcp/core/pypi_client.py

    • Enhanced get_package_info method with version support
    • Updated get_package_versions and get_latest_version calls
  2. /tmp/a/fix-version-parameter/pypi_query_mcp/tools/package_query.py

    • Fixed query_package_dependencies to use version parameter
    • Added validate_version_format function
    • Updated other query functions for consistency

Conclusion

The version parameter fix resolves a significant functional gap in the PyPI Query MCP Server. Users can now reliably query dependencies for specific package versions, enabling accurate dependency analysis for any version available on PyPI. The implementation follows best practices for error handling, validation, and maintains full backward compatibility.