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

@parallel-protocol/mpp

v0.1.1

Published

Parallel Protocol MPP (Machine Payments Protocol) merchant middleware — the Payment HTTP auth-scheme dance (402 challenge → credential → receipt) over the facilitator's /mpp endpoints

Readme

@parallel-protocol/mpp

Merchant middleware for the MPP (Machine Payments Protocol) charge intent — the Payment HTTP auth-scheme dance, settled through the Parallel facilitator.

MPP is the sibling of @parallel-protocol/x402: same merchant ergonomics (a middleware in front of a paid route), same facilitator backend, different wire protocol. Where x402 uses a 402 response with a custom payment-required header, MPP follows the RFC 9110 WWW-Authenticate convention with a Payment auth-scheme.


How it works

Agent                        Merchant (this SDK)           Facilitator
  |                                |                             |
  |── GET /api/quote ─────────────>|                             |
  |                                | (no Authorization header)   |
  |<─ 402 + WWW-Authenticate ──────|                             |
  |   Payment id="...",            |                             |
  |   method="evm",                |                             |
  |   intent="charge",             |                             |
  |   request="<base64url>"        |                             |
  |                                |                             |
  | [agent decodes request,        |                             |
  |  signs EIP-3009 authorization, |                             |
  |  builds credential JSON]       |                             |
  |                                |                             |
  |── GET /api/quote ─────────────>|                             |
  |   Authorization: Payment       |                             |
  |   <base64url(credential)>      |── POST /mpp/verify ────────>|
  |                                |<─ { valid: true } ──────────|
  |                                |                             |
  |                                | [handler runs]              |
  |                                |                             |
  |                                |── POST /mpp/settle ────────>|
  |                                |<─ MppReceipt ───────────────|
  |                                |                             |
  |<─ 200 + Payment-Receipt ───────|                             |
  |   <base64url(receipt)>         |                             |

Key properties:

  • The handler only runs after the credential is verified.
  • Settlement (on-chain) happens after a 2xx handler response — the agent is never charged on errors.
  • If settlement fails, the middleware returns 402 and discards the handler response.
  • CORS preflight (OPTIONS) is always passed through without challenge.
  • The 402 challenge body also includes the challenge object as JSON for agents that prefer parsing the body over parsing the header.

Install

npm install @parallel-protocol/mpp
# or
bun add @parallel-protocol/mpp

Install your framework separately (all are optional peer dependencies):

npm install express       # Express 4 or 5
npm install next          # Next.js 14 or 15
npm install fastify       # Fastify 4 or 5
npm install hono          # Hono 4+

Quick start

Hono

import { Hono } from "hono";
import { paymentMiddleware } from "@parallel-protocol/mpp/hono";

const app = new Hono();

app.use(
  paymentMiddleware({
    facilitator: { url: "https://agents.parallel.best" },
    routes: {
      "/api/quote": {
        price: "0.10",     // 0.10 USDC (6 decimals by default)
        network: "base",
        currency: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  // USDC on Base
        payTo: "0xYourAddress",
        method: "evm",
      },
    },
  }),
);

app.get("/api/quote", (c) => c.json({ quote: 42 }));

Express

import express from "express";
import { paymentMiddleware } from "@parallel-protocol/mpp/express";

const app = express();

app.use(
  paymentMiddleware({
    facilitator: {
      url: "https://agents.parallel.best",
    },
    routes: {
      "/api/data": {
        price: "0.01",
        decimals: 18,          // USDp has 18 decimals
        network: "ethereum",
        currency: "0x9B3a8f7CEC208e247d97dEE13313690977e24459",  // USDp on Ethereum
        payTo: "0xYourAddress",
        method: "parallel",   // unlocks all routes A–L
      },
    },
  }),
);

app.get("/api/data", (req, res) => {
  res.json({ data: "protected content" });
});

Next.js (App Router)

// app/api/data/route.ts
import { withPayment } from "@parallel-protocol/mpp/next";

export const GET = withPayment(
  {
    facilitator: { url: "https://agents.parallel.best" },
    routes: {
      "/api/data": {
        price: "0.05",
        decimals: 18,          // USDp has 18 decimals
        network: "base",
        currency: "0x76A9A0062ec6712b99B4f63bD2b4270185759dd5",  // USDp on Base
        payTo: "0xYourAddress",
      },
    },
  },
  async (req) => {
    return Response.json({ data: "protected content" });
  },
);

Fastify

import Fastify from "fastify";
import { paymentMiddleware } from "@parallel-protocol/mpp/fastify";

const app = Fastify();

await app.register(
  paymentMiddleware({
    facilitator: { url: "https://agents.parallel.best" },
    routes: {
      "/api/data": {
        price: 1000000n,   // 1 USDC as raw bigint (6 decimals → skip parsing)
        network: "avalanche",
        currency: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
        payTo: "0xYourAddress",
      },
    },
  }),
);

app.get("/api/data", async () => ({ data: "protected content" }));

Configuration

MppMiddlewareConfig

interface MppMiddlewareConfig {
  facilitator: MppFacilitatorConfig;
  routes: Record<string, MppRouteConfig>;
}

MppFacilitatorConfig

| Field | Type | Required | Description | |-------|------|----------|-------------| | url | string | Yes | Base URL of the Parallel facilitator (e.g. https://agents.parallel.best). The SDK internally appends /mpp/verify and /mpp/settle to this value. | | apiKey | string | No | Reserved for future use. If provided, sent as X-API-Key header on facilitator requests but currently not enforced server-side. |

MppRouteConfig

| Field | Type | Required | Description | |-------|------|----------|-------------| | price | string \| bigint | Yes | Amount required. A decimal string like "0.10" is parsed using decimals. Pass a bigint to skip parsing. | | decimals | number | No | Token decimals for string price parsing. Default: 6 (USDC). Use 18 for USDp or sUSDp. | | network | string | Yes | Chain slug: "ethereum", "base", "avalanche", "hyperevm", etc. Must be in the Parallel chain catalog. | | payTo | Address | Yes | Merchant receiving address. | | currency | Address | Yes | ERC-20 token the payer authorizes. One token per route. | | method | "evm" \| "parallel" | No | Default: "evm". Controls the credential shape agents must provide (see Methods). | | maxTimeoutSeconds | number | No | Challenge validity window in seconds. Default 300. Informational only — the nonce TTL is enforced by the facilitator. | | description | string | No | Human-readable description included in the challenge. | | realm | string | No | Optional realm identifier in the WWW-Authenticate header. |

Route matching uses longest prefix wins: /api/v1/data matches /api/v1 before /api.

Note: Unlike x402's acceptedTokens list, MPP charges a single currency per route. To accept multiple tokens, declare multiple routes.


Methods

The method field controls what the agent must sign:

"evm" (default) — Route A only

Standard MPP evm/authorization. The agent provides a flat EIP-3009 authorization:

// credential.payload shape for method="evm"
{
  type: "authorization",
  from: "0xAgentAddress",
  to:   "0xMerchantAddress",
  value: "100000",           // in token base units (e.g. 0.10 USDC = 100000 with 6 decimals)
  validAfter: "0",
  validBefore: "1750000000", // Unix timestamp
  nonce: "0xabc...",         // 32-byte random
  signature: "0xabc...",     // EIP-712 signature over TransferWithAuthorization
}

Settles as Route A: a direct transferWithAuthorization on-chain. Maximum compatibility — payable by any MPP evm client.

"parallel" — Routes A–L

Parallel's extension method. The credential payload is a full SignedPaymentPayload (same wire format as x402). This unlocks all 12 facilitator routes (A–L), including:

  • Routes A / F / J: direct transfers (USDp→USDp, USDC→USDC, sUSDp→sUSDp)
  • Routes B / D: USDp → collateral swap via Parallelizer (exact output)
  • Routes E / G: USDC → USDp or sUSDp swap via Parallelizer (exact input)
  • Route C: USDp deposit into sUSDp savings vault
  • Routes H / I / K: sUSDp redemption (→ USDp, USDC, or other collateral)
  • Route L: partial sUSDp redeem combined with USDp

See @parallel-protocol/payment-core for signing helpers and @parallel-protocol/x402 for a full route table.


Wire protocol

402 challenge response

When no valid Authorization: Payment header is present:

HTTP/1.1 402 Payment Required
Content-Type: application/json
WWW-Authenticate: Payment id="a1b2c3d4", method="evm", intent="charge", request="eyJhbW91bnQiOiIxMDAwMDAiLCJjdXJyZW5jeSI6IjB4ODMzNS4uLiIsInJlY2lwaWVudCI6IjB4TWVyY2hhbnQiLCJtZXRob2REZXRhaWxzIjp7ImNoYWluSWQiOjg0NTN9fQ", expires="2025-01-15T12:05:00.000Z", description="AI model inference"

The request param is base64url(JSON<EvmChargeRequest>):

// Decoded EvmChargeRequest
{
  amount: "100000",          // bigint as string, in token base units
  currency: "0x8335...",     // token address
  recipient: "0xMerchant",   // merchant's payTo
  description?: "...",
  methodDetails: { chainId: 8453 }
}

The body is JSON and also includes the challenge object for agents that parse the body:

{
  "challenge": {
    "id": "a1b2c3d4",
    "method": "evm",
    "intent": "charge",
    "request": "<base64url>",
    "expires": "2025-01-15T12:05:00.000Z"
  }
}

On re-challenge (verification failed), the body also includes { "error": "INVALID_SIGNATURE" }.

Request: Authorization: Payment

The agent encodes the full MppChargeCredential as base64url(JSON(...)) and passes it in the Authorization header:

Authorization: Payment eyJjaGFsbGVuZ2UiOnsiLi4uIn0sInBheWxvYWQiOnsidHlwZSI6ImF1dGhvcml6YXRpb24iLC4uLn19

The credential JSON structure:

interface MppChargeCredential {
  challenge: MppChallenge;              // Echo of the server's challenge
  source?: string;                       // Payer DID (optional, org.paymentauth)
  payload: EvmAuthorizationPayload      // method="evm"
          | SignedPaymentPayload;        // method="parallel"
}

200 response: Payment-Receipt

On success, the middleware sets a Payment-Receipt header with base64url(JSON<MppReceipt>):

interface MppReceipt {
  status: "success";
  method: "evm" | "parallel";
  timestamp: string;    // RFC 3339
  reference: string;    // on-chain txHash
}

Exports

Main entry (@parallel-protocol/mpp)

import {
  // Middleware (framework-agnostic)
  createMppPaymentGate,
  createMppPaymentMiddleware,

  // Facilitator client
  MppFacilitatorClient,

  // Challenge codec
  buildChallenge,
  serializeChallenge,
  parseAuthorizationHeader,
  type MppChallenge,

  // Errors
  MppConfigError,
  MppRuntimeError,
  MPP_ERROR_CODES,
  type MppErrorCode,

  // Types
  type MppFacilitatorConfig,
  type MppMiddlewareConfig,
  type MppRouteConfig,
  type MppSettleResult,
  type MppSettleSuccess,
  type MppSettleFailure,
  type PaymentGateResult,
  type HTTPAdapter,

  // Wire helpers (re-exported from mpp-types)
  encodeBase64Url,
  decodeBase64Url,
  type MppChargeCredential,
  type MppReceipt,

  // Utils
  chainIdFromSlug,
  matchRoute,
  parsePrice,
} from "@parallel-protocol/mpp";

Framework adapters

| Import path | Export | Framework | |-------------|--------|-----------| | @parallel-protocol/mpp/express | paymentMiddleware(config) | Express 4/5 | | @parallel-protocol/mpp/next | withPayment(config, handler) | Next.js 14/15 | | @parallel-protocol/mpp/fastify | paymentMiddleware(config) | Fastify 4/5 | | @parallel-protocol/mpp/hono | paymentMiddleware(config) | Hono 4+ |


Advanced: createMppPaymentGate

For custom integrations or frameworks not listed above:

import { createMppPaymentGate } from "@parallel-protocol/mpp";

const gate = createMppPaymentGate(config);

const result = await gate({
  getHeader: (name) => request.headers[name],
  getMethod: () => request.method,
  getPath:   () => request.path,
  getUrl:    () => request.url,
});

if (result.type === "pass") {
  // Route not configured, or OPTIONS — proceed normally
} else if (result.type === "error") {
  // 402 challenge: result.result.status / .headers / .body
} else {
  // result.type === "verified"
  // Run your handler...
  const handlerStatus = await runHandler();

  if (handlerStatus >= 200 && handlerStatus < 300) {
    const settlement = await result.settle();
    if (settlement.success) {
      // settlement.receipt.reference = txHash
    }
  }
}

Challenge codec (server utilities)

buildChallenge(route)

Builds an MppChallenge for a route. Generates a random id, encodes the charge request as base64url, and computes expires from maxTimeoutSeconds.

import { buildChallenge, serializeChallenge } from "@parallel-protocol/mpp";

const challenge = buildChallenge(routeConfig);
// { id: "uuid-v4", method: "evm", intent: "charge", request: "base64url...", expires: "..." }

const headerValue = serializeChallenge(challenge);
// 'Payment id="...", method="evm", intent="charge", request="...", expires="..."'

serializeChallenge(challenge)

Serializes a challenge to a WWW-Authenticate: Payment … header value (RFC 9110 auth-params format). Undefined fields are omitted automatically.

parseAuthorizationHeader(header)

Parses an Authorization: Payment <token> header into a validated MppChargeCredential. Returns null when the header is absent, not the Payment scheme, the base64url token is malformed, or the JSON fails schema validation.

import { parseAuthorizationHeader } from "@parallel-protocol/mpp";

const credential = parseAuthorizationHeader(req.headers.authorization);
if (!credential) {
  // Issue 402 challenge
}

MppFacilitatorClient

Low-level client for the facilitator's MPP endpoints:

import { MppFacilitatorClient } from "@parallel-protocol/mpp";

const client = new MppFacilitatorClient({
  url: "https://agents.parallel.best",
});

// Phase 1: verify signature + reserve nonce (no on-chain tx)
await client.verify(credential);  // throws MppRuntimeError on failure

// Phase 2: submit on-chain, return receipt
const result = await client.settle(credential);
if (result.success) {
  console.log(result.receipt.reference);  // txHash
} else {
  console.error(result.error.code);       // e.g. "SUBMISSION_FAILED"
}

Both methods use a 10-second timeout. verify throws on any failure; settle returns a MppSettleResult union on facilitator errors and throws MppRuntimeError only on connectivity issues.


Error codes

Thrown by the SDK

| Error class | When | |-------------|------| | MppConfigError | Invalid config at startup (bad payTo, bad currency, unknown network, invalid facilitator URL) | | MppRuntimeError | Runtime failure; .code is one of the values below |

| MppRuntimeError.code | Meaning | |------------------------|---------| | FACILITATOR_UNAVAILABLE | Facilitator timed out (10s) or returned a non-JSON error | | FACILITATOR_INVALID_RESPONSE | Facilitator returned an unexpected response shape | | INVALID_CREDENTIAL | Credential could not be parsed or failed schema validation | | METHOD_MISMATCH | Credential challenge.method doesn't match the route's configured method |

Propagated from the facilitator

When the facilitator rejects a credential, its error code is forwarded as the error field in the re-issued 402 body:

| Code | Meaning | |------|---------| | INVALID_SIGNATURE | EIP-712 signature recovery failed | | INVALID_NONCE | Nonce already used (replay detected) | | PAYMENT_EXPIRED | validBefore has passed | | INSUFFICIENT_AMOUNT | Signed amount < required amount | | NETWORK_MISMATCH | Payload chain ≠ route config chain | | RATE_LIMIT_EXCEEDED | Signer exceeded the hourly transaction limit | | PARALLELIZER_PAUSED | On-chain router temporarily paused (swap routes only) |


Utilities

import { chainIdFromSlug, matchRoute, parsePrice } from "@parallel-protocol/mpp";

| Function | Signature | Description | |----------|-----------|-------------| | parsePrice | (price: string \| bigint, decimals?: number) => bigint | Parse "0.10"100000n (6 decimals by default) | | chainIdFromSlug | (network: string) => number \| undefined | "base"8453, undefined for unknown chains | | matchRoute | <T>(path: string, routes: Record<string, T>) => T \| undefined | Longest-prefix route matching |


Comparison with x402

| | @parallel-protocol/x402 | @parallel-protocol/mpp | |-|--------------------------|--------------------------| | Challenge status | 402 | 402 | | Challenge header | payment-required: <base64> | WWW-Authenticate: Payment … | | Credential header | payment-signature: <base64> | Authorization: Payment <base64url> | | Receipt header | payment-response: <base64> | Payment-Receipt: <base64url> | | Token per route | list (acceptedTokens) | single (currency) | | Default decimals | 18 | 6 | | Auth standard | x402 / Coinbase | RFC 9110 Payment auth-scheme | | Facilitator endpoints | /x402/verify, /x402/settle | /mpp/verify, /mpp/settle | | On-chain routes | A–L | A–L (same) | | Cashback | Yes | Yes (same) |

Both protocols share the same facilitator backend, the same SignedPaymentPayload authorization format, and the same settlement routes.


Related packages

| Package | Purpose | |---------|---------| | @parallel-protocol/mpp-types | Wire-format Zod schemas + mppCredentialToPayment() helper | | @parallel-protocol/x402 | HTTP-402 payment middleware (x402 / Coinbase protocol) | | @parallel-protocol/payment-core | EIP-3009 signing helpers shared by x402 and MPP | | @parallel-protocol/chains | 25-chain catalog with contract addresses |


License

MIT © Parallel Protocol