Skip to main content
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.

Creating Tools

Via Web Interface

1

Access Tools Workspace

Navigate to Workspace → Tools in Open WebUI
2

Create New Tool

Click Create New Tool and configure:
  • ID: Unique identifier (lowercase, alphanumeric, underscores)
  • Name: Display name
  • Description: Tool purpose and usage
3

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)}"
4

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)

Tool Structure

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": []}

Managing Tools

List All Tools

import requests

response = requests.get("http://localhost:8080/api/tools/")
tools = response.json()

for tool in tools:
    print(f"{tool['name']}: {tool['meta']['description']}")

Get Tool by ID

response = requests.get("http://localhost:8080/api/tools/id/my_tool")
tool = response.json()

Update Tool

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)

Delete Tool

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)

Tool Examples

import requests
from pydantic import BaseModel, Field

class Tools:
    class Valves(BaseModel):
        api_key: str = Field(
            default="",
            description="OpenWeather API key"
        )
    
    def __init__(self):
        self.valves = self.Valves()
    
    def get_weather(self, city: str, units: str = "metric") -> dict:
        """
        Get current weather for a city.
        
        :param city: City name
        :param units: Temperature units (metric/imperial)
        :return: Weather data
        """
        url = "https://api.openweathermap.org/data/2.5/weather"
        params = {
            "q": city,
            "appid": self.valves.api_key,
            "units": units
        }
        
        response = requests.get(url, params=params)
        return response.json()
import os
from pathlib import Path
from typing import List

class Tools:
    def __init__(self):
        self.base_dir = Path("/app/data")
    
    def list_files(self, directory: str = ".") -> List[str]:
        """
        List files in a directory.
        
        :param directory: Directory path
        :return: List of filenames
        """
        path = self.base_dir / directory
        if not path.exists():
            return []
        return [f.name for f in path.iterdir()]
    
    def read_file(self, filename: str) -> str:
        """
        Read contents of a file.
        
        :param filename: File name or path
        :return: File contents
        """
        path = self.base_dir / filename
        if not path.exists():
            return "File not found"
        return path.read_text()
import sqlite3
from typing import List, Dict, Any

class Tools:
    def __init__(self):
        self.db_path = "/app/data/app.db"
    
    def query(self, sql: str, params: List[Any] = None) -> List[Dict]:
        """
        Execute a SQL query.
        
        :param sql: SQL query string
        :param params: Query parameters
        :return: Query results
        """
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        cursor = conn.cursor()
        
        cursor.execute(sql, params or [])
        results = [dict(row) for row in cursor.fetchall()]
        
        conn.close()
        return results
    
    def execute(self, sql: str, params: List[Any] = None) -> int:
        """
        Execute a SQL statement.
        
        :param sql: SQL statement
        :param params: Statement parameters
        :return: Number of affected rows
        """
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute(sql, params or [])
        conn.commit()
        affected = cursor.rowcount
        
        conn.close()
        return affected

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()

Tool Servers

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

  1. Clear Documentation: Write detailed docstrings for all tool methods
  2. Type Hints: Use proper type annotations
  3. Error Handling: Handle exceptions gracefully
  4. Security: Validate inputs and sanitize outputs
  5. Performance: Keep tools fast and efficient
  6. 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