Tools in Open WebUI enable your AI models to perform specific actions and interact with external systems. They extend model capabilities beyond text generation to include API calls, file operations, calculations, and more.
Overview
Tools provide:
Python-based tool definitions
Integration with function calling models
Built-in code editor
Access control and permissions
User and admin valve configurations
OpenAPI tool server support
Tools are different from Functions. Tools are explicitly called by models that support function/tool calling, while Functions can filter or modify requests and responses.
Via Web Interface
Access Tools Workspace
Navigate to Workspace → Tools in Open WebUI
Create New Tool
Click Create New Tool and configure:
ID : Unique identifier (lowercase, alphanumeric, underscores)
Name : Display name
Description : Tool purpose and usage
Implement Tool Logic
Write your tool code in the editor: """
title: Calculator Tool
description: Performs mathematical calculations
author: Your Name
version: 1.0.0
"""
class Tools :
def __init__ ( self ):
pass
def calculate ( self , expression : str ) -> float :
"""
Evaluate a mathematical expression.
:param expression: The math expression to evaluate
:return: The result of the calculation
"""
try :
result = eval (expression)
return float (result)
except Exception as e:
return f "Error: { str (e) } "
Save and Enable
Save the tool and ensure it’s active
Via API
Create tools programmatically:
import requests
url = "http://localhost:8080/api/tools/create"
payload = {
"id" : "my_tool" ,
"name" : "My Tool" ,
"content" : '''
class Tools:
def get_time(self) -> str:
"""Get the current time."""
from datetime import datetime
return datetime.now().strftime("%Y-%m- %d %H:%M:%S")
''' ,
"meta" : {
"description" : "Returns current time" ,
"manifest" : {}
}
}
response = requests.post(url, json = payload)
A tool must define a Tools class with one or more methods:
from typing import Optional
import requests
class Tools :
def __init__ ( self ):
"""Initialize the tool."""
self .base_url = "https://api.example.com"
def search ( self , query : str , limit : int = 10 ) -> dict :
"""
Search for items.
:param query: Search query string
:param limit: Maximum number of results
:return: Search results
"""
response = requests.get(
f " { self .base_url } /search" ,
params = { "q" : query, "limit" : limit}
)
return response.json()
def get_details ( self , item_id : str ) -> Optional[ dict ]:
"""
Get details for a specific item.
:param item_id: The item identifier
:return: Item details or None
"""
response = requests.get( f " { self .base_url } /items/ { item_id } " )
if response.status_code == 200 :
return response.json()
return None
Method docstrings are important! They’re used to generate tool descriptions for the AI model.
Valves Configuration
Tools support configurable parameters through valves:
from pydantic import BaseModel, Field
class Tools :
class Valves ( BaseModel ):
api_key: str = Field(
default = "" ,
description = "API key for the service"
)
base_url: str = Field(
default = "https://api.example.com" ,
description = "Base URL for API requests"
)
timeout: int = Field(
default = 30 ,
description = "Request timeout in seconds" ,
ge = 1 ,
le = 300
)
def __init__ ( self ):
self .valves = self .Valves()
def make_request ( self , endpoint : str ) -> dict :
"""Make an API request."""
import requests
headers = { "Authorization" : f "Bearer { self .valves.api_key } " }
url = f " { self .valves.base_url } / { endpoint } "
response = requests.get(
url,
headers = headers,
timeout = self .valves.timeout
)
return response.json()
User Valves
Allow individual users to configure their own settings:
from pydantic import BaseModel, Field
class Tools :
class UserValves ( BaseModel ):
api_token: str = Field(
default = "" ,
description = "Your personal API token"
)
preferences: dict = Field(
default_factory = dict ,
description = "User preferences"
)
def __init__ ( self ):
self .user_valves = {}
def get_data ( self , query : str , __user__ : dict ) -> dict :
"""
Fetch data using user-specific credentials.
:param query: The search query
:return: Query results
"""
user_id = __user__[ "id" ]
valves = self .user_valves.get(user_id, self .UserValves())
# Use user's API token
headers = { "Authorization" : f "Bearer { valves.api_token } " }
# Make request with user's credentials
# ...
return { "results" : []}
import requests
response = requests.get( "http://localhost:8080/api/tools/" )
tools = response.json()
for tool in tools:
print ( f " { tool[ 'name' ] } : { tool[ 'meta' ][ 'description' ] } " )
response = requests.get( "http://localhost:8080/api/tools/id/my_tool" )
tool = response.json()
url = "http://localhost:8080/api/tools/id/my_tool/update"
payload = {
"content" : "# Updated tool code" ,
"meta" : {
"description" : "Updated description"
}
}
response = requests.post(url, json = payload)
response = requests.delete(
"http://localhost:8080/api/tools/id/my_tool/delete"
)
Update Valves
# Update admin valves
url = "http://localhost:8080/api/tools/id/my_tool/valves/update"
payload = {
"api_key" : "sk-..." ,
"base_url" : "https://api.example.com" ,
"timeout" : 60
}
response = requests.post(url, json = payload)
# Update user valves
url = "http://localhost:8080/api/tools/id/my_tool/valves/user/update"
payload = {
"api_token" : "user-token-..." ,
"preferences" : { "theme" : "dark" }
}
response = requests.post(url, json = payload)
Access Control
Tools support granular access control:
import requests
url = "http://localhost:8080/api/tools/id/my_tool/access/update"
payload = {
"access_grants" : [
{
"principal_type" : "user" ,
"principal_id" : "user-id-123" ,
"permission" : "read"
},
{
"principal_type" : "group" ,
"principal_id" : "group-id-456" ,
"permission" : "write"
},
{
"principal_type" : "user" ,
"principal_id" : "*" , # All users
"permission" : "read"
}
]
}
response = requests.post(url, json = payload)
Import from URL
Load tools from external sources:
import requests
url = "http://localhost:8080/api/tools/load/url"
payload = {
"url" : "https://github.com/user/repo/blob/main/my_tool.py"
}
response = requests.post(url, json = payload)
tool_data = response.json()
Open WebUI supports OpenAPI tool servers for enterprise integrations:
Configure tool server connections in admin settings
Tools are automatically discovered from OpenAPI specs
Supports authentication and access control
Compatible with MCP (Model Context Protocol) servers
Tool servers appear with the server: prefix in the tools list.
Best Practices
Clear Documentation : Write detailed docstrings for all tool methods
Type Hints : Use proper type annotations
Error Handling : Handle exceptions gracefully
Security : Validate inputs and sanitize outputs
Performance : Keep tools fast and efficient
Testing : Test tools thoroughly before deploying
Next Steps
Learn about MCP Servers for protocol-based integration
Explore Functions for request/response filtering
Configure Skills for reusable capabilities