Skip to main content
Glama
StevenGeller

LDK MCP Server

by StevenGeller

ldk_network_graph

Perform Lightning network graph operations including RapidGossipSync, route queries, and node/channel information retrieval for LDK-based wallet development.

Instructions

Get network graph operations and RapidGossipSync implementation

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
operationYesNetwork graph operation to perform
nodeIdNoNode ID for node_info operation (optional)
channelIdNoChannel ID for channel_info operation (optional)

Implementation Reference

  • The main handler function for the 'ldk_network_graph' tool. It selects and returns comprehensive Swift code examples for LDK NetworkGraph and RapidGossipSync operations (setup, sync, route finding, node/channel queries, update handling) based on the input 'operation' parameter.
      execute: async (args: any): Promise<ToolResult> => {
        const swiftExamples: Record<string, string> = {
          setup: `
    // Network Graph and RapidGossipSync setup in Swift
    import LightningDevKit
    
    class NetworkGraphManager {
        private let networkGraph: Bindings.NetworkGraph
        private let rapidGossipSync: Bindings.RapidGossipSync
        private let logger: Bindings.Logger
        private let network: Bindings.Network
        private var lastSyncTimestamp: UInt64 = 0
        
        init(network: Bindings.Network, logger: Bindings.Logger) {
            self.network = network
            self.logger = logger
            
            // Initialize or load network graph
            if let savedGraph = loadNetworkGraphFromDisk() {
                let readResult = Bindings.NetworkGraph.read(ser: savedGraph, arg: logger)
                if readResult.isOk() {
                    self.networkGraph = readResult.getValue()!
                } else {
                    self.networkGraph = Bindings.NetworkGraph(network: network, logger: logger)
                }
            } else {
                self.networkGraph = Bindings.NetworkGraph(network: network, logger: logger)
            }
            
            // Initialize RapidGossipSync
            self.rapidGossipSync = Bindings.RapidGossipSync(networkGraph: networkGraph, logger: logger)
            
            // Load last sync timestamp
            self.lastSyncTimestamp = UserDefaults.standard.object(forKey: "lastGossipSync") as? UInt64 ?? 0
        }
        
        // Persist network graph to disk
        func saveNetworkGraph() throws {
            let serialized = networkGraph.write()
            let documentsPath = FileManager.default.urls(
                for: .documentDirectory,
                in: .userDomainMask
            ).first!
            let graphPath = documentsPath.appendingPathComponent("network_graph.bin")
            
            try Data(serialized).write(to: graphPath)
        }
        
        private func loadNetworkGraphFromDisk() -> [UInt8]? {
            let documentsPath = FileManager.default.urls(
                for: .documentDirectory,
                in: .userDomainMask
            ).first!
            let graphPath = documentsPath.appendingPathComponent("network_graph.bin")
            
            guard let data = try? Data(contentsOf: graphPath) else {
                return nil
            }
            
            return [UInt8](data)
        }
    }`.trim(),
    
          rapid_gossip_sync: `
    // RapidGossipSync implementation for efficient network updates
    import LightningDevKit
    
    extension NetworkGraphManager {
        // Sync network graph using RapidGossipSync
        func syncNetworkGraph() async throws {
            let currentTime = UInt64(Date().timeIntervalSince1970)
            
            // Build RGS URL with last sync timestamp
            let baseUrl = network == .bitcoin ? 
                "https://rapidsync.lightningdevkit.org/snapshot" :
                "https://rapidsync.lightningdevkit.org/testnet/snapshot"
            
            let url = URL(string: "\\(baseUrl)/\\(lastSyncTimestamp)")!
            
            // Download snapshot
            let (data, response) = try await URLSession.shared.data(from: url)
            
            guard let httpResponse = response as? HTTPURLResponse,
                  httpResponse.statusCode == 200 else {
                throw NetworkError.syncFailed
            }
            
            // Apply update to network graph
            let result = rapidGossipSync.updateNetworkGraphNoStd(
                updateData: [UInt8](data),
                currentTimeUnix: currentTime
            )
            
            if result.isOk() {
                // Update last sync timestamp
                lastSyncTimestamp = currentTime
                UserDefaults.standard.set(lastSyncTimestamp, forKey: "lastGossipSync")
                
                // Persist updated graph
                try saveNetworkGraph()
                
                print("RapidGossipSync successful - updated to timestamp: \\(currentTime)")
            } else if let error = result.getError() {
                throw NetworkError.graphUpdateFailed(error.getValueAsDecodeError()?.getDescription() ?? "Unknown error")
            }
        }
        
        // Schedule automatic sync
        func startAutomaticSync(interval: TimeInterval = 3600) {
            Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in
                Task {
                    do {
                        try await self.syncNetworkGraph()
                    } catch {
                        print("Automatic sync failed: \\(error)")
                    }
                }
            }
            
            // Perform initial sync
            Task {
                try? await syncNetworkGraph()
            }
        }
        
        // Manual incremental update
        func applyGossipUpdate(_ update: [UInt8]) -> Bool {
            let currentTime = UInt64(Date().timeIntervalSince1970)
            let result = rapidGossipSync.updateNetworkGraphNoStd(
                updateData: update,
                currentTimeUnix: currentTime
            )
            
            return result.isOk()
        }
    }
    
    // Background task for iOS
    extension NetworkGraphManager {
        func performBackgroundSync() async -> Bool {
            do {
                try await syncNetworkGraph()
                return true
            } catch {
                print("Background sync failed: \\(error)")
                return false
            }
        }
    }`.trim(),
    
          query_routes: `
    // Query routes using the network graph
    import LightningDevKit
    
    extension NetworkGraphManager {
        // Find routes for a payment
        func findRoute(
            to destination: [UInt8],
            amountMsat: UInt64,
            paymentParams: Bindings.PaymentParameters
        ) -> Result<[RouteInfo], RoutingError> {
            let channelManager = LDKManager.shared.channelManager
            let ourNodeId = channelManager.getOurNodeId()
            let channels = channelManager.listUsableChannels()
            
            // Create route parameters
            let routeParams = Bindings.RouteParameters(
                paymentParamsArg: paymentParams,
                finalValueMsatArg: amountMsat,
                maxTotalRoutingFeeMsatArg: amountMsat / 100 // 1% max fee
            )
            
            // Use router to find route
            let router = LDKManager.shared.router
            let inflightHtlcs = Bindings.InFlightHtlcs()
            
            let routeResult = router.findRoute(
                payer: ourNodeId,
                routeParams: routeParams,
                firstHops: channels,
                inflightHtlcs: inflightHtlcs
            )
            
            if routeResult.isOk(), let route = routeResult.getValue() {
                return .success(analyzeRoute(route))
            } else if let error = routeResult.getError() {
                return .failure(.routingFailed(error.getDescription()))
            }
            
            return .failure(.noRoute)
        }
        
        // Analyze route details
        private func analyzeRoute(_ route: Bindings.Route) -> [RouteInfo] {
            return route.getPaths().map { path in
                let hops = path.getHops().map { hop in
                    HopInfo(
                        pubkey: Data(hop.getPubkey()).hexString,
                        shortChannelId: hop.getShortChannelId(),
                        feeMsat: hop.getFeeMsat(),
                        cltvExpiryDelta: hop.getCltvExpiryDelta()
                    )
                }
                
                let totalFees = path.getFeeMsat()
                let finalValueMsat = path.getFinalValueMsat()
                
                return RouteInfo(
                    hops: hops,
                    totalFeeMsat: totalFees,
                    totalAmountMsat: finalValueMsat
                )
            }
        }
    }
    
    struct RouteInfo {
        let hops: [HopInfo]
        let totalFeeMsat: UInt64
        let totalAmountMsat: UInt64
    }
    
    struct HopInfo {
        let pubkey: String
        let shortChannelId: UInt64
        let feeMsat: UInt64
        let cltvExpiryDelta: UInt32
    }
    
    enum RoutingError: Error {
        case noRoute
        case routingFailed(String)
        case insufficientBalance
    }`.trim(),
    
          node_info: `
    // Query node information from network graph
    import LightningDevKit
    
    extension NetworkGraphManager {
        // Get information about a specific node
        func getNodeInfo(nodeId: String) -> NodeDetails? {
            guard let pubkeyData = Data(hexString: nodeId),
                  pubkeyData.count == 33 else {
                return nil
            }
            
            let nodeIdObj = Bindings.NodeId(pubkey: pubkeyData.bytes)
            
            guard let nodeInfo = networkGraph.readOnly().node(nodeId: nodeIdObj) else {
                return nil
            }
            
            // Extract node details
            let channels = nodeInfo.getChannels()
            let announcement = nodeInfo.getAnnouncementMessage()
            
            var nodeDetails = NodeDetails(
                nodeId: nodeId,
                alias: "",
                color: "",
                addresses: [],
                features: [],
                channelCount: channels.count,
                totalCapacityMsat: 0
            )
            
            // Parse announcement if available
            if let announcement = announcement {
                let contents = announcement.getContents()
                nodeDetails.alias = String(data: Data(contents.getAlias()), encoding: .utf8) ?? ""
                nodeDetails.color = Data(contents.getRgb()).hexString
                
                // Parse addresses
                nodeDetails.addresses = contents.getAddresses().compactMap { address in
                    parseNetworkAddress(address)
                }
                
                // Features
                if let features = contents.getFeatures() {
                    nodeDetails.features = parseNodeFeatures(features)
                }
            }
            
            // Calculate total capacity
            for channelId in channels {
                if let channelInfo = networkGraph.readOnly().channel(shortChannelId: channelId) {
                    nodeDetails.totalCapacityMsat += channelInfo.getCapacityMsat() ?? 0
                }
            }
            
            return nodeDetails
        }
        
        // Get all known nodes
        func getAllNodes() -> [NodeSummary] {
            let readOnly = networkGraph.readOnly()
            let nodeIds = readOnly.listNodes()
            
            return nodeIds.compactMap { nodeId in
                guard let nodeInfo = readOnly.node(nodeId: nodeId) else {
                    return nil
                }
                
                let pubkey = Data(nodeId.asSlice()).hexString
                let channels = nodeInfo.getChannels()
                
                var alias = ""
                if let announcement = nodeInfo.getAnnouncementMessage() {
                    alias = String(data: Data(announcement.getContents().getAlias()), encoding: .utf8) ?? ""
                }
                
                return NodeSummary(
                    nodeId: pubkey,
                    alias: alias,
                    channelCount: channels.count,
                    isReachable: !channels.isEmpty
                )
            }
        }
        
        private func parseNetworkAddress(_ address: Bindings.SocketAddress) -> String? {
            switch address.getValueType() {
            case .TcpIpV4:
                if let ipv4 = address.getValueAsTcpIpV4() {
                    let addr = ipv4.getAddr()
                    return "\\(addr.0).\\(addr.1).\\(addr.2).\\(addr.3):\\(ipv4.getPort())"
                }
            case .TcpIpV6:
                if let ipv6 = address.getValueAsTcpIpV6() {
                    return "[\\(ipv6.getAddr().map{String($0)}.joined(separator:":"))]:\\(ipv6.getPort())"
                }
            case .OnionV3:
                if let onion = address.getValueAsOnionV3() {
                    return "\\(Data(onion.getEd25519Pubkey()).hexString).onion:\\(onion.getPort())"
                }
            default:
                break
            }
            return nil
        }
        
        private func parseNodeFeatures(_ features: Bindings.NodeFeatures) -> [String] {
            var featureList: [String] = []
            
            if features.supportsVariableLengthOnion() {
                featureList.append("Variable Length Onion")
            }
            if features.supportsStaticRemoteKey() {
                featureList.append("Static Remote Key")
            }
            if features.supportsAnchors() {
                featureList.append("Anchor Outputs")
            }
            
            return featureList
        }
    }
    
    struct NodeDetails {
        var nodeId: String
        var alias: String
        var color: String
        var addresses: [String]
        var features: [String]
        var channelCount: Int
        var totalCapacityMsat: UInt64
    }
    
    struct NodeSummary {
        let nodeId: String
        let alias: String
        let channelCount: Int
        let isReachable: Bool
    }`.trim(),
    
          channel_info: `
    // Query channel information from network graph
    import LightningDevKit
    
    extension NetworkGraphManager {
        // Get information about a specific channel
        func getChannelInfo(shortChannelId: UInt64) -> ChannelDetails? {
            guard let channelInfo = networkGraph.readOnly().channel(shortChannelId: shortChannelId) else {
                return nil
            }
            
            let node1 = Data(channelInfo.getNodeOne().asSlice()).hexString
            let node2 = Data(channelInfo.getNodeTwo().asSlice()).hexString
            
            var details = ChannelDetails(
                shortChannelId: shortChannelId,
                node1: node1,
                node2: node2,
                capacityMsat: channelInfo.getCapacityMsat(),
                node1Policy: nil,
                node2Policy: nil,
                lastUpdate: nil
            )
            
            // Get directional policies
            if let node1Policy = channelInfo.getOneToTwo() {
                details.node1Policy = parseChannelPolicy(node1Policy)
            }
            
            if let node2Policy = channelInfo.getTwoToOne() {
                details.node2Policy = parseChannelPolicy(node2Policy)
            }
            
            // Get announcement details
            if let announcement = channelInfo.getAnnouncementMessage() {
                let contents = announcement.getContents()
                details.lastUpdate = Date(timeIntervalSince1970: TimeInterval(contents.getTimestamp()))
            }
            
            return details
        }
        
        // Get all channels for a node
        func getNodeChannels(nodeId: String) -> [ChannelSummary] {
            guard let pubkeyData = Data(hexString: nodeId),
                  pubkeyData.count == 33 else {
                return []
            }
            
            let nodeIdObj = Bindings.NodeId(pubkey: pubkeyData.bytes)
            
            guard let nodeInfo = networkGraph.readOnly().node(nodeId: nodeIdObj) else {
                return []
            }
            
            return nodeInfo.getChannels().compactMap { channelId in
                guard let channelInfo = networkGraph.readOnly().channel(shortChannelId: channelId) else {
                    return nil
                }
                
                let node1 = Data(channelInfo.getNodeOne().asSlice()).hexString
                let node2 = Data(channelInfo.getNodeTwo().asSlice()).hexString
                let isNode1 = node1 == nodeId
                let remoteNode = isNode1 ? node2 : node1
                
                // Get our policy
                let ourPolicy = isNode1 ? channelInfo.getOneToTwo() : channelInfo.getTwoToOne()
                let theirPolicy = isNode1 ? channelInfo.getTwoToOne() : channelInfo.getOneToTwo()
                
                return ChannelSummary(
                    shortChannelId: channelId,
                    remoteNode: remoteNode,
                    capacityMsat: channelInfo.getCapacityMsat(),
                    isEnabled: ourPolicy?.getEnabled() ?? false,
                    baseFee: ourPolicy?.getFeeBaseMsat() ?? 0,
                    feeRate: ourPolicy?.getFeeProportionalMillionths() ?? 0,
                    remoteBaseFee: theirPolicy?.getFeeBaseMsat() ?? 0,
                    remoteFeeRate: theirPolicy?.getFeeProportionalMillionths() ?? 0
                )
            }
        }
        
        private func parseChannelPolicy(_ update: Bindings.ChannelUpdateInfo) -> ChannelPolicy {
            return ChannelPolicy(
                enabled: update.getEnabled(),
                cltvExpiryDelta: update.getCltvExpiryDelta(),
                htlcMinimumMsat: update.getHtlcMinimumMsat(),
                htlcMaximumMsat: update.getHtlcMaximumMsat(),
                feeBaseMsat: update.getFeeBaseMsat(),
                feeProportionalMillionths: update.getFeeProportionalMillionths(),
                lastUpdate: Date(timeIntervalSince1970: TimeInterval(update.getLastUpdate()))
            )
        }
    }
    
    struct ChannelDetails {
        let shortChannelId: UInt64
        let node1: String
        let node2: String
        let capacityMsat: UInt64?
        var node1Policy: ChannelPolicy?
        var node2Policy: ChannelPolicy?
        var lastUpdate: Date?
    }
    
    struct ChannelPolicy {
        let enabled: Bool
        let cltvExpiryDelta: UInt16
        let htlcMinimumMsat: UInt64
        let htlcMaximumMsat: UInt64?
        let feeBaseMsat: UInt32
        let feeProportionalMillionths: UInt32
        let lastUpdate: Date
    }
    
    struct ChannelSummary {
        let shortChannelId: UInt64
        let remoteNode: String
        let capacityMsat: UInt64?
        let isEnabled: Bool
        let baseFee: UInt32
        let feeRate: UInt32
        let remoteBaseFee: UInt32
        let remoteFeeRate: UInt32
    }`.trim(),
    
          update_handling: `
    // Handle network graph updates
    import LightningDevKit
    
    extension NetworkGraphManager {
        // Process channel announcement
        func handleChannelAnnouncement(_ announcement: Bindings.ChannelAnnouncement) -> Bool {
            let result = networkGraph.updateChannelFromAnnouncement(
                msg: announcement,
                utxoLookup: nil
            )
            
            if result.isOk() {
                // Persist updated graph
                try? saveNetworkGraph()
                return true
            }
            
            return false
        }
        
        // Process channel update
        func handleChannelUpdate(_ update: Bindings.ChannelUpdate) -> Bool {
            let result = networkGraph.updateChannelFromUnsignedAnnouncement(
                msg: update.getContents(),
                utxoLookup: nil
            )
            
            if result.isOk() {
                try? saveNetworkGraph()
                return true
            }
            
            return false
        }
        
        // Process node announcement
        func handleNodeAnnouncement(_ announcement: Bindings.NodeAnnouncement) -> Bool {
            let result = networkGraph.updateNodeFromAnnouncement(msg: announcement)
            
            if result.isOk() {
                try? saveNetworkGraph()
                return true
            }
            
            return false
        }
        
        // Prune old channels
        func pruneStaleChannels() {
            let currentTime = UInt64(Date().timeIntervalSince1970)
            let twoWeeksAgo = currentTime - (14 * 24 * 60 * 60)
            
            networkGraph.removeStaleChannelsAndTrackedNodes(currentTimeUnix: twoWeeksAgo)
            
            // Persist pruned graph
            try? saveNetworkGraph()
        }
        
        // Monitor graph statistics
        func getGraphStatistics() -> GraphStatistics {
            let readOnly = networkGraph.readOnly()
            let nodeCount = readOnly.listNodes().count
            
            var channelCount = 0
            var totalCapacityMsat: UInt64 = 0
            
            for node in readOnly.listNodes() {
                if let nodeInfo = readOnly.node(nodeId: node) {
                    let channels = nodeInfo.getChannels()
                    channelCount += channels.count
                    
                    for channelId in channels {
                        if let channelInfo = readOnly.channel(shortChannelId: channelId) {
                            totalCapacityMsat += channelInfo.getCapacityMsat() ?? 0
                        }
                    }
                }
            }
            
            // Channels are counted twice (once per node)
            channelCount /= 2
            
            return GraphStatistics(
                nodeCount: nodeCount,
                channelCount: channelCount,
                totalCapacityMsat: totalCapacityMsat,
                lastSync: Date(timeIntervalSince1970: TimeInterval(lastSyncTimestamp))
            )
        }
    }
    
    struct GraphStatistics {
        let nodeCount: Int
        let channelCount: Int
        let totalCapacityMsat: UInt64
        let lastSync: Date
    }
    
    // SwiftUI View for Network Graph Stats
    struct NetworkGraphView: View {
        @StateObject private var viewModel = NetworkGraphViewModel()
        
        var body: some View {
            List {
                Section("Network Statistics") {
                    StatRow(label: "Nodes", value: "\\(viewModel.stats.nodeCount)")
                    StatRow(label: "Channels", value: "\\(viewModel.stats.channelCount)")
                    StatRow(label: "Total Capacity", value: "\\(viewModel.stats.totalCapacityMsat / 1_000_000_000) BTC")
                }
                
                Section("Sync Status") {
                    HStack {
                        Text("Last Sync")
                        Spacer()
                        Text(viewModel.stats.lastSync, style: .relative)
                            .foregroundColor(.secondary)
                    }
                    
                    Button("Sync Now") {
                        Task {
                            await viewModel.syncNetworkGraph()
                        }
                    }
                    .disabled(viewModel.isSyncing)
                }
                
                if viewModel.isSyncing {
                    Section {
                        HStack {
                            ProgressView()
                            Text("Syncing network graph...")
                                .foregroundColor(.secondary)
                        }
                    }
                }
            }
            .navigationTitle("Network Graph")
            .onAppear {
                viewModel.loadStatistics()
            }
        }
    }
    
    struct StatRow: View {
        let label: String
        let value: String
        
        var body: some View {
            HStack {
                Text(label)
                Spacer()
                Text(value)
                    .fontWeight(.medium)
                    .foregroundColor(.secondary)
            }
        }
    }`.trim()
        };
    
        try {
          const code = swiftExamples[args.operation];
          if (!code) {
            throw new Error(`Unknown operation: ${args.operation}`);
          }
    
          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                success: true,
                operation: args.operation,
                swiftCode: code,
                description: `NetworkGraph and RapidGossipSync implementation for ${args.operation}`
              }, 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 the tool, including required 'operation' enum and optional nodeId/channelId.
    inputSchema: {
      type: 'object',
      properties: {
        operation: {
          type: 'string',
          enum: [
            'setup',
            'rapid_gossip_sync',
            'query_routes',
            'node_info',
            'channel_info',
            'update_handling'
          ],
          description: 'Network graph operation to perform'
        },
        nodeId: {
          type: 'string',
          description: 'Node ID for node_info operation (optional)'
        },
        channelId: {
          type: 'string',
          description: 'Channel ID for channel_info operation (optional)'
        }
      },
      required: ['operation']
    },
  • src/index.ts:38-62 (registration)
    The networkGraphTool is imported and included in the central 'tools' array used by the MCP server to register and expose all available tools.
    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,
    ];
Behavior2/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 mentions 'operations' and 'implementation' but doesn't specify whether this is a read-only tool, if it performs mutations, what permissions are needed, or any side effects like rate limits or data destruction. The vague terms offer minimal insight into how the tool behaves beyond its name.

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 a single, concise sentence that efficiently states the tool's scope without unnecessary words. It's front-loaded with the main purpose, though it could be more structured by listing key operations. There's no wasted text, making it appropriately sized for a high-level overview.

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

Completeness2/5

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

Given the tool's complexity (multiple operations via an enum parameter) and lack of annotations and output schema, the description is incomplete. It doesn't explain what each operation does, what the tool returns, or any behavioral traits. For a tool with 3 parameters and varied functionality, this minimal description leaves significant gaps for an agent to understand and use it effectively.

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 (operation, nodeId, channelId) with descriptions and enum values for 'operation'. The description adds no additional meaning about parameters, such as explaining what each operation does or how optional parameters interact. It meets the baseline of 3 since the schema does the heavy lifting, but doesn't compensate with extra context.

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

Purpose3/5

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

The description states the tool handles 'network graph operations and RapidGossipSync implementation', which gives a general purpose but lacks specificity about what it actually does. It mentions two broad categories but doesn't clarify what 'operations' entail or how RapidGossipSync is implemented. It distinguishes somewhat from siblings like ldk_node_info or ldk_channel_status by focusing on network graph functionality, but the distinction remains vague.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention any prerequisites, exclusions, or specific contexts for use. Given siblings like ldk_node_info and ldk_channel_status that might overlap with some operations (e.g., node_info, channel_info), the lack of differentiation leaves the agent without clear usage instructions.

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

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