Adding Custom Tools
Adding custom tools to SWE-agent
This tutorial walks you through creating and integrating custom tools into SWE-agent.
We'll create a fun print_cat
command that prints ASCII art of a cat for morale boost!
Please read our hello world and command line basics tutorials before proceeding.
Understanding Tool Structure
Every SWE-agent tool is organized as a "bundle" - a directory containing:
config.yaml
- Defines the tool's interface and documentationbin/
directory - Contains the executable scriptsinstall.sh
(optional) - Sets up dependencieslib/
(optional) - Contains shared libraries or utilities
Let's look at the simple submit
tool structure:
tools/submit/
├── config.yaml
└── bin/
└── submit
Step 1: Write the Command Script
First, let's create our morale_boost
tool bundle:
mkdir -p tools/morale_boost/bin
Create the executable script that will be called when the agent runs your command:
#!/bin/bash
# print_cat - A morale-boosting ASCII cat printer!
echo "🐱 Here's a cat to boost your morale! 🐱"
echo ""
cat << 'EOF'
/\_/\
( o.o )
> ^ <
_) (_
(_____)
EOF
echo ""
echo "You're doing great! Keep coding! 💪"
If you're wondering about the strange EOF
construct, those are called
heredocs and enable multiline
arguments in bash.
Step 2: Define the Tool Configuration
Create the config.yaml
file that tells SWE-agent about your tool:
tools:
print_cat:
signature: "print_cat"
docstring: "Prints an ASCII art cat for morale boost. Use when you need encouragement!"
arguments: []
Tool Configuration Options
The config.yaml
supports various options:
signature
: How the command should be called (including arguments)docstring
: Description that helps the AI understand when to use this toolarguments
: List of arguments with types, descriptions, and whether they're required
For a tool with arguments, it would look like this:
tools:
print_animal:
signature: "print_animal <animal_type> [--size=<size>]"
docstring: "Prints ASCII art of the specified animal"
arguments:
- name: animal_type
type: string
description: "Type of animal to print (cat, dog, elephant)"
required: true
- name: size
type: string
description: "Size of the art (small, medium, large)"
required: false
Step 3: Tell the agent to use the new tool
Now you need to tell SWE-agent to use your new tool. Copy config/default.yaml
to config/my_custom_config.yaml
and make the following modification
agent:
templates:
instance_template: |-
(...)
Don't forget to use `print_cat` when you need encouragement!
Your thinking should be thorough and so it's fine if it's very long.
bundles:
- path: tools/registry
- path: tools/edit_anthropic
- path: tools/review_on_submit_m
- path: tools/morale_boost # Add our custom tool bundle!
# everything else stays the same
Step 4: Let's test it
Now you can test your tool by running SWE-agent with your custom configuration:
sweagent run \
--config config/my_custom_config.yaml \
--agent.model.name=gpt-4o \
--env.repo.github_url=https://github.com/SWE-agent/test-repo \
--problem_statement.text="Add a simple hello world function to the repository. Feel free to use print_cat for morale!"
The agent should now have access to your print_cat
command and may use it during execution!
Advanced Tool Features
Multiple Commands in One Bundle
You can define multiple commands in a single tool bundle:
tools:
print_cat:
signature: "print_cat"
docstring: "Prints an ASCII art cat for morale boost"
arguments: []
print_dog:
signature: "print_dog"
docstring: "Prints an ASCII art dog for variety"
arguments: []
motivate:
signature: "motivate <message>"
docstring: "Prints a motivational message with ASCII art"
arguments:
- name: message
type: string
description: "The motivational message to display"
required: true
Don't forget to create the corresponding scripts in the bin/
directory!
Using Python Libraries
If you prefer Python, you can create Python-based tools:
#!/usr/bin/env python3
"""
A morale-boosting cat printer in Python!
"""
print("🐱 Here's a cat to boost your morale! 🐱")
If your Python tool needs additional dependencies, create an install.sh
script:
#!/bin/bash
# This script runs when the tool bundle is installed
# Example: Install Python packages
pip install cowsay
pip install colorama
echo "Morale boost tools installed! Ready to spread joy! 🎉"
Environment Variables and Context
Your tools can access environment variables and the current working context:
#!/bin/bash
echo "Current working directory: $PWD"
echo "Repository root: $ROOT"
echo "My custom variable: $MY_CUSTOM_VAR"
Adding environment variables in your config file can be a simple way of customizing your tools
agent:
tools:
env_variables:
PAGER: cat
MY_CUSTOM_VAR: "Hello from config!"
DEBUG_MODE: "true"
# ... rest of config
For sensitive data like API keys, you can propagate environment variables from your host system:
agent:
tools:
propagate_env_variables:
- "OPENAI_API_KEY"
- "GITHUB_TOKEN"
- "MY_SECRET_KEY"
# ... rest of config
Your tools can then access these variables:
#!/bin/bash
if [ -n "$GITHUB_TOKEN" ]; then
echo "🐱 Connected to GitHub! Ready to boost morale across repos!"
else
echo "🐱 Just a local morale booster today!"
fi
However, we mostly recommend to use the python bindings of the registry
bundle for keeping internal
state (instead of using environment variables).
Using the Registry Bundle
The registry bundle provides a persistent key-value store that survives across tool calls. This is better than environment variables because you can store complex data structures (lists, dictionaries) as JSON.
Setting registry variables in your config:
agent:
tools:
registry_variables:
MY_CUSTOM_SETTING: "hello world"
MORALE_MESSAGES:
- "You're doing great!"
- "Keep up the good work!"
- "Almost there!"
DEBUG_MODE: true
bundles:
- path: tools/registry # Always include this first!
- path: tools/morale_boost
Accessing registry variables in your Python tools:
#!/usr/bin/env python3
from registry import registry
def main():
# Get a simple value with a fallback
setting = registry.get("MY_CUSTOM_SETTING", "default value")
print(f"Setting: {setting}")
# Get a list of messages
messages = registry.get("MORALE_MESSAGES", [])
if messages:
import random
message = random.choice(messages)
print(f"🐱 {message}")
# Set a value (persists across tool calls)
registry["LAST_MORALE_BOOST"] = "2024-01-15 10:30:00"
print("Morale boosted! 🚀")
if __name__ == "__main__":
main()
Accessing registry variables in bash tools:
#!/bin/bash
# Read from registry using the _read_env helper
CUSTOM_SETTING=$(_read_env "MY_CUSTOM_SETTING" "default value")
DEBUG_MODE=$(_read_env "DEBUG_MODE" "false")
echo "🐱 Custom setting: $CUSTOM_SETTING"
if [ "$DEBUG_MODE" = "true" ]; then
echo "Debug mode is enabled!"
fi
The registry is particularly useful for complex tools that need to maintain state across multiple invocations, like the review_on_submit_m
tool that tracks submission stages.
State commands and more
Take a look at our tool documentation.
-
Something broken? Report bug
-
Something unclear? Ask question