Skip to content

Adding a New Tool

Overview

OpenSage-ADK supports three tool types:

  1. Python tools: any callable function (docstring becomes the tool description)
  2. Agent Skills: filesystem-discovered bash/Python scripts described by SKILL.md
  3. MCP toolsets: external services exposed via MCP (typically SSE)

Tool Types

1. Python Tools

Python tools are any callable functions you include in your agent’s tools=[...]. The function signature becomes the tool schema, and the docstring is shown to the model as the tool description.

Example:

def greet(name: str) -> str:
    """Return a friendly greeting."""
    return f"Hello, {name}!"

Enable in your agent:

root_agent = OpenSageAgent(
    name="my_agent",
    model=...,
    instruction="...",
    tools=[greet],
)

2. Agent Skills (Bash Scripts)

Agent Skills are bash scripts organized in a structured directory format with metadata. They are automatically discovered and loaded by the framework.

3. MCP Toolsets

MCP (Model Context Protocol) toolsets provide integration with external services or tools running in separate containers, typically accessed via SSE (Server-Sent Events) connections.

Creating an Agent Skill

Directory Structure

Create a skill directory under src/opensage/bash_tools/:

src/opensage/bash_tools/
└── category/
    └── tool-name/
        ├── SKILL.md          # Tool metadata and documentation
        └── scripts/
            └── tool_script.sh # The actual bash script

Example structure:

src/opensage/bash_tools/
└── retrieval/
    └── grep/
        ├── SKILL.md
        └── scripts/
            └── grep.sh

SKILL.md Format

The SKILL.md file contains YAML frontmatter and markdown documentation:

---
name: tool-name
description: Brief description of what the tool does
should_run_in_sandbox: main
---

# Tool Name

Detailed description of the tool's functionality.

## Usage

```bash
scripts/tool_script.sh arg1 arg2 --option value
```

## Parameters

### param1 (required, positional position 0)

**Type**: `str`

Description of the parameter.

### param2 (optional, positional position 1)

**Type**: `int`

Description of the parameter.

### --option (optional, named parameter)

**Type**: `str`

Description of the option.

### --flag (optional, flag)

**Type**: `bool` (default: `false`)

Description of the flag.

## Return Value

Returns a JSON object with results:

```json
{
  "success": true,
  "result": "..."
}
```

## Requires Sandbox

main

## Timeout

Default timeout: 60 seconds

Notes:

  • should_run_in_sandbox is required for executable Skills (a Skill folder that contains scripts/*.sh or scripts/*.py).
  • Use the Markdown section ## Requires Sandbox for dependency sandboxes. Do not put sandbox / sandboxes fields in YAML frontmatter.

Parameter Types

  • Positional parameters: Specified with positional position N in the parameter description
  • Named parameters: Use --param_name value format
  • Boolean flags: Use --flag (no value needed)

Bash Script Implementation

The bash script should:

  1. Accept command-line arguments (positional and named)
  2. Return JSON output for structured results
  3. Use proper exit codes (0 for success, non-zero for errors)

Example script:

#!/bin/bash

# tool_script.sh - Tool description
# Usage: ./tool_script.sh param1 param2 --option value

if [ -z "$1" ]; then
    echo '{"error": "Missing required parameter"}'
    exit 1
fi

PARAM1="$1"
PARAM2="${2:-default}"

# Process --option if provided
OPTION=""
while [[ $# -gt 0 ]]; do
    case $1 in
        --option)
            OPTION="$2"
            shift 2
            ;;
        *)
            shift
            ;;
    esac
done

# Tool logic here
RESULT=$(some_command "$PARAM1" "$PARAM2")

# Return JSON
echo "{\"success\": true, \"result\": \"$RESULT\"}"

Sandbox Requirements

Specify required sandbox types in the ## Requires Sandbox section:

## Requires Sandbox

main

Or for multiple sandboxes:

## Requires Sandbox

main, fuzz

Automatic Discovery

Tools are automatically discovered from: - src/opensage/bash_tools/ (built-in tools) - ~/.local/opensage/bash_tools/ (user-local skills/extensions)

The framework scans these directories for SKILL.md files and loads them automatically.

enabled_skills (Which Skills Get Loaded)

Agents can restrict which skills are available by setting enabled_skills:

  • None: load no skills
  • "all": load only top-level skills (<root>/*/SKILL.md); nested skills are not loaded by this mode
  • List[str]: each entry must be a relative path to a skill directory under the skill root. The loader recursively loads SKILL.md files below that directory, so "fuzz" loads the fuzz/ subtree and "fuzz/run-fuzzing-campaign" loads only that nested subtree.

Per-Skill Dependency Installers (deps/install.sh)

If a skill needs extra dependencies inside a sandbox, it can provide an installer:

  • deps/<sandbox_type>/install.sh (sandbox-specific), and/or
  • deps/install.sh (generic)

To control which sandbox should run the installer, add YAML frontmatter to SKILL.md:

---
should_run_in_sandbox: main
---

Installers are run during sandbox initialization (best-effort) and are only run once per session (subsequent runs are skipped via a marker under /shared).

Creating an MCP Toolset

MCP toolsets are created via Python functions that return OpenSageMCPToolset instances:

# Example: src/opensage/toolbox/debugger/gdb_mcp/get_toolset.py
from google.adk.tools.mcp_tool.mcp_toolset import SseConnectionParams

from opensage.agents.opensage_agent import OpenSageMCPToolset
from opensage.toolbox.sandbox_requirements import requires_sandbox
from opensage.utils.agent_utils import get_mcp_url_from_session_id


@requires_sandbox("gdb_mcp")
def get_toolset(opensage_session_id: str) -> OpenSageMCPToolset:
    """Create a named MCP toolset for the GDB MCP server.

    Args:
        opensage_session_id: OpenSage session ID used to resolve the MCP SSE URL.

    Returns:
        OpenSageMCPToolset connected to the GDB MCP server over SSE.
    """
    url = get_mcp_url_from_session_id("gdb_mcp", opensage_session_id)
    return OpenSageMCPToolset(
        name="gdb_mcp",
        connection_params=SseConnectionParams(url=url),
        tool_name_prefix="gdb_mcp",
    )

The function should: - Use @safe_tool_execution decorator - Use @requires_sandbox to specify required sandbox types - Return an MCPToolset instance - Be registered in the agent's tools list

Tool Registration

For Python Tools

Add the callable directly to your agent’s tools list.

For Agent Skills

Agent Skills are automatically discovered and registered. No manual registration needed.

For MCP Toolsets

Add the toolset getter function to your agent's tools:

from opensage.toolbox.category.get_toolset import get_toolset

agent = Agent(
    name="my_agent",
    tools=[get_toolset, ...],  # Add the getter function
    ...
)

Best Practices

  1. Use bash scripts for container operations: Prefer bash scripts for tools that interact with sandbox containers
  2. Return JSON: Always return structured JSON from bash scripts for easy parsing
  3. Document thoroughly: Include clear parameter descriptions and usage examples in SKILL.md
  4. Handle errors gracefully: Use proper exit codes and error messages
  5. Specify sandbox requirements: Always document which sandboxes are required
  6. Use MCP for external services: Use MCP toolsets for tools that run in separate containers or services

See Also