<!-- Source: https://docs.squirro.com/en/latest/technical/agents/developing-custom-tools.html -->
# Developing Custom Tools

The Squirro [agent framework](index.md#agents) allows you to create and deploy custom tools using the LangChain framework. LLM agents can use these tools to perform actions in response to user input.

There are two supported approaches for registering tools:

1. Recommended method, integrated with the GenAI UI and simplified deployment.
2. Legacy method, using manual configuration of a YAML file.

## Recommended Method

This method supports UI-driven deployment and does not require editing YAML configuration files or manually copying files to plugin directories.

- The tools are created using a decorated factory class.
- The tools are automatically listed in the `/available_tools` endpoint, and you can activate them directly via the UI.
- The deployment is done using the `squirro_asset` command from the Squirro Toolbox.

### Creating a Tool

On your local system, create a folder for your tool, for example, `greeter_tool/`, with a minimal `__init__.py` file to make it a Python package.

To create the new tool, start with a new file in that folder, such as `greeter.py`. That Python file should contain the following code:

```python
from __future__ import annotations
from langchain_core.tools import tool, BaseTool
from squirro.service.genai.components.registry import ToolBase, deploy_as_agent_tool

@deploy_as_agent_tool(
    "greeter",                    # Tool identifier
    tagline="A new greeter tool",  # Shown in the UI
    material_icon="public",        # Material icon
    category="retriever",          # UI category
)
class GreeterFactory(ToolBase):
    message_format: str = "Hello, {name}!"

    def create_runnable(self) -> BaseTool:
        @tool
        def greeter(name: str) -> str:
            """Greet someone by their name."""
            return self.message_format.format(name=name)
        return greeter
```

### Deploying the Tool

Before deploying a tool, make sure you have [remote access to a Squirro server](../admin/operations/accessing-servers.md#admin-access) via SSH. Also make sure the [Squirro Toolbox](../api/toolbox/index.md#api-sdk-toolbox) is installed, then on your local system run the following command in a terminal:

```bash
squirro_asset genai_plugin upload \
    --token $SQ_API_KEY \
    --cluster $SQ_CLUSTER \
    --folder ./greeter_tool
```

In order for the Squirro platform to detect the new tool, you need to restart the GenAI service running the following command in a terminal connected to the Squirro instance:

```bash
sudo systemctl restart sqgenaid
```

The tool now appears in the **Squirro Chat > Project Configuration > Agents Tab > [Your Agent] > “+ Configure New Tool”** dropdown menu, where you can configured it.

> **Note**
>
> There is no need to edit the YAML file or manage the plugin folders manually.

### Example with Dependencies

One of the advantages of uploading plugins using `squirro_asset` is the ability to include a `requirements.txt` file alongside your tool. That file lists any additional Python packages your tool depends on. When you deploy the plugin, the server automatically installs the dependencies and makes them available to the tool. There is no manual installation required.

For example, the following tool uses the `pyjokes` library to return a random joke.

#### Tool Implementation (jokester.py)

```python
from __future__ import annotations

import logging

from langchain_core.tools import BaseTool, tool
from squirro.service.genai.components.registry import (
    ToolBase,
    deploy_as_agent_tool,
)

import pyjokes

log = logging.getLogger(__name__)

@deploy_as_agent_tool(
    "jokes",  # Tool identifier
    tagline="A tool that generates jokes",  # Shown in the UI
    material_icon="public",  # Material icon name
    category="retriever",  # UI category
)
class JokesterFactory(ToolBase):
    def create_runnable(self) -> BaseTool:
        @tool
        def jokester() -> str:
            """Return a random joke."""
            return pyjokes.get_joke()
        return jokester
```

#### Plugin Directory Structure

Your plugin directory (for example, `jokester_tool/`) should contain:

```text
jokester_tool/
├── jokester.py
├── __init__.py
└── requirements.txt
```

Contents of `requirements.txt`:

```text
pyjokes
```

#### Deployment

Upload the plugin as usual:

```bash
squirro_asset genai_plugin upload \
    --token $SQ_API_KEY \
    --cluster $SQ_CLUSTER \
    --folder ./jokester_tool
```

Restart the GenAI service:

```bash
ssh <host> sudo systemctl restart sqgenaid
```

The tool now appears in the **Squirro Chat > Project Configuration > Agents Tab > “+ Configure New Tool”** dropdown menu, ready for configuration and use.

> **Note**
>
> The `requirements.txt` file is only supported when using the recommended upload method with `squirro_asset`. It is not supported when using the legacy YAML-based method.

## Legacy Configuration

Although a new approach is now available, the legacy method is still currently supported to cover some advanced use cases. However, to ensure future compatibility and avoid potential disruptions, developers are advised to gradually plan their transition to the new method. As the technology continues to evolve, the new approach is likely to become even more suitable for advanced use cases, offering improved performance, features, and capabilities.

### Overview

- You register tools with `@register_component`.
- You reference the tools with a string identifiers in the YAML config files.
- You manually create the plugin and deployment folders.

### Tool Example

```python
import random
from langchain_core.tools import tool, BaseTool
from squirro.service.genai.components.registry import ToolBase, register_component

DAD_JOKES = [
    "I'm reading a book on anti-gravity. It's impossible to put down!",
    ...
]

@tool
async def random_dad_joke_tool() -> str:
    return random.choice(DAD_JOKES)

@register_component(name="examples/tools/dad_joke")
class RandomDadJokeToolFactory(ToolBase):
    def create_runnable(self) -> BaseTool:
        return random_dad_joke_tool
```

> **Note**
>
> For advanced use cases, you can apply both `@deploy_as_agent_tool` and `@register_component` to the same factory class. It allows the tool to be available in the GenAI UI for interactive selection and accessible via the YAML deployment configuration.
>
>
>
> ```python
> @deploy_as_agent_tool(
>     "greeter",
>     tagline="Greeter with UI and YAML support",
>     material_icon="public",
>     category="retriever",
> )
> @register_component(name="examples/tools/greeter")
> class GreeterFactory(ToolBase):
>     ...
> ```

### Referencing in YAML

After placing your plugin in `/var/lib/squirro/genai/plugins`, reference it in the deployment config:

```yaml
tools:
  - $runnable: "examples/tools/dad_joke"
```

### Platform Setup

```bash
sudo mkdir -p /var/lib/squirro/genai/plugins
sudo mkdir -p /etc/squirro/genai.d/deployments

sudo chown -R sqgenai:squirro /var/lib/squirro/genai/plugins
sudo chown -R sqgenai:squirro /etc/squirro/genai.d/deployments

sudo systemctl restart sqgenaid
```

Edit `/etc/squirro/genai.d/run.sh` and add volume mounts for plugins and deployments.

```bash
--volume /var/lib/squirro/genai/plugins/:... \
--volume /etc/squirro/genai.d/deployments/:... \
```

## Accessing Authenticated User Context

The agent framework makes authenticated user information available to tools via the `squirro_client_args` variable. It can be useful when tools require authorization or access control logic based on the current user.

In the **legacy configuration**, you must explicitly wire this context through the deployment YAML and your tool factory.

```YAML
...
extra_input_mapping:
    /retriever/input_model/client_args: squirro_client_args
...
        auth_kwargs:
            $placeholder: squirro_client_args
...
```

Then, in your factory class, define a matching argument and forward it to your tool:

```python
class MyToolFactory(ToolBase):
    auth_kwargs: dict[str, Any] | None = None

    def create_runnable(self) -> Runnable:
        return my_tool(auth=self.auth_kwargs)
```

## Troubleshooting

To troubleshoot issues or unexpected results, the first step is to check the logs of the `genai` service. You can do that by running the following command in a terminal connected to the Squirro instance:

```bash
sudo docker logs -f squirro-service-genai
```

To open a shell in the container for further investigation or debugging, use the following command:

```bash
sudo docker exec -it squirro-service-genai /bin/bash
```

## Getting Assistance

For assistance with Squirro-specific topics, visit the [Squirro Support website](https://go.squirro.com/support) and submit a technical support request. To learn more about LangChain tools, visit the [LangChain documentation](https://python.langchain.com/docs/introduction/).
