AWS Strands SDK Masterclass: Building Custom Tools

Published: June 16, 2025
In our previous posts, we introduced the AWS Strands Agents SDK and explored different model providers. Now, we'll focus on one of the most powerful aspects of Strands: the ability to leverage custom tools that extend your agent's capabilities. Tools allow your agent to interact with external systems, access data, and perform actions beyond simple text generation.
Understanding Tools in Strands
Tools are functions that your agent can call when needed to accomplish specific tasks. They serve as the bridge between your agent's reasoning capabilities and the external world. With tools, your agent can:
Access and manipulate data
Interact with APIs and services
Execute code
Perform calculations
Read and write files
And much more
from strands import Agent
# Create an agent with the default model (Claude 3.7 Sonnet on Bedrock)
agent = Agent()
# This is equivalent to:
agent = Agent(model="us.anthropic.claude-3-7-sonnet-20250219-v1:0")
The agent can perform basic reasoning and computation tasks without any tools:
# Ask the agent a complex multi-part question involving reasoning and computation
response = agent(
"What is 1234 multiplied by 5678, what is the square root of 1444"
)
The Anatomy of a Strands Tool
At its core, a Strands tool is a Python function decorated with the @tool decorator. This decorator transforms a regular function into a tool that can be used by your agent. Let's break down the components of a tool:
from strands import tool
@tool
def my_custom_tool(param1: str, param2: int = 0) -> str:
"""Tool description that helps the agent understand when to use this tool.
Args:
param1: Description of the first parameter
param2: Description of the second parameter with a default value
Returns:
Description of what the tool returns
"""
# Tool implementation
result = f"Processed {param1} with value {param2}"
return result
Key components:
Decorator: The
@tooldecorator marks the function as a toolFunction Signature: Defines the parameters the tool accepts
Type Annotations: Help the agent understand parameter and return types
Docstring: Crucial for the agent to understand when and how to use the tool
Implementation: The actual functionality of the tool
Creating Your First Custom Tool
Let's create a simple custom tool that fetches weather information for a given location:
from strands import Agent, tool
import requests
@tool
def get_weather(location: str) -> str:
"""Get current weather information for a location.
Args:
location: City name or location (e.g., 'Seattle', 'New York')
Returns:
Current weather information including temperature and conditions
"""
try:
# Using a free weather API
api_key = "58fdbd9249a39getyourkey" # Get from environment variable in production
url = f"https://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=metric"
response = requests.get(url)
data = response.json()
if response.status_code == 200:
temp = data["main"]["temp"]
condition = data["weather"][0]["description"]
humidity = data["main"]["humidity"]
return f"Weather in {location}: {condition}, {temp}°C, Humidity: {humidity}%"
else:
return f"Error fetching weather: {data.get('message', 'Unknown error')}"
except Exception as e:
return f"Failed to get weather information: {str(e)}"
# Create an agent with our custom tool
agent = Agent(
tools=[get_weather],
system_prompt="You are a helpful assistant that can provide weather information."
)
# Ask the agent about the weather
response = agent("What's the weather like in Chennai now?")
print(response.message)
When you run this code, the agent will use the get_weather tool to fetch real-time weather data for Chennai and provide a Response like:
I'll check the current weather in Chennai for you.
Tool #1: get_weather
Currently in Chennai, it's 32.69°C (about 91°F) with overcast clouds. The humidity is quite high at 62%, making it likely feel quite warm and muggy. This type of weather is typical for Chennai, which often experiences hot and humid conditions.
Building a Financial Assistant with Custom Tools
Now, let's build something more complex: a financial assistant that can search for and analyze stocks from US markets. This example demonstrates how to integrate multiple external APIs and create a sophisticated agent with domain-specific capabilities.
Step 1: Set Up the Required Libraries
First, we need to install the necessary libraries:
And import the required modules:
from strands import Agent, tool
import requests
import json
from typing import Dict, List, Optional, Union
from functools import lru_cache
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
Step 2 : Create Tools for US Stock Market Data - search_us_stocks and get_us_stock_price
@tool
def search_us_stocks(query: str, limit: int = 5) -> List[Dict]:
"""Search for US stocks matching the query.
Use this tool when the user is looking for US stock information.
The search matches partial company names and ticker symbols.
Args:
query: Search term for company name or ticker symbol
limit: Maximum number of results to return (default: 5)
Returns:
List of matching stocks with their details
"""
try:
api_key = os.getenv("ALPHA_VANTAGE_API_KEY")
url = f"https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords={query}&apikey={api_key}"
response = requests.get(url)
data = response.json()
if "bestMatches" not in data:
return f"Error: Unable to find US stocks matching '{query}'. API response: {data}"
results = []
for match in data["bestMatches"][:limit]:
results.append({
"symbol": match["1. symbol"],
"name": match["2. name"],
"type": match["3. type"],
"region": match["4. region"],
"currency": match["8. currency"],
"market": "US"
})
return results
except ConnectionError:
return "Unable to connect to Alpha Vantage API. Please check your internet connection."
except Exception as e:
return f"Error searching US stocks: {str(e)}"
@tool
def get_us_stock_price(ticker: str) -> Dict:
"""Get the current price and information for a US stock ticker.
Args:
ticker: Stock ticker symbol (e.g., 'AAPL', 'MSFT')
Returns:
Current stock price information and company details
"""
try:
api_key = os.getenv("ALPHA_VANTAGE_API_KEY")
url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={ticker}&apikey={api_key}"
response = requests.get(url)
quote_data = response.json()
if "Global Quote" not in quote_data or not quote_data["Global Quote"]:
return f"Invalid ticker symbol: {ticker}. Please provide a valid US stock symbol."
# Get company overview for additional information
overview_url = f"https://www.alphavantage.co/query?function=OVERVIEW&symbol={ticker}&apikey={api_key}"
overview_response = requests.get(overview_url)
overview_data = overview_response.json()
quote = quote_data["Global Quote"]
result = {
"symbol": ticker,
"price": quote["05. price"],
"change": quote["09. change"],
"change_percent": quote["10. change percent"],
"volume": quote["06. volume"],
"market": "US"
}
# Add company information if available
if "Name" in overview_data:
result["name"] = overview_data["Name"]
result["sector"] = overview_data["Sector"]
result["industry"] = overview_data["Industry"]
result["description"] = overview_data["Description"]
return result
except ConnectionError:
return "Unable to connect to Alpha Vantage API. Please check your internet connection."
except Exception as e:
return f"Error fetching US stock price: {str(e)}"
Step 3: Create a Financial Analysis Tool
@tool
def analyze_financial_stock(ticker: str, market: str = "US", exchange: str = "NSE") -> Dict:
"""Analyze a financial stock and provide key metrics.
Args:
ticker: Stock ticker symbol
market: Market to search in ("US" or "India")
exchange: Exchange code for Indian stocks (default: 'NSE', can be 'BSE')
Returns:
Dictionary with financial analysis and recommendations
"""
try:
if market.lower() == "us":
# For US stocks, use Alpha Vantage
api_key = os.getenv("ALPHA_VANTAGE_API_KEY")
# Get company overview
overview_url = f"https://www.alphavantage.co/query?function=OVERVIEW&symbol={ticker}&apikey={api_key}"
overview_response = requests.get(overview_url)
overview_data = overview_response.json()
if not overview_data or "Symbol" not in overview_data:
return f"Invalid ticker symbol: {ticker}. Please provide a valid US stock symbol."
# Get key metrics
analysis = {
"symbol": ticker,
"name": overview_data["Name"],
"sector": overview_data["Sector"],
"industry": overview_data["Industry"],
"pe_ratio": overview_data["PERatio"],
"peg_ratio": overview_data["PEGRatio"],
"dividend_yield": overview_data["DividendYield"],
"eps": overview_data["EPS"],
"52_week_high": overview_data["52WeekHigh"],
"52_week_low": overview_data["52WeekLow"],
"market_cap": overview_data["MarketCapitalization"],
"profit_margin": overview_data["ProfitMargin"],
"beta": overview_data["Beta"],
"market": "US"
}
return analysis
except Exception as e:
return f"Error analyzing financial stock: {str(e)}"
Step 6: Create the Financial Agent
# Create the agent with our financial tools
financial_agent = Agent(
# Using Claude 3.7 Sonnet model from Bedrock
model="us.anthropic.claude-3-7-sonnet-20250219-v1:0",
# Add our financial tools
tools=[
search_us_stocks,
get_us_stock_price,
analyze_financial_stock
],
# Customize the system prompt for financial analysis
system_prompt="""You are a helpful financial assistant specializing in stock market analysis.
You can search for and analyze stocks from both US markets.
When providing stock information:
1. Always specify which market (US or India) the stock belongs to
2. Include key financial metrics when available
3. Explain what the metrics mean in simple terms
4. Provide context about the company's industry and sector
For financial analysis:
- PE Ratio: Explain if it's high or low compared to industry average
- Dividend Yield: Note if it's attractive for income investors
- Market Cap: Categorize as large, mid, or small cap
- Recent Performance: Note significant price movements
Always be transparent about the limitations of your analysis and remind users
that this information should not be considered financial advice.
"""
)
Test the Financial Agent
# Ask the agent about US technology stocks
response = financial_agent("Find technology stocks in the US market with good growth potential")
print(response.message)
Response Leveraging tools
I'd be happy to help you find some technology stocks in the US market with good growth potential. Let me search for some technology-focused stocks for you.
Tool #14: search_us_stocks
Let me search for some major tech stocks that would be more suitable for individual stock analysis:
Tool #15: search_us_stocks
Let me try searching for them individually:
Tool #16: search_us_stocks
Now I'll analyze several key technology stocks to find ones with good growth potential. Let's start with Microsoft:
Tool #17: analyze_financial_stock
Let me check Apple:
Tool #18: search_us_stocks
Tool #19: analyze_financial_stock
Let's check NVIDIA:
Tool #20: search_us_stocks
Tool #21: analyze_financial_stock
Let's also check Amazon:
Tool #22: search_us_stocks
Tool #23: analyze_financial_stock
Let's check Google/Alphabet:
Tool #24: search_us_stocks
Tool #25: analyze_financial_stock
# US Technology Stocks with Growth Potential
Based on my analysis of major US technology stocks, here's a detailed comparison of stocks with good growth potential:
| **Company** | **Ticker** | **Sector/Industry** | **PE Ratio** | **PEG Ratio** | **Dividend Yield** | **Market Cap** | **Analysis** |
|-------------|------------|---------------------|--------------|---------------|-------------------|-----------------|-------------|
| Microsoft | MSFT | Technology/Prepackaged Software | 36.76 | 2.23 | 0.70% | $3.53T | **Large Cap** leader with strong cloud exposure through Azure. Higher PE ratio shows premium pricing, but steady growth in cloud and AI services justifies valuation. |
| Apple | AAPL | Technology/Electronic Computers | 30.60 | 1.78 | 0.53% | $2.93T | **Large Cap** consumer tech giant with strong ecosystem. Lower PEG ratio than others suggests better value relative to growth. Steady performance with loyal customer base. |
| NVIDIA | NVDA | Manufacturing/Semiconductors | 45.95 | 1.76 | 0.03% | $3.46T | **Large Cap** AI chip leader with exceptional profit margin (51.7%). Higher PE but strong growth prospects in AI, gaming, and data center markets. Higher beta (2.12) indicates more volatility. |
| Amazon | AMZN | Trade & Services/Retail | 34.60 | 2.35 | None | $2.25T | **Large Cap** e-commerce and cloud (AWS) leader. No dividend but reinvests heavily in growth. Higher PEG ratio suggests premium pricing relative to growth rate. |
| Alphabet | GOOGL | Technology/Computer Programming | 19.49 | 1.34 | 0.48% | $2.13T | **Large Cap** with most attractive valuation metrics. Lowest PE and PEG ratios show good value relative to growth potential. Strong position in search, cloud, and AI development. |
## Key Insights:
1. **Best Value for Growth**: Alphabet (GOOGL) has the lowest PE ratio (19.49) and PEG ratio (1.34) among these tech giants, suggesting it may offer the best value relative to its growth rate.
2. **Highest Growth Premium**: NVIDIA (NVDA) has the highest PE ratio (45.95), reflecting investors' expectations for continued exceptional growth in AI chip demand, though its PEG ratio of 1.76 is more reasonable.
3. **Profit Margin Leader**: NVIDIA stands out with an impressive 51.7% profit margin, significantly higher than peers, demonstrating exceptional operational efficiency.
4. **Dividend Income**: Microsoft offers the highest dividend yield at 0.70%, making it more attractive for income-oriented investors.
5. **Risk Assessment**: NVIDIA has the highest beta (2.12), indicating greater price volatility compared to the market, while Microsoft (1.026) and Alphabet (1.014) have betas closest to the market average.
**Reminder**: This analysis is for informational purposes only and should not be considered financial advice. Stock markets involve risk, and past performance doesn't guarantee future results. Consider consulting a financial advisor before making investment decisions based on this information.{'role': 'assistant', 'content': [{'text': "# US Technology Stocks with Growth Potential\n\nBased on my analysis of major US technology stocks, here's a detailed comparison of stocks with good growth potential:\n\n| **Company** | **Ticker** | **Sector/Industry** | **PE Ratio** | **PEG Ratio** | **Dividend Yield** | **Market Cap** | **Analysis** |\n|-------------|------------|---------------------|--------------|---------------|-------------------|-----------------|-------------|\n| Microsoft | MSFT | Technology/Prepackaged Software | 36.76 | 2.23 | 0.70% | $3.53T | **Large Cap** leader with strong cloud exposure through Azure. Higher PE ratio shows premium pricing, but steady growth in cloud and AI services justifies valuation. |\n| Apple | AAPL | Technology/Electronic Computers | 30.60 | 1.78 | 0.53% | $2.93T | **Large Cap** consumer tech giant with strong ecosystem. Lower PEG ratio than others suggests better value relative to growth. Steady performance with loyal customer base. |\n| NVIDIA | NVDA | Manufacturing/Semiconductors | 45.95 | 1.76 | 0.03% | $3.46T | **Large Cap** AI chip leader with exceptional profit margin (51.7%). Higher PE but strong growth prospects in AI, gaming, and data center markets. Higher beta (2.12) indicates more volatility. |\n| Amazon | AMZN | Trade & Services/Retail | 34.60 | 2.35 | None | $2.25T | **Large Cap** e-commerce and cloud (AWS) leader. No dividend but reinvests heavily in growth. Higher PEG ratio suggests premium pricing relative to growth rate. |\n| Alphabet | GOOGL | Technology/Computer Programming | 19.49 | 1.34 | 0.48% | $2.13T | **Large Cap** with most attractive valuation metrics. Lowest PE and PEG ratios show good value relative to growth potential. Strong position in search, cloud, and AI development. |\n\n## Key Insights:\n\n1. **Best Value for Growth**: Alphabet (GOOGL) has the lowest PE ratio (19.49) and PEG ratio (1.34) among these tech giants, suggesting it may offer the best value relative to its growth rate.\n\n2. **Highest Growth Premium**: NVIDIA (NVDA) has the highest PE ratio (45.95), reflecting investors' expectations for continued exceptional growth in AI chip demand, though its PEG ratio of 1.76 is more reasonable.\n\n3. **Profit Margin Leader**: NVIDIA stands out with an impressive 51.7% profit margin, significantly higher than peers, demonstrating exceptional operational efficiency.\n\n4. **Dividend Income**: Microsoft offers the highest dividend yield at 0.70%, making it more attractive for income-oriented investors.\n\n5. **Risk Assessment**: NVIDIA has the highest beta (2.12), indicating greater price volatility compared to the market, while Microsoft (1.026) and Alphabet (1.014) have betas closest to the market average.\n\n**Reminder**: This analysis is for informational purposes only and should not be considered financial advice. Stock markets involve risk, and past performance doesn't guarantee future results. Consider consulting a financial advisor before making investment decisions based on this information."}]}
Best Practices for Tool Design
Based on our experience building the financial agent, here are some best practices for designing effective tools:
1. Clear and Descriptive Docstrings
The docstring is how your agent understands when and how to use your tool. Make it clear and comprehensive:
@tool
def search_stocks(query: str, market: str = "US", limit: int = 5) -> List[Dict]:
"""Search for stocks matching the query in the specified market.
Use this tool when the user is looking for stock information.
The search matches partial company names and ticker symbols.
Args:
query: Search term for company name or ticker symbol
market: Market to search in ("US" or "India", default: "US")
limit: Maximum number of results to return (default: 5)
Returns:
List of matching stocks with their details
"""
# Implementation...
2. Proper Error Handling
Tools should handle errors gracefully and return informative messages:
@tool
def get_stock_price(ticker: str, market: str = "US") -> Union[Dict, str]:
"""Get the current price and information for a stock ticker.
Args:
ticker: Stock ticker symbol (e.g., 'AAPL', 'RELIANCE')
market: Market to search in ("US" or "India", default: "US")
Returns:
Current stock price information and company details or error message
"""
try:
# Implementation...
if not is_valid_ticker(ticker):
return f"Invalid ticker symbol: {ticker}. Please provide a valid stock symbol."
# Fetch and return price...
except ConnectionError:
return "Unable to connect to financial data service. Please try again later."
except Exception as e:
return f"Error fetching stock price: {str(e)}"
3. Caching for Performance
Use caching for expensive API calls to improve performance:
from functools import lru_cache
@lru_cache(maxsize=100)
def cached_api_call(param1, param2):
"""Cache API calls to reduce rate limiting and improve performance"""
# Make the actual API call
return make_api_call(param1, param2)
@tool
def get_data(param1: str, param2: str) -> Dict:
"""Get data using cached API calls.
Args:
param1: First parameter
param2: Second parameter
Returns:
Data from the API
"""
return cached_api_call(param1, param2)
4. Environment Variables for Secrets
Never hardcode API keys or secrets in your tools:
import os
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
@tool
def api_call(param: str) -> Dict:
"""Make an API call with authentication.
Args:
param: Parameter for the API call
Returns:
API response
"""
api_key = os.getenv("API_KEY")
if not api_key:
return "API key not found in environment variables"
# Make authenticated API call
# ...
5. Type Annotations
Always use type annotations to help the agent understand parameter and return types:
from typing import Dict, List, Union, Optional
@tool
def complex_tool(
required_param: str,
optional_param: Optional[int] = None,
list_param: List[str] = []
) -> Union[Dict, str]:
"""Tool with complex parameter types.
Args:
required_param: A required string parameter
optional_param: An optional integer parameter
list_param: A list of strings
Returns:
Either a dictionary with results or an error message
"""
# Implementation...
Conclusion
Custom tools are what make Strands agents truly powerful and versatile. By creating well-designed tools, you can extend your agent's capabilities to interact with virtually any system or service. In this blog post, we've explored how to create custom tools for a financial assistant that can search for and analyze stocks from US markets.
Key takeaways:
Tools bridge the gap between your agent's reasoning capabilities and external systems
Well-designed tools have clear docstrings, proper error handling, and focused functionality
Type annotations help the agent understand how to use your tools
Caching can improve performance for expensive API calls
Environment variables should be used for secrets and API keys
Testing API connections before using them in tools can save debugging time
In our next post, we'll explore advanced tool patterns, including tool composition, tool chaining, and dynamic tool loading.
Resources
This post is part of the AWS Strands SDK Masterclass series, where we explore building intelligent AI agents using AWS Strands Agents SDK.






