DEV Community

Zeke
Zeke

Posted on

Adding Lightning L402 payments to any AI agent framework in 5 lines

Every AI agent that hits your MCP tools gets it for free.

Claude, GPT, AutoGen, LangChain, Semantic Kernel, the one your buddy is hand-rolling on a Tuesday night. They all show up with no wallet, no rate limit, no history. The bill goes to you in GPU time, API credits, and that nagging feeling that someone is scraping your retrieval endpoint at 4 AM while you sleep.

That is the problem.

The fix is older than most of the agents using it. L402 is an HTTP extension where the server says "pay this Lightning invoice to continue." The client pays, replays the request with a payment proof, and the tool body runs. Twenty-some years of HTTP precedent, one BOLT11 invoice, no middleman. We wired it into five agent frameworks so you can gate any tool in about five lines.

All five packages are MIT, on npm under @powforge. Versions below are what is shipping today.

LangChain: @powforge/langchain-l402-middleware@0.1.0

Wrap any chain, tool, or function. The middleware returns a payment-required object on first call and runs the wrapped function on the replay.

npm install @powforge/langchain-l402-middleware
Enter fullscreen mode Exit fullscreen mode
const { wrapWithL402 } = require('@powforge/langchain-l402-middleware');

const gatedSearch = wrapWithL402(mySearchFn, {
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  satsAmount: 5,
});

// Unpaid:  returns { error: 'payment_required', invoice: 'lnbc...' }
// Paid:    passes { __payment_proof__, __tool_input__ } to mySearchFn
Enter fullscreen mode Exit fullscreen mode

MCP SDK: @powforge/mcp-tool-l402@0.1.0

For folks using @modelcontextprotocol/sdk directly without an Express layer. Wraps a single tool handler so the MCP server returns a payment-required response in-protocol.

npm install @powforge/mcp-tool-l402
Enter fullscreen mode Exit fullscreen mode
const { wrapMcpToolWithL402 } = require('@powforge/mcp-tool-l402');

const gatedTool = wrapMcpToolWithL402(myToolHandler, {
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  satsAmount: 10,
});

// register gatedTool with your McpServer like any other tool handler
Enter fullscreen mode Exit fullscreen mode

Semantic Kernel: @powforge/semantic-kernel-l402@0.1.0

Same shape for Microsoft Semantic Kernel functions. Wrap the kernel function, the planner still sees a callable, the runtime gets paid before the body executes.

npm install @powforge/semantic-kernel-l402
Enter fullscreen mode Exit fullscreen mode
const { wrapKernelFunctionWithL402 } = require('@powforge/semantic-kernel-l402');

const gatedKernelFn = wrapKernelFunctionWithL402(myKernelFunction, {
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  satsAmount: 5,
});
Enter fullscreen mode Exit fullscreen mode

paymcp: @powforge/paymcp-l402-provider@0.1.0

If you are already on paymcp with the @price decorator pattern, this is a drop-in LNBits backend. Implements paymcp's BasePaymentProvider so the same @price annotations on your tools route through Lightning instead of card rails.

npm install @powforge/paymcp-l402-provider
Enter fullscreen mode Exit fullscreen mode
const { L402PaymentProvider } = require('@powforge/paymcp-l402-provider');

const provider = new L402PaymentProvider({
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
});
// hand `provider` to paymcp's configuration the same way you'd pass any BasePaymentProvider
Enter fullscreen mode Exit fullscreen mode

Express + MCP: @powforge/mcp-l402-gate@0.3.0

The original, for MCP servers that already run behind Express. One middleware on the route, every request through it gets the 402 dance.

npm install @powforge/mcp-l402-gate
Enter fullscreen mode Exit fullscreen mode
const { mcpL402Middleware } = require('@powforge/mcp-l402-gate');

app.use('/tools/expensive', mcpL402Middleware({
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  satsAmount: 10,
  minScore: 10,
}));
Enter fullscreen mode Exit fullscreen mode

How the payment flow actually works

Agent hits the tool. Server checks for a payment proof. None. Server mints a BOLT11 invoice off your LNBits instance and returns HTTP 402 with WWW-Authenticate: L402 macaroon="..." invoice="lnbc...". Agent pays the invoice with any Lightning wallet. Agent replays the request, this time carrying the preimage as the payment proof. Server verifies the preimage hashes to the invoice payment_hash, the tool body runs, the agent gets its answer. No third-party intermediary in the path, just your LNBits and the agent.

The settlement cache is keyed by payment_hash with a TTL so the same invoice cannot be replayed forever. Default is invoice expiry plus 60 seconds.

Identity scoring, optional

Every adapter accepts a minScore field. Set it and the gate requires the calling agent to carry a minimum reputation score from the depth-of-identity oracle at identity.powforge.dev before the invoice is even minted. That stops freshly-spawned throwaway wallets from grinding the toll one sat at a time. They hit a score check, fail it, and never see the invoice.

What is next

A Python adapter (powforge on PyPI) is queued for next week with wrap_with_l402 covering the same shape for LangChain Python, LlamaIndex, and AutoGen. Same LNBits backend, same 402 dance, same identity-score gate.

If you ship an MCP server or an agent toolkit and you are tired of being the unpaid backend for somebody else's startup, pick the adapter that matches your stack and gate the expensive tool. Five lines, your LNBits keys, your sats.

Top comments (0)