@relayos/mcp-paywall 0.1.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 ADDED
@@ -0,0 +1,208 @@
1
+ # @relayos/mcp-paywall
2
+
3
+ > Add pay-per-call RLUSD micropayments to any MCP tool server in one line of code.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@relayos/mcp-paywall)](https://www.npmjs.com/package/@relayos/mcp-paywall)
6
+ [![license](https://img.shields.io/npm/l/@relayos/mcp-paywall)](./LICENSE)
7
+ [![node](https://img.shields.io/node/v/@relayos/mcp-paywall)](https://nodejs.org)
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i @relayos/mcp-paywall
15
+ ```
16
+
17
+ Requires Node >= 22. Peer deps: `@modelcontextprotocol/sdk >= 1.0.0`, `zod >= 3.0.0`.
18
+
19
+ ---
20
+
21
+ ## Server: Gate any tool behind payment
22
+
23
+ Wrap your existing MCP tool handler with `paywall()`. That's it. No payment infra to run.
24
+
25
+ ```typescript
26
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
27
+ import { paywall, paywallSchema } from "@relayos/mcp-paywall";
28
+ import { z } from "zod";
29
+
30
+ const server = new McpServer({ name: "my-data-server", version: "1.0.0" });
31
+
32
+ server.tool(
33
+ "fetch-prices",
34
+ "Fetches proprietary price data",
35
+ paywallSchema({ symbol: z.string() }),
36
+ paywall(
37
+ {
38
+ priceRlusd: 0.10, // $0.10 RLUSD per call
39
+ recipient: "rYourXRPLAddress",
40
+ network: "xrpl_mainnet",
41
+ },
42
+ async ({ symbol }) => ({
43
+ content: [{ type: "text", text: JSON.stringify(await getPrices(symbol)) }],
44
+ })
45
+ )
46
+ );
47
+ ```
48
+
49
+ - `paywallSchema(shape)` — extends your Zod shape with the optional `_relay_payment` field so MCP lets the proof through
50
+ - `paywall(config, handler)` — returns a drop-in replacement handler that enforces payment before execution
51
+ - Agents without a payment proof receive a structured 402 challenge they can parse and auto-pay
52
+
53
+ ---
54
+
55
+ ## Agent: Auto-pay on 402
56
+
57
+ On the client side, `agentWallet()` intercepts 402 responses, signs an XRPL payment, and retries — transparently.
58
+
59
+ ```typescript
60
+ import { agentWallet } from "@relayos/mcp-paywall";
61
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
62
+
63
+ const mcp = new Client({ name: "my-agent", version: "1.0.0" });
64
+ // ... connect mcp to your transport
65
+
66
+ const wallet = agentWallet({
67
+ seed: process.env.AGENT_SEED!, // XRPL wallet seed — held in memory only
68
+ network: "xrpl_mainnet",
69
+ maxSpendPerCallRlusd: 1.0, // hard cap — never pays more than $1 per call
70
+ });
71
+
72
+ // Transparent auto-pay: call → 402 → sign → retry → result
73
+ const result = await wallet.callWithPayment(
74
+ (name, args) => mcp.callTool({ name, arguments: args }),
75
+ "fetch-prices",
76
+ { symbol: "BTC" }
77
+ );
78
+
79
+ console.log(result.content[0].text);
80
+ ```
81
+
82
+ The agent never pays more than `maxSpendPerCallRlusd`. If the server asks for more, the call throws before signing.
83
+
84
+ ---
85
+
86
+ ## How it works
87
+
88
+ The 402 handshake follows the [x402 protocol](https://x402.org) adapted for XRPL:
89
+
90
+ - **Challenge** — Server returns `{ error: "PAYMENT_REQUIRED", code: 402, invoice: { priceRlusd, recipient, endpointId, expiresAt } }` when no payment proof is present
91
+ - **Sign** — Agent wallet builds and signs an XRPL RLUSD Payment transaction targeting the exact recipient and amount, encodes it as a base64 proof envelope
92
+ - **Verify & Execute** — Server decodes the proof, checks amount, recipient, expiry, and anti-replay uniqueness, then executes the real handler if valid
93
+
94
+ All verification happens locally on the server — no Relay API call required for the basic flow.
95
+
96
+ ---
97
+
98
+ ## Zero-custody guarantees
99
+
100
+ - **Seed never leaves memory** — `agentWallet()` derives the XRPL address at construction time; the seed string is accessed only at signing time and never stored
101
+ - **Seed is never logged or serialised** — not in error messages, not in network requests
102
+ - **Hard spend cap** — `maxSpendPerCallRlusd` is enforced before any signing; mismatched invoices are rejected, not renegotiated
103
+ - **Anti-replay** — each payment proof is single-use; the server's per-instance store rejects duplicate proofs
104
+ - **Expiry enforcement** — invoices carry an `expiresAt` Unix timestamp; stale proofs are rejected on both sides
105
+ - **No shared state** — each `paywall()` call creates an isolated replay store; multi-tool servers can't cross-contaminate
106
+
107
+ ---
108
+
109
+ ## API
110
+
111
+ ### `paywall(config, handler)`
112
+
113
+ Wraps an MCP tool handler behind an RLUSD paywall.
114
+
115
+ ```typescript
116
+ function paywall<P extends Record<string, unknown>>(
117
+ config: PaywallConfig,
118
+ handler: ToolHandler<Omit<P, "_relay_payment">>
119
+ ): ToolHandler<P & { _relay_payment?: string }>
120
+ ```
121
+
122
+ **`PaywallConfig`**
123
+
124
+ | Field | Type | Required | Description |
125
+ |-------|------|----------|-------------|
126
+ | `priceRlusd` | `number` | yes | Price in RLUSD per tool call |
127
+ | `recipient` | `string` | yes | XRPL classic address receiving payment |
128
+ | `network` | `"xrpl_mainnet" \| "xrpl_testnet"` | yes | XRPL network |
129
+ | `description` | `string` | no | Human-readable description of what is being sold |
130
+ | `relayApiUrl` | `string` | no | If set, submits the tx to Relay for on-chain settlement confirmation |
131
+ | `gracePeriodMs` | `number` | no | Payment window in ms. Default: `300_000` (5 min) |
132
+
133
+ ---
134
+
135
+ ### `paywallSchema(shape)`
136
+
137
+ Extends any Zod raw shape with the optional `_relay_payment` field.
138
+
139
+ ```typescript
140
+ function paywallSchema<T extends ZodRawShape>(
141
+ shape: T
142
+ ): T & { _relay_payment: ZodOptional<ZodString> }
143
+ ```
144
+
145
+ Use this whenever you declare the tool schema so MCP passes the proof through instead of stripping it as an unknown field.
146
+
147
+ ---
148
+
149
+ ### `agentWallet(config)`
150
+
151
+ Creates an autonomous XRPL signing wallet for agent-side auto-pay.
152
+
153
+ ```typescript
154
+ function agentWallet(config: AgentWalletConfig): AgentWallet
155
+ ```
156
+
157
+ **`AgentWalletConfig`**
158
+
159
+ | Field | Type | Required | Description |
160
+ |-------|------|----------|-------------|
161
+ | `seed` | `string` | yes | XRPL wallet seed. Held in memory only — never logged or transmitted |
162
+ | `network` | `"xrpl_mainnet" \| "xrpl_testnet"` | yes | XRPL network |
163
+ | `maxSpendPerCallRlusd` | `number` | yes | Hard cap per call — agent refuses to pay more than this |
164
+ | `relayApiUrl` | `string` | no | Relay API base URL for server reputation checks before paying |
165
+ | `minServerReputationScore` | `number` | no | Reject servers whose on-chain reputation is below this score |
166
+
167
+ **`AgentWallet`**
168
+
169
+ ```typescript
170
+ interface AgentWallet {
171
+ readonly address: string; // XRPL classic address of the agent
172
+
173
+ callWithPayment(
174
+ callTool: (name: string, args: Record<string, unknown>) => Promise<CallToolResult>,
175
+ toolName: string,
176
+ toolArgs: Record<string, unknown>
177
+ ): Promise<CallToolResult>;
178
+ }
179
+ ```
180
+
181
+ ---
182
+
183
+ ### Types
184
+
185
+ ```typescript
186
+ // The 402 challenge body returned by a paywalled tool
187
+ interface PaymentInvoice {
188
+ version: "1.0";
189
+ priceRlusd: number;
190
+ recipient: string; // XRPL classic address
191
+ network: Network;
192
+ endpointId: string; // Unique per paywall() registration — prevents cross-tool replays
193
+ expiresAt: number; // Unix timestamp
194
+ }
195
+
196
+ // Base64-encoded JSON: { scheme, network, payload: signed_tx_blob }
197
+ type PaymentProof = string;
198
+
199
+ type Network = "xrpl_mainnet" | "xrpl_testnet";
200
+ ```
201
+
202
+ Additional exports: `is402Response`, `extract402Invoice`, `buildInvoice`, `verifyPayment`, `createInMemoryReplayStore` — see [source](https://github.com/timwal78/squeezeos/tree/main/relay/mcp-paywall/src) for full signatures.
203
+
204
+ ---
205
+
206
+ ## License
207
+
208
+ MIT — [timwal78/squeezeos](https://github.com/timwal78/squeezeos)
@@ -0,0 +1,41 @@
1
+ /**
2
+ * agentWallet() — client-side autonomous XRPL signer.
3
+ *
4
+ * Usage:
5
+ * const wallet = agentWallet({
6
+ * seed: process.env.AGENT_SEED!,
7
+ * network: "xrpl_testnet",
8
+ * maxSpendPerCallRlusd: 1.0,
9
+ * });
10
+ *
11
+ * // Transparent auto-pay: catches 402, pays, retries
12
+ * const result = await wallet.callWithPayment(
13
+ * (name, args) => client.callTool({ name, arguments: args }),
14
+ * "fetch-data",
15
+ * { query: "latest prices" }
16
+ * );
17
+ *
18
+ * SECURITY INVARIANTS (enforced by this module):
19
+ * - `seed` is accessed only at call time — never stored after wallet construction
20
+ * - `seed` is never logged, serialised, or included in any network request
21
+ * - Spending is hard-capped at `maxSpendPerCallRlusd` per call
22
+ * - Reputation gate: if `relayApiUrl` + `minServerReputationScore` are set,
23
+ * the server's on-chain score is verified before any payment is signed
24
+ */
25
+ import type { AgentWalletConfig, CallToolResult } from "./types";
26
+ export type CallToolFn = (name: string, args: Record<string, unknown>) => Promise<CallToolResult>;
27
+ export interface AgentWallet {
28
+ /** The XRPL address of the agent's wallet. */
29
+ readonly address: string;
30
+ /**
31
+ * Call an MCP tool, automatically handling 402 challenges.
32
+ *
33
+ * Flow:
34
+ * 1. Call tool without payment
35
+ * 2. If 402: verify price ≤ limit, optional reputation check, sign + retry
36
+ * 3. If still 402 after retry: throw
37
+ */
38
+ callWithPayment(callTool: CallToolFn, toolName: string, toolArgs: Record<string, unknown>): Promise<CallToolResult>;
39
+ }
40
+ export declare function agentWallet(config: AgentWalletConfig): AgentWallet;
41
+ //# sourceMappingURL=agent-wallet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-wallet.d.ts","sourceRoot":"","sources":["../src/agent-wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EACV,iBAAiB,EAEjB,cAAc,EAEf,MAAM,SAAS,CAAC;AAuFjB,MAAM,MAAM,UAAU,GAAG,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;;;;;OAOG;IACH,eAAe,CACb,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAmElE"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * agentWallet() — client-side autonomous XRPL signer.
4
+ *
5
+ * Usage:
6
+ * const wallet = agentWallet({
7
+ * seed: process.env.AGENT_SEED!,
8
+ * network: "xrpl_testnet",
9
+ * maxSpendPerCallRlusd: 1.0,
10
+ * });
11
+ *
12
+ * // Transparent auto-pay: catches 402, pays, retries
13
+ * const result = await wallet.callWithPayment(
14
+ * (name, args) => client.callTool({ name, arguments: args }),
15
+ * "fetch-data",
16
+ * { query: "latest prices" }
17
+ * );
18
+ *
19
+ * SECURITY INVARIANTS (enforced by this module):
20
+ * - `seed` is accessed only at call time — never stored after wallet construction
21
+ * - `seed` is never logged, serialised, or included in any network request
22
+ * - Spending is hard-capped at `maxSpendPerCallRlusd` per call
23
+ * - Reputation gate: if `relayApiUrl` + `minServerReputationScore` are set,
24
+ * the server's on-chain score is verified before any payment is signed
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.agentWallet = agentWallet;
28
+ const xrpl_1 = require("xrpl");
29
+ const paywall_1 = require("./paywall");
30
+ // ── RLUSD issuers ─────────────────────────────────────────────────────────────
31
+ const RLUSD_ISSUERS = {
32
+ xrpl_mainnet: "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
33
+ xrpl_testnet: "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De",
34
+ };
35
+ const XRPL_NODES = {
36
+ xrpl_mainnet: "wss://xrplcluster.com",
37
+ xrpl_testnet: "wss://s.altnet.rippletest.net:51233",
38
+ };
39
+ // ── Signing ───────────────────────────────────────────────────────────────────
40
+ /**
41
+ * Build a signed RLUSD Payment tx blob for the given invoice.
42
+ *
43
+ * Connects to XRPL to autofill sequence + fee.
44
+ * For test injection, provide `config._signPayment` to bypass network calls.
45
+ */
46
+ async function buildPaymentProof(invoice, config) {
47
+ // Test injection path — zero network access
48
+ if (config._signPayment) {
49
+ const txBlob = await config._signPayment(invoice);
50
+ return buildProofEnvelope(txBlob, config.network);
51
+ }
52
+ const wallet = xrpl_1.Wallet.fromSeed(config.seed);
53
+ const xrpl = new xrpl_1.Client(XRPL_NODES[config.network]);
54
+ await xrpl.connect();
55
+ try {
56
+ const tx = {
57
+ TransactionType: "Payment",
58
+ Account: wallet.classicAddress,
59
+ Destination: invoice.recipient,
60
+ Amount: {
61
+ currency: "USD",
62
+ issuer: RLUSD_ISSUERS[config.network],
63
+ value: invoice.priceRlusd.toString(),
64
+ },
65
+ };
66
+ const prepared = await xrpl.autofill(tx);
67
+ const { tx_blob } = wallet.sign(prepared);
68
+ return buildProofEnvelope(tx_blob, config.network);
69
+ }
70
+ finally {
71
+ await xrpl.disconnect();
72
+ }
73
+ }
74
+ function buildProofEnvelope(txBlob, network) {
75
+ const envelope = {
76
+ scheme: "exact",
77
+ network: network === "xrpl_mainnet" ? "xrpl-mainnet" : "xrpl-testnet",
78
+ payload: txBlob,
79
+ };
80
+ return Buffer.from(JSON.stringify(envelope)).toString("base64");
81
+ }
82
+ // ── Reputation gate ───────────────────────────────────────────────────────────
83
+ async function checkServerReputation(recipient, relayApiUrl, minScore) {
84
+ try {
85
+ const url = `${relayApiUrl.replace(/\/$/, "")}/api/v1/reputation/${recipient}`;
86
+ const res = await fetch(url, { signal: AbortSignal.timeout(3000) });
87
+ if (!res.ok)
88
+ return { safe: true, score: 0 }; // graceful degradation on error
89
+ const data = (await res.json());
90
+ const score = data?.score?.score ?? 0;
91
+ return { safe: score >= minScore, score };
92
+ }
93
+ catch {
94
+ return { safe: true, score: 0 }; // never block on reputation fetch failure
95
+ }
96
+ }
97
+ function agentWallet(config) {
98
+ // Derive address without storing seed reference beyond this call
99
+ const address = xrpl_1.Wallet.fromSeed(config.seed).classicAddress;
100
+ return {
101
+ address,
102
+ async callWithPayment(callTool, toolName, toolArgs) {
103
+ // Attempt 1 — no payment
104
+ const first = await callTool(toolName, toolArgs);
105
+ if (!(0, paywall_1.is402Response)(first))
106
+ return first;
107
+ // Extract challenge
108
+ const invoice = (0, paywall_1.extract402Invoice)(first);
109
+ if (!invoice) {
110
+ throw new Error("Received 402 but could not parse payment invoice");
111
+ }
112
+ // Spending guard — hard cap per call
113
+ if (invoice.priceRlusd > config.maxSpendPerCallRlusd) {
114
+ throw new Error(`Tool "${toolName}" costs ${invoice.priceRlusd} RLUSD which exceeds ` +
115
+ `maxSpendPerCallRlusd limit of ${config.maxSpendPerCallRlusd} RLUSD`);
116
+ }
117
+ // Invoice expiry check
118
+ if (invoice.expiresAt < Math.floor(Date.now() / 1000)) {
119
+ throw new Error(`Payment invoice for "${toolName}" has expired`);
120
+ }
121
+ // Optional: verify server reputation before paying
122
+ if (config.relayApiUrl && config.minServerReputationScore) {
123
+ const { safe, score } = await checkServerReputation(invoice.recipient, config.relayApiUrl, config.minServerReputationScore);
124
+ if (!safe) {
125
+ throw new Error(`Server "${invoice.recipient}" reputation score ${score} is below ` +
126
+ `minimum required ${config.minServerReputationScore} — payment refused`);
127
+ }
128
+ }
129
+ // Sign payment
130
+ const proof = await buildPaymentProof(invoice, config);
131
+ // Attempt 2 — with payment proof
132
+ const second = await callTool(toolName, {
133
+ ...toolArgs,
134
+ _relay_payment: proof,
135
+ });
136
+ if ((0, paywall_1.is402Response)(second)) {
137
+ const fc = second.content[0];
138
+ const rejText = (fc?.type === "text" ? fc.text : undefined) ?? "{}";
139
+ const rejection = JSON.parse(rejText);
140
+ throw new Error(`Payment rejected by server: ${rejection.reason ?? "unknown reason"}`);
141
+ }
142
+ return second;
143
+ },
144
+ };
145
+ }
146
+ //# sourceMappingURL=agent-wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-wallet.js","sourceRoot":"","sources":["../src/agent-wallet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @relayos/mcp-paywall — x402 RLUSD payment layer for Model Context Protocol.
3
+ *
4
+ * Server (earning wedge):
5
+ * import { paywall, paywallSchema } from "@relayos/mcp-paywall";
6
+ *
7
+ * Client (spending wedge):
8
+ * import { agentWallet } from "@relayos/mcp-paywall";
9
+ */
10
+ export { paywall, paywallSchema, is402Response, extract402Invoice, buildInvoice } from "./paywall";
11
+ export { agentWallet } from "./agent-wallet";
12
+ export { verifyPayment, createInMemoryReplayStore } from "./verifier";
13
+ export type { PaywallConfig, AgentWalletConfig, PaymentInvoice, PaymentChallenge, PaymentProof, CallToolResult, ToolHandler, ToolContent, TextContent, ImageContent, VerificationResult, Network, } from "./types";
14
+ export type { AntiReplayStore } from "./verifier";
15
+ export type { AgentWallet, CallToolFn } from "./agent-wallet";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACtE,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,OAAO,GACR,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * @relayos/mcp-paywall — x402 RLUSD payment layer for Model Context Protocol.
4
+ *
5
+ * Server (earning wedge):
6
+ * import { paywall, paywallSchema } from "@relayos/mcp-paywall";
7
+ *
8
+ * Client (spending wedge):
9
+ * import { agentWallet } from "@relayos/mcp-paywall";
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createInMemoryReplayStore = exports.verifyPayment = exports.agentWallet = exports.buildInvoice = exports.extract402Invoice = exports.is402Response = exports.paywallSchema = exports.paywall = void 0;
13
+ var paywall_1 = require("./paywall");
14
+ Object.defineProperty(exports, "paywall", { enumerable: true, get: function () { return paywall_1.paywall; } });
15
+ Object.defineProperty(exports, "paywallSchema", { enumerable: true, get: function () { return paywall_1.paywallSchema; } });
16
+ Object.defineProperty(exports, "is402Response", { enumerable: true, get: function () { return paywall_1.is402Response; } });
17
+ Object.defineProperty(exports, "extract402Invoice", { enumerable: true, get: function () { return paywall_1.extract402Invoice; } });
18
+ Object.defineProperty(exports, "buildInvoice", { enumerable: true, get: function () { return paywall_1.buildInvoice; } });
19
+ var agent_wallet_1 = require("./agent-wallet");
20
+ Object.defineProperty(exports, "agentWallet", { enumerable: true, get: function () { return agent_wallet_1.agentWallet; } });
21
+ var verifier_1 = require("./verifier");
22
+ Object.defineProperty(exports, "verifyPayment", { enumerable: true, get: function () { return verifier_1.verifyPayment; } });
23
+ Object.defineProperty(exports, "createInMemoryReplayStore", { enumerable: true, get: function () { return verifier_1.createInMemoryReplayStore; } });
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,qCAAmG;AAA1F,kGAAA,OAAO,OAAA;AAAE,wGAAA,aAAa,OAAA;AAAE,wGAAA,aAAa,OAAA;AAAE,4GAAA,iBAAiB,OAAA;AAAE,uGAAA,YAAY,OAAA;AAC/E,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AACpB,uCAAsE;AAA7D,yGAAA,aAAa,OAAA;AAAE,qHAAA,yBAAyB,OAAA"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * paywall() — server-side MCP tool wrapper.
3
+ *
4
+ * Usage:
5
+ * import { paywall, paywallSchema } from "@relayos/mcp-paywall";
6
+ * import { z } from "zod";
7
+ *
8
+ * server.tool(
9
+ * "fetch-data",
10
+ * "Fetches proprietary data",
11
+ * paywallSchema({ query: z.string() }),
12
+ * paywall(
13
+ * { priceRlusd: 0.10, recipient: "rYourAddress", network: "xrpl_testnet" },
14
+ * async ({ query }) => ({ content: [{ type: "text", text: yourData(query) }] })
15
+ * )
16
+ * );
17
+ *
18
+ * When a tool call arrives without `_relay_payment`, the wrapper returns a
19
+ * structured 402 challenge the agent wallet can parse and auto-pay.
20
+ * After a valid payment proof is provided the real handler executes — with
21
+ * `_relay_payment` stripped from params so the inner handler stays clean.
22
+ */
23
+ import { z } from "zod";
24
+ import type { PaywallConfig, ToolHandler, CallToolResult, PaymentInvoice } from "./types";
25
+ /**
26
+ * Extend any Zod raw shape with the optional `_relay_payment` field.
27
+ *
28
+ * MCP validates tool arguments against the declared schema; additional fields
29
+ * are stripped. Call this wrapper around your schema to let the payment proof
30
+ * pass through to the handler.
31
+ *
32
+ * server.tool("name", paywallSchema({ foo: z.string() }), paywall(cfg, handler))
33
+ */
34
+ export declare function paywallSchema<T extends z.ZodRawShape>(schema: T): T & {
35
+ _relay_payment: z.ZodOptional<z.ZodString>;
36
+ };
37
+ export declare function buildInvoice(config: PaywallConfig, endpointId: string): PaymentInvoice;
38
+ /**
39
+ * Wrap any MCP tool handler behind an x402 RLUSD paywall.
40
+ *
41
+ * Returns a handler function that:
42
+ * 1. Returns a 402 challenge if `_relay_payment` is absent
43
+ * 2. Verifies the proof (amount, recipient, anti-replay)
44
+ * 3. Calls the real handler with `_relay_payment` stripped from params
45
+ */
46
+ export declare function paywall<P extends Record<string, unknown>>(config: PaywallConfig, handler: ToolHandler<Omit<P, "_relay_payment">>): ToolHandler<P & {
47
+ _relay_payment?: string;
48
+ }>;
49
+ /** Detect whether a CallToolResult carries a Relay 402 challenge. */
50
+ export declare function is402Response(result: CallToolResult): boolean;
51
+ /** Extract the PaymentInvoice from a 402 CallToolResult. Returns null if not a 402. */
52
+ export declare function extract402Invoice(result: CallToolResult): PaymentInvoice | null;
53
+ //# sourceMappingURL=paywall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paywall.d.ts","sourceRoot":"","sources":["../src/paywall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAIjB;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EACnD,MAAM,EAAE,CAAC,GACR,CAAC,GAAG;IAAE,cAAc,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;CAAE,CAEpD;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAStF;AAuBD;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvD,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,GAC9C,WAAW,CAAC,CAAC,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB9C;AAID,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAU7D;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,GAAG,IAAI,CAU/E"}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * paywall() — server-side MCP tool wrapper.
4
+ *
5
+ * Usage:
6
+ * import { paywall, paywallSchema } from "@relayos/mcp-paywall";
7
+ * import { z } from "zod";
8
+ *
9
+ * server.tool(
10
+ * "fetch-data",
11
+ * "Fetches proprietary data",
12
+ * paywallSchema({ query: z.string() }),
13
+ * paywall(
14
+ * { priceRlusd: 0.10, recipient: "rYourAddress", network: "xrpl_testnet" },
15
+ * async ({ query }) => ({ content: [{ type: "text", text: yourData(query) }] })
16
+ * )
17
+ * );
18
+ *
19
+ * When a tool call arrives without `_relay_payment`, the wrapper returns a
20
+ * structured 402 challenge the agent wallet can parse and auto-pay.
21
+ * After a valid payment proof is provided the real handler executes — with
22
+ * `_relay_payment` stripped from params so the inner handler stays clean.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.paywallSchema = paywallSchema;
26
+ exports.buildInvoice = buildInvoice;
27
+ exports.paywall = paywall;
28
+ exports.is402Response = is402Response;
29
+ exports.extract402Invoice = extract402Invoice;
30
+ const zod_1 = require("zod");
31
+ const verifier_1 = require("./verifier");
32
+ // ── paywallSchema ─────────────────────────────────────────────────────────────
33
+ /**
34
+ * Extend any Zod raw shape with the optional `_relay_payment` field.
35
+ *
36
+ * MCP validates tool arguments against the declared schema; additional fields
37
+ * are stripped. Call this wrapper around your schema to let the payment proof
38
+ * pass through to the handler.
39
+ *
40
+ * server.tool("name", paywallSchema({ foo: z.string() }), paywall(cfg, handler))
41
+ */
42
+ function paywallSchema(schema) {
43
+ return { ...schema, _relay_payment: zod_1.z.string().optional() };
44
+ }
45
+ // ── Challenge builder ─────────────────────────────────────────────────────────
46
+ function buildInvoice(config, endpointId) {
47
+ return {
48
+ version: "1.0",
49
+ priceRlusd: config.priceRlusd,
50
+ recipient: config.recipient,
51
+ network: config.network,
52
+ endpointId,
53
+ expiresAt: Math.floor(Date.now() / 1000) + Math.floor((config.gracePeriodMs ?? 300_000) / 1000),
54
+ };
55
+ }
56
+ function challengeResult(config, endpointId) {
57
+ const challenge = {
58
+ error: "PAYMENT_REQUIRED",
59
+ code: 402,
60
+ invoice: buildInvoice(config, endpointId),
61
+ };
62
+ return {
63
+ content: [{ type: "text", text: JSON.stringify(challenge) }],
64
+ isError: true,
65
+ };
66
+ }
67
+ function rejectionResult(reason) {
68
+ return {
69
+ content: [{ type: "text", text: JSON.stringify({ error: "PAYMENT_INVALID", code: 402, reason }) }],
70
+ isError: true,
71
+ };
72
+ }
73
+ // ── paywall ───────────────────────────────────────────────────────────────────
74
+ /**
75
+ * Wrap any MCP tool handler behind an x402 RLUSD paywall.
76
+ *
77
+ * Returns a handler function that:
78
+ * 1. Returns a 402 challenge if `_relay_payment` is absent
79
+ * 2. Verifies the proof (amount, recipient, anti-replay)
80
+ * 3. Calls the real handler with `_relay_payment` stripped from params
81
+ */
82
+ function paywall(config, handler) {
83
+ const endpointId = `${config.recipient}:${config.priceRlusd}:${config.network}`;
84
+ // Per-instance store: each paywall() call is isolated from every other.
85
+ // For multi-replica deployments swap this for a Redis-backed store.
86
+ const store = (0, verifier_1.createInMemoryReplayStore)();
87
+ return async (params, extra) => {
88
+ const { _relay_payment, ...toolParams } = params;
89
+ if (!_relay_payment || typeof _relay_payment !== "string") {
90
+ return challengeResult(config, endpointId);
91
+ }
92
+ const result = await (0, verifier_1.verifyPayment)(_relay_payment, config, store);
93
+ if (!result.valid) {
94
+ return rejectionResult(result.reason ?? "Payment verification failed");
95
+ }
96
+ return handler(toolParams, extra);
97
+ };
98
+ }
99
+ // ── Type guard ────────────────────────────────────────────────────────────────
100
+ /** Detect whether a CallToolResult carries a Relay 402 challenge. */
101
+ function is402Response(result) {
102
+ if (!result.isError)
103
+ return false;
104
+ try {
105
+ const first = result.content[0];
106
+ const text = (first?.type === "text" ? first.text : undefined) ?? "";
107
+ const parsed = JSON.parse(text);
108
+ return parsed.code === 402;
109
+ }
110
+ catch {
111
+ return false;
112
+ }
113
+ }
114
+ /** Extract the PaymentInvoice from a 402 CallToolResult. Returns null if not a 402. */
115
+ function extract402Invoice(result) {
116
+ try {
117
+ const first = result.content[0];
118
+ const text = (first?.type === "text" ? first.text : undefined) ?? "";
119
+ const parsed = JSON.parse(text);
120
+ if (parsed.code === 402 && parsed.invoice)
121
+ return parsed.invoice;
122
+ }
123
+ catch {
124
+ // fall through
125
+ }
126
+ return null;
127
+ }
128
+ //# sourceMappingURL=paywall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paywall.js","sourceRoot":"","sources":["../src/paywall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Shared types for @relayos/mcp-paywall.
3
+ * Self-contained — no imports from relay/sdk to keep the package standalone.
4
+ */
5
+ export type Network = "xrpl_mainnet" | "xrpl_testnet";
6
+ export interface PaymentInvoice {
7
+ /** Version tag for future protocol upgrades. */
8
+ version: "1.0";
9
+ priceRlusd: number;
10
+ recipient: string;
11
+ network: Network;
12
+ /** Unique per tool registration — prevents cross-tool replays. */
13
+ endpointId: string;
14
+ /** Unix timestamp. Client must pay before this. */
15
+ expiresAt: number;
16
+ }
17
+ export interface PaymentChallenge {
18
+ error: "PAYMENT_REQUIRED";
19
+ code: 402;
20
+ invoice: PaymentInvoice;
21
+ }
22
+ /** Base64-encoded JSON: { scheme, network, payload: signed_tx_blob } */
23
+ export type PaymentProof = string;
24
+ export interface PaywallConfig {
25
+ /** Price in RLUSD for one tool call. */
26
+ priceRlusd: number;
27
+ /** XRPL address that receives payment. */
28
+ recipient: string;
29
+ network: Network;
30
+ description?: string;
31
+ /** If set, server submits the tx to XRPL for settlement confirmation. */
32
+ relayApiUrl?: string;
33
+ /** Payment window in ms. Default: 300_000 (5 min). */
34
+ gracePeriodMs?: number;
35
+ }
36
+ export interface AgentWalletConfig {
37
+ /** XRPL wallet seed. Held in memory only — NEVER logged or transmitted. */
38
+ seed: string;
39
+ network: Network;
40
+ /** Maximum price per tool call the agent will auto-pay without human approval. */
41
+ maxSpendPerCallRlusd: number;
42
+ /** Relay API base URL for reputation checks before paying. */
43
+ relayApiUrl?: string;
44
+ /** Reject servers whose reputation score is below this threshold. */
45
+ minServerReputationScore?: number;
46
+ /**
47
+ * Inject a custom signer for testing.
48
+ * Production code omits this — real XRPL signing is used.
49
+ */
50
+ _signPayment?: (invoice: PaymentInvoice) => Promise<string>;
51
+ }
52
+ export interface TextContent {
53
+ type: "text";
54
+ text: string;
55
+ }
56
+ export interface ImageContent {
57
+ type: "image";
58
+ data: string;
59
+ mimeType: string;
60
+ }
61
+ export type ToolContent = TextContent | ImageContent;
62
+ export interface CallToolResult {
63
+ content: ToolContent[];
64
+ isError?: boolean;
65
+ }
66
+ export type ToolHandler<P extends Record<string, unknown> = Record<string, unknown>> = (params: P, extra?: unknown) => CallToolResult | Promise<CallToolResult>;
67
+ export interface VerificationResult {
68
+ valid: boolean;
69
+ reason?: string;
70
+ }
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG,cAAc,CAAC;AAItD,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,OAAO,EAAE,KAAK,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,GAAG,CAAC;IACV,OAAO,EAAE,cAAc,CAAC;CACzB;AAID,wEAAwE;AACxE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAIlC,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7D;AAID,MAAM,WAAW,WAAW;IAAI,IAAI,EAAE,MAAM,CAAC;IAAK,IAAI,EAAE,MAAM,CAAA;CAAE;AAChE,MAAM,WAAW,YAAY;IAAG,IAAI,EAAE,OAAO,CAAC;IAAI,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;AAElF,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACjF,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAI3E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Shared types for @relayos/mcp-paywall.
4
+ * Self-contained — no imports from relay/sdk to keep the package standalone.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Payment proof verification.
3
+ *
4
+ * The proof is a base64-encoded JSON envelope:
5
+ * { scheme: "exact", network: string, payload: "<signed_xrpl_tx_blob>" }
6
+ *
7
+ * Verification steps:
8
+ * 1. Decode the envelope
9
+ * 2. Decode the XRPL tx blob (xrpl.decode)
10
+ * 3. Verify TransactionType === "Payment"
11
+ * 4. Verify Destination === config.recipient
12
+ * 5. Verify Amount >= config.priceRlusd (RLUSD IOU or XRP)
13
+ * 6. Anti-replay: reject if this tx_blob was used in the last gracePeriodMs
14
+ *
15
+ * Anti-replay is in-process (Map with TTL). For multi-instance deployments
16
+ * replace with a shared Redis SET — the `_antiReplay` export is injectable.
17
+ */
18
+ import type { PaywallConfig, VerificationResult } from "./types";
19
+ export interface AntiReplayStore {
20
+ has(key: string): boolean;
21
+ set(key: string, expiresAt: number): void;
22
+ sweep(): void;
23
+ }
24
+ export declare function createInMemoryReplayStore(): AntiReplayStore;
25
+ export declare function verifyPayment(proofBase64: string, config: PaywallConfig, store?: AntiReplayStore): Promise<VerificationResult>;
26
+ //# sourceMappingURL=verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../src/verifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAIjE,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,yBAAyB,IAAI,eAAe,CAe3D;AAcD,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,EACrB,KAAK,GAAE,eAA+B,GACrC,OAAO,CAAC,kBAAkB,CAAC,CAiD7B"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * Payment proof verification.
4
+ *
5
+ * The proof is a base64-encoded JSON envelope:
6
+ * { scheme: "exact", network: string, payload: "<signed_xrpl_tx_blob>" }
7
+ *
8
+ * Verification steps:
9
+ * 1. Decode the envelope
10
+ * 2. Decode the XRPL tx blob (xrpl.decode)
11
+ * 3. Verify TransactionType === "Payment"
12
+ * 4. Verify Destination === config.recipient
13
+ * 5. Verify Amount >= config.priceRlusd (RLUSD IOU or XRP)
14
+ * 6. Anti-replay: reject if this tx_blob was used in the last gracePeriodMs
15
+ *
16
+ * Anti-replay is in-process (Map with TTL). For multi-instance deployments
17
+ * replace with a shared Redis SET — the `_antiReplay` export is injectable.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createInMemoryReplayStore = createInMemoryReplayStore;
21
+ exports.verifyPayment = verifyPayment;
22
+ const xrpl_1 = require("xrpl");
23
+ function createInMemoryReplayStore() {
24
+ const store = new Map();
25
+ return {
26
+ has(key) {
27
+ const exp = store.get(key);
28
+ if (exp === undefined)
29
+ return false;
30
+ if (Date.now() > exp) {
31
+ store.delete(key);
32
+ return false;
33
+ }
34
+ return true;
35
+ },
36
+ set(key, expiresAt) { store.set(key, expiresAt); },
37
+ sweep() {
38
+ const now = Date.now();
39
+ for (const [k, exp] of store)
40
+ if (now > exp)
41
+ store.delete(k);
42
+ },
43
+ };
44
+ }
45
+ // Default store — per-process singleton
46
+ const _defaultStore = createInMemoryReplayStore();
47
+ // ── RLUSD issuer addresses ────────────────────────────────────────────────────
48
+ const RLUSD_ISSUERS = {
49
+ xrpl_mainnet: "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
50
+ xrpl_testnet: "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De",
51
+ };
52
+ // ── Core verification ─────────────────────────────────────────────────────────
53
+ async function verifyPayment(proofBase64, config, store = _defaultStore) {
54
+ // 1. Parse outer envelope
55
+ let envelope;
56
+ try {
57
+ envelope = JSON.parse(Buffer.from(proofBase64, "base64").toString("utf8"));
58
+ }
59
+ catch {
60
+ return { valid: false, reason: "Malformed proof envelope" };
61
+ }
62
+ const txBlob = envelope.payload;
63
+ if (typeof txBlob !== "string" || !txBlob) {
64
+ return { valid: false, reason: "Missing tx payload in proof" };
65
+ }
66
+ // 2. Decode XRPL tx blob
67
+ let decoded;
68
+ try {
69
+ decoded = (0, xrpl_1.decode)(txBlob);
70
+ }
71
+ catch {
72
+ return { valid: false, reason: "Cannot decode XRPL tx blob" };
73
+ }
74
+ // 3. Transaction type
75
+ if (decoded.TransactionType !== "Payment") {
76
+ return { valid: false, reason: `Expected Payment tx, got ${decoded.TransactionType}` };
77
+ }
78
+ // 4. Destination
79
+ if (decoded.Destination !== config.recipient) {
80
+ return {
81
+ valid: false,
82
+ reason: `Wrong recipient: expected ${config.recipient}, got ${decoded.Destination}`,
83
+ };
84
+ }
85
+ // 5. Amount: RLUSD IOU or XRP drops
86
+ const ok = verifyAmount(decoded.Amount, config.priceRlusd, config.network);
87
+ if (!ok.valid)
88
+ return ok;
89
+ // 6. Anti-replay: use tx_blob fingerprint (last 32 chars of blob are signature-unique)
90
+ const replayKey = `${decoded.Destination}:${txBlob.slice(-48)}`;
91
+ if (store.has(replayKey)) {
92
+ return { valid: false, reason: "Payment proof already used (replay attack)" };
93
+ }
94
+ const gracePeriodMs = config.gracePeriodMs ?? 300_000;
95
+ store.set(replayKey, Date.now() + gracePeriodMs);
96
+ return { valid: true };
97
+ }
98
+ function verifyAmount(amount, requiredRlusd, network) {
99
+ if (amount === null || amount === undefined) {
100
+ return { valid: false, reason: "Missing Amount field" };
101
+ }
102
+ // RLUSD IOU: { currency: "USD", issuer: "...", value: "0.10" }
103
+ if (typeof amount === "object") {
104
+ const iou = amount;
105
+ if (iou.currency !== "USD") {
106
+ return { valid: false, reason: `Expected USD currency, got ${iou.currency}` };
107
+ }
108
+ const expectedIssuer = RLUSD_ISSUERS[network];
109
+ if (expectedIssuer && iou.issuer !== expectedIssuer) {
110
+ return { valid: false, reason: "RLUSD issuer mismatch" };
111
+ }
112
+ const paid = parseFloat(iou.value ?? "0");
113
+ if (paid < requiredRlusd) {
114
+ return { valid: false, reason: `Underpayment: ${paid} RLUSD < ${requiredRlusd} RLUSD required` };
115
+ }
116
+ return { valid: true };
117
+ }
118
+ // XRP drops (fallback; servers should prefer RLUSD)
119
+ if (typeof amount === "string") {
120
+ const drops = parseInt(amount, 10);
121
+ const xrp = drops / 1_000_000;
122
+ // Testnet approximation: 1 RLUSD = 0.5 XRP — accept if 2x overcharged to be safe
123
+ if (xrp < requiredRlusd * 0.4) {
124
+ return { valid: false, reason: `XRP amount ${xrp} insufficient for ${requiredRlusd} RLUSD` };
125
+ }
126
+ return { valid: true };
127
+ }
128
+ return { valid: false, reason: "Unrecognised Amount format" };
129
+ }
130
+ //# sourceMappingURL=verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.js","sourceRoot":"","sources":["../src/verifier.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@relayos/mcp-paywall",
3
+ "version": "0.1.0",
4
+ "description": "Zero-friction x402 RLUSD payment layer for Model Context Protocol tools",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "files": ["dist", "README.md"],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/timwal78/squeezeos"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "xrpl",
21
+ "rlusd",
22
+ "x402",
23
+ "agent-payments",
24
+ "model-context-protocol",
25
+ "ai-payments",
26
+ "micropayments"
27
+ ],
28
+ "homepage": "https://github.com/timwal78/squeezeos/tree/main/relay/mcp-paywall",
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "prepublishOnly": "npm run build && npm test",
32
+ "test": "jest --no-coverage",
33
+ "dev": "tsc --watch"
34
+ },
35
+ "peerDependencies": {
36
+ "@modelcontextprotocol/sdk": ">=1.0.0",
37
+ "zod": ">=3.0.0"
38
+ },
39
+ "dependencies": {
40
+ "xrpl": "^4.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.29.0",
44
+ "@types/jest": "^29.5.0",
45
+ "@types/node": "^22.0.0",
46
+ "jest": "^29.7.0",
47
+ "ts-jest": "^29.2.0",
48
+ "typescript": "^5.4.0",
49
+ "zod": "^3.22.0"
50
+ },
51
+ "engines": { "node": ">=22.0.0" },
52
+ "publishConfig": { "access": "public" },
53
+ "license": "MIT"
54
+ }