InitRunner

Tools

Tools give agents the ability to interact with the outside world — reading files, making HTTP requests, connecting to MCP servers, calling APIs, or running custom Python functions. They are configured in the spec.tools list, keyed on the type field.

Tool Types

TypeDescription
filesystemRead/write files within a sandboxed root directory
httpMake HTTP requests to a base URL
mcpConnect to MCP servers (stdio, SSE, streamable-http)
customLoad Python functions from a module
delegateInvoke other agents as tool calls
apiDeclarative REST API endpoints defined in YAML
web_readerFetch web pages and convert to markdown
pythonExecute Python code in a subprocess
datetimeGet current time and parse dates
sqlQuery SQLite databases (read-only)
gitRun git operations in a subprocess
shellExecute shell commands with allowlists
web_scraperScrape web pages and extract structured data
slackSend messages via Slack webhooks
(plugin)Any other type resolved via the plugin registry

Quick Example

spec:
  tools:
    - type: filesystem
      root_path: ./src
      read_only: true
      allowed_extensions: [".py", ".md"]
    - type: http
      base_url: https://api.example.com
      allowed_methods: ["GET", "POST"]
      headers:
        Authorization: Bearer ${API_TOKEN}
    - type: mcp
      transport: stdio
      command: npx
      args: ["-y", "@anthropic/mcp-server-filesystem"]
    - type: custom
      module: my_tools
      config:
        db_url: "postgres://..."
    - type: api
      name: weather
      base_url: https://api.weather.com
      endpoints:
        - name: get_weather
          path: "/current/{city}"
          parameters:
            - name: city
              type: string
              required: true

Filesystem

Sandboxed file operations within a root directory. Paths cannot escape the root (path traversal is blocked).

tools:
  - type: filesystem
    root_path: ./src
    read_only: true
    allowed_extensions: [".py", ".md", ".txt"]
FieldTypeDefaultDescription
root_pathstr"."Root directory for file operations
allowed_extensionslist[str][]File extensions to allow (empty = all)
read_onlybooltrueOnly allow read operations

Registered functions: read_file(path), list_directory(path), and write_file(path, content) (when read_only: false).

HTTP

Makes HTTP requests to a configured base URL.

tools:
  - type: http
    base_url: https://api.example.com
    allowed_methods: ["GET"]
    headers:
      Authorization: Bearer ${API_TOKEN}
FieldTypeDefaultDescription
base_urlstr(required)Base URL for requests
allowed_methodslist[str]["GET"]Allowed HTTP methods
headersdict{}Headers sent with every request

Registered function: http_request(method, path, body).

MCP

Connects to MCP (Model Context Protocol) servers, exposing their tools to the agent.

tools:
  # Stdio transport (local process)
  - type: mcp
    transport: stdio
    command: npx
    args: ["-y", "@anthropic/mcp-server-filesystem"]

  # SSE transport (remote server)
  - type: mcp
    transport: sse
    url: http://localhost:3001/sse

  # Streamable HTTP transport
  - type: mcp
    transport: streamable-http
    url: http://localhost:3001/mcp
    tool_filter: [search, get_document]
FieldTypeDefaultDescription
transportstr"stdio""stdio", "sse", or "streamable-http"
commandstr | nullnullCommand for stdio transport
argslist[str][]Arguments for the stdio command
urlstr | nullnullURL for SSE or streamable-http transport
tool_filterlist[str][]Only expose these tools (empty = all)

Custom

Load Python functions from a module and register them as agent tools.

tools:
  # Auto-discover all public functions
  - type: custom
    module: my_tools

  # Load a single function
  - type: custom
    module: my_tools
    function: search_db

  # With config injection
  - type: custom
    module: my_tools
    config:
      api_key: ${MY_API_KEY}
FieldTypeDefaultDescription
modulestr(required)Python module path (must be importable)
functionstr | nullnullSpecific function to load (null = auto-discover all)
configdict{}Config injected into functions with a tool_config parameter

Functions that declare a tool_config parameter receive the config dict automatically — the parameter is hidden from the LLM.

Scaffold a tool module:

initrunner init --template tool --name my_tools

Complete Custom Tool Walkthrough

Here's a full example with the Python module and the role YAML that uses it.

my_tools.py — every public function becomes an agent tool:

"""Custom tools module for InitRunner.

All public functions are auto-discovered as agent tools. Type annotations and
docstrings are used as tool schemas and descriptions. Functions accepting a
``tool_config`` parameter receive the config dict from role.yaml (hidden from
the LLM).
"""

import hashlib
import json
import uuid


def convert_units(value: float, from_unit: str, to_unit: str) -> str:
    """Convert a numeric value between common measurement units.

    Supported conversions: km/mi, kg/lb, c/f, l/gal, m/ft, cm/in.
    """
    conversions: dict[tuple[str, str], float | None] = {
        ("km", "mi"): 0.621371,
        ("mi", "km"): 1.60934,
        ("kg", "lb"): 2.20462,
        ("lb", "kg"): 0.453592,
        ("c", "f"): None,
        ("f", "c"): None,
        ("l", "gal"): 0.264172,
        ("gal", "l"): 3.78541,
        ("m", "ft"): 3.28084,
        ("ft", "m"): 0.3048,
        ("cm", "in"): 0.393701,
        ("in", "cm"): 2.54,
    }

    key = (from_unit.lower(), to_unit.lower())
    if key == ("c", "f"):
        result = value * 9 / 5 + 32
    elif key == ("f", "c"):
        result = (value - 32) * 5 / 9
    elif key in conversions:
        result = value * conversions[key]
    else:
        return f"Unsupported conversion: {from_unit} -> {to_unit}"

    return f"{value} {from_unit} = {result:.4f} {to_unit}"


def generate_uuid() -> str:
    """Generate a random UUID v4 identifier."""
    return str(uuid.uuid4())


def format_json(text: str) -> str:
    """Pretty-print a JSON string with 2-space indentation."""
    try:
        parsed = json.loads(text)
        return json.dumps(parsed, indent=2, ensure_ascii=False)
    except json.JSONDecodeError as e:
        return f"Invalid JSON: {e}"


def word_count(text: str) -> str:
    """Count words, characters, and lines in a text string."""
    words = len(text.split())
    chars = len(text)
    lines = text.count("\n") + 1 if text else 0
    return f"Words: {words}, Characters: {chars}, Lines: {lines}"


def hash_text(text: str, algorithm: str = "sha256") -> str:
    """Hash text using the specified algorithm (md5, sha1, sha256, sha512)."""
    algo = algorithm.lower()
    if algo not in ("md5", "sha1", "sha256", "sha512"):
        return f"Unsupported algorithm: {algorithm}. Use md5, sha1, sha256, or sha512."
    h = hashlib.new(algo)
    h.update(text.encode())
    return f"{algo}:{h.hexdigest()}"


def lookup_with_config(query: str, tool_config: dict) -> str:
    """Look up a query using the configured prefix and source.

    The tool_config parameter is injected by InitRunner from the role YAML
    and is hidden from the LLM.
    """
    prefix = tool_config.get("prefix", "DEFAULT")
    source = tool_config.get("source", "unknown")
    return f"[{prefix}] Result for '{query}' from source '{source}'"

custom-tools-demo.yaml — the role that loads it:

apiVersion: initrunner/v1
kind: Agent
metadata:
  name: custom-tools-demo
  description: Demonstrates custom tool type with auto-discovered Python functions
spec:
  role: |
    You are a utility assistant with access to custom tools defined in a Python
    module. Use these tools to help the user with practical tasks.

    Available custom tools:
    - convert_units: Convert between common measurement units
    - generate_uuid: Generate a random UUID v4 identifier
    - format_json: Pretty-print a JSON string
    - word_count: Count words, characters, and lines in text
    - hash_text: Hash text with md5, sha1, sha256, or sha512
    - lookup_with_config: Look up a query using the configured prefix and source

    Always use the appropriate tool rather than trying to compute results yourself.
  model:
    provider: openai
    name: gpt-4o-mini
    temperature: 0.1
  tools:
    - type: custom
      module: my_tools
      config:
        prefix: "DEMO"
        source: "custom-tools-demo"
    - type: datetime
  guardrails:
    max_tokens_per_run: 20000
    max_tool_calls: 15
    timeout_seconds: 60

Run from the directory containing both files:

cd examples/roles/custom-tools-demo
initrunner run custom-tools-demo.yaml -i

Example prompts:

> Convert 72 degrees Fahrenheit to Celsius
> Generate a UUID for me
> Hash "hello world" with sha256
> Look up "test query"

Key patterns: Docstrings become tool descriptions. Type annotations become parameter schemas. The tool_config parameter is injected from the YAML config block and hidden from the LLM — the agent never sees prefix or source as callable parameters. Omitting function in the YAML auto-discovers all public functions in the module.

API

Declarative REST API endpoints defined entirely in YAML — no Python required.

tools:
  - type: api
    name: github
    description: GitHub REST API
    base_url: https://api.github.com
    headers:
      Accept: application/vnd.github.v3+json
    auth:
      Authorization: "Bearer ${GITHUB_TOKEN}"
    endpoints:
      - name: get_repo
        method: GET
        path: "/repos/{owner}/{repo}"
        description: Get repository information
        parameters:
          - name: owner
            type: string
            required: true
          - name: repo
            type: string
            required: true
        response_extract: "$.full_name"
      - name: create_issue
        method: POST
        path: "/repos/{owner}/{repo}/issues"
        description: Create a new issue
        parameters:
          - name: owner
            type: string
            required: true
          - name: repo
            type: string
            required: true
          - name: title
            type: string
            required: true
          - name: body
            type: string
            required: false
            default: ""
        body_template:
          title: "{title}"
          body: "{body}"
        response_extract: "$.html_url"
FieldTypeDefaultDescription
namestr(required)API group name
base_urlstr(required)Base URL for all endpoints
headersdict{}Headers sent with every request (supports ${VAR})
authdict{}Auth headers merged into headers
endpointslist(required)Endpoint definitions

Each endpoint supports name, method, path, description, parameters, headers, body_template, query_params, response_extract, and timeout.

Scaffold an API tool agent:

initrunner init --template api --name weather-agent

Delegate

Invoke other agents as tool calls. Each agent reference generates a delegate_to_{name} tool.

tools:
  - type: delegate
    agents:
      - name: summarizer
        role_file: ./roles/summarizer.yaml
        description: "Summarizes long text"
      - name: researcher
        role_file: ./roles/researcher.yaml
        description: "Researches topics"
    mode: inline
    max_depth: 3
    timeout_seconds: 120
FieldTypeDefaultDescription
agentslist(required)Agent references (name + role_file or url)
modestr"inline""inline" (in-process) or "mcp" (HTTP)
max_depthint3Maximum delegation recursion depth
timeout_secondsint120Timeout per delegation call

Git

Subprocess-based git operations with read-only default.

tools:
  - type: git
    repo_path: .
    read_only: true
    timeout_seconds: 30
FieldTypeDefaultDescription
repo_pathstr"."Path to the git repository
read_onlybooltrueOnly allow read operations
timeout_secondsint30Timeout for each git command

Read tools: git_status, git_log, git_diff, git_show, git_blame, git_changed_files, git_list_files. Write tools (when read_only: false): git_checkout, git_commit, git_tag.

Shell

Execute shell commands with an allowlist.

tools:
  - type: shell
    allowed_commands: [kubectl, docker, curl]
    require_confirmation: false
    timeout_seconds: 30
    working_dir: .

Auto-Registered Tools

Document Search (from ingest)

When spec.ingest is configured, a search_documents(query, top_k) tool is auto-registered. See Ingestion.

Memory Tools (from memory)

When spec.memory is configured, three tools are auto-registered: remember(content, category), recall(query, top_k), and list_memories(category, limit). See Memory.

Plugin Tools

Any type value not matching a built-in is resolved via the plugin registry:

tools:
  - type: slack
    channel: "#alerts"
    token: ${SLACK_TOKEN}

List installed plugins: initrunner plugins.

Resource Limits

ToolLimitBehavior
read_file1 MBTruncated with [truncated] note
http_request100 KBTruncated with [truncated] note
git_*100 KBTruncated with recovery hint

On this page