OpenSpry tools are Python packages that give agents new capabilities. A tool can do anything — read emails, send messages, query databases, control smart home devices, call APIs. Building one takes about 15 minutes.
Every tool extends openspry.tools.base.Tool and implements three things:
from openspry.tools.base import Tool
class MyTool(Tool):
name = "my_tool"
description = "What this tool does, in one sentence."
def schema(self) -> dict:
"""JSON Schema for the tool's input parameters."""
return {
"type": "object",
"properties": {
"query": {"type": "string", "description": "..."},
},
"required": ["query"],
}
async def execute(self, **kwargs) -> str:
"""Run the tool. Returns a text result for the agent."""
query = kwargs.get("query", "")
# ... do the actual work ...
return f"Result for {query}"
That's the entire interface. name, description, schema(), and execute().
Let's build a real tool that fetches weather data.
Create a new directory:
openspry-tool-weather/
pyproject.toml
openspry_tool_weather/
__init__.py
weather.py
pyproject.toml:
[project]
name = "openspry-tool-weather"
version = "0.1.0"
dependencies = ["openspry"]
[project.entry-points."openspry.tools"]
weather = "openspry_tool_weather.weather:WeatherTool"
The [project.entry-points."openspry.tools"] section is how OpenSpry discovers your tool at startup. The key (weather) becomes the tool's registered name.
openspry_tool_weather/weather.py:
import urllib.request
import json
from openspry.tools.base import Tool
class WeatherTool(Tool):
name = "weather"
description = "Get current weather for a city."
def schema(self) -> dict:
return {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g. 'London'",
},
},
"required": ["city"],
}
async def execute(self, **kwargs) -> str:
city = kwargs.get("city", "London")
url = f"https://wttr.in/{city}?format=j1"
req = urllib.request.Request(url, headers={"User-Agent": "OpenSpry"})
try:
with urllib.request.urlopen(req, timeout=10) as resp:
data = json.loads(resp.read())
current = data["current_condition"][0]
return (
f"Weather in {city}: {current['weatherDesc'][0]['value']}, "
f"{current['temp_C']}°C, humidity {current['humidity']}%"
)
except Exception as e:
return f"Weather lookup failed: {e}"
# Install locally (in development)
pip install -e ./openspry-tool-weather
# Restart OpenSpry — the tool is auto-discovered
openspry
# You'll see in the logs:
# INFO Registered tool: weather
The tool now appears in the dashboard's tool list and is available to all agents. When an agent is working on an intent that could benefit from weather data, it will automatically call the tool.
# Build and publish
pip install build twine
python -m build
twine upload dist/*
# Now anyone can install it:
pip install openspry-tool-weather
execute() method returns a string that agents can reason about. Keep it concise.description fields in your schema help the LLM decide when and how to use your tool.openspry-tool-* so it's discoverable in PyPI searches.OpenSpry ships with two built-in tools:
{"query": "search terms"}{"command": "ls -la"}View their source in openspry/tools/ for implementation examples.
The tool ecosystem is just getting started. Here are some ideas for tools the community could build:
Read, filter, and respond to emails. Keep inbox goals on track.
Create events, check availability, set deadline-aware reminders.
Summarize conversations, send messages, bridge chat platforms.
Query SQL databases, call REST APIs, integrate with business systems.
Organize, rename, and summarize files. Watch folders for changes.
Control lights, thermostat, sensors. Link home automation to intents.
Built a tool? Open a PR to add it to this list.