Skip to contents

MCP, retrieval, and UI integrations are optional. The core package remains small, while helpers make it straightforward to attach external tools and apps.

MCP tools

mcp_tools() accepts already materialized deep_tool() objects, which is useful when another part of your application has already connected to an MCP server or converted remote capabilities.

remote_echo <- deep_tool(
  function(text) paste("remote echo:", text),
  name = "remote_echo",
  description = "Echo text through a remote-style tool.",
  side_effects = "network",
  returns = "text"
)

tools <- mcp_tools(tools = list(remote_echo))
tools[[1]]$name
#> [1] "remote_echo"
tools[[1]]$fun("hello")
#> [1] "remote echo: hello"

When mcptools is installed, mcp_tools(config = ...) can read an MCP configuration and convert returned ellmer tool definitions into deepagentsr tools. If mcptools or a config file is unavailable, the helper returns an empty list so optional integration code can degrade cleanly.

agent <- create_deep_agent(
  model = ellmer::chat_openai(model = "gpt-4.1"),
  tools = c(
    list(local_tool),
    mcp_tools(config = "~/.config/mcptools/config.json")
  )
)

RAG tools

Use rag_tool() to wrap any R search function with signature query and k. The search function can query a data frame, a database, a vector store, or an external service.

documents <- data.frame(
  title = c("tools", "approval", "memory"),
  text = c(
    "Tools expose R functions to the model.",
    "Approval pauses side effects before they run.",
    "Memory files are loaded into the supervisor prompt."
  ),
  stringsAsFactors = FALSE
)

search_docs <- function(query, k = 5) {
  hits <- grepl(query, documents$text, ignore.case = TRUE)
  head(documents[hits, , drop = FALSE], k)
}

search_tool <- rag_tool(search_docs)
search_tool$fun("approval", 2)
#>      title                                          text
#> 2 approval Approval pauses side effects before they run.

rag_tools() returns a list so it can be concatenated with other tool lists.

agent <- create_deep_agent(
  model = fake_chat(list(
    assistant_tool_call("search_docs", list(query = "Memory", k = 2)),
    assistant_message("Memory files are prompt context.")
  )),
  tools = rag_tools(search_fun = search_docs)
)

agent$invoke("What are memory files?")$text
#> [1] "Memory files are prompt context."

If ragnar is installed, pass a Ragnar store to rag_tools(store = store). The resulting search_docs tool calls ragnar::ragnar_retrieve().

store <- ragnar::ragnar_store_create(embed = my_embedder)

agent <- create_deep_agent(
  model = ellmer::chat_openai(model = "gpt-4.1"),
  tools = rag_tools(store = store, top_k = 5)
)

Shiny chat apps

The package keeps Shiny out of core imports, but ships a small example app under inst/examples/shinychat. It uses as_shinychat_stream() as the stable adapter point between DeepAgent event streams and a Shiny chat UI.

chat_agent <- create_deep_agent(
  model = fake_chat(list(assistant_message("hello from the agent")))
)

stream <- as_shinychat_stream(chat_agent$stream("hello"))
class(stream)
#> [1] "coro_generator_instance"

Run the included app from an installed package:

shiny::runApp(system.file("examples", "shinychat", package = "deepagentsr"))

The app uses OpenAI through ellmer when OPENAI_API_KEY is set and falls back to fake_chat() otherwise.

Live OpenAI smoke tests

The repository includes a guarded live test path for OpenAI model smoke tests. Keep those tests out of normal package checks and opt in only when credentials and model access are available.

readRenviron(".env")
Sys.setenv(RUN_OPENAI_LIVE_TESTS = "true")
devtools::test(filter = "live-openai")

The smoke matrix exercises tool-calling through the same deep_tool() and ellmer adapter path used by regular agents, including GPT 4.1 and GPT 5 family models when the account has access.