guides·

How to Give Your Claude Agent a USDC Wallet in 5 Minutes

Step-by-step tutorial to connect a Claude AI agent to a USDC wallet on Base using Bithaven's MCP SDK. Check balances, send payments, and request human approval — all through Claude's tool_use.

Your Claude agent can reason, plan, and use tools. But it can't spend money — until now. In this tutorial, you'll connect a Claude agent to a USDC wallet on Base, giving it the ability to check balances, send payments, and request your approval for large transactions.

We'll use Bithaven's MCP SDK, which wraps financial operations as standard tool calls that Claude can invoke through tool_use.

Prerequisites

  • A Bithaven account (sign up free)
  • An agent wallet with an API key (created from the dashboard)
  • USDC deposited to your master wallet on Base
  • Node.js 20+ and an Anthropic API key

Step 1: Install the SDK

npm install @bithaven/mcp-sdk @anthropic-ai/sdk

Step 2: Initialize the Bithaven client

import { BithavenClient } from '@bithaven/mcp-sdk';

const bithaven = new BithavenClient({
  apiKey: process.env.BITHAVEN_API_KEY!,
  baseUrl: 'https://api.bithaven.ai',
});

Step 3: Define tools for Claude

Bithaven's SDK exposes four core tools that map directly to Claude's tool_use format:

const tools = [
  {
    name: 'check_balance',
    description: 'Check the agent wallet USDC balance and remaining spend caps.',
    input_schema: { type: 'object', properties: {} },
  },
  {
    name: 'send_payment',
    description: 'Send USDC to an address. Evaluated against spending policies.',
    input_schema: {
      type: 'object',
      properties: {
        to: { type: 'string', description: 'Recipient wallet address' },
        amount: { type: 'string', description: 'USDC amount to send' },
        memo: { type: 'string', description: 'Payment description' },
      },
      required: ['to', 'amount'],
    },
  },
  {
    name: 'get_tx_history',
    description: 'Get recent transaction history for this agent wallet.',
    input_schema: {
      type: 'object',
      properties: {
        page: { type: 'number', description: 'Page number' },
        limit: { type: 'number', description: 'Results per page' },
      },
    },
  },
  {
    name: 'request_approval',
    description: 'Request human approval for a transaction that exceeds policy.',
    input_schema: {
      type: 'object',
      properties: {
        to: { type: 'string', description: 'Recipient address' },
        amount: { type: 'string', description: 'USDC amount' },
        reason: { type: 'string', description: 'Why this payment is needed' },
      },
      required: ['to', 'amount', 'reason'],
    },
  },
];

Step 4: Handle tool calls

When Claude decides to use a financial tool, route the call to Bithaven:

async function handleToolCall(name: string, input: any) {
  switch (name) {
    case 'check_balance':
      return await bithaven.checkBalance();
    case 'send_payment':
      return await bithaven.sendPayment(input);
    case 'get_tx_history':
      return await bithaven.getTxHistory(input);
    case 'request_approval':
      return await bithaven.requestApproval(input);
    default:
      return { error: `Unknown tool: ${name}` };
  }
}

Step 5: Run the agent loop

import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic();

async function runAgent(userMessage: string) {
  let messages = [{ role: 'user', content: userMessage }];

  while (true) {
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 1024,
      tools,
      messages,
    });

    // If Claude wants to use a tool
    if (response.stop_reason === 'tool_use') {
      const toolBlock = response.content.find(
        (block) => block.type === 'tool_use'
      );

      if (toolBlock) {
        const result = await handleToolCall(toolBlock.name, toolBlock.input);

        messages.push({ role: 'assistant', content: response.content });
        messages.push({
          role: 'user',
          content: [{
            type: 'tool_result',
            tool_use_id: toolBlock.id,
            content: JSON.stringify(result),
          }],
        });
        continue;
      }
    }

    // Claude is done — return the final response
    const textBlock = response.content.find(
      (block) => block.type === 'text'
    );
    return textBlock?.text || '';
  }
}

Step 6: Test it

// Check balance
const balance = await runAgent("What's my current USDC balance?");
console.log(balance);

// Send a payment
const payment = await runAgent(
  "Send 5 USDC to 0x1234...abcd for the DataAPI monthly subscription"
);
console.log(payment);

// Request approval for a large transaction
const approval = await runAgent(
  "I need to pay 500 USDC to 0xabcd...1234 for GPU compute. This exceeds my daily limit, so please request approval."
);
console.log(approval);

What happens under the hood

When Claude calls send_payment, Bithaven evaluates the transaction against all active policies on the agent wallet:

  • Within policy? → Transaction is signed and broadcast on Base. Claude receives a confirmation with the transaction hash.
  • Exceeds daily cap? → Transaction is blocked. Claude receives the specific policy violation and can decide to request human approval instead.
  • Address not whitelisted? → Transaction is blocked with a clear error explaining which policy was violated.

This means your agent can operate autonomously for routine transactions while automatically escalating anything unusual for your review.

Next steps


Bithaven gives your AI agents a budget, not a blank check. Create your first agent wallet →

claudetutorialsmcpusdc

Ready to give your agents a budget?

Create an account and have your first agent transacting in minutes.

Get Started Free