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:
- The technical architecture of tool use in Claude SDK
- How to define tools and their parameters
- A complete example: stock price lookup + fundamental analysis
- Token efficiency and error handling patterns
- Real-world considerations for financial data integration
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.
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:
- Structured Control: Claude can't "hallucinate" API calls or make up data. It requests a tool; your code decides execution.
- Autonomy: Claude sees results and iterates without waiting for human instruction. Multi-step reasoning loops happen naturally.
- 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
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:
- User sends message to Claude via SDK
- Claude processes context (system prompt, conversation history, tool definitions)
- Claude generates response:
- If a tool is needed, includes
tool_usecontent block (JSON with name, input parameters) - Otherwise, returns text directly
- Application checks response for tool_use blocks
- If tools present: Execute each tool (call API, query database, run computation)
- Return results to Claude as new message with
tool_resultrole - Claude re-processes with tool results in context and generates next response
- 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:
- name: Machine-readable tool identifier. Must be unique within your agent. Use snake_case.
- description: 1–2 sentences explaining what the tool does. Claude uses this to decide when to invoke it.
- input_schema: JSON Schema (draft 7) describing parameters. Every property should have
typeanddescription. - required: Array of mandatory parameters. Optional parameters should have
defaultor be omitted fromrequired.
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:
- Look up current stock price (get_stock_price tool)
- Retrieve historical data (get_stock_history tool)
- 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:
- Claude reads the query and recognizes it needs current NVDA price + volatility data
- Claude generates a
tool_useblock requestingget_stock_priceandget_stock_historyfor NVDA - Application executes both tools, returns results (price ≈ $140, volatility ≈ 48%)
- Claude receives results, compares to typical S&P 500 volatility (≈15–18%), and generates insight
- 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:
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.
- Base: 200 tokens
- Tool definitions (3 tools): 180 tokens
- Tool result (stock price): 100 tokens
- Total input for Claude's second call: ~480 tokens
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:
- Missing data: Return
{"error": "...", "symbol": "..."}— Claude will note the gap - Invalid input: Return helpful suggestion (e.g., "did you mean MSFT?")
- Temporary failure: Return backoff message — Claude may suggest trying later
- 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
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
Supported US-Listed Companies and Related Tickers
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:
- Single-Stock Dashboards: AAPL, MSFT, NVDA, GOOGL, META, IBM
- AI/Tech Screener: Technology Sector Screener
- Custom Strategy: Build comparative analysis by linking multiple stock dashboards
- Integration: Use Seentio API to feed fundamental data into your Claude agents for enriched analysis
Key Takeaways
- Tool use is agentic: Claude requests execution; your app controls what actually happens.
- Structure beats text: JSON schemas for tool definitions prevent hallucination and enable error handling.
- Multi-turn loops are native: Claude iterates autonomously, calling tools as needed and interpreting results.
- Financial data integration is straightforward: Yahoo Finance + yfinance + Claude tool use = working stock analyst agent in ~200 lines.
- Token efficiency matters: Cache tool definitions, summarize results, and minimize tool count to reduce costs.
- Error handling is critical: Return structured error objects; Claude will interpret and potentially retry or explain.
Sources and References
- Anthropic Claude SDK Documentation
- yfinance: Yahoo Finance Data Fetcher
- IEX Cloud Financial Data API
- Anthropic API Reference
- Yahoo Finance Developer Documentation
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.