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 🙏

© 2025 – Pkg Stats / Ryan Hefner

402ok-mcp

v0.1.4

Published

MCP server middleware for HTTP 402 Payment Required with support for multiple networks including OKX/XLayer

Downloads

503

Readme

402ok-mcp

MCP (Model Context Protocol) server middleware for HTTP 402 Payment Required protocol with XLayer-compatible multi-network blockchain payment support.

Features

  • 🔥 XLayer Compatible - Full support for OKX XLayer network with native facilitator integration
  • HTTP 402 Payment Required - Standard implementation of the x402 protocol for MCP
  • 🌐 Multi-Network Support - XLayer, Base, Base Sepolia, and any EVM-compatible networks
  • 🔐 OKX Facilitator - Built-in OKX signature authentication for XLayer
  • 🔌 Standard Facilitator - Support for x402.org and other standard facilitators
  • 💳 USDC Payments - EIP-712 signature-based USDC transfers
  • 🎯 Paid & Free Tools - Define both paid and free tools in the same server
  • Complete Lifecycle - Automatic verify → fulfill → settle flow

Why XLayer?

XLayer is a Layer 2 blockchain built by OKX, offering:

  • Low transaction fees
  • Fast confirmation times
  • Seamless integration with OKX ecosystem
  • Native USDC support

This middleware provides first-class XLayer support with optimized OKX facilitator integration.

Installation

npm install 402ok-mcp

Quick Start

Pure JSON-RPC HTTP (No SDK Dependency)

MCP is just JSON-RPC 2.0 over HTTP. You can build a simple MCP server without @modelcontextprotocol/sdk:

import express from "express";

const app = express();
app.use(express.json());

// Tool definitions
const tools = [
  {
    name: "premium_analysis",
    description: "AI-powered premium analysis",
    inputSchema: {
      type: "object",
      properties: { query: { type: "string" } },
      required: ["query"]
    },
    price: "0.01",  // USDC
    handler: async (args: any) => `Analysis result for: ${args.query}`
  }
];

// JSON-RPC 2.0 handler
app.post("/mcp", async (req, res) => {
  const { jsonrpc, method, params, id } = req.body;

  if (jsonrpc !== "2.0") {
    return res.json({ jsonrpc: "2.0", error: { code: -32600, message: "Invalid Request" }, id });
  }

  try {
    let result;

    switch (method) {
      case "initialize":
        // MCP handshake
        result = {
          protocolVersion: "2024-11-05",
          capabilities: { tools: {} },
          serverInfo: { name: "paid-mcp-server", version: "1.0.0" }
        };
        break;

      case "tools/list":
        // List available tools
        result = {
          tools: tools.map(t => ({
            name: t.name,
            description: t.description,
            inputSchema: t.inputSchema
          }))
        };
        break;

      case "tools/call":
        // Call a tool
        const tool = tools.find(t => t.name === params.name);
        if (!tool) {
          throw { code: -32601, message: `Tool not found: ${params.name}` };
        }

        // Check payment (from _meta)
        const payment = params._meta?.["x402.payment"];

        if (!payment && tool.price) {
          // Return 402 payment required
          result = {
            isError: true,
            content: [{
              type: "text",
              text: JSON.stringify({
                x402Version: 1,
                error: "_meta.x402.payment is required",
                accepts: [{
                  scheme: "exact",
                  network: "xlayer",
                  maxAmountRequired: (parseFloat(tool.price) * 1_000_000).toString(),
                  payTo: "0xYourWalletAddress",
                  asset: "0x74b7f16337b8972027f6196a17a631ac6de26d22",
                  extra: { name: "USD Coin", version: "2" }
                }]
              })
            }]
          };
        } else {
          // Execute tool (with payment verification if needed)
          const output = await tool.handler(params.arguments);
          result = {
            content: [{ type: "text", text: output }]
          };
        }
        break;

      case "notifications/initialized":
        // Client notification - no response needed
        return res.status(204).send();

      default:
        throw { code: -32601, message: `Method not found: ${method}` };
    }

    res.json({ jsonrpc: "2.0", result, id });
  } catch (error: any) {
    res.json({
      jsonrpc: "2.0",
      error: { code: error.code || -32603, message: error.message },
      id
    });
  }
});

app.listen(3000, () => {
  console.log("Pure JSON-RPC MCP server running on http://localhost:3000/mcp");
});

Key JSON-RPC Methods:

| Method | Description | |--------|-------------| | initialize | Client handshake, returns server capabilities | | tools/list | Returns available tools with schemas | | tools/call | Executes a tool, params include name, arguments, _meta | | notifications/initialized | Client confirms initialization (no response) |

Payment Flow via _meta:

  • Payment is passed in params._meta["x402.payment"] as base64-encoded JSON
  • If missing, return payment options in the response
  • If present, verify → execute → settle

Using with SDK (Stdio Transport)

import { createPaidMcpHandler } from "402ok-mcp";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create MCP server with payment support
const server = createPaidMcpHandler(
  (mcp) => {
    // Define a paid tool
    mcp.paidTool(
      "premium_analysis",
      "Perform premium data analysis",
      {
        payments: [
          {
            price: "0.01",              // 0.01 USDC
            chainId: 196,               // XLayer mainnet
            token: "0x74b7f16337b8972027f6196a17a631ac6de26d22", // USDC on XLayer
            usdcName: "USD Coin",
            usdcVersion: "2",
            network: "xlayer",
            config: {
              description: "Premium analysis service"
            }
          }
        ]
      },
      z.object({
        data: z.string().describe("Data to analyze")
      }),
      async (args) => {
        // Your tool logic here
        const result = await analyzeData(args.data);
        return {
          content: [{ type: "text", text: result }]
        };
      }
    );

    // Define a free tool
    mcp.tool(
      "basic_info",
      "Get basic information (free)",
      z.object({
        query: z.string()
      }),
      async (args) => {
        return {
          content: [{ type: "text", text: `Info for: ${args.query}` }]
        };
      }
    );
  },
  { name: "my-paid-mcp-server", version: "1.0.0" },
  {
    recipient: "0xe8fb62154382af0812539cfe61b48321d8f846a8", // Your wallet
    facilitators: {
      xlayer: {
        url: "https://www.okx.com",
        type: "okx",
        okxCredentials: {
          apiKey: process.env.OKX_API_KEY!,
          secretKey: process.env.OKX_SECRET_KEY!,
          passphrase: process.env.OKX_PASSPHRASE!
        }
      }
    }
  }
);

// Connect via stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);

HTTP Server Mode (StreamableHTTPServerTransport)

Deploy as an HTTP server for web-based MCP clients:

import express from "express";
import { randomUUID } from "node:crypto";
import { createPaidMcpHandler } from "402ok-mcp";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

const app = express();
app.use(express.json());

// Store active sessions
const transports: Record<string, StreamableHTTPServerTransport> = {};

// Server configuration
const serverConfig = {
  recipient: "0xe8fb62154382af0812539cfe61b48321d8f846a8",
  facilitators: {
    xlayer: {
      url: "https://www.okx.com",
      type: "okx" as const,
      okxCredentials: {
        apiKey: process.env.OKX_API_KEY!,
        secretKey: process.env.OKX_SECRET_KEY!,
        passphrase: process.env.OKX_PASSPHRASE!
      }
    }
  }
};

// Tool setup function
const setupTools = (mcp: any) => {
  mcp.paidTool(
    "premium_analysis",
    "AI-powered premium analysis",
    {
      payments: [{
        price: "0.01",
        chainId: 196,
        token: "0x74b7f16337b8972027f6196a17a631ac6de26d22",
        usdcName: "USD Coin",
        usdcVersion: "2",
        network: "xlayer",
        config: { description: "Premium AI analysis" }
      }]
    },
    z.object({ query: z.string() }),
    async (args) => {
      return { content: [{ type: "text", text: `Analysis: ${args.query}` }] };
    }
  );
};

// Handle MCP requests
app.post("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"] as string | undefined;
  let transport: StreamableHTTPServerTransport;

  if (sessionId && transports[sessionId]) {
    // Reuse existing session
    transport = transports[sessionId];
  } else if (!sessionId && isInitializeRequest(req.body)) {
    // New session initialization
    transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: () => randomUUID(),
      onsessioninitialized: (id) => {
        transports[id] = transport;
        console.log("Session initialized:", id);
      },
      onsessionclosed: (id) => {
        delete transports[id];
        console.log("Session closed:", id);
      }
    });

    transport.onclose = () => {
      if (transport.sessionId) {
        delete transports[transport.sessionId];
      }
    };

    // Create paid MCP server and connect
    const server = createPaidMcpHandler(
      setupTools,
      { name: "paid-mcp-http-server", version: "1.0.0" },
      serverConfig
    );
    await server.connect(transport);
  } else {
    res.status(400).json({
      jsonrpc: "2.0",
      error: { code: -32000, message: "Invalid session" },
      id: null
    });
    return;
  }

  await transport.handleRequest(req, res, req.body);
});

// Handle SSE for streaming responses
app.get("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"] as string;
  const transport = transports[sessionId];
  if (transport) {
    await transport.handleRequest(req, res);
  } else {
    res.status(400).send("Invalid session");
  }
});

// Handle session cleanup
app.delete("/mcp", async (req, res) => {
  const sessionId = req.headers["mcp-session-id"] as string;
  const transport = transports[sessionId];
  if (transport) {
    await transport.handleRequest(req, res);
  } else {
    res.status(400).send("Invalid session");
  }
});

app.listen(3000, () => {
  console.log("Paid MCP HTTP server running on http://localhost:3000/mcp");
});

Stateless HTTP Mode

For serverless/edge deployments, use stateless mode:

const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined  // Disable session management
});

Multi-Network Setup

Allow users to pay with XLayer OR Base Sepolia:

const server = createPaidMcpHandler(
  (mcp) => {
    mcp.paidTool(
      "premium_service",
      "Premium service with multi-network payment",
      {
        payments: [
          // Option 1: XLayer (recommended for lower fees)
          {
            price: "0.1",
            chainId: 196,
            token: "0x74b7f16337b8972027f6196a17a631ac6de26d22",
            usdcName: "USD Coin",
            usdcVersion: "2",
            network: "xlayer",
            config: {
              description: "Pay with XLayer (lower fees)"
            }
          },
          // Option 2: Base Sepolia
          {
            price: "0.1",
            chainId: 84532,
            token: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
            usdcName: "USDC",
            usdcVersion: "2",
            network: "base-sepolia",
            config: {
              description: "Pay with Base Sepolia"
            }
          }
        ]
      },
      z.object({ input: z.string() }),
      async (args) => {
        return { content: [{ type: "text", text: "Result" }] };
      }
    );
  },
  { name: "multi-network-server", version: "1.0.0" },
  {
    recipient: "0xe8fb62154382af0812539cfe61b48321d8f846a8",
    facilitators: {
      xlayer: {
        url: "https://www.okx.com",
        type: "okx",
        okxCredentials: {
          apiKey: process.env.OKX_API_KEY!,
          secretKey: process.env.OKX_SECRET_KEY!,
          passphrase: process.env.OKX_PASSPHRASE!
        }
      },
      "base-sepolia": {
        url: "https://x402.org/facilitator",
        type: "standard"
      }
    }
  }
);

How It Works

  1. Client calls MCP tool → Server checks if tool requires payment
  2. No payment provided → Returns error with payment options (x402 format)
  3. Client signs payment → Creates EIP-712 signature for USDC transfer
  4. Client retries with _meta.x402.payment → Includes signed payment
  5. Server verifies payment → Calls facilitator to verify signature
  6. Server executes tool → Runs your tool logic
  7. Server settles payment → Calls facilitator to execute on-chain transfer
  8. Server returns result → Includes settlement confirmation in _meta

All of this happens automatically!

API Reference

createPaidMcpHandler(setupTools, serverInfo, config)

Creates an MCP server with payment support.

Parameters

  • setupTools (server: PaidMcpServer) => void - Function to register tools
  • serverInfo { name: string; version: string } - Server metadata
  • config ServerConfig - Server configuration

Types

interface PaymentConfig {
  price: string;           // Price in USDC (e.g., "0.1")
  chainId: number;         // Network chain ID
  token: string;           // USDC token contract address
  usdcName: string;        // USDC contract name (for EIP-712)
  usdcVersion: string;     // USDC contract version (for EIP-712)
  network: string;         // Network name (e.g., "xlayer")
  config?: {
    description?: string;
    metadata?: Record<string, any>;
  };
}

interface ServerConfig {
  recipient: string;       // Wallet address to receive payments
  facilitators: {
    [network: string]: FacilitatorConfig;
  };
}

interface FacilitatorConfig {
  url: string;
  type?: "okx" | "standard";
  okxCredentials?: {
    apiKey: string;
    secretKey: string;
    passphrase: string;
  };
}

PaidMcpServer Methods

paidTool(name, description, paymentOptions, paramsSchema, callback)

Register a paid tool that requires payment before execution.

tool(name, description, paramsSchema, callback)

Register a free tool (no payment required).

Supported Networks

XLayer (Recommended)

  • Mainnet: Chain ID 196
  • Testnet: Chain ID 195
  • USDC Contract: 0x74b7f16337b8972027f6196a17a631ac6de26d22 (mainnet)
  • Facilitator: OKX facilitator with API authentication

Other Networks

  • Base: Standard facilitator
  • Base Sepolia: Standard facilitator
  • Any EVM-compatible network with USDC support

Getting OKX Credentials

To use XLayer with OKX facilitator:

  1. Create an OKX account at https://www.okx.com
  2. Go to API settings
  3. Create API key with x402 permissions
  4. Copy API Key, Secret Key, and Passphrase to your .env:
OKX_API_KEY=your_api_key
OKX_SECRET_KEY=your_secret_key
OKX_PASSPHRASE=your_passphrase

Client Integration

MCP clients need to handle the x402 payment flow. When a tool returns an error with payment options:

// Example handling payment in an MCP client
const result = await mcpClient.callTool("premium_analysis", { data: "..." });

if (result.isError) {
  const errorData = JSON.parse(result.content[0].text);

  if (errorData.error === "_meta.x402.payment is required") {
    // Get payment options
    const paymentOptions = errorData.accepts;

    // User signs payment with their wallet
    const signedPayment = await signPayment(paymentOptions[0]);

    // Retry with payment
    const paidResult = await mcpClient.callTool(
      "premium_analysis",
      { data: "..." },
      { _meta: { "x402.payment": signedPayment } }
    );
  }
}

Security

  • EIP-712 signature verification for all payments
  • Facilitator double-verification (verify + settle)
  • No direct blockchain access required
  • Automatic payment settlement on successful tool execution only
  • Failed tool execution = no payment settlement

License

MIT

Links