Skip to main content
Glama

home_manager_list_options

Enumerate Home Manager configuration option categories with their counts to help users discover available settings for customizing their NixOS environment.

Instructions

List all Home Manager option categories.

Enumerates all top-level categories with their option counts.

Returns: Plain text list of categories sorted alphabetically with option counts

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The core handler function for the 'home_manager_list_options' MCP tool. Registered via @mcp.tool() decorator. Parses Home Manager documentation HTML to extract and count top-level option categories, returning a sorted plain-text list.
    @mcp.tool()
    async def home_manager_list_options() -> str:
        """List all Home Manager option categories.
    
        Enumerates all top-level categories with their option counts.
    
        Returns:
            Plain text list of categories sorted alphabetically with option counts
        """
        try:
            # Get more options to see all categories (default 100 is too few)
            options = parse_html_options(HOME_MANAGER_URL, limit=5000)
            categories: dict[str, int] = {}
    
            for opt in options:
                name = opt["name"]
                # Process option names
                if name and not name.startswith("."):
                    if "." in name:
                        cat = name.split(".")[0]
                    else:
                        cat = name  # Option without dot is its own category
                    # Valid categories should:
                    # - Be more than 1 character
                    # - Be a valid identifier (allows underscores)
                    # - Not be common value words
                    # - Match typical nix option category patterns
                    if (
                        len(cat) > 1 and cat.isidentifier() and (cat.islower() or cat.startswith("_"))
                    ):  # This ensures valid identifier
                        # Additional filtering for known valid categories
                        valid_categories = {
                            "accounts",
                            "dconf",
                            "editorconfig",
                            "fonts",
                            "gtk",
                            "home",
                            "i18n",
                            "launchd",
                            "lib",
                            "manual",
                            "news",
                            "nix",
                            "nixgl",
                            "nixpkgs",
                            "pam",
                            "programs",
                            "qt",
                            "services",
                            "specialisation",
                            "systemd",
                            "targets",
                            "wayland",
                            "xdg",
                            "xresources",
                            "xsession",
                        }
                        # Only include if it's in the known valid list or looks like a typical category
                        if cat in valid_categories or (len(cat) >= 3 and not any(char.isdigit() for char in cat)):
                            categories[cat] = categories.get(cat, 0) + 1
    
            results = []
            results.append(f"Home Manager option categories ({len(categories)} total):\n")
    
            # Sort by count descending, then alphabetically
            sorted_cats = sorted(categories.items(), key=lambda x: (-x[1], x[0]))
    
            for cat, count in sorted_cats:
                results.append(f"• {cat} ({count} options)")
    
            return "\n".join(results)
    
        except Exception as e:
            return error(str(e))
  • Key helper function used by home_manager_list_options (and other *_manager tools) to parse HTML documentation pages from Home Manager/nix-darwin sites, extracting option names, descriptions, and types.
    def parse_html_options(url: str, query: str = "", prefix: str = "", limit: int = 100) -> list[dict[str, str]]:
        """Parse options from HTML documentation."""
        try:
            resp = requests.get(url, timeout=30)  # Increase timeout for large docs
            resp.raise_for_status()
            # Use resp.content to let BeautifulSoup handle encoding detection
            # This prevents encoding errors like "unknown encoding: windows-1252"
            soup = BeautifulSoup(resp.content, "html.parser")
            options = []
    
            # Get all dt elements
            dts = soup.find_all("dt")
    
            for dt in dts:
                # Get option name
                name = ""
                if "home-manager" in url:
                    # Home Manager uses anchor IDs like "opt-programs.git.enable"
                    anchor = dt.find("a", id=True)
                    if anchor:
                        anchor_id = anchor.get("id", "")
                        # Remove "opt-" prefix and convert underscores
                        if anchor_id.startswith("opt-"):
                            name = anchor_id[4:]  # Remove "opt-" prefix
                            # Convert _name_ placeholders back to <name>
                            name = name.replace("_name_", "<name>")
                    else:
                        # Fallback to text content
                        name_elem = dt.find(string=True, recursive=False)
                        if name_elem:
                            name = name_elem.strip()
                        else:
                            name = dt.get_text(strip=True)
                else:
                    # Darwin and fallback - use text content
                    name = dt.get_text(strip=True)
    
                # Skip if it doesn't look like an option (must contain a dot)
                # But allow single-word options in some cases
                if "." not in name and len(name.split()) > 1:
                    continue
    
                # Filter by query or prefix
                if query and query.lower() not in name.lower():
                    continue
                if prefix and not (name.startswith(prefix + ".") or name == prefix):
                    continue
    
                # Find the corresponding dd element
                dd = dt.find_next_sibling("dd")
                if dd:
                    # Extract description (first p tag or direct text)
                    desc_elem = dd.find("p")
                    if desc_elem:
                        description = desc_elem.get_text(strip=True)
                    else:
                        # Get first text node, handle None case
                        text = dd.get_text(strip=True)
                        description = text.split("\n")[0] if text else ""
    
                    # Extract type info - look for various patterns
                    type_info = ""
                    # Pattern 1: <span class="term">Type: ...</span>
                    type_elem = dd.find("span", class_="term")
                    if type_elem and "Type:" in type_elem.get_text():
                        type_info = type_elem.get_text(strip=True).replace("Type:", "").strip()
                    # Pattern 2: Look for "Type:" in text
                    elif "Type:" in dd.get_text():
                        text = dd.get_text()
                        type_start = text.find("Type:") + 5
                        type_end = text.find("\n", type_start)
                        if type_end == -1:
                            type_end = len(text)
                        type_info = text[type_start:type_end].strip()
    
                    options.append(
                        {
                            "name": name,
                            "description": description[:200] if len(description) > 200 else description,
                            "type": type_info,
                        }
                    )
    
                    if len(options) >= limit:
                        break
    
            return options
        except Exception as exc:
            raise DocumentParseError(f"Failed to fetch docs: {str(exc)}") from exc

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