> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/open-webui/open-webui/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

> Create and manage custom tools for your AI models

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

<Note>
  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.
</Note>

## Creating Tools

### Via Web Interface

<Steps>
  <Step title="Access Tools Workspace">
    Navigate to **Workspace → Tools** in Open WebUI
  </Step>

  <Step title="Create New Tool">
    Click **Create New Tool** and configure:

    * **ID**: Unique identifier (lowercase, alphanumeric, underscores)
    * **Name**: Display name
    * **Description**: Tool purpose and usage
  </Step>

  <Step title="Implement Tool Logic">
    Write your tool code in the editor:

    ```python theme={null}
    """
    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)}"
    ```
  </Step>

  <Step title="Save and Enable">
    Save the tool and ensure it's active
  </Step>
</Steps>

### Via API

Create tools programmatically:

```python theme={null}
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:

```python theme={null}
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
```

<Note>
  Method docstrings are important! They're used to generate tool descriptions for the AI model.
</Note>

## Valves Configuration

Tools support configurable parameters through valves:

```python theme={null}
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:

```python theme={null}
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

```python theme={null}
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

```python theme={null}
response = requests.get("http://localhost:8080/api/tools/id/my_tool")
tool = response.json()
```

### Update Tool

```python theme={null}
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

```python theme={null}
response = requests.delete(
    "http://localhost:8080/api/tools/id/my_tool/delete"
)
```

### Update Valves

```python theme={null}
# 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)
```

```python theme={null}
# 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:

```python theme={null}
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

<Accordion title="Weather Tool">
  ```python theme={null}
  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()
  ```
</Accordion>

<Accordion title="File System Tool">
  ```python theme={null}
  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()
  ```
</Accordion>

<Accordion title="Database Tool">
  ```python theme={null}
  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
  ```
</Accordion>

## Import from URL

Load tools from external sources:

```python theme={null}
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

<Note>
  Tool servers appear with the `server:` prefix in the tools list.
</Note>

## 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](/advanced/mcp-servers) for protocol-based integration
* Explore [Functions](/advanced/functions) for request/response filtering
* Configure [Skills](/advanced/skills) for reusable capabilities
