402-mcp 4.2.2 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -15
- package/build/config.d.ts +6 -0
- package/build/config.js +41 -10
- package/build/config.js.map +1 -1
- package/build/fetch/resilient-fetch.js +15 -4
- package/build/fetch/resilient-fetch.js.map +1 -1
- package/build/fetch/transport.d.ts +1 -0
- package/build/fetch/transport.js +37 -14
- package/build/fetch/transport.js.map +1 -1
- package/build/http-security.d.ts +13 -0
- package/build/http-security.js +38 -0
- package/build/http-security.js.map +1 -0
- package/build/index.js +184 -119
- package/build/index.js.map +1 -1
- package/build/spend-tracker.d.ts +31 -4
- package/build/spend-tracker.js +102 -17
- package/build/spend-tracker.js.map +1 -1
- package/build/store/cashu-tokens.d.ts +19 -0
- package/build/store/cashu-tokens.js +32 -0
- package/build/store/cashu-tokens.js.map +1 -1
- package/build/store/credentials.d.ts +5 -0
- package/build/store/credentials.js +1 -0
- package/build/store/credentials.js.map +1 -1
- package/build/store/pending-payments.d.ts +36 -0
- package/build/store/pending-payments.js +88 -0
- package/build/store/pending-payments.js.map +1 -0
- package/build/tools/buy-credits.d.ts +9 -0
- package/build/tools/buy-credits.js +40 -3
- package/build/tools/buy-credits.js.map +1 -1
- package/build/tools/config.d.ts +5 -0
- package/build/tools/config.js.map +1 -1
- package/build/tools/confirm.d.ts +12 -0
- package/build/tools/confirm.js +27 -0
- package/build/tools/confirm.js.map +1 -0
- package/build/tools/fetch.d.ts +7 -1
- package/build/tools/fetch.js +255 -140
- package/build/tools/fetch.js.map +1 -1
- package/build/tools/nostr-subscribe.js.map +1 -1
- package/build/tools/pay.d.ts +9 -0
- package/build/tools/pay.js +65 -8
- package/build/tools/pay.js.map +1 -1
- package/build/tools/reconcile.d.ts +33 -0
- package/build/tools/reconcile.js +111 -0
- package/build/tools/reconcile.js.map +1 -0
- package/build/tools/redeem-cashu.js +1 -1
- package/build/tools/redeem-cashu.js.map +1 -1
- package/build/tools/register-widgets.js +5 -5
- package/build/tools/register-widgets.js.map +1 -1
- package/build/tools/search.js +17 -3
- package/build/tools/search.js.map +1 -1
- package/build/tools/untrusted.d.ts +3 -0
- package/build/tools/untrusted.js +18 -0
- package/build/tools/untrusted.js.map +1 -0
- package/build/wallet/cashu.js +101 -21
- package/build/wallet/cashu.js.map +1 -1
- package/build/wallet/lnurlcash.js +25 -7
- package/build/wallet/lnurlcash.js.map +1 -1
- package/build/wallet/nwc.js +51 -3
- package/build/wallet/nwc.js.map +1 -1
- package/build/wallet/types.d.ts +17 -0
- package/build/x402/parse.d.ts +15 -3
- package/build/x402/parse.js +23 -9
- package/build/x402/parse.js.map +1 -1
- package/build/x402/payment.js +11 -6
- package/build/x402/payment.js.map +1 -1
- package/llms-full.txt +71 -33
- package/llms.txt +48 -21
- package/package.json +3 -3
- package/src/widgets/dist/service-directory.html +11 -2
package/llms-full.txt
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## What is 402-mcp?
|
|
6
6
|
|
|
7
|
-
402-mcp is an MCP (Model Context Protocol) server that gives AI agents economic agency
|
|
7
|
+
402-mcp is an MCP (Model Context Protocol) server that gives AI agents economic agency: the ability to discover, pay for, and consume L402 and other Lightning or ecash payment-gated APIs without human registration, API keys, or middlemen.
|
|
8
8
|
|
|
9
9
|
It implements the client side of the L402 protocol: when an API responds with HTTP 402 (Payment Required), 402-mcp automatically parses the challenge, pays the Lightning invoice, stores the credential, and retries the request — all within a single tool call from the agent's perspective.
|
|
10
10
|
|
|
@@ -80,7 +80,7 @@ src/
|
|
|
80
80
|
bolt11.ts # Decode BOLT11 invoices (amount extraction)
|
|
81
81
|
challenge-cache.ts # Cache parsed challenges for l402-pay
|
|
82
82
|
x402/
|
|
83
|
-
parse.ts #
|
|
83
|
+
parse.ts # Experimental custom x402 format: X-Payment-Required: x402 header, EVM challenge body
|
|
84
84
|
payment.ts # Format payment request with EIP-681 deeplink for human payment
|
|
85
85
|
xcashu/
|
|
86
86
|
parse.ts # Detect X-Cashu header; parse NUT-18 payment request
|
|
@@ -151,18 +151,23 @@ tests/ # Mirrors src/ structure
|
|
|
151
151
|
|
|
152
152
|
**Parameters:**
|
|
153
153
|
- `url` (string, required) — the endpoint to fetch
|
|
154
|
+
- `urls` (string[], optional): all transport URLs from `l402-search`, tried in preference order
|
|
155
|
+
- `pubkey` (string, optional): service pubkey from `l402-search`, used to share a credential across the service's transports
|
|
154
156
|
- `method` (string, optional) — HTTP method (default: GET)
|
|
155
157
|
- `headers` (object, optional) — additional headers
|
|
156
158
|
- `body` (string, optional) — request body
|
|
157
|
-
- `
|
|
159
|
+
- `autoPay` (boolean, optional, default false): pay a challenge within the limits
|
|
160
|
+
- `maxCostSats` (number, optional): most this call may pay; lowers `MAX_AUTO_PAY_SATS`, never raises it
|
|
161
|
+
- `txHash` (string, optional): transaction hash for the experimental x402 format
|
|
158
162
|
|
|
159
163
|
**Behaviour:**
|
|
160
|
-
1.
|
|
164
|
+
1. Uses a stored credential only if it is bound to every origin the request may reach
|
|
161
165
|
2. Makes the HTTP request (with credential if available)
|
|
162
|
-
3. If 402 received:
|
|
163
|
-
4. If within
|
|
164
|
-
5.
|
|
165
|
-
6.
|
|
166
|
+
3. If 402 received and `autoPay` is set: refuses if an earlier payment to the service is unresolved, then checks the price against `MAX_AUTO_PAY_SATS`, `maxCostSats` and the rolling spend caps
|
|
167
|
+
4. If within the limits: pays, stores the credential bound to the request's origins, retries
|
|
168
|
+
5. Otherwise: returns the price and `paymentHash` unpaid, and caches the challenge for `l402-pay`
|
|
169
|
+
6. A payment whose outcome is unknown is reported as an error and recorded for `l402-reconcile`; it never falls through to another rail
|
|
170
|
+
7. Returns response body (in UNTRUSTED CONTENT markers), status, headers, and remaining credit balance
|
|
166
171
|
|
|
167
172
|
**When to use:** Primary tool for consuming L402 APIs. Handles the entire flow.
|
|
168
173
|
|
|
@@ -170,19 +175,44 @@ tests/ # Mirrors src/ structure
|
|
|
170
175
|
|
|
171
176
|
### l402-pay
|
|
172
177
|
|
|
173
|
-
**Purpose:** Pay a
|
|
178
|
+
**Purpose:** Pay a challenge that `l402-fetch` or `l402-discover` returned.
|
|
174
179
|
|
|
175
180
|
**Parameters:**
|
|
176
|
-
- `
|
|
177
|
-
- `
|
|
178
|
-
- `
|
|
181
|
+
- `paymentHash` (string): payment hash of the cached challenge
|
|
182
|
+
- `invoice`, `macaroon` (string, optional): only for an invoice that did not come from a challenge; the human must approve it through MCP elicitation, or it is refused
|
|
183
|
+
- `method` (string, optional): `nwc`, `cashu`, `lnurlcash` or `human`
|
|
179
184
|
|
|
180
185
|
**Returns:**
|
|
181
|
-
- `paid` (boolean)
|
|
182
|
-
- `
|
|
186
|
+
- `paid` (boolean) and `paymentState` (`paid`, `failed`, `unknown` or `blocked`)
|
|
187
|
+
- `credentialsStored` (boolean)
|
|
183
188
|
- `method` (string) — which wallet paid
|
|
184
189
|
|
|
185
|
-
|
|
190
|
+
For human wallets with a toll-booth payment page, polls for settlement for up to 120 seconds.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### l402-reconcile
|
|
195
|
+
|
|
196
|
+
**Purpose:** Resolve payments whose outcome is unknown.
|
|
197
|
+
|
|
198
|
+
**Parameters:**
|
|
199
|
+
- `paymentHash` (string, optional): omit to list unresolved payments
|
|
200
|
+
- `preimage` (string, optional): settles the payment if it hashes to `paymentHash`
|
|
201
|
+
- `abandon` (boolean, optional): stop tracking it; requires the human's approval through MCP elicitation
|
|
202
|
+
|
|
203
|
+
**Behaviour:** Asks the wallet that made the payment (NWC `lookup_invoice`, the Cashu mint for a reserved melt, or the LNURLcash verify URL). Settled with a matching preimage clears it and stores the L402 credential; definitely failed clears it and releases reserved Cashu proofs. While any is unresolved for a service, auto-pay to it is paused.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### l402-fetch-preview
|
|
208
|
+
|
|
209
|
+
**Purpose:** Show what an endpoint costs without paying. Its dependencies include no wallet or spend tracker, so it cannot spend. Widget hosts render the result as a payment confirmation dialog.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### Widget tools
|
|
214
|
+
|
|
215
|
+
`l402-service-directory` (searchable service directory) and `l402-wallet-dashboard` (wallet status, limits, credentials) are MCP Apps widgets for hosts that support them.
|
|
186
216
|
|
|
187
217
|
---
|
|
188
218
|
|
|
@@ -235,6 +265,8 @@ tests/ # Mirrors src/ structure
|
|
|
235
265
|
- `topics` — topic tags
|
|
236
266
|
- `capabilities` — array of `{ name, description }`
|
|
237
267
|
|
|
268
|
+
Names, descriptions and capability text are wrapped in UNTRUSTED CONTENT markers. Topic and payment-method filters are sent to the relays; the query text is matched locally.
|
|
269
|
+
|
|
238
270
|
**When to use:** When the agent needs a paid API but doesn't know the URL. Searches the decentralised Nostr network for kind 31402 service announcements. Services may advertise multiple transport URLs (clearnet, .onion, Handshake); pass the `urls` array and `pubkey` to `l402-fetch` for automatic transport selection and fallback.
|
|
239
271
|
|
|
240
272
|
---
|
|
@@ -246,7 +278,7 @@ tests/ # Mirrors src/ structure
|
|
|
246
278
|
**Parameters:**
|
|
247
279
|
- `url` (string, required) — toll-booth server URL
|
|
248
280
|
- `amountSats` (number, optional) — specific tier to purchase
|
|
249
|
-
- `
|
|
281
|
+
- `method` (string, optional): `nwc`, `cashu` or `human`
|
|
250
282
|
|
|
251
283
|
**Behaviour:** Fetches available tiers, purchases the specified tier, stores the credential with the credit balance.
|
|
252
284
|
|
|
@@ -282,7 +314,7 @@ The L402 protocol is an extension of HTTP 402 (Payment Required):
|
|
|
282
314
|
|
|
283
315
|
## Supported 402 Protocol Variants
|
|
284
316
|
|
|
285
|
-
`l402-fetch` detects and handles
|
|
317
|
+
`l402-fetch` detects and handles four HTTP 402 challenge variants, plus an experimental x402 format. Detection order in source: LNURLcash, x402, IETF Payment, L402, with xCashu tried immediately after LNURLcash.
|
|
286
318
|
|
|
287
319
|
| Protocol | Detection | Credential sent | Autonomous? |
|
|
288
320
|
|----------|-----------|----------------|-------------|
|
|
@@ -290,7 +322,7 @@ The L402 protocol is an extension of HTTP 402 (Payment Required):
|
|
|
290
322
|
| **IETF Payment** (`draft-ryan-httpauth-payment-01`) | `WWW-Authenticate: Payment id=… method=lightning …` | `Authorization: Payment <base64url-json>` | Yes (NWC or Cashu) |
|
|
291
323
|
| **LNURLcash** (LUD-25) | `X-LNURLcash: lnurlcashreq1<base64url>` | `X-LNURLcash: <bearer note URL>` | Yes (note store only) |
|
|
292
324
|
| **xCashu** (NUT-18) | `X-Cashu: creqA<base64url>` | `X-Cashu: <cashuB-token>` | Yes (Cashu wallet only) |
|
|
293
|
-
| **x402** | `X-Payment-Required: x402` + JSON body |
|
|
325
|
+
| **x402** (experimental, custom format) | `X-Payment-Required: x402` + JSON body | `X-Payment: <tx hash>` after the human pays | No: payment details shown to the human. Not the x402 specification, whose servers send a base64 `PAYMENT-REQUIRED` header |
|
|
294
326
|
|
|
295
327
|
LNURLcash is tried before the others because a bearer note is already money in hand: the note URL goes straight into the retry header and the server settles it by rotating the note. A note worth exactly the price is used as it is; otherwise one is split at the mint and the change stays in the store. The chosen note is held pending until the outcome is known, so nothing else can spend it in the meantime. xCashu uses coin selection via `wallet.send(amount, proofs, { includeFees: true })` and restores change proofs to the token store. IETF Payment decodes the base64url charge request to extract the BOLT11 invoice, then pays it through the normal wallet stack and wraps the preimage in a JSON credential.
|
|
296
328
|
|
|
@@ -305,20 +337,17 @@ Wallets are tried in order: NWC → Cashu → LNURLcash → Human. The first wal
|
|
|
305
337
|
| LNURLcash | Fully autonomous | `LNURLCASH_NOTES` file with LUD-25 bearer notes | ~2-6 seconds |
|
|
306
338
|
| Human | Requires human | None — always available as fallback | Minutes |
|
|
307
339
|
|
|
308
|
-
The agent can force a specific method
|
|
340
|
+
The agent can force a specific method on `l402-pay` and `l402-buy-credits` with the `method` parameter.
|
|
309
341
|
|
|
310
342
|
## Safety and Spend Limits
|
|
311
343
|
|
|
312
344
|
### Per-payment cap
|
|
313
345
|
|
|
314
|
-
`MAX_AUTO_PAY_SATS` (default: 1000) caps any single payment.
|
|
315
|
-
- The agent is told the price and asked to seek human approval
|
|
316
|
-
- The human wallet (QR code) is used for the actual payment
|
|
317
|
-
- The credential is stored normally after payment
|
|
346
|
+
`MAX_AUTO_PAY_SATS` (default: 1000) caps any single payment. A dearer challenge is not paid from any configured wallet, and the human wallet does not take over: the price and invoice go back to the agent, and whether it asks the human is up to the agent. `maxCostSats` on `l402-fetch` lowers the cap for one call. `0` disables auto-pay.
|
|
318
347
|
|
|
319
348
|
### Rolling spend cap
|
|
320
349
|
|
|
321
|
-
`MAX_SPEND_PER_MINUTE_SATS` (default: 10000) caps total spend in any 60-second window.
|
|
350
|
+
`MAX_SPEND_PER_MINUTE_SATS` (default: 10000) caps total spend in any 60-second window, and `MAX_SPEND_PER_DAY_SATS` (default: 5000) in any 24 hours; the daily window is kept in `~/.402-mcp/spend-ledger.json` so a restart does not reset it. `0` in either blocks auto-pay. The budget set on the NWC connection is the real hard limit. These prevent:
|
|
322
351
|
- Rapid successive small payments exceeding a total budget
|
|
323
352
|
- Runaway loops where the agent keeps buying credits
|
|
324
353
|
- Both caps use atomic `tryRecord()` to prevent TOCTOU race conditions
|
|
@@ -333,7 +362,7 @@ All outbound HTTP requests pass through an SSRF guard:
|
|
|
333
362
|
### Credential Security
|
|
334
363
|
|
|
335
364
|
- Credentials encrypted with AES-256-GCM
|
|
336
|
-
- Encryption key stored in OS keychain (macOS
|
|
365
|
+
- Encryption key stored in the OS keychain when available (macOS through the `security` command-line tool; elsewhere through the optional `keytar` module)
|
|
337
366
|
- Falls back to file-based key at `~/.402-mcp/encryption.key` with a console warning
|
|
338
367
|
- NWC capability and response events authenticated with NIP-44 v2 and signatures
|
|
339
368
|
- Returned NWC preimages verified against the challenged BOLT11 payment hash
|
|
@@ -352,13 +381,16 @@ npx 402-mcp
|
|
|
352
381
|
|
|
353
382
|
### HTTP
|
|
354
383
|
|
|
355
|
-
StreamableHTTP transport
|
|
384
|
+
StreamableHTTP transport, stateless. Prefer stdio wherever the client can start a local process: the HTTP transport can spend from the wallet.
|
|
356
385
|
|
|
357
386
|
```bash
|
|
358
|
-
|
|
387
|
+
openssl rand -hex 32 > ~/.402-mcp/http.token && chmod 600 ~/.402-mcp/http.token
|
|
388
|
+
TRANSPORT=http HTTP_AUTH_TOKEN_FILE=~/.402-mcp/http.token PORT=3402 npx 402-mcp
|
|
359
389
|
```
|
|
360
390
|
|
|
361
391
|
HTTP transport includes:
|
|
392
|
+
- Bearer token required on every request (`HTTP_AUTH_TOKEN_FILE`, a private 0600 file); the server refuses to start in http mode without one
|
|
393
|
+
- DNS rebinding protection: only requests whose `Host` header is a loopback name with the port, the bound address, or listed in `HTTP_ALLOWED_HOSTS` are answered
|
|
362
394
|
- Rate limiting: 100 requests per 60 seconds per IP
|
|
363
395
|
- Security headers: CSP, X-Frame-Options, X-Content-Type-Options, Cache-Control
|
|
364
396
|
- Loopback-only binding by default (`BIND_ADDRESS=127.0.0.1`)
|
|
@@ -372,11 +404,14 @@ HTTP transport includes:
|
|
|
372
404
|
| `NWC_URI_FILE` | — | Path to a private 0600 file containing the NWC bearer URI. Raw NWC_URI is refused. |
|
|
373
405
|
| `CASHU_TOKENS` | — | Path to Cashu token store file. Must be within home directory. |
|
|
374
406
|
| `MAX_AUTO_PAY_SATS` | 1000 | Per-payment safety cap (satoshis). 0 disables autonomous payments. |
|
|
375
|
-
| `MAX_SPEND_PER_MINUTE_SATS` | 10000 | Rolling 60-second spend cap (satoshis). |
|
|
407
|
+
| `MAX_SPEND_PER_MINUTE_SATS` | 10000 | Rolling 60-second spend cap (satoshis). 0 blocks auto-pay. |
|
|
408
|
+
| `MAX_SPEND_PER_DAY_SATS` | 5000 | Rolling 24-hour spend cap (satoshis), persisted across restarts. 0 blocks auto-pay. |
|
|
376
409
|
| `CREDENTIAL_STORE` | `~/.402-mcp/credentials.json` | Path to encrypted credential store. Must be within home directory. |
|
|
377
410
|
| `TRANSPORT` | `stdio` | Transport mode: `stdio` or `http`. |
|
|
378
411
|
| `PORT` | 3402 | HTTP server port (when `TRANSPORT=http`). |
|
|
379
412
|
| `BIND_ADDRESS` | `127.0.0.1` | HTTP bind address. Warning emitted for non-loopback addresses. |
|
|
413
|
+
| `HTTP_AUTH_TOKEN_FILE` | - | Private 0600 file holding the bearer token (32+ characters). Required when `TRANSPORT=http`. Raw HTTP_AUTH_TOKEN is refused. |
|
|
414
|
+
| `HTTP_ALLOWED_HOSTS` | - | Extra `Host` header values to accept (comma-separated), e.g. a reverse proxy's name. |
|
|
380
415
|
| `CORS_ORIGIN` | disabled | CORS origin for HTTP transport. |
|
|
381
416
|
| `FETCH_TIMEOUT_MS` | 30000 | HTTP request timeout in milliseconds. |
|
|
382
417
|
| `FETCH_MAX_RETRIES` | 2 | Maximum retry attempts for failed requests. Money-mutating POSTs use 0. |
|
|
@@ -388,7 +423,7 @@ HTTP transport includes:
|
|
|
388
423
|
| `TRANSPORT_PREFERENCE` | `onion,hns,https,http` | Preferred transport order for multi-URL services. |
|
|
389
424
|
| `TOR_PROXY` | — | SOCKS5 proxy for .onion requests only (e.g. `socks5h://127.0.0.1:9050`). |
|
|
390
425
|
| `SOCKS_PROXY` | — | SOCKS5 proxy for every paid-API request, names resolved by the proxy. Set this or `TOR_PROXY`, not both. Wallet and relay traffic is not proxied. |
|
|
391
|
-
| `HNS_GATEWAY_URL` | `https://query.hdns.io/` | DNS-over-HTTPS
|
|
426
|
+
| `HNS_GATEWAY_URL` | `https://query.hdns.io/` | DNS-over-HTTPS resolver for Handshake (HNS) names. Any host name ordinary DNS cannot find is sent here. |
|
|
392
427
|
|
|
393
428
|
## Multi-Transport Endpoints
|
|
394
429
|
|
|
@@ -416,7 +451,7 @@ Handshake names are resolved via DNS-over-HTTPS (RFC 8484 wire format) through t
|
|
|
416
451
|
|
|
417
452
|
### Credential Portability
|
|
418
453
|
|
|
419
|
-
Credentials are keyed by service **pubkey** (from the kind 31402 event)
|
|
454
|
+
Credentials are keyed by service **pubkey** (from the kind 31402 event) and bound to the transport origins they were bought through. A macaroon obtained via a Tor hidden service works over the service's clearnet URL, and vice versa, when both were in the `urls` list at purchase. A credential is never sent to an origin it is not bound to, whatever pubkey the request names.
|
|
420
455
|
|
|
421
456
|
## Ecosystem
|
|
422
457
|
|
|
@@ -468,7 +503,10 @@ Credentials are keyed by service **pubkey** (from the kind 31402 event), not by
|
|
|
468
503
|
### Agent with no wallet (human-in-the-loop)
|
|
469
504
|
|
|
470
505
|
```
|
|
471
|
-
1. l402-fetch(url)
|
|
472
|
-
2.
|
|
473
|
-
3.
|
|
506
|
+
1. l402-fetch(url, autoPay) → 402 received; payment page URL or QR shown
|
|
507
|
+
2. Human pays via wallet app
|
|
508
|
+
3. l402-pay(paymentHash) → polls the toll-booth payment page, stores the credential
|
|
509
|
+
4. l402-fetch(url) → data returned
|
|
510
|
+
|
|
511
|
+
Settlement can only be confirmed where the server reports it (toll-booth).
|
|
474
512
|
```
|
package/llms.txt
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# 402-mcp
|
|
2
2
|
|
|
3
|
-
> 402-mcp is an MCP server that gives AI agents economic agency
|
|
3
|
+
> 402-mcp is an MCP server that gives AI agents economic agency: the ability to discover, pay for, and consume L402 and other Lightning or ecash payment-gated APIs without human registration, API keys, or middlemen. It speaks the L402 protocol natively, carries its own payment methods, and makes autonomous purchasing decisions within human-set safety limits.
|
|
4
4
|
|
|
5
|
-
402-mcp is protocol-loyal: it works with any L402-compliant server (toll-booth, Aperture, or any conforming implementation), not just a specific vendor's stack. Bonus features activate automatically when talking to a toll-booth server.
|
|
5
|
+
402-mcp is protocol-loyal: it works with any L402-compliant server (toll-booth, Aperture, or any conforming implementation), not just a specific vendor's stack. Bonus features activate automatically when talking to a toll-booth server. Four payer methods are supported (NWC (Nostr Wallet Connect), Cashu ecash, LNURLcash bearer notes, and human-in-the-loop), tried in that priority order so the agent picks the most autonomous option available.
|
|
6
6
|
|
|
7
7
|
Agents can also discover paid APIs without knowing URLs upfront. The `l402-search` tool queries Nostr relays for kind 31402 service announcements — a decentralised registry of L402-enabled services. Discovered services may advertise multiple transport endpoints (clearnet HTTPS, Tor .onion, Handshake HNS domains). The agent automatically selects the best available transport and falls back through alternatives on connection failure — .onion endpoints need `TOR_PROXY` or `SOCKS_PROXY`; clearnet and HNS need no configuration.
|
|
8
8
|
|
|
@@ -14,12 +14,15 @@ Run instantly with no configuration required:
|
|
|
14
14
|
npx 402-mcp
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Use stdio wherever the client can start a local process. The HTTP transport can spend from your wallet, so it refuses to start without a bearer token, and only answers requests whose `Host` header it recognises:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
|
|
20
|
+
openssl rand -hex 32 > ~/.402-mcp/http.token && chmod 600 ~/.402-mcp/http.token
|
|
21
|
+
TRANSPORT=http HTTP_AUTH_TOKEN_FILE=~/.402-mcp/http.token npx 402-mcp
|
|
21
22
|
```
|
|
22
23
|
|
|
24
|
+
Clients send `Authorization: Bearer <token>`. It listens on `127.0.0.1` only by default. To reach it from another machine, put it behind a TLS reverse proxy and add the proxy's host name to `HTTP_ALLOWED_HOSTS`; never expose it directly.
|
|
25
|
+
|
|
23
26
|
### Claude Desktop / Cursor
|
|
24
27
|
|
|
25
28
|
Add to your MCP configuration:
|
|
@@ -45,25 +48,29 @@ Add to your MCP configuration:
|
|
|
45
48
|
|----------|---------|-------------|
|
|
46
49
|
| `NWC_URI_FILE` | — | Private 0600 file containing the Nostr Wallet Connect bearer URI |
|
|
47
50
|
| `CASHU_TOKENS` | — | Path to Cashu token store file |
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
51
|
+
| `LNURLCASH_NOTES` | - | Path to LNURLcash (LUD-25) bearer note store file |
|
|
52
|
+
| `MAX_AUTO_PAY_SATS` | 1000 | Most a single automatic payment may cost. Dearer challenges are not paid; they go back to the agent. `0` disables auto-pay |
|
|
53
|
+
| `MAX_SPEND_PER_MINUTE_SATS` | 10000 | Automatic spend allowed in any rolling 60 seconds. `0` blocks all auto-pay |
|
|
54
|
+
| `MAX_SPEND_PER_DAY_SATS` | 5000 | Automatic spend allowed in any rolling 24 hours, persisted across restarts. `0` blocks all auto-pay |
|
|
50
55
|
| `CREDENTIAL_STORE` | `~/.402-mcp/credentials.json` | Persistent macaroon/credential storage |
|
|
51
56
|
| `TRANSPORT` | `stdio` | Transport mode: `stdio` or `http` |
|
|
52
57
|
| `PORT` | 3402 | HTTP server port (when `TRANSPORT=http`) |
|
|
58
|
+
| `HTTP_AUTH_TOKEN_FILE` | - | Private 0600 file holding the bearer token; required when `TRANSPORT=http` |
|
|
59
|
+
| `HTTP_ALLOWED_HOSTS` | - | Extra `Host` header values the HTTP transport accepts (comma-separated) |
|
|
53
60
|
| `TRANSPORT_PREFERENCE` | `onion,hns,https,http` | Preferred transport order for multi-URL services |
|
|
54
61
|
| `TOR_PROXY` | — | SOCKS5 proxy for .onion requests only (e.g. `socks5h://127.0.0.1:9050`) |
|
|
55
62
|
| `SOCKS_PROXY` | — | SOCKS5 proxy for every paid-API request, names resolved by the proxy. Set this or `TOR_PROXY`, not both |
|
|
56
|
-
| `HNS_GATEWAY_URL` | `https://query.hdns.io/` | DNS-over-HTTPS
|
|
63
|
+
| `HNS_GATEWAY_URL` | `https://query.hdns.io/` | DNS-over-HTTPS resolver for Handshake names; any host name ordinary DNS cannot find is looked up here |
|
|
57
64
|
|
|
58
65
|
## Key Concepts
|
|
59
66
|
|
|
60
67
|
- **L402** — an HTTP payment protocol where a server responds to unauthenticated requests with HTTP 402, a Lightning invoice, and a macaroon. The client pays the invoice, then sends `Authorization: L402 <macaroon>:<preimage>` to access the API.
|
|
61
68
|
- **Economic agency** — the agent discovers a service, reads the price, pays the invoice, and retrieves the data, all within a single tool call. No human registration required.
|
|
62
|
-
- **
|
|
69
|
+
- **Spending limits**: `MAX_AUTO_PAY_SATS` is the most any single automatic payment can be; a dearer challenge is returned to the agent unpaid. Rolling 60-second and persisted 24-hour caps (`MAX_SPEND_PER_MINUTE_SATS`, `MAX_SPEND_PER_DAY_SATS`) stop runaway spending. The budget on the NWC connection itself is the real hard limit.
|
|
63
70
|
- **Credential store** — paid macaroons are persisted locally and encrypted with AES-256-GCM. Subsequent requests to the same server reuse credentials rather than paying again.
|
|
64
71
|
- **Credit balance** — many L402 servers (including toll-booth) grant a credit balance after payment. 402-mcp tracks and caches that balance so the agent can reason about remaining capacity.
|
|
65
72
|
- **Service discovery** — agents can search for L402 services on Nostr relays by querying kind 31402 announcements, finding paid APIs by topic, capability, or payment method.
|
|
66
|
-
- **Multi-transport
|
|
73
|
+
- **Multi-transport**: discovered services may advertise multiple endpoints (HTTPS, Tor .onion, Handshake HNS). The agent selects the best transport by preference (default: onion > hns > https > http), filters by capability (e.g. .onion requires a Tor proxy), and falls back automatically on connection failure. Credentials are keyed by service pubkey and bound to the transport origins they were bought through, so a macaroon obtained via one advertised transport works on the others, and is never sent to an origin it was not bought from.
|
|
67
74
|
|
|
68
75
|
## Core L402 Tools
|
|
69
76
|
|
|
@@ -77,13 +84,21 @@ Introspect the agent's payment capabilities and current configuration. Returns w
|
|
|
77
84
|
|
|
78
85
|
Probe an endpoint to discover its pricing without making a payment. Returns the invoice amount in satoshis, the macaroon challenge, and whether the server is a toll-booth instance (which enables extension tools). Use this before committing to a purchase so the agent can reason about value.
|
|
79
86
|
|
|
87
|
+
### `l402-fetch-preview`
|
|
88
|
+
|
|
89
|
+
Show what an endpoint (or each of a service's transport URLs) costs without paying: protocol, price and which wallet would pay. Cannot spend by construction. Widget hosts render it as a payment confirmation dialog.
|
|
90
|
+
|
|
80
91
|
### `l402-fetch`
|
|
81
92
|
|
|
82
|
-
Make an HTTP request to an L402-protected endpoint.
|
|
93
|
+
Make an HTTP request to an L402-protected endpoint. With `autoPay: true` it detects the 402, pays if the price is within `MAX_AUTO_PAY_SATS`, the optional per-call `maxCostSats` and the rolling spend caps, stores the credential, and retries. Without `autoPay` the challenge comes back with its price and `paymentHash`. Response bodies are wrapped in UNTRUSTED CONTENT markers. This is the primary tool for consuming L402 APIs.
|
|
83
94
|
|
|
84
95
|
### `l402-pay`
|
|
85
96
|
|
|
86
|
-
Pay a
|
|
97
|
+
Pay a challenge that `l402-fetch` or `l402-discover` returned, by its `paymentHash`, using the wallet priority NWC, Cashu, LNURLcash, human. An invoice that did not come from such a challenge is paid only if the human approves it in the client (MCP elicitation); otherwise it is refused.
|
|
98
|
+
|
|
99
|
+
### `l402-reconcile`
|
|
100
|
+
|
|
101
|
+
List payments whose outcome is unknown, or resolve one by asking the wallet that made it (NWC `lookup_invoice`, the Cashu mint, or the LNURLcash verify URL). While any is unresolved for a service, auto-pay to that service is paused.
|
|
87
102
|
|
|
88
103
|
### `l402-credentials`
|
|
89
104
|
|
|
@@ -95,7 +110,11 @@ Check the cached credit balance for a specific server URL. Returns the number of
|
|
|
95
110
|
|
|
96
111
|
### `l402-search`
|
|
97
112
|
|
|
98
|
-
Search for L402 services on Nostr relays. Queries kind 31402 announcement events and returns matching services with their names, URLs, pricing, capabilities, and supported payment methods. Filter by query text, topics (e.g. "ai", "data"), or payment method. Services may advertise multiple transport URLs (clearnet, .onion, HNS); pass the returned URLs array to `l402-fetch` for automatic transport selection and fallback. This enables agents to find paid APIs without knowing URLs upfront
|
|
113
|
+
Search for L402 services on Nostr relays. Queries kind 31402 announcement events (topic and payment-method filters are sent to the relays; query text is matched locally) and returns matching services with their names, URLs, pricing, capabilities, and supported payment methods. Filter by query text, topics (e.g. "ai", "data"), or payment method. Services may advertise multiple transport URLs (clearnet, .onion, HNS); pass the returned URLs array to `l402-fetch` for automatic transport selection and fallback. This enables agents to find paid APIs without knowing URLs upfront: a decentralised alternative to API directories. Announcement text is wrapped in UNTRUSTED CONTENT markers.
|
|
114
|
+
|
|
115
|
+
## Widget Tools
|
|
116
|
+
|
|
117
|
+
For hosts that support MCP Apps: `l402-service-directory` opens a searchable directory of services from `l402-search`, and `l402-wallet-dashboard` shows wallet status, limits and stored credentials. `l402-fetch-preview` has a payment confirmation widget.
|
|
99
118
|
|
|
100
119
|
## toll-booth Extension Tools
|
|
101
120
|
|
|
@@ -111,30 +130,36 @@ Redeem Cashu ecash tokens directly with the toll-booth server, bypassing the Lig
|
|
|
111
130
|
|
|
112
131
|
## Payment Methods
|
|
113
132
|
|
|
114
|
-
|
|
133
|
+
Four payer methods are tried in priority order:
|
|
115
134
|
|
|
116
135
|
1. **NWC (Nostr Wallet Connect)** — fully autonomous. The agent pays invoices directly from a connected Lightning wallet via the NWC protocol. Configure with `NWC_URI_FILE`; raw bearer values are refused. No human interaction required.
|
|
117
136
|
|
|
118
137
|
2. **Cashu** — fully autonomous. The agent melts ecash tokens to pay invoices. Configure with `CASHU_TOKENS` pointing to a token store file. No Lightning node required on the agent side.
|
|
119
138
|
|
|
120
|
-
3. **
|
|
139
|
+
3. **LNURLcash**: fully autonomous. The agent melts LUD-25 bearer notes to pay invoices. Configure with `LNURLCASH_NOTES`.
|
|
140
|
+
|
|
141
|
+
4. **Human-in-the-loop**: used when no autonomous payment method is configured. The agent presents a payment QR code or payment page, and `l402-pay` confirms settlement where the server reports it (toll-booth does). It does not take over above `MAX_AUTO_PAY_SATS`: a dearer challenge is simply returned to the agent unpaid.
|
|
121
142
|
|
|
122
143
|
The agent can override the payment method per-call, or you can configure only the methods you want available.
|
|
123
144
|
|
|
124
|
-
`l402-fetch` handles
|
|
145
|
+
`l402-fetch` handles four HTTP 402 challenge variants, plus an experimental x402 format:
|
|
125
146
|
|
|
126
147
|
- **L402** (`WWW-Authenticate: L402`) — the primary protocol; Lightning invoice paid via the wallet stack.
|
|
127
148
|
- **IETF Payment** (`WWW-Authenticate: Payment`, `draft-ryan-httpauth-payment-01`) — alternative Lightning path; builds a base64url credential after payment.
|
|
128
149
|
- **LNURLcash** (`X-LNURLcash: lnurlcashreq1…`, LUD-25): autonomous bearer-note payment; hands over a note worth exactly the price, splitting one at the mint if needed. Tried first: no Lightning hop and no swap at the mint.
|
|
129
150
|
- **xCashu** (`X-Cashu: creqA…`, NUT-18) — autonomous ecash payment; selects and sends the exact token amount without a Lightning round-trip.
|
|
130
|
-
- **x402** (`X-Payment-Required: x402`)
|
|
151
|
+
- **x402** (`X-Payment-Required: x402`), experimental: a custom format, not the x402 specification (whose servers send a base64 `PAYMENT-REQUIRED` header), so real x402 services are not supported. Payment details are shown to the human, who pays from their own wallet; nothing is paid autonomously.
|
|
131
152
|
|
|
132
153
|
## Safety Model
|
|
133
154
|
|
|
134
|
-
|
|
155
|
+
These limits apply to every automatic payment:
|
|
156
|
+
|
|
157
|
+
1. **`MAX_AUTO_PAY_SATS`** caps any single payment. A dearer challenge is returned to the agent unpaid; whether the agent then asks the human is up to the agent.
|
|
158
|
+
2. **`maxCostSats`** on `l402-fetch` lowers that cap for one call, so a price shown in a preview is binding.
|
|
159
|
+
3. **`MAX_SPEND_PER_MINUTE_SATS`** and **`MAX_SPEND_PER_DAY_SATS`** cap total spend over rolling 60-second and 24-hour windows; the daily window survives restarts. `0` blocks auto-pay.
|
|
160
|
+
4. **Unknown outcomes** pause auto-pay to that service until `l402-reconcile` resolves them.
|
|
135
161
|
|
|
136
|
-
|
|
137
|
-
2. **`MAX_SPEND_PER_MINUTE_SATS`** — rolling 60-second window cap across all payments. Prevents rapid successive payments from exceeding a total budget even if each individual payment is below the per-payment cap.
|
|
162
|
+
All of these are enforced in software by this process. The budget set on the NWC connection in the wallet is the real hard limit.
|
|
138
163
|
|
|
139
164
|
The human stays in control of three things: the safety caps, which payment methods are available, and credential visibility. The agent operates within those boundaries, not around them.
|
|
140
165
|
|
|
@@ -142,9 +167,11 @@ The human stays in control of three things: the safety caps, which payment metho
|
|
|
142
167
|
|
|
143
168
|
- **SSRF protection** — all outbound requests pass through an SSRF guard that blocks private/internal IP ranges by default. Handshake names are resolved via DNS-over-HTTPS with the resolved IP validated against blocked ranges. .onion addresses bypass DNS and route through the configured SOCKS proxy. With `SOCKS_PROXY`, no host name is resolved locally; IP literals and local names are still refused. Wallet and relay traffic is not proxied. Set `SSRF_ALLOW_PRIVATE=true` only for local development.
|
|
144
169
|
- **Credential encryption** — stored credentials are encrypted with AES-256-GCM. The encryption key is stored in the OS keychain when available, falling back to a file-based key with a warning.
|
|
145
|
-
- **Settlement proof
|
|
170
|
+
- **Settlement proof**: NWC responses are authenticated with NIP-44 v2 and signed events. NWC and Cashu preimages are verified against the invoice's payment hash; a payment reported without a matching preimage is treated as an unknown outcome, not as unpaid.
|
|
171
|
+
- **Prompt-injection hardening**: third-party text is marked as untrusted, and `l402-pay` only pays invoices from challenges this process received unless the human approves.
|
|
172
|
+
- **Credential binding**: stored credentials are sent only to the origins they were obtained from.
|
|
146
173
|
- **Spend tracking** — atomic `tryRecord()` prevents TOCTOU race conditions in concurrent spend checks.
|
|
147
|
-
- **HTTP transport hardening
|
|
174
|
+
- **HTTP transport hardening**: bearer token required (`HTTP_AUTH_TOKEN_FILE`), DNS rebinding protection through a `Host` allowlist, rate limiting (100 req/60s per IP), security headers (CSP, X-Frame-Options, nosniff), and loopback-only binding by default.
|
|
148
175
|
|
|
149
176
|
## Ecosystem
|
|
150
177
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "402-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"mcpName": "dev.forgesworn/402-mcp",
|
|
5
|
-
"description": "L402
|
|
5
|
+
"description": "L402 client MCP: AI agents discover, pay for and consume Lightning and ecash payment-gated APIs (x402 support is experimental)",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"402-mcp": "./build/index.js"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@cashu/cashu-ts": "^3.6.1",
|
|
40
|
-
"@forgesworn/nwc-kit": "0.1.
|
|
40
|
+
"@forgesworn/nwc-kit": "0.1.8",
|
|
41
41
|
"@modelcontextprotocol/ext-apps": "^1.3.1",
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
43
43
|
"@scure/base": "^2.0.0",
|
|
@@ -161,6 +161,15 @@ function el(tag, attrs, ...children) {
|
|
|
161
161
|
root.replaceChildren(el("div", { className: "status-text" }, text));
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// l402-search delimits announcement text as untrusted for the agent. The
|
|
165
|
+
// card shows the text itself; it is rendered as text, never as markup.
|
|
166
|
+
function plain(value) {
|
|
167
|
+
const m = typeof value === "string"
|
|
168
|
+
? /^\[UNTRUSTED CONTENT from [^\]\n]*\]\n([\s\S]*)\n\[END UNTRUSTED CONTENT\]$/.exec(value)
|
|
169
|
+
: null;
|
|
170
|
+
return m ? m[1] : value;
|
|
171
|
+
}
|
|
172
|
+
|
|
164
173
|
function renderServices(services, query) {
|
|
165
174
|
const searchInput = el("input", {
|
|
166
175
|
className: "search-box",
|
|
@@ -205,8 +214,8 @@ function el(tag, attrs, ...children) {
|
|
|
205
214
|
});
|
|
206
215
|
},
|
|
207
216
|
},
|
|
208
|
-
el("div", { className: "service-name" }, svc.name || "Unnamed Service"),
|
|
209
|
-
svc.about ? el("div", { className: "service-desc" }, svc.about) : null,
|
|
217
|
+
el("div", { className: "service-name" }, plain(svc.name) || "Unnamed Service"),
|
|
218
|
+
svc.about ? el("div", { className: "service-desc" }, plain(svc.about)) : null,
|
|
210
219
|
chips,
|
|
211
220
|
);
|
|
212
221
|
list.appendChild(card);
|