SDK

⚙️ Latch SDK
The Latch SDK gives developers full control to define tools (free or paid), assign on-chain pricing, and connect clients that support MCP and x402. It automatically manages the pay-per-call flow — from pricing to payment verification and retries.
✨ Features
Supports EVM and Solana
Extensible with hooks and plugins
Simple x402 setup
Fully open source
Supported Networks
EVM
base, base-sepolia, avalanche, avalanche-fuji, polygon, polygon-amoy, iotex, sei, sei-testnet
SVM
solana, solana-devnet
🔗 For a complete working template, visit: github.com/latchmcp/examples

🧱 Creating a Server
You can use the createLatchHandler method and integrate it with any modern framework — such as Express, Hono, Next.js, or Workers.
# Install
npm i latchmcp @modelcontextprotocol/sdk viem zod@3
# or
pnpm add latchmcp @modelcontextprotocol/sdk viem zod@3Example (Hono)
import { Hono } from "hono";
import { createLatchHandler } from "latchmcp/handler";
import { z } from "zod";
const app = new Hono();
const handler = createLatchHandler(
(server) => {
// Paid tool
server.paidTool(
"weather",
"Returns current weather data",
"$0.001",
{ city: z.string() },
{},
async ({ city }) => ({
content: [{ type: "text", text: `The weather in ${city} is sunny` }],
})
);
// Free tool
server.tool(
"ping",
"Simple ping tool",
{ name: z.string() },
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}` }],
})
);
},
{
facilitator: {
url: "https://facilitator.latchmcp.app",
},
recipient: {
evm: { address: "0xYourEvmAddress", isTestnet: true },
svm: { address: "YourSolanaAddressHere", isTestnet: true },
},
},
{
serverInfo: { name: "latch-mcp-server", version: "1.0.0" },
}
);
app.use("*", (c) => handler(c.req.raw));
export default app;🧭 Client: x402 Payment Wrapper
Use the withLatchClient helper to intercept requests and automatically handle x402 payments inside your MCP client.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { withLatchClient } from "latchmcp/client";
import { createSigner, isEvmSignerWallet, isSvmSignerWallet } from "x402/types";
export const getClient = async () => {
const client = new Client({
name: "latch-example-client",
version: "1.0.0",
});
const LATCH_MCP_URL = "https://api.latchmcp.app/mcp";
const transport = new StreamableHTTPClientTransport(new URL(LATCH_MCP_URL));
await client.connect(transport);
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 evmSigner = await createSigner("base-sepolia", EVM_PRIVATE_KEY);
const svmSigner = await createSigner("solana-devnet", SOLANA_PRIVATE_KEY);
if (!isEvmSignerWallet(evmSigner)) throw new Error("EVM signer unavailable");
if (!isSvmSignerWallet(svmSigner)) throw new Error("SVM signer unavailable");
return withLatchClient(client, {
wallet: { evm: evmSigner, svm: svmSigner },
confirmationCallback: async (payment) => {
console.log("[Latch] Payment required:", payment);
return true;
},
});
};
export const getClientResponse = async () => {
const client = await getClient();
const tools = await client.listTools();
console.log("Tools:", JSON.stringify(tools, null, 2));
const res = await client.callTool({
name: "weather",
arguments: { city: "London" },
});
return res;
};
try {
console.log("[main] Running Latch SDK example...");
const response = await getClientResponse();
console.log("[main] Final response:", response);
} catch (err) {
console.error("[Latch Error]", err);
}Last updated
