Skip to main content
Glama
StevenGeller

LDK MCP Server

by StevenGeller

ldk_pay_invoice

Pay Lightning invoices to test payment flows, with optional maximum fee settings for development validation.

Instructions

Test payment flows by paying a Lightning invoice

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
invoiceYesBOLT11 Lightning invoice to pay
maxFeeSatsNoMaximum fee in satoshis willing to pay

Implementation Reference

  • The main handler function for the 'ldk_pay_invoice' tool. It calls LightningService.payInvoice with the provided invoice and returns formatted results including payment details and example Swift code for iOS integration.
      execute: async (args: any): Promise<ToolResult> => {
        try {
          const payment = await lightningService.payInvoice(args.invoice);
          
          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                success: true,
                payment: {
                  paymentHash: payment.paymentHash,
                  paymentPreimage: payment.paymentPreimage,
                  amountSats: Math.floor(payment.amountMsat / 1000),
                  feeSats: Math.floor((payment.feeMsat || 0) / 1000),
                  status: payment.status,
                  timestamp: payment.timestamp
                },
                swiftExample: `
    // Swift code to handle payment in your iOS app
    import LightningDevKit
    
    func payInvoice(invoice: String, maxFeeSats: UInt64) async throws -> PaymentResult {
        let parsedInvoice = Bolt11Invoice.fromStr(s: invoice)
        
        guard let invoiceVal = parsedInvoice.getValue() else {
            throw PaymentError.invalidInvoice
        }
        
        let invoicePaymentResult = Bindings.paymentParametersFromInvoice(invoice: invoiceVal)
        guard invoicePaymentResult.isOk() else {
            throw PaymentError.invalidPaymentParams
        }
        
        let (paymentHash, recipientOnion, routeParams) = invoicePaymentResult.getValue()!
        let paymentId = invoiceVal.paymentHash()!
        
        // Set max fee
        routeParams.setMaxTotalFeeMsat(val: maxFeeSats * 1000)
        
        let res = channelManager.sendPayment(
            paymentHash: paymentHash,
            recipientOnion: recipientOnion,
            paymentId: paymentId,
            routeParams: routeParams,
            retryStrategy: .initWithTimeout(a: 15)
        )
        
        if res.isOk() {
            // Payment initiated successfully
            return PaymentResult(
                paymentHash: paymentHash.toHex(),
                status: .pending
            )
        } else {
            throw PaymentError.sendFailed(res.getError()!)
        }
    }
    
    // Handle payment events
    func handlePaymentEvent(event: Event) {
        if let paymentSent = event.getValueAsPaymentSent() {
            let paymentHash = paymentSent.getPaymentHash().toHex()
            let feePaidMsat = paymentSent.getFeePaidMsat()?.getValue() ?? 0
            
            print("Payment sent successfully!")
            print("Payment hash: \\(paymentHash)")
            print("Fee paid: \\(feePaidMsat / 1000) sats")
            
            // Update UI
            DispatchQueue.main.async {
                self.updatePaymentStatus(hash: paymentHash, status: .succeeded)
            }
        } else if let paymentFailed = event.getValueAsPaymentFailed() {
            let paymentHash = paymentFailed.getPaymentHash().toHex()
            let reason = paymentFailed.getReason()
            
            print("Payment failed: \\(reason?.description ?? "Unknown")")
            
            // Update UI
            DispatchQueue.main.async {
                self.updatePaymentStatus(hash: paymentHash, status: .failed)
            }
        }
    }`.trim()
              }, null, 2)
            }]
          };
        } catch (error) {
          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                success: false,
                error: error instanceof Error ? error.message : 'Unknown error'
              }, null, 2)
            }],
            isError: true
          };
        }
      }
  • Input schema defining the parameters for paying a Lightning invoice: required 'invoice' string and optional 'maxFeeSats' number.
    inputSchema: {
      type: 'object',
      properties: {
        invoice: {
          type: 'string',
          description: 'BOLT11 Lightning invoice to pay'
        },
        maxFeeSats: {
          type: 'number',
          description: 'Maximum fee in satoshis willing to pay',
          default: 10
        }
      },
      required: ['invoice']
    },
  • src/index.ts:38-62 (registration)
    Registration of the payInvoiceTool (line 40) in the central tools array used by the MCP server's ListTools and CallTool request handlers.
    const tools = [
      generateInvoiceTool,
      payInvoiceTool,
      getChannelStatusTool,
      getNodeInfoTool,
      backupStateTool,
      keychainTestTool,
      backgroundTestTool,
      pushNotificationTool,
      biometricAuthTool,
      createChannelTool,
      closeChannelTool,
      getBalanceTool,
      decodeInvoiceTool,
      listPaymentsTool,
      estimateFeeTool,
      generateMnemonicTool,
      deriveAddressTool,
      getSwiftCodeTool,
      getArchitectureTool,
      testScenarioTool,
      networkGraphTool,
      eventHandlingTool,
      chainSyncTool,
    ];
  • Helper method in LightningService that implements the core payment logic: decodes the BOLT11 invoice, simulates successful payment, calculates fee, generates preimage, and stores the payment.
    async payInvoice(bolt11Invoice: string): Promise<Payment> {
      try {
        const decoded = bolt11.decode(bolt11Invoice);
        const paymentHash = decoded.tags.find(t => t.tagName === 'payment_hash')?.data as string;
        const amountMsat = parseInt(decoded.millisatoshis || '0');
        const description = decoded.tags.find(t => t.tagName === 'description')?.data as string;
    
        const payment: Payment = {
          paymentHash,
          amountMsat,
          status: PaymentStatus.Succeeded,
          timestamp: Date.now(),
          description,
          bolt11: bolt11Invoice,
          feeMsat: Math.floor(amountMsat * 0.001), // 0.1% fee
          paymentPreimage: crypto.randomBytes(32).toString('hex')
        };
    
        this.payments.set(paymentHash, payment);
        return payment;
      } catch (error) {
        throw new Error(`Failed to pay invoice: ${error}`);
      }
    }
  • src/index.ts:14-14 (registration)
    Import statement that brings the payInvoiceTool into the main index for registration.
    import { payInvoiceTool } from './tools/payInvoice.js';

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/StevenGeller/ldk-mcp'

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