Skip to main content
Glama

install_package_secure

Destructive

Install packages with automated security validation. Checks official repos first, then evaluates AUR package trust and PKGBUILD to block unsafe installations.

Instructions

[LIFECYCLE] Install a package with comprehensive security checks. Workflow: 1. Check official repos first (safer) 2. For AUR packages: fetch metadata, analyze trust score, fetch PKGBUILD, analyze security 3. Block installation if critical security issues found 4. Check for AUR helper (paru > yay) 5. Install with --noconfirm if all checks pass. Only works on Arch Linux. Requires sudo access and paru/yay for AUR packages.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
package_nameYesName of package to install (checks official repos first, then AUR)

Implementation Reference

  • The main handler function for install_package_secure. Implements the full secure installation workflow: verifies sudo, checks official repos first, fetches AUR metadata and PKGBUILD, runs security analysis, checks AUR helper (paru/yay), and installs with --noconfirm if all checks pass.
    async def install_package_secure(package_name: str) -> Dict[str, Any]:
        """
        Install a package with comprehensive security checks.
        
        Workflow:
        1. Check if package exists in official repos first (safer)
        2. For AUR packages:
           a. Fetch package metadata and analyze trust
           b. Fetch and analyze PKGBUILD for security issues
           c. Only proceed if security checks pass
        3. Check for AUR helper availability (paru > yay)
        4. Install with --noconfirm if all checks pass
        
        Args:
            package_name: Package name to install
        
        Returns:
            Dict with installation status and security analysis
        """
        logger.info(f"Starting secure installation workflow for: {package_name}")
        
        # Only supported on Arch Linux
        if not IS_ARCH:
            return create_error_response(
                "NotSupported",
                "Package installation is only supported on Arch Linux systems",
                "This server is not running on Arch Linux"
            )
        
        result = {
            "package": package_name,
            "installed": False,
            "security_checks": {},
            "messages": []
        }
        
        # ========================================================================
        # STEP 0: Verify sudo is configured properly
        # ========================================================================
        logger.info("[STEP 0/5] Verifying sudo configuration...")
        
        # Test if sudo password is cached or passwordless sudo is configured
        # Use skip_sudo_check=True to avoid recursive check
        test_exit_code, _, test_stderr = await run_command(
            ["sudo", "-n", "true"],
            timeout=5,
            check=False,
            skip_sudo_check=True
        )
        
        if test_exit_code != 0:
            result["messages"].append("⚠️  SUDO PASSWORD REQUIRED")
            result["messages"].append("")
            result["messages"].append("Package installation requires sudo privileges.")
            result["messages"].append("Please choose one of these options:")
            result["messages"].append("")
            result["messages"].append("Option 1: Configure passwordless sudo for pacman:")
            result["messages"].append("  sudo visudo -f /etc/sudoers.d/arch-package-install")
            result["messages"].append("  Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
            result["messages"].append("")
            result["messages"].append("Option 2: Cache sudo password temporarily:")
            result["messages"].append("  Run: sudo -v")
            result["messages"].append("  Then retry the installation")
            result["messages"].append("")
            result["messages"].append("Option 3: Install manually in terminal:")
            result["messages"].append(f"  sudo pacman -S {package_name}")
            result["security_checks"]["decision"] = "SUDO_REQUIRED"
            return result
        
        result["messages"].append("✅ Sudo privileges verified")
        
        # ========================================================================
        # STEP 1: Check if package is in official repos first
        # ========================================================================
        logger.info(f"[STEP 1/5] Checking if '{package_name}' is in official repos...")
        result["messages"].append("🔍 Checking official repositories first...")
        
        from .pacman import get_official_package_info
        official_pkg = await get_official_package_info(package_name)
        
        # If found in official repos, install directly with pacman
        if not official_pkg.get("error"):
            logger.info(f"Package '{package_name}' found in official repos - installing via pacman")
            result["messages"].append(f"✅ Package found in official repository: {official_pkg.get('repository', 'unknown')}")
            result["is_official"] = True
            result["security_checks"]["source"] = "official_repository"
            result["security_checks"]["risk_level"] = "LOW"
            result["security_checks"]["recommendation"] = "✅ SAFE - Official repository package"
            
            # Install using sudo pacman -S --noconfirm
            try:
                result["messages"].append("📦 Installing from official repository...")
                exit_code, stdout, stderr = await run_command(
                    ["sudo", "pacman", "-S", "--noconfirm", package_name],
                    timeout=300,  # 5 minutes for installation
                    check=False
                )
                
                if exit_code == 0:
                    result["installed"] = True
                    result["messages"].append(f"✅ Successfully installed {package_name} from official repository")
                    logger.info(f"Successfully installed official package: {package_name}")
                else:
                    result["messages"].append(f"❌ Installation failed: {stderr}")
                    logger.error(f"pacman installation failed: {stderr}")
                    
                    # Check for sudo password issues
                    if "password" in stderr.lower() or "sudo" in stderr.lower():
                        result["messages"].append("")
                        result["messages"].append("⚠️  SUDO PASSWORD REQUIRED")
                        result["messages"].append("To enable passwordless installation, run one of these commands:")
                        result["messages"].append("1. For passwordless sudo (less secure):")
                        result["messages"].append("   sudo visudo -f /etc/sudoers.d/arch-package-install")
                        result["messages"].append("   Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
                        result["messages"].append("2. Or run the installation manually in your terminal:")
                        result["messages"].append(f"   sudo pacman -S {package_name}")
                    
                result["install_output"] = stdout
                result["install_errors"] = stderr
                
                return result
                
            except Exception as e:
                logger.error(f"Installation failed: {e}")
                return create_error_response(
                    "InstallError",
                    f"Failed to install official package: {str(e)}"
                )
        
        # ========================================================================
        # STEP 2: Package is in AUR - fetch and analyze metadata
        # ========================================================================
        logger.info(f"[STEP 2/5] Package not in official repos - checking AUR...")
        result["messages"].append("⚠️  Package not in official repos - checking AUR...")
        result["is_official"] = False
        
        # Search AUR for package
        aur_info = await get_aur_info(package_name)
        
        if aur_info.get("error"):
            return create_error_response(
                "NotFound",
                f"Package '{package_name}' not found in official repos or AUR"
            )
        
        # Extract actual package data (may be wrapped in warning)
        pkg_data = aur_info.get("data", aur_info)
        result["messages"].append(f"📦 Found in AUR: {pkg_data.get('name')} v{pkg_data.get('version')}")
        
        # Analyze package metadata for trust
        logger.info(f"[STEP 3/5] Analyzing package metadata for trust indicators...")
        result["messages"].append("🔍 Analyzing package metadata (votes, maintainer, age)...")
        
        metadata_analysis = analyze_package_metadata_risk(pkg_data)
        result["security_checks"]["metadata_analysis"] = metadata_analysis
        result["messages"].append(f"📊 Trust Score: {metadata_analysis['trust_score']}/100")
        result["messages"].append(f"   {metadata_analysis['recommendation']}")
        
        # ========================================================================
        # STEP 3: Fetch and analyze PKGBUILD
        # ========================================================================
        logger.info(f"[STEP 4/5] Fetching and analyzing PKGBUILD for security issues...")
        result["messages"].append("🔍 Fetching PKGBUILD for security analysis...")
        
        try:
            pkgbuild_content = await get_pkgbuild(package_name)
            result["messages"].append(f"✅ PKGBUILD fetched ({len(pkgbuild_content)} bytes)")
            
            # Analyze PKGBUILD for security issues
            result["messages"].append("🛡️  Analyzing PKGBUILD for security threats...")
            pkgbuild_analysis = analyze_pkgbuild_safety(pkgbuild_content)
            result["security_checks"]["pkgbuild_analysis"] = pkgbuild_analysis
            result["messages"].append(f"🛡️  Risk Score: {pkgbuild_analysis['risk_score']}/100")
            result["messages"].append(f"   {pkgbuild_analysis['recommendation']}")
            
            # Log findings
            if pkgbuild_analysis["red_flags"]:
                result["messages"].append(f"   🚨 {len(pkgbuild_analysis['red_flags'])} CRITICAL issues found!")
                for flag in pkgbuild_analysis["red_flags"][:3]:  # Show first 3
                    result["messages"].append(f"      - Line {flag['line']}: {flag['issue']}")
            
            if pkgbuild_analysis["warnings"]:
                result["messages"].append(f"   ⚠️  {len(pkgbuild_analysis['warnings'])} warnings found")
            
            # Check if package is safe to install
            if not pkgbuild_analysis["safe"]:
                result["messages"].append("❌ INSTALLATION BLOCKED - Security analysis failed")
                result["messages"].append("   Package has critical security issues and will NOT be installed")
                result["security_checks"]["decision"] = "BLOCKED"
                result["security_checks"]["reason"] = "Critical security issues detected in PKGBUILD"
                logger.warning(f"Installation blocked for {package_name} due to security issues")
                return result
            
            # Additional check for high-risk warnings
            if len(pkgbuild_analysis["warnings"]) >= 5:
                result["messages"].append("⚠️  HIGH RISK - Multiple suspicious patterns detected")
                result["messages"].append("   Manual review recommended before installation")
                result["security_checks"]["decision"] = "REVIEW_RECOMMENDED"
            
        except ValueError as e:
            logger.error(f"Failed to fetch PKGBUILD: {e}")
            return create_error_response(
                "FetchError",
                f"Failed to fetch PKGBUILD for security analysis: {str(e)}"
            )
        
        # ========================================================================
        # STEP 4: Check for AUR helper
        # ========================================================================
        logger.info(f"[STEP 5/5] Checking for AUR helper (paru/yay)...")
        result["messages"].append("🔧 Checking for AUR helper...")
        
        aur_helper = get_aur_helper()
        
        if not aur_helper:
            result["messages"].append("❌ No AUR helper found (paru or yay)")
            result["messages"].append("   Please install an AUR helper:")
            result["messages"].append("   - Recommended: paru (pacman -S paru)")
            result["messages"].append("   - Alternative: yay")
            result["security_checks"]["decision"] = "NO_HELPER"
            return result
        
        result["messages"].append(f"✅ Using AUR helper: {aur_helper}")
        result["aur_helper"] = aur_helper
        
        # ========================================================================
        # STEP 5: Install package with AUR helper
        # ========================================================================
        result["messages"].append(f"📦 Installing {package_name} via {aur_helper} (no confirmation)...")
        logger.info(f"Installing AUR package {package_name} with {aur_helper}")
        
        try:
            # Install with --noconfirm flag
            exit_code, stdout, stderr = await run_command(
                [aur_helper, "-S", "--noconfirm", package_name],
                timeout=600,  # 10 minutes for AUR package build
                check=False
            )
            
            if exit_code == 0:
                result["installed"] = True
                result["messages"].append(f"✅ Successfully installed {package_name} from AUR")
                result["security_checks"]["decision"] = "INSTALLED"
                logger.info(f"Successfully installed AUR package: {package_name}")
            else:
                result["messages"].append(f"❌ Installation failed with exit code {exit_code}")
                result["messages"].append(f"   Error: {stderr}")
                result["security_checks"]["decision"] = "INSTALL_FAILED"
                logger.error(f"AUR installation failed for {package_name}: {stderr}")
                
                # Check for sudo password issues
                if "password" in stderr.lower() or "sudo" in stderr.lower():
                    result["messages"].append("")
                    result["messages"].append("⚠️  SUDO PASSWORD REQUIRED")
                    result["messages"].append("To enable passwordless installation for AUR packages:")
                    result["messages"].append("1. For passwordless sudo for pacman:")
                    result["messages"].append("   sudo visudo -f /etc/sudoers.d/arch-aur-install")
                    result["messages"].append("   Add: your_username ALL=(ALL) NOPASSWD: /usr/bin/pacman")
                    result["messages"].append("2. Or run the installation manually in your terminal:")
                    result["messages"].append(f"   {aur_helper} -S {package_name}")
            
            result["install_output"] = stdout
            result["install_errors"] = stderr
            
        except Exception as e:
            logger.error(f"Installation failed: {e}")
            result["messages"].append(f"❌ Installation exception: {str(e)}")
            result["security_checks"]["decision"] = "INSTALL_ERROR"
        
        return result
  • Tool registration handler in the MCP server call_tool dispatcher. Routes the 'install_package_secure' tool name to the install_package_secure async function, with a platform check for Arch Linux.
    elif name == "install_package_secure":
        if not IS_ARCH:
            return [TextContent(type="text", text=create_platform_error_message("install_package_secure"))]
        
        package_name = arguments["package_name"]
        result = await install_package_secure(package_name)
        return [TextContent(type="text", text=json.dumps(result, indent=2))]
  • Tool input schema defined in the list_tools function. Defines the Tool object with name 'install_package_secure', description, input schema (requires package_name string), and annotations marking it as destructive.
    Tool(
        name="install_package_secure",
        description="[LIFECYCLE] Install a package with comprehensive security checks. Workflow: 1. Check official repos first (safer) 2. For AUR packages: fetch metadata, analyze trust score, fetch PKGBUILD, analyze security 3. Block installation if critical security issues found 4. Check for AUR helper (paru > yay) 5. Install with --noconfirm if all checks pass. Only works on Arch Linux. Requires sudo access and paru/yay for AUR packages.",
        inputSchema={
            "type": "object",
            "properties": {
                "package_name": {
                    "type": "string",
                    "description": "Name of package to install (checks official repos first, then AUR)"
                }
            },
            "required": ["package_name"]
        },
        annotations=ToolAnnotations(destructiveHint=True)
    ),
  • Import and export of install_package_secure from the aur module, making it available through the package's public API (also listed in __all__ at line 134).
        install_package_secure,
    )
  • Helper function get_aur_helper() used by install_package_secure to detect available AUR helpers (paru preferred over yay).
    def get_aur_helper() -> Optional[str]:
        """
        Detect available AUR helper with priority: paru > yay.
        
        Returns:
            str: Name of available AUR helper ('paru' or 'yay'), or None if neither exists
        """
        # Check in priority order
        if check_command_exists("paru"):
            logger.info("Found AUR helper: paru")
            return "paru"
        elif check_command_exists("yay"):
            logger.info("Found AUR helper: yay")
            return "yay"
        else:
            logger.warning("No AUR helper found (paru or yay)")
            return None
  • Tool metadata definition for install_package_secure, specifying category='lifecycle', platform='arch', permission='write', workflow='installation', and listing related and prerequisite tools.
    "install_package_secure": ToolMetadata(
        name="install_package_secure",
        category="lifecycle",
        platform="arch",
        permission="write",
        workflow="installation",
        related_tools=[
            "check_updates_dry_run",
            "verify_package_integrity",
            "query_package_history"
        ],
        prerequisite_tools=[
            "get_official_package_info",
            "audit_package_security"
        ]
    ),
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructiveHint annotation, the description details the 5-step security workflow, including blocking installation on critical issues, requiring sudo and specific helpers, and checking repos in order. This goes well beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a numbered workflow and front-loaded purpose. While it is somewhat lengthy, every sentence adds value and the structure aids readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one parameter and no output schema, the description covers prerequisites, workflow, security checks, and limitations comprehensively. No additional information seems necessary for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already describes the package_name parameter with 100% coverage. The description adds context: 'checks official repos first, then AUR', which clarifies the search order and adds meaning beyond the schema definition.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with a clear verb and resource: 'Install a package with comprehensive security checks.' It distinguishes from sibling tools like remove_packages and search_aur by emphasizing the security workflow.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description specifies the tool's limitations ('Only works on Arch Linux', 'Requires sudo access and paru/yay for AUR packages') and outlines the workflow steps. However, it does not explicitly state when to use this tool over alternatives like audit_package_security or verify_package_integrity, which could provide clearer guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/nihalxkumar/arch-linux-mcp'

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