本文へスキップ
MCP ThesaurusMCP Thesaurus

mcp l402 gate

コミュニティIncomplete39/100申請する

npm @powforge/mcp-l402-gatestdioupdated 4mo ago

Identity-scored Lightning paywall for MCP server operators.

ソースウェブサイト

mcp l402 gate で何ができる?

@powforge/mcp-l402-gate

npm license node

Identity-scored Lightning paywall for MCP server operators.

L402 alone proves the caller paid 10 sats. It does not prove the caller has a reputation, has been around for more than 10 minutes, or that pricing one tool call shifts their economics at all. A fresh wallet pays the same 10 sats as a real user.

This package adds a Depth-of-Identity check on top of the L402 invoice. Drop it in front of an MCP tool and a caller has to (a) settle a Lightning invoice and (b) carry a DoI score above your threshold before the tool body runs. Cheap sybils still pay the toll, but the toll plus the per-pubkey reputation requirement is harder to grind than either piece on its own.

See it in action

A clone-and-run example server lives at github.com/zekebuilds-lab/mcp-l402-gate-example. It exposes one tool, bitcoin_data, that fetches the BTC/USD price plus mempool fees from mempool.space, gated by L402 + DoI. Clone it, fill in your LNBits creds, npm start, and you have a Lightning-gated MCP server running locally.

The Gap

Sats4AI's own documentation states the limitation plainly:

"autonomous agents cannot build reputation or receive preferential treatment across sessions."

@powforge/mcp-l402-gate closes that gap by composing L402 payment gating with the DoI oracle's composite identity score. A paying caller is also a known caller, with a per-pubkey reputation that survives across sessions and that costs irreversible work to fake.

Why not just L402

L402 is great wire format, weak abuse control. Recent MCP billing tools (sats4ai-mcp, invinoveritas, l402-kit, 402-mcp, coinopai-mcp) all ship the same 402 -> macaroon -> paid -> tool body flow, and an attacker can replay the flow from a fresh node every minute. coinopai-mcp's own author put it: "x402 is payment transport only. It doesn't handle agent identity, rate negotiation, multi-agent splits, or reputation."

PowForge has been shipping the missing piece. The DoI oracle at https://identity.powforge.dev returns a Schnorr-signed score for any Nostr pubkey, computed from observable irreversible work across four dimensions (social, access, vouch, economic). This package wires that score into the L402 path so a paying caller is also a costly-to-fake caller.

Requirements

  • Node >= 18
  • An LNBits wallet (URL + invoice/read API key) for the Lightning side
  • An accessible PowForge oracle URL (default: https://identity.powforge.dev)

5-line integration (Express)

const express = require('express');
const { mcpL402Middleware } = require('@powforge/mcp-l402-gate');

const app = express();
app.use('/tools/expensive', mcpL402Middleware({
  secret: process.env.GATE_HMAC_SECRET,
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  satsAmount: 10,
  minScore: 10, // composite >= 10 means "emerging" tier on the oracle
}));

app.post('/tools/expensive', (req, res) => {
  // Reached only when L402 paid AND req.doiScore >= 10
  res.json({ ok: true, doiScore: req.doiScore, l402: req.l402Token });
});

The caller passes their pubkey via the X-Caller-Pubkey header or ?pubkey= query string. v0.1.0 treats this as caller-asserted; v0.2.0 will bind it cryptographically via NIP-98.

MCP tool wrapping

const { mcpL402Tool } = require('@powforge/mcp-l402-gate');

const expensiveTool = mcpL402Tool({
  secret: process.env.GATE_HMAC_SECRET,
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  satsAmount: 10,
  minScore: 10,
}, {
  name: 'image_render',
  description: 'Render an image. 10 sats. Requires DoI score >= 10.',
  inputSchema: {
    type: 'object',
    properties: {
      prompt: { type: 'string' },
      pubkey: { type: 'string' },
      auth: { type: 'object', properties: { macaroon: { type: 'string' }, preimage: { type: 'string' } } },
    },
    required: ['prompt', 'pubkey'],
  },
}, async (args, ctx) => {
  // Runs only when paid AND ctx.doiScore >= 10
  return { image_url: `https://example/r/${args.prompt}`, billed_to: ctx.doiScore };
});

// Register expensiveTool with your MCP server. On first call without args.auth,
// the tool returns { paid: false, challenge: { macaroon, invoice, ... } }.
// The MCP client pays the invoice, then re-calls with args.auth set.

Config reference

Field Default Notes
secret required HMAC key for macaroon signing. Rotate periodically.
lnbitsUrl required LNBits base URL.
lnbitsApiKey required LNBits invoice/read key. NEVER pass admin key.
satsAmount 10 Invoice amount per call.
minScore 10 Reject paid callers below this composite score.
failClosed true If oracle errors, reject the call. Set false to fall through with req.doiScoreError.
oracleUrl https://identity.powforge.dev Override for self-hosted oracles.
scope mcp-l402-gate:call L402 macaroon scope.
ttlSeconds 600 Macaroon validity.
scoreField composite Which envelope field to compare to minScore.
callerPubkeyHeader x-caller-pubkey HTTP header carrying the caller's asserted pubkey.
oracleAuth optional {macaroon, preimage} if your oracle is itself L402-paywalled.
createInvoiceFn optional Test seam. Async (memo) => {payment_hash, bolt11}.
checkPaidFn optional Test seam. Async (payment_hash) => boolean.
lookupScoreFn optional Test seam. Async (pubkey) => {composite, rank, depth}.

Score thresholds (composite)

Same buckets the oracle reports as rank:

Threshold Rank Use it when
0 unknown You only want pay-to-call. Skip this package and use L402 directly.
10 emerging First-call abuse hurts. Default for most public MCP tools.
40 active The tool burns real GPU or has expensive side effects.
100 established Compliance-sensitive or single-tenant SaaS-style endpoints.
200 trusted High-trust admin tooling.

Failure modes

Status Body Meaning
402 {error: "payment required", macaroon, invoice, payment_hash} First call. Pay the invoice, retry with Authorization: L402 <macaroon>:<preimage>.
401 {error: "invalid macaroon", reason} Macaroon malformed, expired, wrong scope, or wrong signature.
401 {error: "preimage does not match payment hash"} Preimage failed sha256 check against the macaroon's payment hash.
409 {error: "macaroon already redeemed"} Replay guard fired. Mint a fresh macaroon.
400 {error: "caller_pubkey_required"} No X-Caller-Pubkey header or ?pubkey= query.
403 {error: "score_too_low", score, min, rank} Caller paid but DoI score is below threshold.
503 {error: "oracle_unavailable", mode: "fail_closed"} Oracle error and failClosed is on (the default).
502 {error: "invoice provider unavailable"} LNBits unreachable on first-call mint.

Why this is a separate package

The L402 macaroon mint and verify code, the LNBits client, and the oracle client are all already shipping inside other PowForge packages. The point of @powforge/mcp-l402-gate is to make the composition trivial: one factory, one config object, one middleware OR one tool wrapper. Operators do not have to assemble three packages by hand to get a defended endpoint.

Tests

npm test

16 unit tests, no real network. The macaroon HMAC is real; LNBits and oracle are stubbed.

License

MIT.