
Tool Creator
- 3 installs
- 48 repo stars
- Updated August 5, 2026
- aws-samples/sample-deep-insight
tool-creator is a Claude skill that guides creating new tools for a Strands SDK agent system, supporting both Agent-as-a-Tool and regular function-based tools.
About
Provides guidance for creating tools for a Strands SDK-based agent system. It supports two tool types, Agent-as-a-Tool (agents wrapped as tools) and Regular Tools (function-based), and walks type detection, naming, input parameters, implementation logic, and error handling. A developer uses it when adding a new tool to a Strands agent system.
- Creates new tools for a Strands SDK agent system
- Supports both Agent-as-a-Tool and regular function-based tools
- Walks tool type detection, spec (TOOL_SPEC), input schema, and error handling
Tool Creator by the numbers
- 3 all-time installs (skills.sh)
- Ranked #13,657 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
tool-creator capabilities & compatibility
Free; guidance-only skill; the tools it produces target a Strands SDK agent system
- Capabilities
- tool creation · agent tooling · skill creation · system prompt writer
- Use cases
- orchestration
- Pricing
- Free
What tool-creator says it does
It supports creating both agent-as-a-tool (complex agents wrapped as tools) and regular tools (simple function-based tools).
This skill provides comprehensive guidance for creating effective tools for the Strands SDK-based agent system.
npx skills add https://github.com/aws-samples/sample-deep-insight --skill tool-creatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 48 |
| Last updated | August 5, 2026 |
| Repository | aws-samples/sample-deep-insight ↗ |
What it does
Create a new Agent-as-a-Tool or regular function-based tool for a Strands SDK agent system.
Who is it for?
Adding a new tool to a Strands SDK agent, whether a wrapped sub-agent or a simple function tool
Skip if: Non-Strands agent frameworks or tool systems that do not use the TOOL_SPEC pattern
When should I use this skill?
A user requests to create, build, or add a new tool for a Strands SDK agent
What you get
A working Strands tool with a proper TOOL_SPEC, input schema, handler, and error handling, of the right type for the task
- A Strands SDK tool (TOOL_SPEC, handler, and wrapper) of the chosen type
By the numbers
- Supports 2 tool types: Agent-as-a-Tool and Regular Tools
Files
Tool Creator Skill
This skill provides comprehensive guidance for creating effective tools for the Strands SDK-based agent system. It supports two types of tools: Agent-as-a-Tool (agents wrapped as tools) and Regular Tools (function-based tools).
About Tools in This System
Tools extend agent capabilities by providing: 1. Agent-as-a-Tool: Specialized agents with their own prompts, models, and sub-tools 2. Regular Tools: Direct function execution for system operations, API calls, or data processing
Tool Anatomy
Every tool in src/tools/ consists of:
# Required components
TOOL_SPEC = {
"name": "tool_name",
"description": "What the tool does",
"inputSchema": {"json": {...}}
}
def handle_tool_name(param: Annotated[type, "description"]):
"""Implementation logic"""
pass
def tool_name(tool: ToolUse, **kwargs: Any) -> ToolResult:
"""Strands SDK tool wrapper"""
passTool Creation Process
Follow these steps to create a tool. The process supports both full specification upfront and interactive information gathering.
Step 1: Determine Tool Type
Automatic Detection:
- If user mentions "agent tool", "agent-as-a-tool", or describes complex multi-step operations → Agent-as-a-Tool
- If user mentions "simple tool", "regular tool", or describes direct operations → Regular Tool
- If ambiguous → Ask user
Question to ask if ambiguous:
Which type of tool would you like to create?
1. **Agent-as-a-Tool**: A specialized agent with its own prompt, model, and sub-tools (e.g., coder_agent_tool, reporter_agent_tool)
- Use when: Complex reasoning, multi-step operations, or domain expertise needed
2. **Regular Tool**: A simple function-based tool (e.g., bash_tool, python_repl_tool)
- Use when: Direct operations like API calls, file operations, or system commandsStep 2: Gather Basic Tool Information
Collect the following information. If user provided some details already, only ask for missing information.
Required for Both Types:
1. Tool Name (if not provided)
- Question: "What should the tool be named? (Use snake_case, e.g., 'data_analyzer_tool')"
- Validation: Must end with '_tool', use snake_case
2. Tool Description (if not provided)
- Question: "What does this tool do? Provide a clear description of its purpose and capabilities."
- This becomes the tool's description field that helps other agents decide when to use it
3. Input Parameters (if not provided)
- Question: "What input parameters does this tool need?"
- For agent tools, typically:
task(string describing what to do) - For regular tools: specific parameters (e.g.,
cmdfor bash,codefor python)
Step 3: Gather Type-Specific Information
For Regular Tools:
Collect these details (skip if already provided):
1. Implementation Logic
- Question: "What operation should this tool perform? (e.g., execute subprocess, call API, read file)"
- Common patterns: subprocess execution, HTTP requests, file operations, data transformations
2. Error Handling
- Question: "What errors should be handled? (Default: try/except with error logging)"
3. External Dependencies (optional)
- Question: "Does this tool require external libraries? If yes, which ones?"
For Agent-as-a-Tool:
Collect these details (skip if already provided):
1. Agent's Purpose and Role
- Question: "What is the agent's primary purpose? What role does it play in the system?"
- This informs the system prompt creation
2. Agent Model Type
- Question: "Which LLM model should the agent use?"
- Options:
claude-sonnet-3-7(recommended for most tasks)claude-sonnet-4(advanced reasoning)claude-sonnet-3-5-v-2(legacy)
3. Reasoning Capability
- Question: "Should this agent use extended thinking/reasoning? (True/False)"
- Default: False
- Use True for: complex analysis, planning, strategic decisions
4. Prompt Caching
- Question: "Should prompt caching be enabled? (Recommended: True for agents called frequently)"
- Default: (True, None)
5. Sub-tools (if not provided)
- Question: "Which tools should this agent have access to?"
- Common options:
python_repl_tool,bash_tool,file_read - Reference existing tools in
src/tools/
6. System Prompt Creation
- IMPORTANT: For system prompt creation, refer to
references/system-prompt-guidelines.md - If user hasn't provided a system prompt, ask: "Do you want to create a custom system prompt for this agent?"
- If yes: Use system-prompt-writer guidelines from references to create an effective prompt
- If no: Create a basic prompt based on the agent's purpose
Step 4: Create the Tool File
Generate the tool file in src/tools/ using the appropriate template:
- Regular Tool: Use
templates/regular_tool_template.py - Agent-as-a-Tool: Use
templates/agent_tool_template.py
File Creation Steps:
1. Load the appropriate template 2. Replace template variables with gathered information 3. If creating system prompt:
- Create prompt file in
src/prompts/[tool_name_without_tool].md - Follow system-prompt-writer guidelines from
references/system-prompt-guidelines.md - Use proper template variable escaping (double braces
{{}}for code samples)
4. Write the tool file to src/tools/[tool_name].py 5. Inform user of file locations
Step 5: Validation and Next Steps
After creating the tool:
1. Verify File Creation
- Confirm tool file exists at
src/tools/[tool_name].py - If agent tool with prompt, confirm prompt file at
src/prompts/[name].md
2. Integration Guidance
- Inform user how to import and use the new tool:
from src.tools.[tool_name] import [tool_name]
# Use in agent
agent = strands_utils.get_agent(
agent_name="example",
tools=[tool_name, other_tool],
...
)3. Testing Recommendations
- Suggest testing the tool in isolation
- For agent tools: Test with sample tasks
- For regular tools: Test with sample inputs
Key Design Principles
For All Tools
1. Clear Naming: Tool names should be descriptive and end with _tool 2. Comprehensive Descriptions: Description should clearly state what the tool does and when to use it 3. Annotated Parameters: Use Annotated[type, "description"] for all parameters 4. Consistent Error Handling: Return error messages, don't raise exceptions 5. Logging: Use color-coded logging for visibility
For Agent-as-a-Tool
1. Global State Integration: Always access _global_node_states for shared context 2. Streaming Support: Use async streaming pattern with process_streaming_response_yield 3. State Updates: Update clues, history, and messages in shared state 4. Response Format: Use standard response format templates 5. Prompt Templates: Use apply_prompt_template() with proper context variables
For Regular Tools
1. Simplicity: Keep logic straightforward and focused 2. Decorator Usage: Use @log_io decorator for input/output logging 3. Subprocess Safety: Set timeouts and handle errors for subprocess calls 4. Result Formatting: Return results in consistent format (e.g., "cmd||output")
Common Patterns
Pattern 1: Agent Tool with Analysis Capabilities
# Agent for data analysis tasks
- Model: claude-sonnet-3-7
- Reasoning: False
- Tools: [python_repl_tool, bash_tool]
- Purpose: Execute data analysis and calculationsPattern 2: Agent Tool for Report Generation
# Agent for creating reports
- Model: claude-sonnet-3-7
- Reasoning: False
- Tools: [python_repl_tool, bash_tool, file_read]
- Purpose: Generate formatted reports from analysis resultsPattern 3: Simple Execution Tool
# Tool for direct command execution
- Type: Regular Tool
- Operation: subprocess.run()
- Error Handling: Capture stderr, return error messagesReferences
- System Prompt Creation: See
references/system-prompt-guidelines.mdfor comprehensive prompt writing guidance - Template Files: See
templates/for tool code templates - Example Tools: See
references/tool-examples.mdfor complete real-world examples
Iteration and Improvement
After creating the initial tool:
1. Test with Real Scenarios: Try the tool with actual use cases 2. Gather Feedback: Identify what works and what doesn't 3. Refine Prompts: For agent tools, improve system prompts based on behavior 4. Optimize Parameters: Adjust input schemas if needed 5. Update Documentation: Keep descriptions accurate
The goal is creating effective, reliable tools that seamlessly integrate with the Strands SDK agent system.
System Prompt Guidelines for Agent Tools
This document provides guidelines for creating effective system prompts for agent-as-a-tool implementations. It references the comprehensive system-prompt-writer skill with key points specific to tool creation.
Quick Reference
For complete system prompt writing guidance, refer to /skills/system-prompt-writer/SKILL.md.
This document focuses on the most critical aspects for agent tool prompts.
Essential Principles for Agent Tool Prompts
1. Template Variable Escaping (CRITICAL)
This is the #1 cause of agent tool failures.
The project uses a template system (src/prompts/template.py) that processes prompts with .format(). You MUST follow escaping rules:
Escaping Rule:
- Single braces `{}` → Template variables (e.g.,
{USER_REQUEST},{FULL_PLAN}) - Double braces `{{}}` → Escaped to single braces in output (for code samples)
Common Mistakes:
❌ WRONG (Will cause KeyError):
result = {"key": "value"}
print(f"Total: {amount}")✅ CORRECT:
result = {{"key": "value"}}
print(f"Total: {{amount}}")Required Template Variables for Agent Tools:
Always include these in your prompt frontmatter:
---
USER_REQUEST: {USER_REQUEST}
FULL_PLAN: {FULL_PLAN}
---2. Recommended Structure for Agent Tool Prompts
Use the Hybrid (Markdown + XML) approach:
## Role
<role>
You are a [specific role]. Your objective is to [clear goal].
</role>
## Instructions
<instructions>
- [Key principle 1]
- [Key principle 2]
- When [situation], do [action]
</instructions>
## Tool Guidance
<tool_guidance>
- tool_name: Use when [specific condition]
- tool_name_2: Use when [specific condition]
</tool_guidance>
## Constraints
<constraints>
- Do not [constraint 1]
- Always [requirement 1]
</constraints>3. Tool Guidance - Be Specific
Poor Tool Guidance:
You have access to python_repl_tool and bash_tool. Use them as needed.Good Tool Guidance:
## Tool Guidance
<tool_guidance>
- python_repl_tool(code): Use for data analysis, calculations, and generating visualizations
- bash_tool(cmd): Use for file system operations, checking file existence, and listing directories
Decision Framework:
- Data manipulation or analysis → python_repl_tool
- File operations → bash_tool
</tool_guidance>4. Domain-Specific Patterns for Common Agent Tools
Execution/Worker Agents (e.g., Coder)
Focus on:
- Tool usage and execution safety
- Error handling
- Output formatting
- Validation before execution
Template:
## Role
<role>
You are a code execution specialist. Execute Python/bash commands safely and return results.
</role>
## Capabilities
<capabilities>
- Execute Python in REPL environment
- Run bash commands for file operations
- Handle errors gracefully
- Save artifacts to designated locations
</capabilities>
## Safety Constraints
<constraints>
- Validate inputs before execution
- Never execute potentially harmful code
- Respect file system boundaries
</constraints>Report/Content Generation Agents (e.g., Reporter)
Focus on:
- Content structure
- Formatting standards
- Visualization integration
- Multi-format output
Template:
## Role
<role>
You are a report generation specialist. Create comprehensive, well-formatted reports.
</role>
## Report Structure
<structure>
Standard sections:
1. Executive Summary
2. Findings/Analysis
3. Visualizations
4. Conclusions/Recommendations
</structure>
## Formatting Standards
<formatting>
- Use consistent heading levels
- Label all charts and tables
- Keep paragraphs concise
- Use bullet points for key findings
</formatting>Validation/Quality Assurance Agents (e.g., Validator)
Focus on:
- Validation criteria
- Quality checks
- Error detection
- Feedback format
Template:
## Role
<role>
You are a validation specialist. Verify outputs meet quality standards.
</role>
## Validation Criteria
<validation_criteria>
- Completeness: All required sections present
- Accuracy: Data and calculations correct
- Format: Proper structure and formatting
- Consistency: Style and terminology consistent
</validation_criteria>
## Validation Process
<process>
1. Check structural requirements
2. Verify data accuracy
3. Review formatting
4. Provide detailed feedback
</process>Progress Tracking Agents (e.g., Tracker)
Focus on:
- State monitoring
- Progress reporting
- Completion tracking
- Next step identification
Template:
## Role
<role>
You are a progress tracking specialist. Monitor task completion and guide next steps.
</role>
## Tracking Responsibilities
<responsibilities>
- Monitor completed tasks
- Identify remaining work
- Suggest next actions
- Report overall progress
</responsibilities>
## Progress Reporting
<reporting>
Format progress reports with:
- Completed tasks (with checkmarks)
- In-progress tasks
- Pending tasks
- Recommended next step
</reporting>5. Minimum Effective Information
Key Question: What's the smallest amount of context needed for the agent to succeed?
Before (Over-specified):
You are a data analyst for the XYZ project. You should always be helpful and professional. When analyzing data, make sure to follow best practices. Use Python for calculations. Always double-check your work...After (Optimized):
You are a data analyst. Execute Python code for data analysis and calculations. Validate results before returning.6. Iterative Development
Don't try to write the perfect prompt on the first try.
1. Start minimal (role + basic instructions) 2. Test with real tasks 3. Identify failure modes 4. Add targeted improvements 5. Re-test
Example Evolution:
Version 1 (Minimal):
You are a coder agent. Execute Python and bash commands.Version 2 (After finding it doesn't validate inputs):
You are a coder agent. Execute Python and bash commands.
Before execution:
- Validate code syntax
- Check for potentially destructive operationsVersion 3 (After finding poor error messages):
You are a coder agent. Execute Python and bash commands.
Before execution:
- Validate code syntax
- Check for potentially destructive operations
Error Handling:
- Capture all errors with full stack traces
- Provide clear error messages
- Suggest fixes when possiblePre-Writing Checklist
Before creating any agent tool prompt:
- [ ] Identified agent's primary role and purpose
- [ ] Determined which tools the agent needs
- [ ] Planned template variables:
{USER_REQUEST},{FULL_PLAN} - [ ] Will use double braces
{{}}for ALL code samples - [ ] Chosen appropriate domain-specific pattern
- [ ] Started with minimal prompt (will iterate)
Common Pitfalls to Avoid
❌ Missing brace escaping - Causes KeyError, agent won't load ❌ Over-specification - Writing step-by-step algorithms instead of guidance ❌ Vague tool guidance - "Use tools as needed" instead of specific conditions ❌ Redundancy - Repeating information from tool descriptions ❌ Premature optimization - Writing complex prompt before testing ❌ Missing template variables - Not including {USER_REQUEST} and {FULL_PLAN}
Complete Example: Coder Agent Prompt
See references/tool-examples.md for complete working examples.
For More Details
This is a quick reference. For comprehensive guidance:
- Full system prompt guide:
/skills/system-prompt-writer/SKILL.md - Examples:
/skills/system-prompt-writer/references/examples.md - Section organization:
/skills/system-prompt-writer/references/section-organization-guide.md - Real tool prompts:
/src/prompts/coder.md,/src/prompts/reporter.md, etc.
Validation
After writing your prompt, verify:
1. Template Variables: Used single braces {} for variables, double braces {{}} for code 2. Structure: Clear sections with Markdown + XML 3. Tool Guidance: Specific conditions for each tool 4. Role: Clear and focused 5. Constraints: Explicit boundaries 6. Testing: Plan to test and iterate
Remember: Effective > Perfect. Start simple, test, and improve based on real behavior.
Tool Implementation Examples
This document provides complete, real-world examples of both tool types to serve as reference during tool creation.
Example 1: Regular Tool - Bash Tool
Purpose: Execute bash commands for system operations
File: src/tools/bash_tool.py
import logging
import subprocess
from typing import Any, Annotated
from strands.types.tools import ToolResult, ToolUse
from src.tools.decorators import log_io
# Simple logger setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
TOOL_SPEC = {
"name": "bash_tool",
"description": "Use this to execute bash command and do necessary operations.",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"cmd": {
"type": "string",
"description": "The bash command to be executed."
}
},
"required": ["cmd"]
}
}
}
class Colors:
GREEN = '\033[92m'
RED = '\033[91m'
END = '\033[0m'
@log_io
def handle_bash_tool(cmd: Annotated[str, "The bash command to be executed."]):
"""Use this to execute bash command and do necessary operations."""
print() # Add newline before log
logger.info(f"\n{Colors.GREEN}Executing Bash: {cmd}{Colors.END}")
try:
# Execute the command and capture output
result = subprocess.run(
cmd, shell=True, check=True, text=True, capture_output=True
)
# Return stdout as the result
results = "||".join([cmd, result.stdout])
return results + "\n"
except subprocess.CalledProcessError as e:
# If command fails, return error information
error_message = f"Command failed with exit code {e.returncode}.\nStdout: {e.stdout}\nStderr: {e.stderr}"
logger.error(f"{Colors.RED}Command failed: {e.returncode}{Colors.END}")
return error_message
except Exception as e:
# Catch any other exceptions
error_message = f"Error executing command: {str(e)}"
logger.error(f"{Colors.RED}Error: {str(e)}{Colors.END}")
return error_message
# Function name must match tool name
def bash_tool(tool: ToolUse, **_kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
cmd = tool["input"]["cmd"]
# Use the existing handle_bash_tool function
result = handle_bash_tool(cmd)
# Check if execution was successful based on the result string
if "Command failed" in result or "Error executing command" in result:
return {
"toolUseId": tool_use_id,
"status": "error",
"content": [{"text": result}]
}
else:
return {
"toolUseId": tool_use_id,
"status": "success",
"content": [{"text": result}]
}
if __name__ == "__main__":
# Test example using the handle_bash_tool function directly
print(handle_bash_tool("ls -all"))Key Features:
@log_iodecorator for automatic I/O logging- Subprocess execution with error handling
- Color-coded console output
- Result format:
"cmd||output" - Test section in
__main__
---
Example 2: Regular Tool - Python REPL Tool
Purpose: Execute Python code for data analysis and calculations
File: src/tools/python_repl_tool.py
import sys
import logging
import subprocess
from typing import Any, Annotated
from strands.types.tools import ToolResult, ToolUse
from src.tools.decorators import log_io
# Simple logger setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
TOOL_SPEC = {
"name": "python_repl_tool",
"description": "Use this to execute python code and do data analysis or calculation. If you want to see the output of a value, you should print it out with `print(...)`. This is visible to the user.",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The python code to execute to do further analysis or calculation."
}
},
"required": ["code"]
}
}
}
class Colors:
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
END = '\033[0m'
class PythonREPL:
def __init__(self):
pass
def run(self, command):
try:
# Execute the command
result = subprocess.run(
[sys.executable, "-c", command],
capture_output=True,
text=True,
timeout=600 # Timeout setting
)
# Return result
if result.returncode == 0:
return result.stdout
else:
return f"Error: {result.stderr}"
except Exception as e:
return f"Exception: {str(e)}"
repl = PythonREPL()
@log_io
def handle_python_repl_tool(code: Annotated[str, "The python code to execute to do further analysis or calculation."]):
"""
Use this to execute python code and do data analysis or calculation. If you want to see the output of a value,
you should print it out with `print(...)`. This is visible to the user.
"""
print() # Add newline before log
logger.info(f"{Colors.GREEN}===== Executing Python code ====={Colors.END}")
try:
result = repl.run(code)
except BaseException as e:
error_msg = f"Failed to execute. Error: {repr(e)}"
logger.debug(f"{Colors.RED}Failed to execute. Error: {repr(e)}{Colors.END}")
return error_msg
# Truncate code to first 7 lines for context efficiency
code_lines = code.split('\n')
if len(code_lines) > 7:
code_preview = '\n'.join(code_lines[:7])
code_summary = f"{code_preview}\n... ({len(code_lines) - 7} more lines omitted)"
else:
code_summary = code
result_str = f"Successfully executed:\n||{code_summary}||{result}"
logger.info(f"{Colors.GREEN}===== Code execution successful ====={Colors.END}")
return result_str
# Function name must match tool name
def python_repl_tool(tool: ToolUse, **kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
code = tool["input"]["code"]
# Use the existing handle_python_repl_tool function
result = handle_python_repl_tool(code)
# Check if execution was successful based on the result string
if "Failed to execute" in result:
return {
"toolUseId": tool_use_id,
"status": "error",
"content": [{"text": result}]
}
else:
return {
"toolUseId": tool_use_id,
"status": "success",
"content": [{"text": result}]
}Key Features:
- Dedicated
PythonREPLclass for code execution - Code truncation for long outputs (context efficiency)
- Timeout protection (600 seconds)
- Result format:
"Successfully executed:\n||code||output"
---
Example 3: Agent-as-a-Tool - Coder Agent Tool
Purpose: Execute Python and bash commands using a specialized coder agent
File: src/tools/coder_agent_tool.py
import logging
import asyncio
from typing import Any, Annotated
from strands.types.tools import ToolResult, ToolUse
from src.utils.strands_sdk_utils import strands_utils
from src.prompts.template import apply_prompt_template
from src.utils.common_utils import get_message_from_string
from src.tools import python_repl_tool, bash_tool
# Simple logger setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
TOOL_SPEC = {
"name": "coder_agent_tool",
"description": "Execute Python code and bash commands using a specialized coder agent. This tool provides access to a coder agent that can execute Python code for data analysis and calculations, run bash commands for system operations, and handle complex programming tasks.",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "The coding task or question that needs to be executed by the coder agent."
}
},
"required": ["task"]
}
}
}
RESPONSE_FORMAT = "Response from {}:\n\n<response>\n{}\n</response>\n\n*Please execute the next step.*"
CLUES_FORMAT = "Here is clues from {}:\n\n<clues>\n{}\n</clues>\n\n"
class Colors:
GREEN = '\033[92m'
YELLOW = '\033[93m'
END = '\033[0m'
def handle_coder_agent_tool(task: Annotated[str, "The coding task or question that needs to be executed by the coder agent."]):
"""
Execute Python code and bash commands using a specialized coder agent.
This tool provides access to a coder agent that can:
- Execute Python code for data analysis and calculations
- Run bash commands for system operations
- Handle complex programming tasks
Args:
task: The coding task or question that needs to be executed
Returns:
The result of the code execution or analysis
"""
print() # Add newline before log
logger.info(f"\n{Colors.GREEN}Coder Agent Tool starting task{Colors.END}")
# Try to extract shared state from global storage
from src.graph.nodes import _global_node_states
shared_state = _global_node_states.get('shared', None)
if not shared_state:
logger.warning("No shared state found")
return "Error: No shared state available"
request_prompt, full_plan = shared_state.get("request_prompt", ""), shared_state.get("full_plan", "")
clues, messages = shared_state.get("clues", ""), shared_state.get("messages", [])
# Create coder agent with specialized tools using consistent pattern
coder_agent = strands_utils.get_agent(
agent_name="coder",
system_prompts=apply_prompt_template(prompt_name="coder", prompt_context={"USER_REQUEST": request_prompt, "FULL_PLAN": full_plan}),
agent_type="claude-sonnet-3-7",
enable_reasoning=False,
tools=[python_repl_tool, bash_tool],
streaming=True
)
# Prepare message with context if available
message = '\n\n'.join([messages[-1]["content"][-1]["text"], clues])
# Process streaming response and collect text in one pass
async def process_coder_stream():
full_text = ""
async for event in strands_utils.process_streaming_response_yield(
coder_agent, message, agent_name="coder", source="coder_tool"
):
if event.get("event_type") == "text_chunk": full_text += event.get("data", "")
return {"text": full_text}
response = asyncio.run(process_coder_stream())
result_text = response['text']
# Update clues
clues = '\n\n'.join([clues, CLUES_FORMAT.format("coder", response["text"])])
# Update history
history = shared_state.get("history", [])
history.append({"agent":"coder", "message": response["text"]})
# Update shared state
shared_state['messages'] = [get_message_from_string(role="user", string=RESPONSE_FORMAT.format("coder", response["text"]), imgs=[])]
shared_state['clues'] = clues
shared_state['history'] = history
logger.info(f"\n{Colors.GREEN}Coder Agent Tool completed successfully{Colors.END}")
return result_text
# Function name must match tool name
def coder_agent_tool(tool: ToolUse, **_kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
task = tool["input"]["task"]
# Use the existing handle_coder_agent_tool function
result = handle_coder_agent_tool(task)
# Check if execution was successful based on the result string
if "Error in coder agent tool" in result:
return {
"toolUseId": tool_use_id,
"status": "error",
"content": [{"text": result}]
}
else:
return {
"toolUseId": tool_use_id,
"status": "success",
"content": [{"text": result}]
}Companion System Prompt: src/prompts/coder.md
Key Features:
- Global state integration (
_global_node_states) - Streaming async pattern
- Context propagation (USER_REQUEST, FULL_PLAN)
- State updates (clues, history, messages)
- Sub-tools access (python_repl_tool, bash_tool)
- Response formatting with XML tags
---
Example 4: Agent-as-a-Tool - Reporter Agent Tool
Purpose: Generate comprehensive reports using a specialized reporter agent
File: src/tools/reporter_agent_tool.py
import logging
import asyncio
from typing import Any, Annotated
from strands.types.tools import ToolResult, ToolUse
from src.utils.strands_sdk_utils import strands_utils
from src.prompts.template import apply_prompt_template
from src.utils.common_utils import get_message_from_string
from src.tools import python_repl_tool, bash_tool
from strands_tools import file_read
# Simple logger setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
TOOL_SPEC = {
"name": "reporter_agent_tool",
"description": "Generate comprehensive reports based on analysis results using a specialized reporter agent. This tool provides access to a reporter agent that can read analysis results from artifacts, create structured reports with visualizations, and generate output in multiple formats (HTML, PDF, Markdown).",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "The reporting task or instruction for generating the report (e.g., 'Create a comprehensive analysis report', 'Generate PDF report with all findings')."
}
},
"required": ["task"]
}
}
}
RESPONSE_FORMAT = "Response from {}:\n\n<response>\n{}\n</response>\n\n*Please execute the next step.*"
CLUES_FORMAT = "Here is clues from {}:\n\n<clues>\n{}\n</clues>\n\n"
class Colors:
GREEN = '\033[92m'
END = '\033[0m'
def handle_reporter_agent_tool(_task: Annotated[str, "The reporting task or instruction for generating the report."]):
"""
Generate comprehensive reports based on analysis results using a specialized reporter agent.
This tool provides access to a reporter agent that can:
- Read analysis results from artifacts directory
- Create structured reports with executive summaries, key findings, and detailed analysis
- Generate reports in multiple formats (HTML, PDF, Markdown)
- Include visualizations and charts in reports
Args:
task: The reporting task or instruction for generating the report
Returns:
The generated report content and confirmation of file creation
"""
print() # Add newline before log
logger.info(f"\n{Colors.GREEN}Reporter Agent Tool starting{Colors.END}")
# Try to extract shared state from global storage
from src.graph.nodes import _global_node_states
shared_state = _global_node_states.get('shared', None)
if not shared_state:
logger.warning("No shared state found")
return "Error: No shared state available"
request_prompt, full_plan = shared_state.get("request_prompt", ""), shared_state.get("full_plan", "")
clues, messages = shared_state.get("clues", ""), shared_state.get("messages", [])
# Create reporter agent with specialized tools
reporter_agent = strands_utils.get_agent(
agent_name="reporter",
system_prompts=apply_prompt_template(prompt_name="reporter", prompt_context={"USER_REQUEST": request_prompt, "FULL_PLAN": full_plan}),
agent_type="claude-sonnet-3-7",
enable_reasoning=False,
prompt_cache_info=(True, None),
tools=[python_repl_tool, bash_tool, file_read],
streaming=True
)
# Prepare message with context
message = '\n\n'.join([messages[-1]["content"][-1]["text"], clues])
# Process streaming response
async def process_reporter_stream():
full_text = ""
async for event in strands_utils.process_streaming_response_yield(
reporter_agent, message, agent_name="reporter", source="reporter_tool"
):
if event.get("event_type") == "text_chunk": full_text += event.get("data", "")
return {"text": full_text}
response = asyncio.run(process_reporter_stream())
result_text = response['text']
# Update clues
clues = '\n\n'.join([clues, CLUES_FORMAT.format("reporter", response["text"])])
# Update history
history = shared_state.get("history", [])
history.append({"agent":"reporter", "message": response["text"]})
# Update shared state
shared_state['messages'] = [get_message_from_string(role="user", string=RESPONSE_FORMAT.format("reporter", response["text"]), imgs=[])]
shared_state['clues'] = clues
shared_state['history'] = history
logger.info(f"\n{Colors.GREEN}Reporter Agent Tool completed{Colors.END}")
return result_text
# Function name must match tool name
def reporter_agent_tool(tool: ToolUse, **_kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
task = tool["input"]["task"]
# Use the existing handle function
result = handle_reporter_agent_tool(task)
# Check if execution was successful
if "Error in reporter agent tool" in result:
return {
"toolUseId": tool_use_id,
"status": "error",
"content": [{"text": result}]
}
else:
return {
"toolUseId": tool_use_id,
"status": "success",
"content": [{"text": result}]
}Key Features:
- Prompt caching enabled:
prompt_cache_info=(True, None) - External tool integration:
file_readfrom strands_tools - Same state management pattern as coder
- Domain-specific tools (python, bash, file_read)
---
Pattern Comparison
Regular Tool Pattern
Structure: 1. TOOL_SPEC definition 2. Colors class for logging 3. @log_io decorated handler function 4. Direct operation (subprocess, API call, etc.) 5. ToolResult wrapper function 6. Optional __main__ test section
When to use:
- Simple, direct operations
- No need for reasoning or multi-step logic
- Deterministic behavior
- Fast execution
Agent Tool Pattern
Structure: 1. TOOL_SPEC definition 2. Response format constants (RESPONSE_FORMAT, CLUES_FORMAT) 3. Colors class for logging 4. Handler function with:
- Global state extraction
- Agent creation with
strands_utils.get_agent() - Context preparation
- Async streaming processing
- State updates (clues, history, messages)
5. ToolResult wrapper function
When to use:
- Complex, multi-step operations
- Requires reasoning or decision-making
- Needs access to other tools
- Domain-specific expertise
---
Common Code Patterns
Pattern 1: Global State Access (Agent Tools)
from src.graph.nodes import _global_node_states
shared_state = _global_node_states.get('shared', None)
if not shared_state:
logger.warning("No shared state found")
return "Error: No shared state available"
request_prompt = shared_state.get("request_prompt", "")
full_plan = shared_state.get("full_plan", "")
clues = shared_state.get("clues", "")
messages = shared_state.get("messages", [])Pattern 2: Agent Creation (Agent Tools)
agent = strands_utils.get_agent(
agent_name="agent_name",
system_prompts=apply_prompt_template(
prompt_name="agent_name",
prompt_context={"USER_REQUEST": request_prompt, "FULL_PLAN": full_plan}
),
agent_type="claude-sonnet-3-7",
enable_reasoning=False,
prompt_cache_info=(True, None), # Optional
tools=[tool1, tool2],
streaming=True
)Pattern 3: Streaming Processing (Agent Tools)
async def process_stream():
full_text = ""
async for event in strands_utils.process_streaming_response_yield(
agent, message, agent_name="name", source="source"
):
if event.get("event_type") == "text_chunk":
full_text += event.get("data", "")
return {"text": full_text}
response = asyncio.run(process_stream())Pattern 4: State Update (Agent Tools)
# Update clues
clues = '\n\n'.join([clues, CLUES_FORMAT.format("agent_name", response["text"])])
# Update history
history = shared_state.get("history", [])
history.append({"agent": "agent_name", "message": response["text"]})
# Update shared state
shared_state['messages'] = [get_message_from_string(
role="user",
string=RESPONSE_FORMAT.format("agent_name", response["text"]),
imgs=[]
)]
shared_state['clues'] = clues
shared_state['history'] = historyPattern 5: Error Handling (Both Types)
try:
# Operation
result = perform_operation()
return result
except SpecificException as e:
error_message = f"Error: {str(e)}"
logger.error(f"{Colors.RED}Error: {str(e)}{Colors.END}")
return error_message
except Exception as e:
error_message = f"Unexpected error: {str(e)}"
logger.error(f"{Colors.RED}Error: {str(e)}{Colors.END}")
return error_message---
Usage in Agent Systems
Importing Tools
# Regular tools
from src.tools.bash_tool import bash_tool
from src.tools.python_repl_tool import python_repl_tool
# Agent tools
from src.tools.coder_agent_tool import coder_agent_tool
from src.tools.reporter_agent_tool import reporter_agent_toolUsing in Agent Creation
# Supervisor with agent tools
supervisor_agent = strands_utils.get_agent(
agent_name="supervisor",
system_prompts=supervisor_prompt,
tools=[coder_agent_tool, reporter_agent_tool],
streaming=True
)
# Coder agent with regular tools
coder_agent = strands_utils.get_agent(
agent_name="coder",
system_prompts=coder_prompt,
tools=[python_repl_tool, bash_tool],
streaming=True
)---
Key Takeaways
1. Regular tools are simpler, faster, and deterministic 2. Agent tools are more powerful but more complex 3. Both follow consistent patterns for integration 4. State management is critical for agent tools 5. Streaming is the standard for agent interactions 6. Error handling should be graceful and informative 7. Logging provides visibility into tool execution 8. Templates variables must be properly escaped in system prompts
import logging
import asyncio
from typing import Any, Annotated
from strands.types.tools import ToolResult, ToolUse
from src.utils.strands_sdk_utils import strands_utils
from src.prompts.template import apply_prompt_template
from src.utils.common_utils import get_message_from_string
{{TOOL_IMPORTS}}
# Simple logger setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
TOOL_SPEC = {
"name": "{{TOOL_NAME}}",
"description": "{{TOOL_DESCRIPTION}}",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"task": {
"type": "string",
"description": "{{TASK_DESCRIPTION}}"
}
},
"required": ["task"]
}
}
}
RESPONSE_FORMAT = "Response from {{AGENT_NAME}}:\n\n<response>\n{{}}\n</response>\n\n*Please execute the next step.*"
CLUES_FORMAT = "Here is clues from {{AGENT_NAME}}:\n\n<clues>\n{{}}\n</clues>\n\n"
class Colors:
GREEN = '\033[92m'
YELLOW = '\033[93m'
END = '\033[0m'
def handle_{{TOOL_NAME}}(task: Annotated[str, "{{TASK_DESCRIPTION}}"]):
"""
{{TOOL_DESCRIPTION}}
Args:
task: {{TASK_DESCRIPTION}}
Returns:
The result of the agent's execution
"""
print() # Add newline before log
logger.info(f"\n{{Colors.GREEN}}{{AGENT_NAME}} Agent Tool starting task{{Colors.END}}")
# Try to extract shared state from global storage
from src.graph.nodes import _global_node_states
shared_state = _global_node_states.get('shared', None)
if not shared_state:
logger.warning("No shared state found")
return "Error: No shared state available"
request_prompt, full_plan = shared_state.get("request_prompt", ""), shared_state.get("full_plan", "")
clues, messages = shared_state.get("clues", ""), shared_state.get("messages", [])
# Create agent with specialized tools
agent = strands_utils.get_agent(
agent_name="{{AGENT_NAME}}",
system_prompts=apply_prompt_template(prompt_name="{{AGENT_NAME}}", prompt_context={{"USER_REQUEST": request_prompt, "FULL_PLAN": full_plan}}),
agent_type="{{MODEL_TYPE}}",
enable_reasoning={{ENABLE_REASONING}},
prompt_cache_info={{PROMPT_CACHE_INFO}},
tools=[{{AGENT_TOOLS}}],
streaming=True
)
# Prepare message with context
message = '\n\n'.join([messages[-1]["content"][-1]["text"], clues])
# Process streaming response
async def process_stream():
full_text = ""
async for event in strands_utils.process_streaming_response_yield(
agent, message, agent_name="{{AGENT_NAME}}", source="{{TOOL_NAME}}"
):
if event.get("event_type") == "text_chunk":
full_text += event.get("data", "")
return {{"text": full_text}}
response = asyncio.run(process_stream())
result_text = response['text']
# Update clues
clues = '\n\n'.join([clues, CLUES_FORMAT.format("{{AGENT_NAME}}", response["text"])])
# Update history
history = shared_state.get("history", [])
history.append({{"agent": "{{AGENT_NAME}}", "message": response["text"]}})
# Update shared state
shared_state['messages'] = [get_message_from_string(role="user", string=RESPONSE_FORMAT.format("{{AGENT_NAME}}", response["text"]), imgs=[])]
shared_state['clues'] = clues
shared_state['history'] = history
logger.info(f"\n{{Colors.GREEN}}{{AGENT_NAME}} Agent Tool completed successfully{{Colors.END}}")
return result_text
# Function name must match tool name
def {{TOOL_NAME}}(tool: ToolUse, **_kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
task = tool["input"]["task"]
# Use the existing handle function
result = handle_{{TOOL_NAME}}(task)
# Check if execution was successful
if "Error in {{TOOL_NAME}}" in result:
return {
"toolUseId": tool_use_id,
"status": "error",
"content": [{{"text": result}}]
}
else:
return {
"toolUseId": tool_use_id,
"status": "success",
"content": [{{"text": result}}]
}
import logging
from typing import Any, Annotated
from strands.types.tools import ToolResult, ToolUse
from src.tools.decorators import log_io
# Simple logger setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
TOOL_SPEC = {
"name": "{{TOOL_NAME}}",
"description": "{{TOOL_DESCRIPTION}}",
"inputSchema": {
"json": {
"type": "object",
"properties": {
{{INPUT_PROPERTIES}}
},
"required": {{REQUIRED_FIELDS}}
}
}
}
class Colors:
GREEN = '\033[92m'
RED = '\033[91m'
END = '\033[0m'
@log_io
def handle_{{TOOL_NAME}}({{FUNCTION_PARAMETERS}}):
"""{{TOOL_DESCRIPTION}}"""
print() # Add newline before log
logger.info(f"\n{{Colors.GREEN}}Executing {{TOOL_NAME}}{{Colors.END}}")
try:
{{IMPLEMENTATION_LOGIC}}
logger.info(f"{{Colors.GREEN}}{{TOOL_NAME}} completed successfully{{Colors.END}}")
return result
except Exception as e:
error_message = f"Error in {{TOOL_NAME}}: {{str(e)}}"
logger.error(f"{{Colors.RED}}Error: {{str(e)}}{{Colors.END}}")
return error_message
# Function name must match tool name
def {{TOOL_NAME}}(tool: ToolUse, **_kwargs: Any) -> ToolResult:
tool_use_id = tool["toolUseId"]
{{EXTRACT_INPUTS}}
# Use the existing handle function
result = handle_{{TOOL_NAME}}({{CALL_PARAMETERS}})
# Check if execution was successful
if "Error" in result:
return {
"toolUseId": tool_use_id,
"status": "error",
"content": [{"text": result}]
}
else:
return {
"toolUseId": tool_use_id,
"status": "success",
"content": [{"text": result}]
}
if __name__ == "__main__":
# Test example
print(handle_{{TOOL_NAME}}({{TEST_PARAMETERS}}))
Related skills
FAQ
What two tool types does this skill support?
Agent-as-a-Tool (a specialized agent with its own prompt, model, and sub-tools) and Regular Tools (simple function-based tools for direct operations).
What does every Strands tool consist of?
A TOOL_SPEC (name, description, inputSchema), a handler function with the implementation logic, and a Strands SDK tool wrapper function.