Skill: roasbeef/lightning-mcp-server (version 1.0.0)

--- name: lightning-mcp-server description: Build and configure the MCP server for Lightning Node Connect (LNC). Connects AI assistants to lnd nodes via encrypted WebSocket tunnels using pairing phrases — no direct network access or TLS certs needed. Read-only by default (18 tools for querying node state, channels, payments, invoices, peers, on-chain data). --- # MCP LNC Server Build and configure the MCP server that connects AI assistants to Lightning nodes via **Lightning Node Connect (LNC)**. LNC uses encrypted WebSocket tunnels through a mailbox relay, so the agent never needs direct gRPC access, TLS certificates, or macaroons — just a 10-word pairing phrase from Lightning Terminal. The MCP server is **read-only by default** — it exposes 18 tools for querying node state but cannot send payments or modify channels. ## Quick Start ```bash # 1. Build the MCP server binary skills/lightning-mcp-server/scripts/install.sh # 2. Configure environment (mailbox server, dev mode, etc.) skills/lightning-mcp-server/scripts/configure.sh # 3. Add to Claude Code as an MCP server skills/lightning-mcp-server/scripts/setup-claude-config.sh ``` Then restart Claude Code. The `lnc_connect` tool will be available to connect to any lnd node using a pairing phrase. ## How It Works ``` Claude Code <--stdio--> lightning-mcp-server <--LNC WebSocket--> Mailbox <--> lnd ``` 1. Claude Code launches `lightning-mcp-server` as a subprocess (stdio transport) 2. Agent calls `lnc_connect` with a pairing phrase and password 3. Server generates an ephemeral ECDSA keypair and opens an encrypted WebSocket tunnel through the mailbox relay 4. Once connected, the agent can call any of the 18 read-only tools 5. `lnc_disconnect` closes the tunnel No keys, certs, or macaroons are stored on disk — the pairing phrase is the only credential, and it's handled in-memory only. ## Installation ```bash # Build from source (requires Go 1.24+) skills/lightning-mcp-server/scripts/install.sh # Verify lightning-mcp-server -version ``` The install script builds from the `lightning-mcp-server/` directory in this repo. ## Configuration ```bash # Generate .env with defaults skills/lightning-mcp-server/scripts/configure.sh # Production (mainnet via Lightning Terminal) skills/lightning-mcp-server/scripts/configure.sh --production # Development (local regtest) skills/lightning-mcp-server/scripts/configure.sh --dev --mailbox aperture:11110 ``` Configuration is stored in `lightning-mcp-server/.env`. Key settings: | Variable | Default | Description | |----------|---------|-------------| | `LNC_MAILBOX_SERVER` | `mailbox.terminal.lightning.today:443` | Mailbox relay server | | `LNC_DEV_MODE` | `false` | Enable development mode | | `LNC_INSECURE` | `false` | Skip TLS verification (dev only) | | `LNC_CONNECT_TIMEOUT` | `30` | Connection timeout in seconds | ## Claude Code Integration ### Option 1: `claude mcp add` (recommended) Register the MCP server with a single command — no build step required: ```bash # Zero-install via npx (downloads pre-built binary) claude mcp add --transport stdio lnc -- npx -y @lightninglabs/lightning-mcp-server # With environment variables for production claude mcp add --transport stdio \ --env LNC_MAILBOX_SERVER=mailbox.terminal.lightning.today:443 \ lnc -- npx -y @lightninglabs/lightning-mcp-server # For development/regtest claude mcp add --transport stdio \ --env LNC_MAILBOX_SERVER=localhost:11110 \ --env LNC_DEV_MODE=true \ --env LNC_INSECURE=true \ lnc -- npx -y @lightninglabs/lightning-mcp-server ``` Scope options: `--scope local` (default, just you), `--scope project` (shared via `.mcp.json`), `--scope user` (all your projects). ### Option 2: Setup script (from source) ```bash # Add lightning-mcp-server to Claude Code's MCP config skills/lightning-mcp-server/scripts/setup-claude-config.sh # Project-level config (current project only) skills/lightning-mcp-server/scripts/setup-claude-config.sh --scope project # Global config (all projects) skills/lightning-mcp-server/scripts/setup-claude-config.sh --scope global ``` This adds the server to Claude Code's `.mcp.json` (project) or `~/.claude.json` (global) configuration. After restarting Claude Code, the LNC tools will be available. ### Option 3: Manual configuration Add to `.mcp.json` in your project root: ```json { "mcpServers": { "lnc": { "command": "npx", "args": ["-y", "@lightninglabs/lightning-mcp-server"], "env": { "LNC_MAILBOX_SERVER": "mailbox.terminal.lightning.today:443" } } } } ``` Or with a locally built binary: ```json { "mcpServers": { "lnc": { "command": "lightning-mcp-server", "env": { "LNC_MAILBOX_SERVER": "mailbox.terminal.lightning.today:443" } } } } ``` Or run via Docker: ```json { "mcpServers": { "lnc": { "command": "docker", "args": [ "run", "--rm", "-i", "--network", "host", "--env", "LNC_MAILBOX_SERVER", "--env", "LNC_DEV_MODE", "--env", "LNC_INSECURE", "lightning-mcp-server" ] } } } ``` ## Available Tools (18) ### Connection | Tool | Description | |------|-------------| | `lnc_connect` | Connect to lnd via LNC pairing phrase | | `lnc_disconnect` | Close active LNC connection | ### Node | Tool | Description | |------|-------------| | `lnc_get_info` | Node alias, version, sync status, block height | | `lnc_get_balance` | Wallet balance (on-chain) and channel balance | ### Channels | Tool | Description | |------|-------------| | `lnc_list_channels` | Active/inactive channels with capacity, balances | | `lnc_pending_channels` | Channels being opened or closed | ### Invoices | Tool | Description | |------|-------------| | `lnc_decode_invoice` | Decode a BOLT11 invoice | | `lnc_list_invoices` | List invoices with pagination | | `lnc_lookup_invoice` | Look up invoice by payment hash | ### Payments | Tool | Description | |------|-------------| | `lnc_list_payments` | Payment history with pagination | | `lnc_track_payment` | Track specific payment by hash | ### Peers & Network | Tool | Description | |------|-------------| | `lnc_list_peers` | Connected peers with stats | | `lnc_describe_graph` | Lightning Network topology sample | | `lnc_get_node_info` | Detailed info about a specific node | ### On-Chain | Tool | Description | |------|-------------| | `lnc_list_unspent` | UTXOs with confirmations | | `lnc_get_transactions` | On-chain transaction history | | `lnc_estimate_fee` | Fee estimates for confirmation targets | ## Security Model - **No stored credentials:** Pairing phrase is handled in-memory only. Ephemeral ECDSA keypairs are generated per session. - **Read-only:** No payment, channel, or state-changing operations are exposed. The agent can observe but not modify. - **Encrypted tunnels:** All traffic is encrypted end-to-end through the mailbox relay. The mailbox cannot read the traffic. - **No direct access:** The agent machine never connects directly to the lnd node's gRPC port — all traffic goes through the mailbox. ### Comparison with Direct gRPC Access | | MCP LNC Server | Direct lncli/gRPC | |---|---|---| | **Credential** | Pairing phrase (in-memory) | TLS cert + macaroon (on disk) | | **Network** | WebSocket via mailbox relay | Direct TCP to gRPC port | | **Firewall** | No inbound ports needed | Port 10009 must be reachable | | **Permissions** | Read-only (hardcoded) | Depends on macaroon scope | | **Setup** | Pairing phrase from Lightning Terminal | Export cert + macaroon files | ## Prerequisites - **Go 1.24+** for building from source - **Lightning Terminal (litd)** on the target node for generating pairing phrases - **Claude Code** for MCP integration ## Troubleshooting ### "pairing phrase must be exactly 10 words" The pairing phrase is generated by Lightning Terminal. It must be exactly 10 space-separated words. ### "connection timeout" Check that the mailbox server is reachable. For production, ensure `mailbox.terminal.lightning.today:443` is not blocked by a firewall. ### "TLS handshake failure" If using a local regtest setup, enable dev mode and insecure mode: ```bash skills/lightning-mcp-server/scripts/configure.sh --dev --insecure ``` ### Tools not appearing in Claude Code Restart Claude Code after running `setup-claude-config.sh`. Check that `lightning-mcp-server` is on your `$PATH`: ```bash which lightning-mcp-server ```

path: templates/env.txt
content: # MCP LNC Server Configuration
# Copy this file to lightning-mcp-server/.env and customize for your environment.

# Default mailbox server address
# Production: mailbox.terminal.lightning.today:443
# Regtest:    aperture:11110
LNC_MAILBOX_SERVER=mailbox.terminal.lightning.today:443

# Enable dev mode for local/regtest environments
LNC_DEV_MODE=false

# Skip TLS verification (only for development!)
LNC_INSECURE=false

# Connection timeout in seconds
LNC_CONNECT_TIMEOUT=30


path: scripts/install.sh
content: #!/usr/bin/env bash
# Build and install the MCP LNC server from source.
#
# Usage:
#   install.sh              # Build from lightning-mcp-server/ in this repo
#   install.sh --release    # Build optimized release binary
#
# Prerequisites: Go 1.24+

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MCP_SERVER_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)/lightning-mcp-server"
RELEASE=false

# Parse arguments.
while [[ $# -gt 0 ]]; do
    case $1 in
        --release)
            RELEASE=true
            shift
            ;;
        -h|--help)
            echo "Usage: install.sh [--release]"
            echo ""
            echo "Build and install the MCP LNC server."
            echo ""
            echo "Options:"
            echo "  --release  Build optimized release binary (no dev tags)"
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
    esac
done

echo "=== Installing MCP LNC Server ==="
echo ""

# Verify Go is installed.
if ! command -v go &>/dev/null; then
    echo "Error: Go is not installed." >&2
    echo "Install Go from https://go.dev/dl/" >&2
    exit 1
fi

GO_VERSION=$(go version | grep -oE 'go[0-9]+\.[0-9]+' | head -1)
echo "Go version: $GO_VERSION"

# Verify lightning-mcp-server directory exists.
if [ ! -d "$MCP_SERVER_DIR" ]; then
    echo "Error: lightning-mcp-server/ directory not found at $MCP_SERVER_DIR" >&2
    echo "Are you running this from the lightning-agent-kit repo?" >&2
    exit 1
fi

if [ ! -f "$MCP_SERVER_DIR/go.mod" ]; then
    echo "Error: go.mod not found in $MCP_SERVER_DIR" >&2
    exit 1
fi

echo "Source:  $MCP_SERVER_DIR"
echo ""

GOBIN=$(go env GOPATH)/bin

# Build the binary.
cd "$MCP_SERVER_DIR"

if [ "$RELEASE" = true ]; then
    echo "Building release binary..."
    COMMIT=$(git describe --abbrev=40 --always --dirty 2>/dev/null || echo "unknown")
    PKG="github.com/lightninglabs/lightning-agent-kit/lightning-mcp-server"
    go build -v -ldflags "-X ${PKG}.Commit=${COMMIT}" -o "$GOBIN/lightning-mcp-server" .
else
    echo "Building debug binary..."
    COMMIT=$(git describe --abbrev=40 --always --dirty 2>/dev/null || echo "unknown")
    PKG="github.com/lightninglabs/lightning-agent-kit/lightning-mcp-server"
    go build -v -tags "dev" -ldflags "-X ${PKG}.Commit=${COMMIT}" -o "$GOBIN/lightning-mcp-server" .
fi

echo "Done."
echo ""

# Verify installation.
if command -v lightning-mcp-server &>/dev/null; then
    echo "lightning-mcp-server installed: $(which lightning-mcp-server)"
    lightning-mcp-server -version 2>/dev/null || true
else
    echo "Warning: lightning-mcp-server not found on PATH." >&2
    echo "Binary built at: $GOBIN/lightning-mcp-server" >&2
    echo "Ensure \$GOPATH/bin is in your PATH." >&2
    echo "  export PATH=\$PATH:\$(go env GOPATH)/bin" >&2
fi

echo ""
echo "Installation complete."
echo ""
echo "Next steps:"
echo "  1. Configure: skills/lightning-mcp-server/scripts/configure.sh"
echo "  2. Add to Claude Code: skills/lightning-mcp-server/scripts/setup-claude-config.sh"


path: scripts/setup-claude-config.sh
content: #!/usr/bin/env bash
# Add the MCP LNC server to Claude Code's MCP configuration.
#
# Usage:
#   setup-claude-config.sh                  # Project-level config (.mcp.json)
#   setup-claude-config.sh --scope global   # Global config (~/.claude.json)
#   setup-claude-config.sh --scope project  # Project config (.mcp.json)
#   setup-claude-config.sh --docker         # Use Docker instead of local binary
#
# Requires: jq

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MCP_SERVER_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)/lightning-mcp-server"

SCOPE="project"
USE_DOCKER=false

# Parse arguments.
while [[ $# -gt 0 ]]; do
    case $1 in
        --scope)
            SCOPE="$2"
            shift 2
            ;;
        --docker)
            USE_DOCKER=true
            shift
            ;;
        -h|--help)
            echo "Usage: setup-claude-config.sh [options]"
            echo ""
            echo "Add the MCP LNC server to Claude Code's MCP configuration."
            echo ""
            echo "Options:"
            echo "  --scope project    Add to .mcp.json in repo root (default)"
            echo "  --scope global     Add to ~/.claude.json (all projects)"
            echo "  --docker           Use Docker to run the server"
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
    esac
done

# Verify jq is available.
if ! command -v jq &>/dev/null; then
    echo "Error: jq is required. Install with: brew install jq" >&2
    exit 1
fi

# Determine config file path.
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
case $SCOPE in
    project)
        CONFIG_FILE="$REPO_ROOT/.mcp.json"
        ;;
    global)
        CONFIG_FILE="$HOME/.claude.json"
        ;;
    *)
        echo "Error: --scope must be 'project' or 'global'" >&2
        exit 1
        ;;
esac

echo "=== Setting Up Claude Code MCP Config ==="
echo ""
echo "Scope:  $SCOPE"
echo "Config: $CONFIG_FILE"
echo ""

# Load .env to read mailbox config.
ENV_FILE="$MCP_SERVER_DIR/.env"
LNC_MAILBOX_SERVER="mailbox.terminal.lightning.today:443"
LNC_DEV_MODE="false"
LNC_INSECURE="false"

if [ -f "$ENV_FILE" ]; then
    # Source only known variables.
    while IFS='=' read -r key value; do
        # Skip comments and empty lines.
        [[ "$key" =~ ^#.*$ ]] && continue
        [[ -z "$key" ]] && continue
        # Trim whitespace.
        key=$(echo "$key" | xargs)
        value=$(echo "$value" | xargs)
        case "$key" in
            LNC_MAILBOX_SERVER) LNC_MAILBOX_SERVER="$value" ;;
            LNC_DEV_MODE) LNC_DEV_MODE="$value" ;;
            LNC_INSECURE) LNC_INSECURE="$value" ;;
        esac
    done < "$ENV_FILE"
fi

# Build the MCP server config JSON.
if [ "$USE_DOCKER" = true ]; then
    MCP_ENTRY=$(jq -n \
        --arg mailbox "$LNC_MAILBOX_SERVER" \
        --arg devmode "$LNC_DEV_MODE" \
        --arg insecure "$LNC_INSECURE" \
        '{
            command: "docker",
            args: ["run", "--rm", "-i", "--network", "host",
                   "--env", "LNC_MAILBOX_SERVER",
                   "--env", "LNC_DEV_MODE",
                   "--env", "LNC_INSECURE",
                   "lightning-mcp-server"],
            env: {
                LNC_MAILBOX_SERVER: $mailbox,
                LNC_DEV_MODE: $devmode,
                LNC_INSECURE: $insecure
            }
        }')
else
    # Check that binary exists.
    BINARY_PATH=$(command -v lightning-mcp-server 2>/dev/null || echo "")
    if [ -z "$BINARY_PATH" ]; then
        echo "Warning: lightning-mcp-server not found on PATH." >&2
        echo "Run install.sh first, or use --docker." >&2
        echo ""
        BINARY_PATH="lightning-mcp-server"
    fi

    MCP_ENTRY=$(jq -n \
        --arg cmd "$BINARY_PATH" \
        --arg mailbox "$LNC_MAILBOX_SERVER" \
        --arg devmode "$LNC_DEV_MODE" \
        --arg insecure "$LNC_INSECURE" \
        '{
            command: $cmd,
            env: {
                LNC_MAILBOX_SERVER: $mailbox,
                LNC_DEV_MODE: $devmode,
                LNC_INSECURE: $insecure
            }
        }')
fi

# Read or create config file.
if [ -f "$CONFIG_FILE" ]; then
    CONFIG=$(cat "$CONFIG_FILE")
else
    CONFIG='{}'
fi

# Add or update the lnc server entry.
UPDATED=$(echo "$CONFIG" | jq --argjson entry "$MCP_ENTRY" '.mcpServers.lnc = $entry')

echo "$UPDATED" | jq . > "$CONFIG_FILE"

echo "MCP server config written to $CONFIG_FILE"
echo ""
echo "Entry added:"
echo "$MCP_ENTRY" | jq .
echo ""
echo "Next steps:"
echo "  1. Restart Claude Code to pick up the new MCP server"
echo "  2. Use lnc_connect with a pairing phrase to connect to your node"


path: scripts/configure.sh
content: #!/usr/bin/env bash
# Configure the MCP LNC server environment.
#
# Usage:
#   configure.sh                        # Interactive defaults (production mailbox)
#   configure.sh --production           # Production settings
#   configure.sh --dev                  # Development/regtest settings
#   configure.sh --dev --mailbox host   # Custom mailbox server
#   configure.sh --dev --insecure       # Dev mode with TLS verification disabled
#
# Writes to lightning-mcp-server/.env in the lightning-agent-kit repo.

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MCP_SERVER_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)/lightning-mcp-server"

MAILBOX=""
DEV_MODE=""
INSECURE=""
TIMEOUT=""
MODE=""

# Parse arguments.
while [[ $# -gt 0 ]]; do
    case $1 in
        --production)
            MODE="production"
            shift
            ;;
        --dev)
            MODE="dev"
            shift
            ;;
        --mailbox)
            MAILBOX="$2"
            shift 2
            ;;
        --insecure)
            INSECURE="true"
            shift
            ;;
        --timeout)
            TIMEOUT="$2"
            shift 2
            ;;
        -h|--help)
            echo "Usage: configure.sh [options]"
            echo ""
            echo "Configure the MCP LNC server environment."
            echo ""
            echo "Modes:"
            echo "  --production      Production settings (default mailbox, TLS enabled)"
            echo "  --dev             Development settings (dev mode, insecure TLS)"
            echo ""
            echo "Options:"
            echo "  --mailbox SERVER  Mailbox server address (host:port)"
            echo "  --insecure        Skip TLS verification (dev only)"
            echo "  --timeout SECS    Connection timeout (default: 30)"
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
    esac
done

# Verify lightning-mcp-server directory exists.
if [ ! -d "$MCP_SERVER_DIR" ]; then
    echo "Error: lightning-mcp-server/ directory not found at $MCP_SERVER_DIR" >&2
    exit 1
fi

# Apply mode defaults.
case $MODE in
    production)
        MAILBOX="${MAILBOX:-mailbox.terminal.lightning.today:443}"
        DEV_MODE="${DEV_MODE:-false}"
        INSECURE="${INSECURE:-false}"
        ;;
    dev)
        MAILBOX="${MAILBOX:-mailbox.terminal.lightning.today:443}"
        DEV_MODE="${DEV_MODE:-true}"
        INSECURE="${INSECURE:-true}"
        ;;
    *)
        MAILBOX="${MAILBOX:-mailbox.terminal.lightning.today:443}"
        DEV_MODE="${DEV_MODE:-false}"
        INSECURE="${INSECURE:-false}"
        ;;
esac

TIMEOUT="${TIMEOUT:-30}"

ENV_FILE="$MCP_SERVER_DIR/.env"

echo "=== Configuring MCP LNC Server ==="
echo ""
echo "Mailbox:   $MAILBOX"
echo "Dev mode:  $DEV_MODE"
echo "Insecure:  $INSECURE"
echo "Timeout:   ${TIMEOUT}s"
echo "Output:    $ENV_FILE"
echo ""

cat > "$ENV_FILE" <<EOF
# MCP LNC Server Configuration
# Generated by configure.sh on $(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Mailbox relay server
LNC_MAILBOX_SERVER=$MAILBOX

# Enable dev mode for local/regtest environments
LNC_DEV_MODE=$DEV_MODE

# Skip TLS verification (only for development!)
LNC_INSECURE=$INSECURE

# Connection timeout in seconds
LNC_CONNECT_TIMEOUT=$TIMEOUT
EOF

chmod 600 "$ENV_FILE"

echo "Configuration written to $ENV_FILE (mode 0600)"
echo ""
echo "Next steps:"
echo "  1. Add to Claude Code: skills/lightning-mcp-server/scripts/setup-claude-config.sh"
echo "  2. Restart Claude Code to pick up the new MCP server"


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)