Skip to main content
Glama

nixhub_find_version

Find specific package versions in NixHub by searching with increasing limits. Input package name and version to get version info and commit hash.

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

TableJSON Schema
NameRequiredDescriptionDefault
package_nameYes
versionYes

Implementation Reference

  • The main execution logic for the 'nixhub_find_version' tool. This async function queries NixHub API to find specific package versions, handles errors, formats responses with commit hashes and installation instructions.
    @mcp.tool()
    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)
  • Input schema defined by function parameters (package_name: str, version: str) and output str as plain text response. Includes detailed docstring with examples.
    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
  • Tool registration via FastMCP @mcp.tool() decorator, which registers the function under the name 'nixhub_find_version'.
    @mcp.tool()
  • Helper function used by nixhub_find_version to format successful version findings including 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)
  • Utility function to parse and compare version strings for sorting available versions when target not found.
    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)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/utensils/mcp-nixos'

If you have feedback or need assistance with the MCP directory API, please join our Discord server