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@3

Example (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