TL;DR
Open WebUI’s Tools feature transforms your local LLM into an AI agent capable of executing real-world tasks through function calling. Instead of just chatting with your model, you can build custom tools that let it query APIs, run system commands, process files, or integrate with external services – all while keeping your data local.
Tools work by defining Python functions that your LLM can invoke during conversations. When you ask your model to check the weather, search documentation, or analyze a log file, Open WebUI intercepts the request, executes your custom tool code, and feeds the results back to the model for a natural language response. This creates powerful workflows without sending data to third-party AI services.
The Tools ecosystem supports both simple utilities and complex integrations. You can create a tool that queries your local Prometheus metrics, another that searches your company wiki, and a third that validates configuration files – all callable through natural conversation with models running via Ollama. Tools use a straightforward Python-based format with decorators for parameter validation and type hints for better LLM understanding.
Key capabilities include HTTP requests to REST APIs, file system operations, database queries, and subprocess execution. You can chain multiple tools together, pass data between them, and build sophisticated automation workflows. The system handles authentication, error handling, and response formatting automatically.
Critical security note: Tools execute arbitrary code on your host system. Always validate tool definitions before enabling them, especially if importing from community sources. AI-generated tool code should be reviewed line-by-line before production use. Run Open WebUI in a containerized environment with limited permissions, and never grant tools access to sensitive credentials or unrestricted system commands. Test tools thoroughly in isolated environments before deploying them to production instances.
This guide covers tool development from basic examples to production-ready integrations with external systems.
Understanding the Open WebUI Tools Architecture
Open WebUI’s Tools system extends your local LLM capabilities through function-calling workflows that connect models to external services and APIs. Unlike basic chat interfaces, Tools enable your AI to execute real actions – querying databases, fetching live data, or controlling home automation systems.
Tools in Open WebUI consist of three primary elements: the tool definition (metadata and parameters), the execution function (Python code that runs when called), and the response handler (formatting results for the LLM). When you create a tool, you’re essentially building a bridge between your model’s natural language understanding and concrete system operations.
The architecture uses a sandboxed Python environment where your tool code executes. Each tool receives structured input from the LLM based on the function signature you define, processes that input through your custom logic, and returns formatted output that the model incorporates into its response.
Function-Calling Flow
When a user asks “What’s the current temperature in Denver?”, the LLM recognizes this matches your weather tool’s description. It extracts the location parameter, calls your tool with {"location": "Denver"}, and your code fetches data from a weather API. The tool returns structured JSON, which the model transforms into natural language: “The current temperature in Denver is 72 degrees.”
This differs from RAG workflows – Tools execute dynamic operations rather than retrieving static documents. You might create a system_monitor tool that runs df -h and free -m commands, or a database_query tool that connects to PostgreSQL.
Caution: Always validate AI-generated commands before executing them in production environments. Tools run with the permissions of your Open WebUI container, so implement input sanitization and restrict dangerous operations. Never allow tools to execute arbitrary shell commands without strict allowlisting.
Creating Your First Custom Tool
Open WebUI tools use Python functions that your local LLM can invoke during conversations. The simplest approach starts with a basic function that returns data without external dependencies.
Navigate to Settings > Admin Panel > Tools in your Open WebUI instance. Click “Create Tool” and enter this weather lookup example:
"""
title: Simple Weather Tool
author: your-name
version: 0.1.0
"""
from typing import Callable
class Tools:
def __init__(self):
pass
def get_weather(self, city: str) -> str:
"""
Get current weather for a city.
:param city: City name to check weather
:return: Weather information string
"""
# Mock data for demonstration
weather_data = {
"Seattle": "Rainy, 55F",
"Phoenix": "Sunny, 85F",
"Boston": "Cloudy, 62F"
}
return weather_data.get(city, f"Weather data unavailable for {city}")
The docstring format matters – Open WebUI parses it to understand when the LLM should invoke your tool. The function signature with type hints tells the model what parameters to pass.
Testing Tool Execution
After saving, enable the tool in your chat settings. Ask your model: “What’s the weather in Seattle?” The LLM recognizes the intent, calls get_weather("Seattle"), and incorporates the response into its answer.
Caution: When building tools that execute system commands or API calls based on LLM output, always validate inputs. LLMs can hallucinate malformed commands or attempt unintended operations. Implement allowlists for critical parameters and sanitize all user-provided strings before passing them to shell commands or external services.
Start with read-only operations before creating tools that modify system state. Test thoroughly with various phrasings to ensure your tool handles edge cases gracefully.
Integrating External APIs and Services
Open WebUI tools can connect your local LLMs to external services through HTTP requests, enabling workflows that combine AI reasoning with real-world data. The tools system supports both simple REST API calls and complex multi-step integrations.
Create a tool that fetches current weather data and lets your LLM provide context-aware responses:
import requests
import json
def get_weather(location: str) -> dict:
"""
Fetch weather data for a location
"""
api_key = "your_openweathermap_key"
url = f"https://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=metric"
response = requests.get(url)
data = response.json()
return {
"temperature": data["main"]["temp"],
"conditions": data["weather"][0]["description"],
"humidity": data["main"]["humidity"]
}
Register this in Open WebUI’s Tools section with proper input validation. The LLM can now answer questions like “Should I bring an umbrella to Seattle today?” by calling your tool and interpreting the results.
Database Query Tools
Connect to PostgreSQL or MySQL databases to let your LLM query internal data:
import psycopg2
def query_inventory(product_name: str) -> dict:
conn = psycopg2.connect(
host="localhost",
database="inventory",
user="readonly_user",
password="secure_password"
)
cursor = conn.cursor()
cursor.execute("SELECT stock_level, location FROM products WHERE name = %s", (product_name,))
result = cursor.fetchone()
conn.close()
return {"stock": result[0], "warehouse": result[1]}
Critical security note: Always validate and sanitize LLM-generated SQL queries before execution. Use parameterized queries and read-only database users. Never grant write access to AI-controlled database connections without human approval workflows.
Building Advanced Function-Calling Workflows
Function-calling workflows in Open WebUI let your local LLMs interact with external systems, execute code, and chain multiple operations together. These workflows transform static chat interfaces into dynamic automation platforms.
Create workflows where one tool’s output feeds into another. For example, a weather tool fetches current conditions, then a notification tool sends alerts based on temperature thresholds:
# tools/weather_alert.py
def get_weather_and_notify(location: str, threshold: int) -> dict:
weather_data = fetch_weather(location)
if weather_data['temp'] > threshold:
send_notification(f"High temp alert: {weather_data['temp']}F")
return weather_data
The LLM decides when to invoke this based on user queries like “Monitor temperature in Denver and alert me if it exceeds 85 degrees.”
Conditional Execution Patterns
Build tools that execute different code paths based on LLM reasoning. A database query tool might validate user intent before running destructive operations:
def execute_query(query: str, confirm_destructive: bool = False) -> str:
if is_destructive(query) and not confirm_destructive:
return "This query modifies data. Set confirm_destructive=true to proceed."
return run_sql(query)
Validation and Safety Guards
Critical: Always validate AI-generated commands before production deployment. Implement sandboxing for code execution tools and use read-only database connections for query tools. Add explicit confirmation steps for any operation that modifies system state or external resources.
Test function-calling workflows extensively with edge cases. LLMs sometimes hallucinate tool parameters or combine tools in unexpected ways. Log all tool invocations and monitor for anomalous patterns, especially when tools interact with production systems or sensitive data stores.
Tool Security and Sandboxing for Self-Hosted Environments
When running Open WebUI tools in self-hosted environments, security becomes your responsibility rather than a cloud provider’s. Tools execute code based on LLM outputs, creating potential attack vectors if not properly sandboxed.
Run Open WebUI in a dedicated Docker container with restricted capabilities. Avoid mounting your entire filesystem – limit volumes to specific directories:
docker run -d -p 3000:8080 \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
-v ./tools:/app/backend/data/tools:ro \
-v ./uploads:/app/backend/data/uploads \
ghcr.io/open-webui/open-webui:main
The read-only mount for tools prevents runtime modification. Tools requiring write access should use dedicated volumes with explicit paths.
Validating Tool Inputs
Custom tools should validate all parameters before execution. For a weather API tool, verify location strings against a whitelist or regex pattern:
import re
def get_weather(location: str):
if not re.match(r'^[a-zA-Z\s,]+$', location):
return {"error": "Invalid location format"}
# Proceed with API call
Never pass LLM-generated strings directly to shell commands. A file search tool should sanitize paths and prevent directory traversal:
import os
def search_files(query: str, directory: str):
safe_dir = os.path.abspath(directory)
if not safe_dir.startswith('/app/backend/data/uploads'):
return {"error": "Access denied"}
Network Restrictions
Tools calling external APIs should use explicit allowlists. Configure Docker networking to restrict outbound connections or use a proxy that logs all requests. For homelab deployments, consider running Open WebUI on an isolated VLAN.
Caution: Always review AI-generated tool code before enabling it in production. LLMs may suggest tools that execute arbitrary commands or access sensitive files. Test new tools in isolated environments first, and implement rate limiting to prevent abuse through repeated tool invocations.
Real-World Tool Examples for Local AI
A weather tool demonstrates how to connect external APIs to your local LLM. This example uses OpenWeatherMap’s free tier to provide current conditions:
import requests
import os
def get_weather(city: str) -> str:
api_key = os.getenv("OPENWEATHER_API_KEY")
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
temp = data["main"]["temp"]
desc = data["weather"][0]["description"]
return f"Current weather in {city}: {temp}C, {desc}"
return "Unable to fetch weather data"
Store your API key in Open WebUI’s environment variables rather than hardcoding credentials. The model can now answer “What’s the weather in Berlin?” by calling this function.
System Command Execution
Tools can execute local system commands, useful for homelab monitoring:
import subprocess
def check_disk_space() -> str:
result = subprocess.run(["df", "-h", "/"], capture_output=True, text=True)
return result.stdout
Caution: Always validate AI-generated system commands before execution. Never grant unrestricted shell access to LLM-generated code. Use allowlists for permitted commands and sanitize all inputs. Consider running tools in isolated containers to limit potential damage from malformed commands.
Database Query Tool
Connect your LLM to local databases for natural language queries:
import sqlite3
def query_inventory(product_name: str) -> str:
conn = sqlite3.connect("/data/inventory.db")
cursor = conn.execute("SELECT stock, price FROM products WHERE name=?", (product_name,))
result = cursor.fetchone()
conn.close()
if result:
return f"Stock: {result[0]} units, Price: ${result[1]}"
return "Product not found"
This enables conversational database access without writing SQL manually, particularly valuable for internal tools where you control the data schema.
