Guide, Report 2026-04-16 · By Joshua Dalton, Chief of Staff to the CEO at Seentio

Claude SDK Tool Use: Building AI Agents with Financial Data

Introduction

Large language models excel at reasoning but lack real-time access to external data and the ability to take structured actions. Tool use (also called function calling or agentic control) bridges this gap by allowing an AI model to request execution of external functions—APIs, databases, computation engines—within a structured conversation loop.

Anthropic's Claude SDK (available for Python, TypeScript, and other languages) implements tool use through a formal mechanism where Claude generates tool_use content blocks describing what should be executed. Your application then handles execution, returns results, and Claude continues reasoning. This creates a genuine agentic loop: Claude observes outcomes and decides on next steps autonomously.

This article explains Claude SDK tool use with a concrete, reproducible example: building an agent that retrieves and analyzes stock data from Yahoo Finance. We'll cover:

By the end, you'll understand how to implement agentic AI systems that augment Claude's capabilities with live external data.


What Is Tool Use?

Definition and Motivation

Tool use is a mechanism that enables an LLM to request the execution of external functions. Unlike traditional APIs where the model generates text and a downstream system parses it (fragile, error-prone), Claude SDK's tool use is structured: Claude generates a machine-readable specification of what to do, and the host application decides whether and how to execute it.

\[ \text{Agent Loop} = \text{Claude Reasoning} \to \text{Tool Request} \to \text{External Execution} \to \text{Claude Interpretation} \]

Variables and explanation: - Claude Reasoning: The model processes context and generates a response (including possible tool_use blocks). - Tool Request: A structured block specifying tool name, parameters, and context. - External Execution: Your code calls the actual external system (API, database, computation). - Claude Interpretation: Claude receives the result and decides whether to use another tool, refine the query, or return a final answer to the user.

Why Tool Use Matters

Three key advantages:

  1. Structured Control: Claude can't "hallucinate" API calls or make up data. It requests a tool; your code decides execution.
  2. Autonomy: Claude sees results and iterates without waiting for human instruction. Multi-step reasoning loops happen naturally.
  3. Auditability: Every tool call is logged and traceable. You know exactly what Claude asked for and what data it received.

Claude SDK Tool Use Architecture

System Design: How Tool Use Works

graph TD A["User Query"] --> B["Claude Agent Loop"] B --> C{"Claude generates
response"} C -->|No tool needed| D["Return text to user"] C -->|Tool use required| E["Tool Use Block
tool_name, parameters"] E --> F["Application: Execute Tool"] F --> G["External System
API, Database, etc."] G --> H["Return Result
to Agent"] H --> B B -->|Final answer| D style B fill:#4a90e2,color:#fff style E fill:#f5a623,color:#000 style G fill:#7ed321,color:#000

Loop sequence:

  1. User sends message to Claude via SDK
  2. Claude processes context (system prompt, conversation history, tool definitions)
  3. Claude generates response:
  4. If a tool is needed, includes tool_use content block (JSON with name, input parameters)
  5. Otherwise, returns text directly
  6. Application checks response for tool_use blocks
  7. If tools present: Execute each tool (call API, query database, run computation)
  8. Return results to Claude as new message with tool_result role
  9. Claude re-processes with tool results in context and generates next response
  10. Repeat until Claude returns text (no tool_use block) or reaches token limit

Defining Tools: SDK Syntax and Best Practices

Tool Definition Structure

In Claude SDK, tools are defined as JSON schemas. The Python SDK provides decorators or explicit schema objects. Here's the anatomy:

{
  "name": "get_stock_price",           # Unique identifier; no spaces, snake_case
  "description": "Fetch current price and key metrics for a stock",  # Clear, concise
  "input_schema": {
    "type": "object",
    "properties": {
      "symbol": {
        "type": "string",
        "description": "Stock ticker symbol (e.g., AAPL, MSFT)"
      },
      "include_history": {
        "type": "boolean",
        "description": "If true, return 52-week high/low",
        "default": false
      }
    },
    "required": ["symbol"]
  }
}

Key fields:

Parameter Design Rules

Aspect Best Practice Why
Naming Semantic, descriptive (e.g., symbol, not x) Claude is more likely to use the correct parameter
Types Specific (string, number, boolean, enum) Reduces ambiguity; SDK can validate input types
Description Include examples (e.g., "AAPL, MSFT, GOOGL") Claude learns from examples in descriptions
Defaults Set for non-critical parameters Reduces required fields; simplifies Claude's request
Enum Use for restricted choices (e.g., timeframe: ["1d", "1mo", "1y"]) Prevents invalid values

Real-World Example: Stock Data Agent

Scenario

We'll build an AI agent that answers investment questions using live stock data. The agent can:

  1. Look up current stock price (get_stock_price tool)
  2. Retrieve historical data (get_stock_history tool)
  3. Analyze fundamentals (get_stock_fundamentals tool)

The data source is Yahoo Finance (via Python's yfinance library, which wraps Yahoo's public APIs).

Complete Implementation

Step 1: Define Tools

import anthropic

# Initialize Claude client
client = anthropic.Anthropic(api_key="YOUR_ANTHROPIC_API_KEY")

# Define tools as JSON schemas
tools = [
    {
        "name": "get_stock_price",
        "description": "Fetch current price and intraday metrics for a stock symbol. "
                       "Returns current price, change, percent change, market cap.",
        "input_schema": {
            "type": "object",
            "properties": {
                "symbol": {
                    "type": "string",
                    "description": "Stock ticker symbol (e.g., AAPL, MSFT, NVDA)"
                }
            },
            "required": ["symbol"]
        }
    },
    {
        "name": "get_stock_history",
        "description": "Retrieve historical OHLC (Open, High, Low, Close) data and volume. "
                       "Use this to analyze trends and volatility.",
        "input_schema": {
            "type": "object",
            "properties": {
                "symbol": {
                    "type": "string",
                    "description": "Stock ticker symbol"
                },
                "period": {
                    "type": "string",
                    "enum": ["1mo", "3mo", "6mo", "1y", "5y"],
                    "description": "Historical data period",
                    "default": "1y"
                }
            },
            "required": ["symbol"]
        }
    },
    {
        "name": "get_stock_fundamentals",
        "description": "Retrieve fundamental metrics: P/E ratio, earnings, revenue, "
                       "dividend yield, book value. Useful for value analysis.",
        "input_schema": {
            "type": "object",
            "properties": {
                "symbol": {
                    "type": "string",
                    "description": "Stock ticker symbol"
                }
            },
            "required": ["symbol"]
        }
    }
]

Step 2: Implement Tool Executors

import yfinance as yf
from datetime import datetime, timedelta
import json

def execute_get_stock_price(symbol: str) -> dict:
    """Fetch current price data from Yahoo Finance."""
    try:
        ticker = yf.Ticker(symbol)
        info = ticker.info

        return {
            "symbol": symbol,
            "price": info.get("currentPrice", info.get("regularMarketPrice")),
            "change": info.get("regularMarketChange"),
            "percent_change": info.get("regularMarketChangePercent"),
            "market_cap": info.get("marketCap"),
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        return {"error": f"Failed to fetch {symbol}: {str(e)}"}

def execute_get_stock_history(symbol: str, period: str = "1y") -> dict:
    """Fetch historical OHLCV data."""
    try:
        ticker = yf.Ticker(symbol)
        hist = ticker.history(period=period)

        # Summarize: return latest data + volatility
        latest = hist.iloc[-1]
        volatility = hist["Close"].pct_change().std() * 100  # Annualized-ish

        return {
            "symbol": symbol,
            "period": period,
            "latest_close": float(latest["Close"]),
            "52_week_high": float(hist["High"].max()),
            "52_week_low": float(hist["Low"].min()),
            "volatility_percent": round(volatility, 2),
            "data_points": len(hist),
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        return {"error": f"Failed to fetch history for {symbol}: {str(e)}"}

def execute_get_stock_fundamentals(symbol: str) -> dict:
    """Fetch fundamental metrics."""
    try:
        ticker = yf.Ticker(symbol)
        info = ticker.info

        return {
            "symbol": symbol,
            "pe_ratio": info.get("trailingPE"),
            "earnings_per_share": info.get("trailingEps"),
            "revenue_per_share": info.get("revenuePerShare"),
            "dividend_yield": info.get("dividendYield"),
            "book_value": info.get("bookValue"),
            "roe": info.get("returnOnEquity"),
            "debt_to_equity": info.get("debtToEquity"),
            "timestamp": datetime.now().isoformat()
        }
    except Exception as e:
        return {"error": f"Failed to fetch fundamentals for {symbol}: {str(e)}"}

def process_tool_call(tool_name: str, tool_input: dict) -> str:
    """Route tool calls to executors and return JSON result."""
    if tool_name == "get_stock_price":
        result = execute_get_stock_price(tool_input["symbol"])
    elif tool_name == "get_stock_history":
        result = execute_get_stock_history(
            tool_input["symbol"],
            tool_input.get("period", "1y")
        )
    elif tool_name == "get_stock_fundamentals":
        result = execute_get_stock_fundamentals(tool_input["symbol"])
    else:
        result = {"error": f"Unknown tool: {tool_name}"}

    return json.dumps(result)

Step 3: Agent Loop

def run_stock_agent(user_query: str):
    """
    Main agent loop: Claude reasons about the query, calls tools as needed,
    and iterates until it has sufficient information.
    """

    messages = [
        {
            "role": "user",
            "content": user_query
        }
    ]

    system_prompt = """You are a financial analyst AI agent. You have access to tools that fetch 
stock price data, historical performance, and fundamental metrics from Yahoo Finance.

When a user asks about a stock or investment question:
1. Identify the ticker symbol(s) involved
2. Use tools to gather relevant data
3. Analyze the data and provide clear insights
4. Always cite the data sources and timestamps

Be concise but thorough. If data is missing or tools return errors, explain what you tried."""

    print(f"\n{'='*60}")
    print(f"Query: {user_query}")
    print(f"{'='*60}\n")

    # Agent loop
    while True:
        # Call Claude with tools available
        response = client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1024,
            tools=tools,
            messages=messages,
            system=system_prompt
        )

        # Check if Claude wants to stop
        if response.stop_reason == "end_turn":
            # No more tool calls; extract final text response
            for block in response.content:
                if hasattr(block, "text"):
                    print("Agent Response:")
                    print(block.text)
            break

        # Process tool calls
        if response.stop_reason == "tool_use":
            # Add Claude's response to messages (including tool_use blocks)
            messages.append({
                "role": "assistant",
                "content": response.content
            })

            # Extract and execute tool calls
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    print(f"Executing tool: {block.name}")
                    print(f"Parameters: {json.dumps(block.input, indent=2)}")

                    # Execute tool
                    result = process_tool_call(block.name, block.input)
                    print(f"Result: {result}\n")

                    # Store result
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": result
                    })

            # Add tool results to messages
            messages.append({
                "role": "user",
                "content": tool_results
            })
        else:
            # Unexpected stop reason
            print(f"Unexpected stop reason: {response.stop_reason}")
            break

# Example usage
if __name__ == "__main__":
    query = "Compare Apple and Microsoft. Which has better fundamentals right now?"
    run_stock_agent(query)

Step 4: Full Agent Interaction Example

User Query:

"What's the current price of NVDA? Is it more volatile than the S&P 500 average?"

Agent Reasoning:

  1. Claude reads the query and recognizes it needs current NVDA price + volatility data
  2. Claude generates a tool_use block requesting get_stock_price and get_stock_history for NVDA
  3. Application executes both tools, returns results (price ≈ $140, volatility ≈ 48%)
  4. Claude receives results, compares to typical S&P 500 volatility (≈15–18%), and generates insight
  5. Claude returns final answer to user

Sample Output:

Executing tool: get_stock_price
Parameters: {"symbol": "NVDA"}
Result: {"symbol": "NVDA", "price": 140.25, "change": 2.15, "percent_change": 1.56, ...}

Executing tool: get_stock_history
Parameters: {"symbol": "NVDA", "period": "1y"}
Result: {"symbol": "NVDA", "52_week_high": 167.80, "52_week_low": 68.50, 
         "volatility_percent": 48.3, ...}

Agent Response:
NVDA (NVIDIA) is currently trading at $140.25, up 1.56% today. 

NVIDIA is significantly more volatile than the S&P 500 average:
- **NVDA volatility**: ~48.3% (annualized)
- **S&P 500 typical volatility**: 15–18%

This makes sense given NVIDIA's exposure to the AI/semiconductor cycle, which drives 
larger price swings than the broader market. The 52-week range ($68.50–$167.80) reflects 
this high volatility—a 144% span compared to typical large-cap stocks.

How Tool Use Differs from Traditional Function Calling

Comparison Table

Aspect Claude SDK Tool Use Traditional OpenAI Function Calling Text Parsing
Structure Formal JSON schema in API spec JSON schema in API spec Free text; regex or NLP
Control Model requests; app decides Model requests; often auto-executed Model generates text; app guesses
Iteration Multi-turn loop; Claude sees results Single turn or requires app loop No structure for iteration
Error Handling Tool returns error dict; Claude interprets Often breaks the flow Silent failures; hallucination risk
Auditability Every call logged (name, params, result) Depends on OpenAI logging No structured audit trail

Key distinction: Claude SDK tool use is agentic. Claude doesn't execute tools—it requests them. Your application remains in control of execution, allowing you to add validation, error recovery, rate limiting, and cost optimization.


Token Efficiency and Cost Optimization

Token Accounting

Tool use adds overhead at three points:

\[ \text{Total Input Tokens} = \text{Base Query} + \text{Tool Definitions} + \text{Tool Results Overhead} \]

Variable explanation: - Base Query: User message + system prompt (usually small) - Tool Definitions: Each tool's name, description, and input schema (10–200 tokens each) - Tool Results Overhead: When Claude re-reads results in next turn (variable)

Cost Reduction Strategies

Strategy Impact Implementation
Minimize tool count −30–50% overhead Combine related tools (e.g., stock + fundamentals)
Concise descriptions −10–20% Remove fluff; use examples sparingly
Summarize results −20–40% Return key metrics, not full datasets
Cache tool definitions −15–25% (with Anthropic caching) Use prompt caching for static tool definitions
Streaming results Neutral Use streaming for better UX; costs same

Example: Yahoo Finance query.

If you removed tool definitions from every turn (by caching), you'd save ≈180 tokens per interaction, reducing cost by ~37%.


Financial Data Integration: Practical Considerations

Data Providers and APIs

Provider Coverage Rate Limits Cost Latency Tool Integration
Yahoo Finance (yfinance) 60k+ global symbols ~2,000 req/min Free <500ms Easy
Alpha Vantage 40k+ stocks, forex, crypto 5 req/min (free) $29+/mo <1s Easy
IEX Cloud US equities, detailed fundamentals 100 req/second $9+/mo <100ms Easy
Bloomberg Terminal Institutional data Custom $$$$ Instant Hard (auth required)
Financial Modeling Prep 100k+ symbols, DCF models 250 req/min (free) $299+/mo <1s Easy

Recommendation for MVP/Production: Start with yfinance (free) + IEX Cloud (low cost, fast). Upgrade to Bloomberg/Refinitiv as you scale enterprise revenue.

Error Handling Patterns

def execute_get_stock_price_safe(symbol: str) -> dict:
    """Robust execution with error recovery."""

    # Validation
    if not symbol or len(symbol) > 5:
        return {"error": "Invalid symbol", "symbol": symbol}

    try:
        ticker = yf.Ticker(symbol.upper())
        info = ticker.info

        # Check for missing data
        if not info.get("currentPrice"):
            return {
                "error": "Symbol not found or data unavailable",
                "symbol": symbol,
                "suggestion": "Verify ticker is correct (e.g., AAPL, not APPLE)"
            }

        return {
            "symbol": symbol.upper(),
            "price": info.get("currentPrice"),
            "timestamp": datetime.now().isoformat(),
            "data_quality": "current"
        }

    except Exception as e:
        return {
            "error": f"API error: {type(e).__name__}",
            "symbol": symbol,
            "message": "Yahoo Finance temporarily unavailable. Try again in 30s."
        }

Error patterns Claude understands:

  1. Missing data: Return {"error": "...", "symbol": "..."} — Claude will note the gap
  2. Invalid input: Return helpful suggestion (e.g., "did you mean MSFT?")
  3. Temporary failure: Return backoff message — Claude may suggest trying later
  4. Rate limit: Return clear message — Claude won't retry immediately

Advanced Techniques

Parallel Tool Execution

Claude SDK supports parallel tool use: Claude can request multiple tools in a single response, and your application executes them concurrently.

import asyncio
import aiohttp

async def execute_multiple_tools(tool_calls: list) -> list:
    """Execute multiple tool calls in parallel."""
    tasks = []
    for call in tool_calls:
        if call.name == "get_stock_price":
            tasks.append(execute_get_stock_price(call.input["symbol"]))
        elif call.name == "get_stock_history":
            tasks.append(execute_get_stock_history(
                call.input["symbol"],
                call.input.get("period", "1y")
            ))

    results = await asyncio.gather(*tasks)
    return results

Benefit: If Claude requests data for AAPL, MSFT, and GOOGL simultaneously, you fetch all three in parallel (~1s total) instead of sequentially (~3s). Faster user experience, same token cost.

Custom Instructions and Few-Shot Prompts

Claude performs better with examples. Add to your system prompt:

Example of good stock analysis:

User: "Is Tesla overvalued?"
Agent reasoning:
1. Fetch TSLA current price: $240
2. Fetch fundamentals: P/E = 65, industry average = 22
3. Fetch history: YTD return = +45%
Analysis: TSLA trades at 3x industry P/E but has higher growth. Depends on growth expectations.

End example.

This teaches Claude how you want it to use tools and interpret results.

Stateful Agents (Multi-Turn Conversations)

The agent loop naturally supports multi-turn conversation:

def run_stateful_stock_agent():
    """Maintain conversation history across multiple queries."""
    messages = []

    while True:
        user_input = input("You: ")
        if user_input.lower() in ["quit", "exit"]:
            break

        # Add user message
        messages.append({"role": "user", "content": user_input})

        # Agent loop (same as before)
        while True:
            response = client.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=1024,
                tools=tools,
                messages=messages,
                system="You are a financial analyst..."
            )

            if response.stop_reason == "end_turn":
                # Extract and print response
                for block in response.content:
                    if hasattr(block, "text"):
                        print(f"Agent: {block.text}")
                # Add to history
                messages.append({"role": "assistant", "content": response.content})
                break

            # Handle tool use (same as before)
            ...

Benefit: Claude remembers earlier queries and context, enabling follow-up questions like "Which one is cheaper?" without re-specifying symbols.


Comparing Stock Data Tools: Architecture and Latency

graph LR A["Claude Agent"] -->|tool_use:
get_stock_price| B["SDK Router"] B -->|symbol=AAPL| C["Yahoo Finance
yfinance"] C -->|JSON: price,
change| D["Tool Result"] D -->|tool_result| E["Claude Receives
Result"] E -->|Interprets
& Responds| F["Final Answer
to User"] G["IEX Cloud API
Alternative"] -.->|Faster,
more detailed| C style A fill:#4a90e2,color:#fff style B fill:#f5a623,color:#000 style C fill:#7ed321,color:#000 style D fill:#bd10e0,color:#fff style E fill:#4a90e2,color:#fff style F fill:#50e3c2,color:#000

The example in this article centers on financial data retrieval, so we'll identify key companies in the AI, software, and semiconductors space that Claude SDK users typically analyze:

Ticker Company Current Price Range Market Cap Exchange Role
AAPL Apple Inc. $220–240 ~$3.0T NASDAQ Mega-cap, AI integration in products
MSFT Microsoft Corporation $415–450 ~$3.2T NASDAQ AI leadership (OpenAI partnership), Copilot
NVDA NVIDIA Corporation $130–170 ~$2.5T NASDAQ GPU dominant supplier for LLM training/inference
GOOGL Alphabet Inc. $175–195 ~$1.8T NASDAQ AI research (Gemini, LLaMA), Cloud platform
META Meta Platforms $520–580 ~$1.0T NASDAQ AI applications (Llama), research investment
IBM International Business Machines $190–210 ~$210B NYSE Enterprise software, quantum computing

Note: These are illustrative price ranges and market caps as of April 2026. For real-time data, use the Seentio dashboard or the tools demonstrated in this article.


How to Track This on Seentio

Seentio dashboards provide real-time stock price tracking, fundamentals, and historical analysis. Use these tools alongside Claude SDK agents to enhance your financial analysis:


Key Takeaways

  1. Tool use is agentic: Claude requests execution; your app controls what actually happens.
  2. Structure beats text: JSON schemas for tool definitions prevent hallucination and enable error handling.
  3. Multi-turn loops are native: Claude iterates autonomously, calling tools as needed and interpreting results.
  4. Financial data integration is straightforward: Yahoo Finance + yfinance + Claude tool use = working stock analyst agent in ~200 lines.
  5. Token efficiency matters: Cache tool definitions, summarize results, and minimize tool count to reduce costs.
  6. Error handling is critical: Return structured error objects; Claude will interpret and potentially retry or explain.

Sources and References


Disclaimer

This article is for informational purposes only and is not investment advice. Seentio is not a registered investment adviser. The code examples and data retrieval methods shown here are for educational purposes. Always validate API responses, handle errors gracefully, and test agents thoroughly before deploying to production. Stock market analysis and investment decisions should involve multiple data sources and professional financial advice.

Frequently Asked Questions

What is tool use in the Claude SDK?

Tool use is a mechanism that allows Claude to request execution of external functions (like API calls or database queries) within a structured conversation loop. Claude doesn't execute tools directly—it generates structured tool_use content blocks describing what should be executed, and your application handles the actual execution, then returns results for Claude to interpret.

How does tool use differ from function calling in other APIs?

Claude's tool use is explicitly agentic: Claude can see the results of tool execution and decide on next steps autonomously. It's a multi-turn loop, not a single request-response. Claude can chain multiple tools, handle errors, and refine queries based on intermediate results.

Can I use Claude tool use with real-time financial data?

Yes. You define tools that call Yahoo Finance APIs (or other data providers) and return current prices, historical data, or financial metrics. Claude then interprets those results and can perform further analysis or make recommendations based on the retrieved data.

What are the token costs of tool use?

Tool use adds token overhead for the tool definition (usually 50–200 tokens per tool in the system prompt) and for each tool_use block (10–50 tokens). Results returned are counted as input tokens in the next turn. Optimize by defining only necessary tools and keeping tool descriptions concise.

How do I ensure Claude uses the right tool for a given query?

Provide clear, specific tool descriptions and parameter documentation. Use tool names that are semantically obvious (get_stock_price instead of query_data). Test with diverse prompts. If Claude misuses tools, refine descriptions and add examples in your system prompt.

Related Research

Track these stocks in real time

See the data behind the research. Start with Seentio's free tier.

Get started free