Skip to main content
LangChain’s DynamicStructuredTool lets you expose any HTTP endpoint as a callable tool inside an agent graph. By combining it with createAgentClient from @meterlane/x402/client, your agent handles x402 payment negotiation invisibly — it sends a request, receives 402 if unpaid, signs an ERC-3009 USDC authorization, and retries with the payment header, all before returning a result to the model.
All examples on this page target Base Sepolia (eip155:84532) and the public sandbox demo route. Do not use mainnet keys or mainnet URLs in development or CI.
1

Install dependencies

You need @langchain/core for the tool primitive, zod for the parameter schema, and @meterlane/x402 for the payment client.
npm add @meterlane/x402 @langchain/core zod
Import the MIT-licensed client entry point specifically — not the server middleware:
import { createAgentClient } from "@meterlane/x402/client";
2

Set your environment variable

Never hard-code your private key in source files or commit it to version control. Store it in an environment variable and load it at runtime. Use a dedicated hot wallet funded only with the USDC you need for testing — do not reuse cold-storage or exchange keys.
Create a .env file (and add it to .gitignore):
AGENT_PRIVATE_KEY=0xYOUR_BASE_SEPOLIA_PRIVATE_KEY
Get test USDC for Base Sepolia from the Circle faucet — select Base Sepolia before requesting.
3

Build the tool

Save the following as langchain-meterlane-tool.ts. The tool calls the public sandbox demo route, but you can replace the URL with any active Meterlane gateway route.
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
import type { Hex } from "viem";
import { createAgentClient } from "@meterlane/x402/client";

const privateKey = process.env.AGENT_PRIVATE_KEY as Hex | undefined;
if (!privateKey?.startsWith("0x")) {
  throw new Error("Set AGENT_PRIVATE_KEY to a 0x-prefixed Base Sepolia funded key");
}

// paidFetch is a drop-in replacement for fetch that handles 402 automatically
const paidFetch = createAgentClient(privateKey, "eip155:84532");

const gatewayBase =
  process.env.GATEWAY_URL ?? "https://gateway.meterlane.app";
const orgSlug = process.env.ORG_SLUG ?? "demo";

export const meterlanePaidGetTool = new DynamicStructuredTool({
  name: "meterlane_paid_http_get",
  description:
    "GET an x402-protected Meterlane gateway route. Signs and pays on HTTP 402 using USDC on Base Sepolia.",
  schema: z.object({
    path: z
      .string()
      .describe("Route path under /gateway/:orgSlug, e.g. /demo"),
  }),
  func: async ({ path }) => {
    const normalized = path.startsWith("/") ? path : `/${path}`;
    const url = `${gatewayBase}/gateway/${orgSlug}${normalized}`;

    const res = await paidFetch(url, { method: "GET" });
    const body = await res.text();

    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${body.slice(0, 500)}`);
    }

    return body;
  },
});

// Standalone smoke test — runs without a model
async function main() {
  const result = await meterlanePaidGetTool.invoke({ path: "/demo" });
  console.log("Paid response (first 200 chars):", result.slice(0, 200));
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
Run the smoke test directly:
AGENT_PRIVATE_KEY=0xYOUR_KEY npx tsx langchain-meterlane-tool.ts
You should see the first 200 characters of the demo route’s 200 response body.
4

Register the tool with your agent

Wire meterlanePaidGetTool into your agent the same way you would any other LangChain tool — via bindTools, a ReAct executor, or a LangGraph tool node:
import { ChatOpenAI } from "@langchain/openai";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { meterlanePaidGetTool } from "./langchain-meterlane-tool";

const model = new ChatOpenAI({ model: "gpt-4o-mini" });

const agent = createReactAgent({
  llm: model,
  tools: [meterlanePaidGetTool],
});

const result = await agent.invoke({
  messages: [
    {
      role: "user",
      content: "Fetch the Meterlane demo route at /demo and summarize the response.",
    },
  ],
});

console.log(result.messages.at(-1)?.content);
The agent decides when to call the tool; createAgentClient handles the 402 → pay → retry cycle transparently each time.

How it works

When your tool calls paidFetch(url) instead of raw fetch, the following happens automatically:
  1. The client sends the original GET request.
  2. If the server returns 402, it reads the X-Payment-Requirements header for the USDC amount and receiver address.
  3. ExactEvmScheme uses your private key to produce an ERC-3009 signed authorization on Base Sepolia.
  4. The client retries the request with the payment authorization header attached.
  5. On 200, your tool function receives the response body and returns it to the model.
The MIT license on @meterlane/x402/client means you can — and should — audit the payment logic before running it with a funded wallet.