Ryan Malloy 43f36b60fb
Some checks are pending
Bump version / Bump version and create changelog with commitizen (push) Waiting to run
Tests / test (macos-latest, 3.10) (push) Waiting to run
Tests / test (macos-latest, 3.11) (push) Waiting to run
Tests / test (macos-latest, 3.12) (push) Waiting to run
Tests / test (ubuntu-latest, 3.10) (push) Waiting to run
Tests / test (ubuntu-latest, 3.11) (push) Waiting to run
Tests / test (ubuntu-latest, 3.12) (push) Waiting to run
Tests / test (windows-latest, 3.10) (push) Waiting to run
Tests / test (windows-latest, 3.11) (push) Waiting to run
Tests / test (windows-latest, 3.12) (push) Waiting to run
Tests / security (push) Waiting to run
feat: add comprehensive security, license, health, and requirements analysis tools
- Add security vulnerability scanning with OSV and GitHub advisories integration
- Add license compatibility analysis with SPDX normalization and risk assessment
- Add package health scoring across 7 categories with GitHub metrics integration
- Add requirements file analysis supporting multiple formats (requirements.txt, pyproject.toml, etc.)
- Fix search functionality MCP wrapper and error handling
- Fix Python compatibility checking parameter order issue
- Fix package recommendations NoneType handling
- Add 8 new MCP tool endpoints for enhanced analysis capabilities

This brings the total to 37 comprehensive MCP tools across 8 categories for complete PyPI package analysis and management.
2025-09-06 10:28:57 -06:00

154 lines
6.2 KiB
Python

"""License compatibility analysis tools for PyPI packages."""
import logging
from typing import Any, Dict, List, Optional
from ..core.exceptions import InvalidPackageNameError, NetworkError, SearchError
from ..tools.license_analyzer import analyze_package_license_compatibility, check_license_compliance_bulk
logger = logging.getLogger(__name__)
async def analyze_pypi_package_license(
package_name: str,
version: Optional[str] = None,
include_dependencies: bool = True
) -> Dict[str, Any]:
"""
Analyze license compatibility for a PyPI package.
This tool provides comprehensive license analysis including license identification,
dependency license scanning, compatibility checking, and risk assessment to help
ensure your project complies with open source license requirements.
Args:
package_name: Name of the package to analyze for license compatibility
version: Specific version to analyze (optional, defaults to latest version)
include_dependencies: Whether to analyze dependency licenses for compatibility
Returns:
Dictionary containing comprehensive license analysis including:
- License identification and normalization (SPDX format)
- License categorization (permissive, copyleft, proprietary, etc.)
- Dependency license analysis and compatibility matrix
- Risk assessment with score and risk level (minimal, low, medium, high, critical)
- Compatibility analysis highlighting conflicts and review-required combinations
- Actionable recommendations for license compliance
Raises:
InvalidPackageNameError: If package name is empty or invalid
PackageNotFoundError: If package is not found on PyPI
NetworkError: For network-related errors
SearchError: If license analysis fails
"""
if not package_name or not package_name.strip():
raise InvalidPackageNameError(package_name)
logger.info(f"MCP tool: Analyzing license compatibility for package {package_name}")
try:
result = await analyze_package_license_compatibility(
package_name=package_name,
version=version,
include_dependencies=include_dependencies
)
logger.info(f"MCP tool: License analysis completed for {package_name} - {result.get('analysis_summary', {}).get('license_conflicts', 0)} conflicts found")
return result
except (InvalidPackageNameError, NetworkError, SearchError) as e:
logger.error(f"Error analyzing license for {package_name}: {e}")
return {
"error": f"License analysis failed: {e}",
"error_type": type(e).__name__,
"package": package_name,
"version": version,
"analysis_timestamp": "",
"license_info": {
"normalized_license": "Unknown",
"license_category": "unknown",
"license_confidence": "low",
},
"dependency_licenses": [],
"compatibility_analysis": {
"main_license": "Unknown",
"compatible": [],
"incompatible": [],
"review_required": [],
"conflicts": [],
},
"risk_assessment": {
"risk_score": 100,
"risk_level": "critical",
"risk_factors": [f"License analysis failed: {e}"],
"compliance_status": "unknown",
},
"recommendations": [f"❌ License analysis failed: {e}"],
"analysis_summary": {
"total_dependencies_analyzed": 0,
"unique_licenses_found": 0,
"license_conflicts": 0,
"review_required_count": 0,
}
}
async def check_bulk_license_compliance(
package_names: List[str],
target_license: Optional[str] = None
) -> Dict[str, Any]:
"""
Check license compliance for multiple PyPI packages.
This tool performs bulk license compliance checking across multiple packages,
providing a consolidated report to help ensure your entire package ecosystem
complies with license requirements and identifying potential legal risks.
Args:
package_names: List of package names to check for license compliance
target_license: Target license for compatibility checking (optional)
Returns:
Dictionary containing bulk compliance analysis including:
- Summary statistics (total packages, compliant/non-compliant counts)
- Detailed license analysis for each package
- High-risk packages requiring immediate attention
- Unknown license packages needing investigation
- Prioritized recommendations for compliance remediation
Raises:
ValueError: If package_names list is empty
NetworkError: For network-related errors during analysis
SearchError: If bulk compliance checking fails
"""
if not package_names:
raise ValueError("Package names list cannot be empty")
logger.info(f"MCP tool: Starting bulk license compliance check for {len(package_names)} packages")
try:
result = await check_license_compliance_bulk(
package_names=package_names,
target_license=target_license
)
logger.info(f"MCP tool: Bulk license compliance completed - {result.get('summary', {}).get('non_compliant_packages', 0)} non-compliant packages found")
return result
except (ValueError, NetworkError, SearchError) as e:
logger.error(f"Error in bulk license compliance check: {e}")
return {
"error": f"Bulk license compliance check failed: {e}",
"error_type": type(e).__name__,
"summary": {
"total_packages": len(package_names),
"compliant_packages": 0,
"non_compliant_packages": 0,
"unknown_license_packages": len(package_names),
"high_risk_packages": [],
"analysis_timestamp": ""
},
"detailed_results": {},
"target_license": target_license,
"recommendations": [f"❌ Bulk license compliance check failed: {e}"]
}