nixhub_find_version
Locate specific package versions in NixHub by automatically searching with increasing limits. Input package name and version to retrieve version details and commit hash or a helpful response if not found.
Instructions
Find a specific version of a package in NixHub with smart search.
Automatically searches with increasing limits to find the requested version.
Args: package_name: Name of the package to query (e.g., "ruby", "python") version: Specific version to find (e.g., "2.6.7", "3.5.9")
Returns: Plain text with version info and commit hash if found, or helpful message if not
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| package_name | Yes | ||
| version | Yes |
Implementation Reference
- mcp_nixos/server.py:1697-1830 (handler)Main handler function for the nixhub_find_version tool. Queries NixHub API incrementally with increasing limits (10,25,50) to find exact version match. Returns formatted results with commit hashes, last updated date, platforms, and usage instructions. Handles errors gracefully with specific error codes.async def nixhub_find_version(package_name: str, version: str) -> str: """Find a specific version of a package in NixHub with smart search. Automatically searches with increasing limits to find the requested version. Args: package_name: Name of the package to query (e.g., "ruby", "python") version: Specific version to find (e.g., "2.6.7", "3.5.9") Returns: Plain text with version info and commit hash if found, or helpful message if not """ # Validate inputs if not package_name or not package_name.strip(): return error("Package name is required") if not version or not version.strip(): return error("Version is required") # Sanitize inputs if not re.match(r"^[a-zA-Z0-9\-_.]+$", package_name): return error("Invalid package name. Only letters, numbers, hyphens, underscores, and dots are allowed") # Try with incremental limits limits_to_try = [10, 25, 50] found_version = None all_versions: list[dict[str, Any]] = [] for limit in limits_to_try: try: # Make request - handle special cases for package names nixhub_name = package_name # Common package name mappings if package_name == "python": nixhub_name = "python3" elif package_name == "python2": nixhub_name = "python" url = f"https://www.nixhub.io/packages/{nixhub_name}?_data=routes%2F_nixhub.packages.%24pkg._index" headers = {"Accept": "application/json", "User-Agent": "mcp-nixos/1.0.0"} resp = requests.get(url, headers=headers, timeout=15) if resp.status_code == 404: return error(f"Package '{package_name}' not found in NixHub", "NOT_FOUND") if resp.status_code >= 500: return error("NixHub service temporarily unavailable", "SERVICE_ERROR") resp.raise_for_status() data = resp.json() if not isinstance(data, dict): return error("Invalid response format from NixHub") releases = data.get("releases", []) # Collect all versions seen for release in releases[:limit]: release_version = release.get("version", "") if release_version and release_version not in [v["version"] for v in all_versions]: all_versions.append({"version": release_version, "release": release}) # Check if this is the version we're looking for if release_version == version: found_version = release break if found_version: break except requests.Timeout: return error("Request to NixHub timed out", "TIMEOUT") except requests.RequestException as e: return error(f"Network error accessing NixHub: {str(e)}", "NETWORK_ERROR") except Exception as e: return error(f"Unexpected error: {str(e)}") # Format response if found_version: return _format_nixhub_found_version(package_name, version, found_version) # Version not found - provide helpful information results = [] results.append(f"✗ {package_name} version {version} not found in NixHub\n") # Show available versions if all_versions: results.append(f"Available versions (checked {len(all_versions)} total):") # Sort versions properly using version comparison sorted_versions = sorted(all_versions, key=lambda x: _version_key(x["version"]), reverse=True) # Find newest and oldest newest = sorted_versions[0]["version"] oldest = sorted_versions[-1]["version"] results.append(f"• Newest: {newest}") results.append(f"• Oldest: {oldest}") # Show version range summary major_versions = set() for v in all_versions: parts = v["version"].split(".") if parts: major_versions.add(parts[0]) if major_versions: results.append(f"• Major versions available: {', '.join(sorted(major_versions, reverse=True))}") # Check if requested version is older than available try: requested_parts = version.split(".") oldest_parts = oldest.split(".") if len(requested_parts) >= 2 and len(oldest_parts) >= 2: req_major = int(requested_parts[0]) req_minor = int(requested_parts[1]) old_major = int(oldest_parts[0]) old_minor = int(oldest_parts[1]) if req_major < old_major or (req_major == old_major and req_minor < old_minor): results.append(f"\nVersion {version} is older than the oldest available ({oldest})") results.append("This version may have been removed after reaching end-of-life.") except (ValueError, IndexError): pass results.append("\nAlternatives:") results.append("• Use a newer version if possible") results.append("• Build from source with a custom derivation") results.append("• Use Docker/containers with the specific version") results.append("• Find an old nixpkgs commit from before the version was removed") return "\n".join(results)
- mcp_nixos/server.py:1697-1697 (registration)FastMCP tool registration decorator for nixhub_find_version.async def nixhub_find_version(package_name: str, version: str) -> str:
- mcp_nixos/server.py:1492-1534 (helper)Helper function to format successful version found response with dates, platforms, commit hashes, and usage instructions.def _format_nixhub_found_version(package_name: str, version: str, found_version: dict[str, Any]) -> str: """Format a found version for display.""" results = [] results.append(f"✓ Found {package_name} version {version}\n") last_updated = found_version.get("last_updated", "") if last_updated: try: from datetime import datetime dt = datetime.fromisoformat(last_updated.replace("Z", "+00:00")) formatted_date = dt.strftime("%Y-%m-%d %H:%M UTC") results.append(f"Last updated: {formatted_date}") except Exception: results.append(f"Last updated: {last_updated}") platforms_summary = found_version.get("platforms_summary", "") if platforms_summary: results.append(f"Platforms: {platforms_summary}") # Show commit hashes platforms = found_version.get("platforms", []) if platforms: results.append("\nNixpkgs commits:") seen_commits = set() for platform in platforms: attr_path = platform.get("attribute_path", "") commit_hash = platform.get("commit_hash", "") if commit_hash and commit_hash not in seen_commits: seen_commits.add(commit_hash) if re.match(r"^[a-fA-F0-9]{40}$", commit_hash): results.append(f"• {commit_hash}") if attr_path: results.append(f" Attribute: {attr_path}") results.append("\nTo use this version:") results.append("1. Pin nixpkgs to one of the commit hashes above") results.append("2. Install using the attribute path") return "\n".join(results)
- mcp_nixos/server.py:1466-1490 (helper)Helper to parse and compare version strings numerically for sorting available versions.def _version_key(version_str: str) -> tuple[int, int, int]: """Convert version string to tuple for proper sorting.""" try: parts = version_str.split(".") # Handle versions like "3.9.9" or "3.10.0-rc1" numeric_parts = [] for part in parts[:3]: # Major.Minor.Patch # Extract numeric part numeric = "" for char in part: if char.isdigit(): numeric += char else: break if numeric: numeric_parts.append(int(numeric)) else: numeric_parts.append(0) # Pad with zeros if needed while len(numeric_parts) < 3: numeric_parts.append(0) return (numeric_parts[0], numeric_parts[1], numeric_parts[2]) except Exception: return (0, 0, 0)
- mcp_nixos/server.py:1697-1707 (schema)Input schema: package_name (str, required), version (str, required). Returns str with formatted results or error.async def nixhub_find_version(package_name: str, version: str) -> str: """Find a specific version of a package in NixHub with smart search. Automatically searches with increasing limits to find the requested version. Args: package_name: Name of the package to query (e.g., "ruby", "python") version: Specific version to find (e.g., "2.6.7", "3.5.9") Returns: Plain text with version info and commit hash if found, or helpful message if not