ldk_node_info
Retrieve real-time node status and connectivity details to monitor and manage Lightning Network operations with LDK MCP Server tools.
Instructions
Get current node status and connectivity information
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Input Schema (JSON Schema)
{
"properties": {},
"type": "object"
}
Implementation Reference
- src/tools/getNodeInfo.ts:13-242 (handler)The main handler function for the 'ldk_node_info' tool. It fetches node information and balance from LightningService, formats the response as JSON with additional SwiftUI example code, and handles errors.execute: async (args: any): Promise<ToolResult> => { try { const nodeInfo = await lightningService.getNodeInfo(); const balance = await lightningService.getBalance(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, nodeInfo: { nodeId: nodeInfo.nodeId, alias: nodeInfo.alias, version: nodeInfo.version, blockHeight: nodeInfo.blockHeight, syncedToChain: nodeInfo.syncedToChain, channels: { total: nodeInfo.numChannels, usable: nodeInfo.numUsableChannels }, balance: { totalSats: Math.floor(balance.totalMsat / 1000), spendableSats: Math.floor(balance.spendableMsat / 1000) }, peers: nodeInfo.numPeers }, swiftExample: ` // Swift code to display node info in your iOS app import SwiftUI import LightningDevKit struct NodeInfoView: View { @State private var nodeInfo: NodeInfo? @State private var isLoading = true @State private var isSyncing = false var body: some View { ScrollView { VStack(spacing: 20) { // Node Identity Card VStack(alignment: .leading, spacing: 12) { HStack { Image(systemName: "bolt.circle.fill") .font(.largeTitle) .foregroundColor(.orange) VStack(alignment: .leading) { Text(nodeInfo?.alias ?? "Lightning Node") .font(.title2) .fontWeight(.semibold) Text(nodeInfo?.nodeId.prefix(16) ?? "") .font(.caption) .foregroundColor(.secondary) .monospaced() } Spacer() } Divider() // Sync Status HStack { Label( nodeInfo?.syncedToChain == true ? "Synced" : "Syncing...", systemImage: nodeInfo?.syncedToChain == true ? "checkmark.circle.fill" : "arrow.triangle.2.circlepath" ) .foregroundColor(nodeInfo?.syncedToChain == true ? .green : .orange) Spacer() Text("Block \\(nodeInfo?.blockHeight ?? 0)") .font(.caption) .foregroundColor(.secondary) } } .padding() .background(Color(UIColor.secondarySystemBackground)) .cornerRadius(12) // Stats Grid LazyVGrid(columns: [ GridItem(.flexible()), GridItem(.flexible()) ], spacing: 16) { StatCard( title: "Channels", value: "\\(nodeInfo?.numUsableChannels ?? 0)/\\(nodeInfo?.numChannels ?? 0)", icon: "link", color: .blue ) StatCard( title: "Peers", value: "\\(nodeInfo?.numPeers ?? 0)", icon: "person.2", color: .green ) StatCard( title: "Total Balance", value: "\\(formatSats(nodeInfo?.totalBalanceSats ?? 0))", icon: "bitcoinsign.circle", color: .orange ) StatCard( title: "Spendable", value: "\\(formatSats(nodeInfo?.spendableBalanceSats ?? 0))", icon: "paperplane", color: .purple ) } // Actions VStack(spacing: 12) { Button(action: syncToTip) { Label("Sync to Chain Tip", systemImage: "arrow.clockwise") .frame(maxWidth: .infinity) } .buttonStyle(.bordered) .disabled(isSyncing) Button(action: openChannel) { Label("Open Channel", systemImage: "plus.circle") .frame(maxWidth: .infinity) } .buttonStyle(.borderedProminent) } .padding(.top) } .padding() } .navigationTitle("Node Info") .navigationBarTitleDisplayMode(.inline) .refreshable { await loadNodeInfo() } .task { await loadNodeInfo() } .overlay { if isLoading { ProgressView("Loading node info...") .padding() .background(Color(UIColor.systemBackground)) .cornerRadius(10) .shadow(radius: 5) } } } func loadNodeInfo() async { isLoading = true defer { isLoading = false } // Fetch node info from LDK let ldkManager = LDKManager.shared nodeInfo = await ldkManager.getNodeInfo() } func syncToTip() { Task { isSyncing = true defer { isSyncing = false } await LDKManager.shared.syncToChainTip() await loadNodeInfo() } } func openChannel() { // Navigate to channel opening view } func formatSats(_ sats: Int64) -> String { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.groupingSeparator = "," return formatter.string(from: NSNumber(value: sats)) ?? "0" } } struct StatCard: View { let title: String let value: String let icon: String let color: Color var body: some View { VStack(spacing: 8) { HStack { Image(systemName: icon) .foregroundColor(color) Spacer() } VStack(alignment: .leading, spacing: 4) { Text(value) .font(.title3) .fontWeight(.semibold) Text(title) .font(.caption) .foregroundColor(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) } .padding() .background(Color(UIColor.secondarySystemBackground)) .cornerRadius(10) } }`.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 }; } }
- src/index.ts:16-62 (registration)The tool is imported and added to the central tools array used by the MCP server to handle listTools and callTool requests.import { getNodeInfoTool } from './tools/getNodeInfo.js'; import { backupStateTool } from './tools/backupState.js'; import { keychainTestTool } from './tools/iosKeychainTest.js'; import { backgroundTestTool } from './tools/iosBackgroundTest.js'; import { pushNotificationTool } from './tools/iosPushNotification.js'; import { biometricAuthTool } from './tools/iosBiometricAuth.js'; import { createChannelTool } from './tools/createChannel.js'; import { closeChannelTool } from './tools/closeChannel.js'; import { getBalanceTool } from './tools/getBalance.js'; import { decodeInvoiceTool } from './tools/decodeInvoice.js'; import { listPaymentsTool } from './tools/listPayments.js'; import { estimateFeeTool } from './tools/estimateFee.js'; import { generateMnemonicTool } from './tools/generateMnemonic.js'; import { deriveAddressTool } from './tools/deriveAddress.js'; import { getSwiftCodeTool } from './tools/getSwiftCode.js'; import { getArchitectureTool } from './tools/getArchitecture.js'; import { testScenarioTool } from './tools/testScenario.js'; import { networkGraphTool } from './tools/networkGraph.js'; import { eventHandlingTool } from './tools/eventHandling.js'; import { chainSyncTool } from './tools/chainSync.js'; // Aggregate all 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, ];
- src/tools/getNodeInfo.ts:9-12 (schema)Input schema for the tool, which takes no parameters (empty object).inputSchema: { type: 'object', properties: {} },
- Helper method in LightningService that returns the current mock NodeInfo object used by the tool handler.async getNodeInfo(): Promise<NodeInfo> { return this.nodeInfo; }
- Helper method in LightningService that calculates total and spendable balance from channels, used in the tool response.async getBalance(): Promise<{ totalMsat: number; spendableMsat: number }> { let totalMsat = 0; let spendableMsat = 0; for (const channel of this.channels.values()) { if (channel.state === ChannelState.Open) { totalMsat += channel.localBalanceMsat; if (channel.isUsable) { // Reserve 1% for fees spendableMsat += Math.floor(channel.localBalanceMsat * 0.99); } } } return { totalMsat, spendableMsat }; }