Skip to contents

Tools are how agents interact with the world. deputy provides built-in tools for common tasks and makes it easy to create your own.

Built-in Tools

deputy includes the following tools out of the box:

Tool Description Annotations
tool_read_file Read a file’s contents read-only
tool_read_markdown Convert a supported document to markdown read-only
tool_write_file Write or append to a file destructive
tool_edit_file Apply one targeted file edit destructive
tool_multi_edit Apply multiple file edits destructive
tool_list_files List files in a directory read-only
tool_glob_files Find files by glob pattern read-only
tool_grep_files Search file contents read-only
tool_run_r_code Execute R code open-world
tool_run_bash Execute a bash command destructive, open-world
tool_read_csv Read and summarise a CSV read-only
tool_web_fetch Fetch a web page read-only, open-world
tool_web_search Search the web read-only, open-world
tool_ask_user Ask structured questions read-only

Each tool carries annotations that the permission system uses to decide whether the agent is allowed to use it. See vignette("permissions") for details.

Tool Bundles

Bundles group related tools together:

library(deputy)

tools_file()  # read_file, read_markdown, write_file, list_files
tools_code()  # run_r_code, run_bash
tools_data()  # read_csv, read_file, read_markdown
tools_web()   # universal web_fetch and web_search
tools_all()   # everything

Combine bundles by concatenating:

tools <- c(tools_file(), tools_code())
agent <- Agent$new(chat = ellmer::chat_openai(), tools = tools)

Tool Presets

Presets are named collections with opinionated defaults:

tools_preset("minimal")
tools_preset("standard")
tools_preset("dev")
tools_preset("data")
tools_preset("full")

Creating Custom Tools

Use ellmer::tool() to define custom tools with type-safe arguments and annotations:

tool_lookup_user <- ellmer::tool(
  fun = function(user_id) {
    # your implementation
    list(name = "Alice", email = "alice@example.com")
  },
  name = "lookup_user",
  description = "Look up a user by their ID",
  arguments = list(
    user_id = ellmer::type_string("The user ID to look up")
  ),
  annotations = ellmer::tool_annotations(
    read_only_hint = TRUE,
    destructive_hint = FALSE,
    open_world_hint = FALSE,
    idempotent_hint = TRUE
  )
)

The annotations are optional but recommended. They tell the permission system about the tool’s behaviour:

  • read_only_hint – Tool does not change state
  • destructive_hint – Tool may delete or overwrite data
  • open_world_hint – Tool accesses external systems (network, APIs)
  • idempotent_hint – Safe to call multiple times with same input

Using a Custom Tool

library(deputy)

tool_dice <- ellmer::tool(
  fun = function(n = 1, sides = 6) {
    rolls <- sample(sides, n, replace = TRUE)
    paste("Rolled:", paste(rolls, collapse = ", "))
  },
  name = "roll_dice",
  description = "Roll one or more dice",
  arguments = list(
    n = ellmer::type_integer("Number of dice to roll"),
    sides = ellmer::type_integer("Number of sides per die")
  )
)

chat <- ellmer::chat_openai(model = "gpt-4o-mini")
agent <- Agent$new(chat = chat, tools = list(tool_dice))
result <- agent$run_sync("Roll 2d20 for me")
cat(result$response)

Web Tools

deputy provides web tools that are provider-aware. Pass the chat to tools_web() to select provider-native web tools when available. Calling it without a chat returns Deputy’s universal web tools:

chat <- ellmer::chat_anthropic()
agent <- Agent$new(
  chat = chat,
  tools = tools_web(chat)
)

For providers without native support, deputy falls back to tool_web_fetch (fetches a URL and extracts text) and tool_web_search (searches via DuckDuckGo).

MCP Integration

deputy can load tools from Model Context Protocol servers via the mcptools package:

# Load tools from selected configured servers
agent <- Agent$new(
  chat = ellmer::chat_anthropic(),
  tools = c(tools_file(), tools_mcp(servers = "github"))
)

tools_mcp() returns an empty list with a warning when mcptools is not installed or no tools are available. MCP servers are configured in ~/.config/mcptools/config.json; see the mcptools documentation for setup.

Human-in-the-Loop

The tool_ask_user tool lets an agent ask the user a question during execution. This is useful for confirmations, clarifications, or collecting input:

agent <- Agent$new(
  chat = ellmer::chat_openai(),
  tools = c(tools_file(), tools_interactive())
)

# In interactive sessions, a readline prompt appears.
# For non-interactive use, set a custom callback:
set_ask_user_callback(function(questions) {
  answers <- list()
  for (question in questions) {
    answers[[question$question]] <- question$options[[1]]$label
  }
  answers
})

The tools_interactive() function returns a list containing tool_ask_user.