npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@powforge/mcp-tool-l402

v0.1.0

Published

L402 Lightning payment middleware for Model Context Protocol (MCP) tool handlers. Wrap any MCP tool callback with a pay-per-call Lightning gate backed by LNBits. Returns valid MCP CallToolResult envelopes on both the payment-required and paid paths so the

Downloads

140

Readme

@powforge/mcp-tool-l402

L402 Lightning payment middleware for Model Context Protocol (MCP) tool handlers. Wrap any MCP tool callback with a pay-per-call Lightning gate backed by LNBits. Returns valid MCP CallToolResult envelopes on both the payment-required and paid paths so the wire format stays spec-compliant.

npm i @powforge/mcp-tool-l402

Why

You run an MCP server. You want some tools to be free (list_files) and others to cost a few sats per call (search_archive, generate_image, run_inference). This package gives you a single function-wrapper that adds an L402 gate to any tool callback you're already passing to server.registerTool(...) or server.tool(...).

The wrapper does the right thing on the wire:

  • No proof in args → mints a Lightning invoice via LNBits and returns a CallToolResult with isError: true and a JSON envelope describing how to pay.
  • Proof present + paid → runs your handler and returns the result as a valid CallToolResult.
  • Proof present + unpaid → returns CallToolResult { error: 'invoice_not_paid', payment_hash, next_step }.
  • LNBits down → returns CallToolResult { error: 'payment_provider_unavailable' | 'payment_verifier_unavailable', detail }.

The agent on the other side parses the JSON envelope from content[0].text, pays the invoice, then re-calls the tool with args._paymentProof = "<payment_hash>".

Quickstart

const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
const { z } = require('zod');
const { wrapMcpToolWithL402 } = require('@powforge/mcp-tool-l402');

const server = new McpServer({ name: 'paid-search', version: '0.1.0' });

server.registerTool(
  'search',
  {
    description: 'Search the archive (10 sats per call)',
    inputSchema: {
      query: z.string(),
      _paymentProof: z.string().optional(), // L402 payment hash
    },
  },
  wrapMcpToolWithL402(
    async ({ query }) => {
      // Your real handler. Payment is already verified by the time
      // this runs. The _paymentProof key is stripped from args.
      const hits = await searchArchive(query);
      return { content: [{ type: 'text', text: JSON.stringify(hits) }] };
    },
    {
      lnbitsUrl: process.env.LNBITS_URL,
      lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
      satsAmount: 10,
    },
  ),
);

Factory form

If you prefer to keep tool metadata next to the wrapper, use createL402McpTool:

const { createL402McpTool } = require('@powforge/mcp-tool-l402');

const tool = createL402McpTool({
  name: 'search',
  description: 'Search the archive',
  handler: async ({ query }) => `hits for ${query}`,
  satsAmount: 10,
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
});

server.tool(tool.name, tool.description, tool.callback);

Config

| Key | Required | Default | Notes | |---|---|---|---| | lnbitsUrl | yes (unless injecting fns) | — | e.g. https://lnbits.example | | lnbitsApiKey | yes (unless injecting fns) | — | invoice/read key (NOT admin) | | satsAmount | no | 10 | invoice amount per call | | memo | no | 'mcp-tool-l402' | LNBits invoice memo | | proofKey | no | '_paymentProof' | args key the wrapper reads for proof | | fetchImpl | no | globalThis.fetch | inject for tests / non-Node runtimes | | createInvoiceFn | no | LNBits-backed | inject to bypass LNBits entirely | | checkPaidFn | no | LNBits-backed | inject to bypass LNBits entirely | | state | no | new in-memory map | inject _createPaymentState() to share the paid-cache across wrappers |

402 envelope (embedded as JSON text in content[0].text)

{
  "error": "payment_required",
  "invoice": "lnbc100n1...",
  "payment_hash": "abc...",
  "sats": 10,
  "next_step": "Pay the invoice, then re-call this tool with the payment_hash in args._paymentProof."
}

The result isError field is set to true so MCP clients can branch on it without parsing the envelope.

Why a payment_hash and not a 32-byte preimage

This adapter accepts the LNBits payment_hash (the simpler form). The wrapper still verifies the hash is actually paid against LNBits before unlocking the call. Callers who need cryptographic preimage proof should use @powforge/mcp-l402-gate — the full macaroon-based server-level gate.

Position in the PowForge L402 family

| Package | Surface | Use when | |---|---|---| | @powforge/mcp-l402-gate | HTTP server gate (macaroon + WWW-Authenticate: L402) | You want transport-level paywalling for the whole MCP server | | @powforge/mcp-tool-l402 | per-tool MCP callback wrapper (this package) | You want some tools paid and some free on the same server | | @powforge/paymcp-l402-provider | BasePaymentProvider for paymcp | You're already using paymcp and want to plug in L402 | | @powforge/langchain-l402-middleware | LangChain.js DynamicTool wrapper | You're building a LangChain agent | | @powforge/semantic-kernel-l402 | Semantic Kernel KernelFunction wrapper | You're building a Semantic Kernel agent |

All four use the same LNBits backend (POST /api/v1/payments, GET /api/v1/payments/{hash}), so a single LNBits wallet can power your whole agent surface.

Tests

npm test

43 tests covering config validation, the no-proof path, the paid path, the unpaid/down LNBits paths, handler-result normalization (string / object / CallToolResult pass-through), proof-key configurability, state-sharing, and the factory.

License

MIT