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

@relayos/mcp-paywall

v0.1.0

Published

Zero-friction x402 RLUSD payment layer for Model Context Protocol tools

Readme

@relayos/mcp-paywall

Add pay-per-call RLUSD micropayments to any MCP tool server in one line of code.

npm license node


Install

npm i @relayos/mcp-paywall

Requires Node >= 22. Peer deps: @modelcontextprotocol/sdk >= 1.0.0, zod >= 3.0.0.


Server: Gate any tool behind payment

Wrap your existing MCP tool handler with paywall(). That's it. No payment infra to run.

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { paywall, paywallSchema } from "@relayos/mcp-paywall";
import { z } from "zod";

const server = new McpServer({ name: "my-data-server", version: "1.0.0" });

server.tool(
  "fetch-prices",
  "Fetches proprietary price data",
  paywallSchema({ symbol: z.string() }),
  paywall(
    {
      priceRlusd: 0.10,          // $0.10 RLUSD per call
      recipient: "rYourXRPLAddress",
      network: "xrpl_mainnet",
    },
    async ({ symbol }) => ({
      content: [{ type: "text", text: JSON.stringify(await getPrices(symbol)) }],
    })
  )
);
  • paywallSchema(shape) — extends your Zod shape with the optional _relay_payment field so MCP lets the proof through
  • paywall(config, handler) — returns a drop-in replacement handler that enforces payment before execution
  • Agents without a payment proof receive a structured 402 challenge they can parse and auto-pay

Agent: Auto-pay on 402

On the client side, agentWallet() intercepts 402 responses, signs an XRPL payment, and retries — transparently.

import { agentWallet } from "@relayos/mcp-paywall";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";

const mcp = new Client({ name: "my-agent", version: "1.0.0" });
// ... connect mcp to your transport

const wallet = agentWallet({
  seed: process.env.AGENT_SEED!,      // XRPL wallet seed — held in memory only
  network: "xrpl_mainnet",
  maxSpendPerCallRlusd: 1.0,          // hard cap — never pays more than $1 per call
});

// Transparent auto-pay: call → 402 → sign → retry → result
const result = await wallet.callWithPayment(
  (name, args) => mcp.callTool({ name, arguments: args }),
  "fetch-prices",
  { symbol: "BTC" }
);

console.log(result.content[0].text);

The agent never pays more than maxSpendPerCallRlusd. If the server asks for more, the call throws before signing.


How it works

The 402 handshake follows the x402 protocol adapted for XRPL:

  • Challenge — Server returns { error: "PAYMENT_REQUIRED", code: 402, invoice: { priceRlusd, recipient, endpointId, expiresAt } } when no payment proof is present
  • Sign — Agent wallet builds and signs an XRPL RLUSD Payment transaction targeting the exact recipient and amount, encodes it as a base64 proof envelope
  • Verify & Execute — Server decodes the proof, checks amount, recipient, expiry, and anti-replay uniqueness, then executes the real handler if valid

All verification happens locally on the server — no Relay API call required for the basic flow.


Zero-custody guarantees

  • Seed never leaves memoryagentWallet() derives the XRPL address at construction time; the seed string is accessed only at signing time and never stored
  • Seed is never logged or serialised — not in error messages, not in network requests
  • Hard spend capmaxSpendPerCallRlusd is enforced before any signing; mismatched invoices are rejected, not renegotiated
  • Anti-replay — each payment proof is single-use; the server's per-instance store rejects duplicate proofs
  • Expiry enforcement — invoices carry an expiresAt Unix timestamp; stale proofs are rejected on both sides
  • No shared state — each paywall() call creates an isolated replay store; multi-tool servers can't cross-contaminate

API

paywall(config, handler)

Wraps an MCP tool handler behind an RLUSD paywall.

function paywall<P extends Record<string, unknown>>(
  config: PaywallConfig,
  handler: ToolHandler<Omit<P, "_relay_payment">>
): ToolHandler<P & { _relay_payment?: string }>

PaywallConfig

| Field | Type | Required | Description | |-------|------|----------|-------------| | priceRlusd | number | yes | Price in RLUSD per tool call | | recipient | string | yes | XRPL classic address receiving payment | | network | "xrpl_mainnet" \| "xrpl_testnet" | yes | XRPL network | | description | string | no | Human-readable description of what is being sold | | relayApiUrl | string | no | If set, submits the tx to Relay for on-chain settlement confirmation | | gracePeriodMs | number | no | Payment window in ms. Default: 300_000 (5 min) |


paywallSchema(shape)

Extends any Zod raw shape with the optional _relay_payment field.

function paywallSchema<T extends ZodRawShape>(
  shape: T
): T & { _relay_payment: ZodOptional<ZodString> }

Use this whenever you declare the tool schema so MCP passes the proof through instead of stripping it as an unknown field.


agentWallet(config)

Creates an autonomous XRPL signing wallet for agent-side auto-pay.

function agentWallet(config: AgentWalletConfig): AgentWallet

AgentWalletConfig

| Field | Type | Required | Description | |-------|------|----------|-------------| | seed | string | yes | XRPL wallet seed. Held in memory only — never logged or transmitted | | network | "xrpl_mainnet" \| "xrpl_testnet" | yes | XRPL network | | maxSpendPerCallRlusd | number | yes | Hard cap per call — agent refuses to pay more than this | | relayApiUrl | string | no | Relay API base URL for server reputation checks before paying | | minServerReputationScore | number | no | Reject servers whose on-chain reputation is below this score |

AgentWallet

interface AgentWallet {
  readonly address: string;  // XRPL classic address of the agent

  callWithPayment(
    callTool: (name: string, args: Record<string, unknown>) => Promise<CallToolResult>,
    toolName: string,
    toolArgs: Record<string, unknown>
  ): Promise<CallToolResult>;
}

Types

// The 402 challenge body returned by a paywalled tool
interface PaymentInvoice {
  version: "1.0";
  priceRlusd: number;
  recipient: string;          // XRPL classic address
  network: Network;
  endpointId: string;         // Unique per paywall() registration — prevents cross-tool replays
  expiresAt: number;          // Unix timestamp
}

// Base64-encoded JSON: { scheme, network, payload: signed_tx_blob }
type PaymentProof = string;

type Network = "xrpl_mainnet" | "xrpl_testnet";

Additional exports: is402Response, extract402Invoice, buildInvoice, verifyPayment, createInMemoryReplayStore — see source for full signatures.


License

MIT — timwal78/squeezeos