Quickstart
SDK
The most complete Typescript SDK for x402 + MCP
The SDK gives you full control to define tools (free or paid), wire pricing, and integrate from clients. It handles x402 automatically.
Features
- Supports EVM and Solana
- Extensible with plugins
- Simple x402 setup
- Open source
EVMs supported
base-sepolia, base, avalanche-fuji, avalanche, iotex, sei, sei-testnet, polygon, polygon-amoy
SVMs supported
solana-devnet, solana
For a full deployable template, follow this link github/x402-mcp.
Creating a Server
You can use the createMcpPaidHandler and plug that into any popular frameworks like Express, Hono, NextJS, and others.
npm i mcpay @modelcontextprotocol/sdk viem zod@3
pnpm add mcpay @modelcontextprotocol/sdk viem zod@3
bun add mcpay @modelcontextprotocol/sdk viem zod@3
Hono
Here's how you can create a server.
import { Hono } from "hono"
import { createMcpPaidHandler } from "mcpay/handler"
import { z } from "zod"
const app = new Hono()
const handler = createMcpPaidHandler(
(server) => {
server.paidTool(
"weather",
"Paid tool",
"$0.001",
{ city: z.string() },
{},
async ({ city }) => ({
content: [{ type: "text", text: `The weather in ${city} is sunny` }],
})
)
server.tool(
"free_tool",
"Free to use",
{ s: z.string(), city: z.string() },
async ({ s, city }) => ({
content: [{ type: "text", text: `We support ${city}` }],
})
)
},
{
facilitator: {
url: "https://facilitator.mcpay.tech"
},
recipient: {
"evm": {address: "0xc9343113c791cB5108112CFADa453Eef89a2E2A2", isTestnet: true},
"svm": {address: "4VQeAqyPxR9pELndskj38AprNj1btSgtaCrUci8N4Mdg", isTestnet: true}
}
},
{
serverInfo: { name: "paid-mcp", version: "1.0.0" },
},
)
app.use("*", (c) => handler(c.req.raw))
export default appClient: X402 Payment Wrapper
Using the withX402Client you can intercept and make payment requests in your MCP Client.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { withX402Client } from "mcpay/client";
import { createSigner, isEvmSignerWallet, isSvmSignerWallet } from "x402/types";
export const getClient = async () => {
const client = new Client({
name: "example-client",
version: "1.0.0",
});
const EVM_PRIVATE_KEY = process.env.EVM_PRIVATE_KEY as `0x${string}`;
const SOLANA_PRIVATE_KEY = process.env.SOLANA_PRIVATE_KEY as `0x${string}`;
const MCP_SERVER_URL = "http://localhost:3000/mcp"
const transport = new StreamableHTTPClientTransport(new URL(MCP_SERVER_URL));
// ✅ Wait for the connection
await client.connect(transport);
const evmSigner = await createSigner("base-sepolia", EVM_PRIVATE_KEY);
const svmSigner = await createSigner("solana-devnet", SOLANA_PRIVATE_KEY);
if (!isEvmSignerWallet(evmSigner)) {
throw new Error("Failed to create EVM signer");
}
if (!isSvmSignerWallet(svmSigner)) {
throw new Error("Failed to create SVM signer");
}
return withX402Client(client, {
wallet: {
evm: evmSigner,
svm: svmSigner
},
confirmationCallback: async (payment) => {
return true
}
});
};
export const getClientResponse = async () => {
const client = await getClient();
const tools = await client.listTools();
console.log("Tools:", JSON.stringify(tools, null, 2));
// ✅ Correct overload: (name: string, args?: Record<string, unknown>)
const res = await client.callTool({
name: "hello",
arguments: {
name: "Yo"
},
});
return res;
};
try {
console.log("[main] Starting test...");
const response = await getClientResponse();
console.log("[main] Final response:", response);
} catch (err) {
console.error(err);
}