Skip to main content
Glama

access_l402_resource

Fetch URLs with automatic Bitcoin Lightning payments when encountering 402 Payment Required responses, enabling AI agents to access paid resources autonomously.

Instructions

Fetch a URL with automatic L402 payment handling. If the server returns a 402 Payment Required response, the invoice will be automatically paid and the request retried.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYesThe URL to fetch
methodNoHTTP method (GET, POST, PUT, DELETE)GET
headersNoOptional additional request headers
bodyNoOptional request body for POST/PUT requests
max_satsNoMaximum satoshis to pay for this request
confirmedNoSet to true to confirm a payment that requires approval. Use when previous call returned requiresConfirmation=true.

Implementation Reference

  • The core implementation of the access_l402_resource tool, which handles HTTP requests with integrated L402 payment and budget checking.
    async def access_l402_resource(
        url: str,
        method: str = "GET",
        headers: dict[str, str] | None = None,
        body: str | None = None,
        max_sats: int = 1000,
        confirmed: bool = False,
        l402_client: "L402Client | None" = None,
        budget_manager: "BudgetManager | None" = None,
        budget_service: "BudgetService | None" = None,
    ) -> str:
        """
        Fetch a URL with automatic L402 payment handling.
    
        If the server returns a 402 Payment Required response with an L402 challenge,
        this function will automatically pay the invoice and retry the request.
    
        NOTE: Most MCP clients (including Claude Code) don't support elicitation yet.
        L402 payments above auto_approve threshold require explicit confirmation
        by calling this tool again with confirmed=True.
    
        Args:
            url: The URL to fetch
            method: HTTP method (GET, POST, PUT, DELETE)
            headers: Optional additional request headers
            body: Optional request body for POST/PUT requests
            max_sats: Maximum satoshis to pay for this request
            confirmed: Set to True to confirm a payment above the auto-approve threshold
            l402_client: L402 client instance
            budget_manager: Legacy budget manager (deprecated, use budget_service)
            budget_service: BudgetService for multi-tier approval logic
    
        Returns:
            Response body text or error message
        """
        if not l402_client:
            return "Error: L402 client not initialized. Check NWC connection."
    
        headers = headers or {}
        method = method.upper()
    
        # Validate method
        if method not in ("GET", "POST", "PUT", "DELETE"):
            return f"Error: Invalid HTTP method: {method}"
    
        try:
            # Use new BudgetService if available, otherwise fall back to legacy BudgetManager
            if budget_service:
                # Check approval level using new multi-tier system
                result = await budget_service.check_approval_level(max_sats)
    
                if result.level == ApprovalLevel.DENY:
                    return json.dumps({
                        "success": False,
                        "error": "Payment denied by budget policy",
                        "denialReason": result.denial_reason,
                        "url": url,
                        "budget": {
                            "maxSats": max_sats,
                            "maxUsd": float(result.amount_usd),
                            "remainingSessionUsd": float(result.remaining_session_budget_usd),
                        },
                        "note": "Edit ~/.lightning-enable/config.json to change limits."
                    })
    
                # Check if payment requires confirmation (FORM_CONFIRM or URL_CONFIRM)
                if result.requires_confirmation and not confirmed:
                    url_display = url[:50] + "..." if len(url) > 50 else url
                    return json.dumps({
                        "success": False,
                        "requiresConfirmation": True,
                        "approvalLevel": result.level.value,
                        "error": "L402 payment requires your confirmation",
                        "message": f"This L402 request to {url_display} may cost up to ${result.amount_usd:.2f} ({max_sats:,} sats). "
                                  "To proceed, call access_l402_resource again with confirmed=True.",
                        "howToConfirm": 'Call: access_l402_resource(url="...", confirmed=True)',
                        "amount": {
                            "maxSats": max_sats,
                            "maxUsd": float(result.amount_usd)
                        },
                        "budget": {
                            "remainingSessionUsd": float(result.remaining_session_budget_usd),
                        }
                    })
    
                # LOG_AND_APPROVE: Log for user awareness but proceed
                if result.level == ApprovalLevel.LOG_AND_APPROVE:
                    logger.info(f"Log-and-approve L402 request: up to {max_sats} sats (${result.amount_usd:.2f}) for {url[:50]}...")
    
            elif budget_manager:
                # Legacy budget manager fallback
                status = budget_manager.get_status()
                if status["remaining"] <= 0:
                    return json.dumps({
                        "success": False,
                        "error": "Session budget exhausted",
                        "message": f"Spent {status['spent']}/{status['limits']['per_session']} sats. "
                                  "Use configure_budget to increase limit.",
                        "budget": status
                    })
    
                # Check if payment requires confirmation (above auto_approve threshold)
                auto_approve_sats = getattr(budget_manager, 'auto_approve_sats', 1000)
                if max_sats > auto_approve_sats and not confirmed:
                    # Estimate USD value (~$0.001 per sat at ~$100k/BTC)
                    estimated_usd = max_sats * 0.001
                    url_display = url[:50] + "..." if len(url) > 50 else url
                    return json.dumps({
                        "success": False,
                        "requiresConfirmation": True,
                        "error": "L402 payment requires your confirmation",
                        "message": f"This L402 request to {url_display} may cost up to ~${estimated_usd:.2f} ({max_sats:,} sats), "
                                  f"which exceeds the auto-approve threshold of {auto_approve_sats:,} sats. "
                                  "To proceed, call access_l402_resource again with confirmed=True.",
                        "howToConfirm": 'Call: access_l402_resource(url="...", confirmed=True)',
                        "amount": {
                            "maxSats": max_sats,
                            "estimatedUsd": round(estimated_usd, 2)
                        },
                        "thresholds": {
                            "autoApprove": auto_approve_sats,
                            "note": "Payments above this require confirmation"
                        }
                    })
    
            # Make request with L402 handling
            response_text, amount_paid = await l402_client.fetch(
                url=url,
                method=method,
                headers=headers,
                body=body,
                max_sats=max_sats,
            )
    
            # Record payment if one was made
            if amount_paid is not None:
                if budget_service:
                    budget_service.record_spend(amount_paid)
                    budget_service.record_payment_time()
                    logger.info(f"Paid {amount_paid} sats for L402 access to {url}")
    
                    # Get updated session info
                    status = budget_service.get_status()
                    session_info = {
                        "spentSats": status["session"]["spentSats"],
                        "spentUsd": status["session"]["spentUsd"],
                        "remainingUsd": status["session"]["remainingUsd"],
                        "requestCount": status["session"]["requestCount"],
                    }
                elif budget_manager:
                    budget_manager.record_payment(
                        url=url,
                        amount_sats=amount_paid,
                        invoice="(auto-paid)",
                        preimage="(auto-paid)",
                        status="success",
                    )
                    logger.info(f"Paid {amount_paid} sats for L402 access to {url}")
                    session_info = {
                        "spentSats": budget_manager.session_spent,
                        "remainingSats": budget_manager.max_per_session - budget_manager.session_spent,
                    }
                else:
                    session_info = None
            else:
                session_info = None
    
            # Format response
            result = {
                "success": True,
                "url": url,
                "method": method,
                "paid_sats": amount_paid,
                "response": response_text[:5000] if len(response_text) > 5000 else response_text,
            }
    
            if amount_paid:
                result["message"] = f"Paid {amount_paid} sats for access"
    
            if session_info:
                result["session"] = session_info
    
            return json.dumps(result, indent=2)
    
        except Exception as e:
            logger.exception(f"Error accessing {url}")
    
            error_result = {
                "success": False,
                "url": url,
                "method": method,
                "error": sanitize_error(str(e)),
            }
    
            return json.dumps(error_result, indent=2)
Behavior3/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It explains the automatic payment and retry behavior, which is valuable, but lacks details on error handling, rate limits, authentication needs, or what happens if payment fails. It adequately describes the core behavior but misses additional operational context.

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

Conciseness5/5

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

The description is two sentences, front-loaded with the main purpose and followed by key behavioral detail. Every sentence earns its place by explaining the tool's unique functionality without redundancy or unnecessary elaboration.

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

Completeness3/5

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

Given the complexity (6 parameters, no output schema, no annotations), the description covers the core purpose and payment retry behavior but lacks details on return values, error cases, or integration with sibling tools. It is minimally adequate but has clear gaps for a tool with this level of complexity.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description does not add any parameter-specific semantics beyond what the schema provides, such as examples or usage nuances, meeting the baseline for high schema coverage.

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 clearly states the tool's purpose with a specific verb ('fetch') and resource ('URL'), and distinguishes it from siblings by mentioning 'automatic L402 payment handling' and '402 Payment Required response' retry logic, which no other sibling tool describes.

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 provides clear context for when to use this tool ('fetch a URL with automatic L402 payment handling'), but it does not explicitly mention when not to use it or name alternatives among siblings, such as simpler fetch tools or manual payment handling tools.

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/refined-element/lightning-enable-mcp'

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