Claude & Anthropic SDK

This guide shows how to build a Claude agent that can autonomously discover and purchase products on the Epanya marketplace using tool_use (Claude's function calling). Claude decides when to search, which products to evaluate, and when to commit to a purchase — your code just handles the tool execution.

What you'll build: A Claude agent that accepts a natural-language procurement request (e.g. "find me a GPU inference endpoint under $2 per request") and handles the full discovery → evaluation → purchase flow autonomously.

Tool definitions

Define three tools in your Anthropic SDK call. Claude will call these when it decides to interact with the marketplace.

const EPANYA_TOOLS = [
  {
    name: "discover_products",
    description:
      "Search the Epanya marketplace for products. Use this to find compute " +
      "resources, APIs, datasets, AI models, tools, or agent labor services. " +
      "Returns a list of matching products with names, prices, and IDs.",
    input_schema: {
      type: "object",
      properties: {
        q: {
          type: "string",
          description: "Free-text search query, e.g. 'GPU inference' or 'translation API'",
        },
        category: {
          type: "string",
          enum: ["compute", "datasets", "apis", "models", "tools", "physical", "agent_labor"],
          description: "Product category to filter by",
        },
        max_price: {
          type: "number",
          description: "Maximum price in USDC, e.g. 2.5",
        },
        sort: {
          type: "string",
          enum: ["price_asc", "price_desc", "rating", "newest"],
          description: "Sort order for results",
        },
        limit: {
          type: "number",
          description: "Maximum results to return (default 10)",
        },
      },
      required: [],
    },
  },
  {
    name: "purchase_product",
    description:
      "Purchase a product from the Epanya marketplace. Use this only after " +
      "discovering and evaluating products with discover_products. Returns " +
      "the transaction ID and the endpoint URL to call the purchased service.",
    input_schema: {
      type: "object",
      properties: {
        product_id: {
          type: "string",
          description: "The product UUID returned by discover_products",
        },
      },
      required: ["product_id"],
    },
  },
  {
    name: "check_budget",
    description:
      "Check how much USDC this agent has spent and whether the budget limit " +
      "has been reached. Call this before making purchases if you are unsure " +
      "about remaining budget.",
    input_schema: {
      type: "object",
      properties: {},
      required: [],
    },
  },
];

Agent setup

Create an Epanya client and wire up the tool execution loop. The client handles the x402 payment round-trip automatically.

import Anthropic from "@anthropic-ai/sdk";
import { EpanyaClient, createTestSigner } from "@epanya/agent-sdk";

const anthropic = new Anthropic(); // reads ANTHROPIC_API_KEY from env

const AGENT_WALLET = process.env.AGENT_WALLET_ADDRESS!;

const epanya = new EpanyaClient({
  walletAddress: AGENT_WALLET,
  apiUrl: "https://api.epanya.ai",
  signer: createTestSigner(AGENT_WALLET), // swap for a real signer in production
});

Next, write the tool executor — the function that maps Claude's tool_use blocks to real Epanya SDK calls:

async function executeTool(
  toolName: string,
  toolInput: Record<string, unknown>
): Promise<string> {
  switch (toolName) {
    case "discover_products": {
      const products = await epanya.discover({
        q:        toolInput.q        as string | undefined,
        category: toolInput.category as string | undefined,
        maxPrice: toolInput.max_price as number | undefined,
        sort:     toolInput.sort     as string | undefined,
        limit:    (toolInput.limit   as number | undefined) ?? 10,
      });
      if (products.length === 0) return "No products found matching your criteria.";
      const summary = products.map((p, i) =>
        `${i + 1}. ${p.name} — $${p.priceUsdc} USDC (${p.pricingModel}) | ID: ${p.id}\n   ${p.description}`
      ).join("\n\n");
      return `Found ${products.length} product(s):\n\n${summary}`;
    }

    case "purchase_product": {
      const result = await epanya.purchase(toolInput.product_id as string);
      return JSON.stringify({
        transactionId: result.transactionId,
        endpointUrl:   result.product.endpointUrl,
        amountPaid:    result.amount + " USDC",
        status:        "escrowed",
      });
    }

    case "check_budget": {
      const budget = await epanya.checkBudget();
      return JSON.stringify({
        spent:          budget.spent + " USDC",
        budgetLimit:    budget.budgetLimit ? budget.budgetLimit + " USDC" : "unlimited",
        remaining:      budget.remaining  ? budget.remaining  + " USDC" : "unlimited",
        budgetExceeded: budget.budgetExceeded,
      });
    }

    default:
      throw new Error(`Unknown tool: ${toolName}`);
  }
}

Example flow

The agentic loop keeps running until Claude returns a final answer with no pending tool calls. Each tool_use block is executed and its result is fed back to Claude as a tool_result message.

async function runProcurementAgent(userRequest: string): Promise<string> {
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userRequest },
  ];

  const systemPrompt = `You are a procurement agent for an AI system. You have access to the
Epanya marketplace where you can discover and purchase compute resources, APIs, datasets,
AI models, and other tools your system needs.

When asked to procure something:
1. Check the budget first if there's any uncertainty about remaining funds.
2. Search for products that match the requirements.
3. Evaluate the options — consider price, rating, and fit for the task.
4. Purchase the best option and report the endpoint URL.

Always confirm the purchase before making it by explaining your choice.`;

  // Agentic loop
  while (true) {
    const response = await anthropic.messages.create({
      model:      "claude-opus-4-6",
      max_tokens: 4096,
      system:     systemPrompt,
      tools:      EPANYA_TOOLS,
      messages,
    });

    // Append Claude's response to the conversation
    messages.push({ role: "assistant", content: response.content });

    // If Claude is done, return the final text
    if (response.stop_reason === "end_turn") {
      const textBlock = response.content.find((b) => b.type === "text");
      return textBlock?.type === "text" ? textBlock.text : "";
    }

    // Execute all tool calls and collect results
    const toolResults: Anthropic.ToolResultBlockParam[] = [];
    for (const block of response.content) {
      if (block.type !== "tool_use") continue;
      console.log(`[tool] ${block.name}`, block.input);
      try {
        const output = await executeTool(block.name, block.input as Record<string, unknown>);
        toolResults.push({ type: "tool_result", tool_use_id: block.id, content: output });
      } catch (err) {
        toolResults.push({
          type:        "tool_result",
          tool_use_id: block.id,
          content:     `Error: ${err instanceof Error ? err.message : String(err)}`,
          is_error:    true,
        });
      }
    }

    // Feed tool results back to Claude
    messages.push({ role: "user", content: toolResults });
  }
}

Run it with a natural-language request:

const result = await runProcurementAgent(
  "I need a GPU inference endpoint for running image generation. " +
  "My budget is $3 per request maximum. Find the best option and purchase it."
);
console.log(result);

A typical agent conversation looks like this:

  1. User: "Find a GPU inference endpoint under $3/req and purchase it."
  2. Claude calls check_budget → sees $12.50 spent, $100 limit, no exceeded flag.
  3. Claude calls discover_products with category: "compute", max_price: 3.
  4. Tool returns 5 GPU products. Claude evaluates them in its reasoning.
  5. Claude calls purchase_product with the ID of the highest-rated option under budget.
  6. Tool returns transactionId and endpointUrl.
  7. Claude replies with a summary: product name, price paid, and the endpoint URL to use.

Full code

All pieces together in a single runnable file:

import Anthropic from "@anthropic-ai/sdk";
import { EpanyaClient, createTestSigner } from "@epanya/agent-sdk";

const anthropic   = new Anthropic();
const AGENT_WALLET = process.env.AGENT_WALLET_ADDRESS ?? "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const epanya      = new EpanyaClient({
  walletAddress: AGENT_WALLET,
  apiUrl: "https://api.epanya.ai",
  signer: createTestSigner(AGENT_WALLET),
});

const EPANYA_TOOLS: Anthropic.Tool[] = [
  {
    name: "discover_products",
    description: "Search the Epanya marketplace for products.",
    input_schema: {
      type: "object",
      properties: {
        q:         { type: "string" },
        category:  { type: "string", enum: ["compute","datasets","apis","models","tools","physical","agent_labor"] },
        max_price: { type: "number" },
        sort:      { type: "string", enum: ["price_asc","price_desc","rating","newest"] },
        limit:     { type: "number" },
      },
      required: [],
    },
  },
  {
    name: "purchase_product",
    description: "Purchase a product by ID. Returns transactionId and endpointUrl.",
    input_schema: {
      type: "object",
      properties: { product_id: { type: "string" } },
      required: ["product_id"],
    },
  },
  {
    name: "check_budget",
    description: "Check USDC spend vs budget limit.",
    input_schema: { type: "object", properties: {}, required: [] },
  },
];

async function executeTool(name: string, input: Record<string, unknown>): Promise<string> {
  if (name === "discover_products") {
    const products = await epanya.discover({
      q: input.q as string | undefined,
      category: input.category as string | undefined,
      maxPrice: input.max_price as number | undefined,
      sort: input.sort as string | undefined,
      limit: (input.limit as number | undefined) ?? 10,
    });
    if (!products.length) return "No products found.";
    return products.map((p, i) =>
      `${i+1}. ${p.name} — $${p.priceUsdc} USDC (${p.pricingModel})\n   ID: ${p.id}\n   ${p.description}`
    ).join("\n\n");
  }
  if (name === "purchase_product") {
    const r = await epanya.purchase(input.product_id as string);
    return JSON.stringify({ transactionId: r.transactionId, endpointUrl: r.product.endpointUrl, paid: r.amount + " USDC" });
  }
  if (name === "check_budget") {
    const b = await epanya.checkBudget();
    return JSON.stringify({ spent: b.spent, limit: b.budgetLimit ?? "unlimited", exceeded: b.budgetExceeded });
  }
  throw new Error(`Unknown tool: ${name}`);
}

async function main() {
  const messages: Anthropic.MessageParam[] = [{
    role: "user",
    content: "Find a GPU inference endpoint under $3 per request and purchase the best option.",
  }];

  while (true) {
    const res = await anthropic.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      system: "You are a procurement agent. Use your tools to discover and purchase marketplace products.",
      tools: EPANYA_TOOLS,
      messages,
    });

    messages.push({ role: "assistant", content: res.content });

    if (res.stop_reason === "end_turn") {
      const text = res.content.find(b => b.type === "text");
      console.log(text?.type === "text" ? text.text : "(no text)");
      break;
    }

    const results: Anthropic.ToolResultBlockParam[] = [];
    for (const block of res.content) {
      if (block.type !== "tool_use") continue;
      const out = await executeTool(block.name, block.input as Record<string, unknown>);
      results.push({ type: "tool_result", tool_use_id: block.id, content: out });
    }
    messages.push({ role: "user", content: results });
  }
}

main().catch(console.error);

Production tips