Skip to main content
Glama

home_manager_stats

Retrieve detailed statistics on Home Manager options, including total options, category counts, and top categories, to streamline system configuration and decision-making.

Instructions

Get statistics about Home Manager options.

Retrieves overall statistics including total options, categories, and top categories.

Returns: Plain text summary with total options, category count, and top 5 categories

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The handler function for the 'home_manager_stats' tool. It parses the Home Manager HTML documentation, counts options by category and type, and returns formatted statistics as plain text.
    async def home_manager_stats() -> str: """Get statistics about Home Manager options. Retrieves overall statistics including total options, categories, and top categories. Returns: Plain text summary with total options, category count, and top 5 categories """ try: # Parse all options to get statistics options = parse_html_options(HOME_MANAGER_URL, limit=5000) if not options: return error("Failed to fetch Home Manager statistics") # Count categories categories: dict[str, int] = {} for opt in options: cat = opt["name"].split(".")[0] categories[cat] = categories.get(cat, 0) + 1 # Count types types: dict[str, int] = {} for opt in options: opt_type = opt.get("type", "unknown") if opt_type: # Simplify complex types if "null or" in opt_type: opt_type = "nullable" elif "list of" in opt_type: opt_type = "list" elif "attribute set" in opt_type: opt_type = "attribute set" types[opt_type] = types.get(opt_type, 0) + 1 # Build statistics return f"""Home Manager Statistics: • Total options: {len(options):,} • Categories: {len(categories)} • Top categories: - programs: {categories.get("programs", 0):,} options - services: {categories.get("services", 0):,} options - home: {categories.get("home", 0):,} options - wayland: {categories.get("wayland", 0):,} options - xsession: {categories.get("xsession", 0):,} options""" except Exception as e: return error(str(e))
  • Helper function parse_html_options that fetches and parses HTML documentation to extract option names, descriptions, and types. Critical for home_manager_stats data source.
    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
  • Utility function to format error messages, used in home_manager_stats exception handling.
    def error(msg: str, code: str = "ERROR") -> str: """Format error as plain text.""" # Ensure msg is always a string, even if empty msg = str(msg) if msg is not None else "" return f"Error ({code}): {msg}"
  • The @mcp.tool() decorator registers the home_manager_stats function as an MCP tool.
    @mcp.tool() async def home_manager_stats() -> str:

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