About 402ok
402ok is an MCP server in the Blockchain category: mCP server middleware for HTTP 402 Payment Required with support for multiple networks including OKX/XLayer.
Install
claude mcp add 402ok -- npx -y 402ok-mcpnpx -y 402ok-mcpThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about 402ok
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
5 checks have not been run yet.
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
- Client calls MCP tool → Server checks if tool requires payment
- No payment provided → Returns error with payment options (x402 format)
- Client signs payment → Creates EIP-712 signature for USDC transfer
- Client retries with
_meta.x402.payment→ Includes signed payment - Server verifies payment → Calls facilitator to verify signature
- Server executes tool → Runs your tool logic
- Server settles payment → Calls facilitator to execute on-chain transfer
- 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:
- Create an OKX account at https://www.okx.com
- Go to API settings
- Create API key with x402 permissions
- 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
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.