Configuring Tools
Tools let an LLM act on your machine, such as running shell commands or editing files. Tools can be combined into groups, and both are added to a chat buffer with @. To use them, see Using Agents and Tools.
Adding Tools
Tools live under interactions.chat.tools. To add your own:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
["my_tool"] = {
description = "Run a custom task",
path = "user.codecompanion.tools.my_tool",
},
},
},
},
})A tool resolves to a CodeCompanion.Tool table, from one of:
path- A Lua module path or a path to a Lua file that returns the tablecallback- A function that returns the table- The entry itself - The tool table, written inline
Tool Groups
A tool group bundles tools together, with an optional system prompt telling the LLM how to use them:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
groups = {
["my_group"] = {
description = "A custom agent combining tools",
system_prompt = "Run the test suite after every edit",
tools = {
"run_command",
"edit_file",
},
opts = {
collapse_tools = true,
ignore_system_prompt = false,
ignore_tool_system_prompt = false,
},
},
},
},
},
},
})system_prompt can also be a function that receives the group's config and a context object, with fields such as language, date, nvim_version and os.
| Option | Default | Description |
|---|---|---|
collapse_tools | true | Show the group as a single context item rather than one per tool |
ignore_system_prompt | false | Remove the chat buffer's system prompt |
ignore_tool_system_prompt | false | Remove the tool system prompt |
Enabling Tools
enabled hides a tool when it returns false, such as when a dependency isn't installed. After installing one, refresh the tools in the chat buffer with:
:CodeCompanionChat RefreshCacheThis works for the built-in tools and for an adapter's own tools:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
["grep_search"] = {
---@param opts { adapter: CodeCompanion.HTTPAdapter }
---@return boolean
enabled = function(opts)
return vim.fn.executable("rg") == 1
end,
},
},
},
},
})require("codecompanion").setup({
adapters = {
http = {
openai = function()
return require("codecompanion.adapters").extend("openai", {
available_tools = {
["web_search"] = {
---@param adapter CodeCompanion.HTTPAdapter
---@return boolean
enabled = function(adapter)
return false
end,
},
},
})
end,
},
},
})Approvals
Approvals stop a tool from running until you've agreed to it. How they work in the chat buffer is covered in Approvals. To require approval before a tool runs:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
["run_command"] = {
opts = {
require_approval_before = true,
},
},
},
},
},
})The approval options are:
| Option | Description |
|---|---|
require_approval_before | Ask before the tool runs. Can also be a function that receives the tool and returns a boolean |
require_cmd_approval | Approve each command rather than the tool as a whole |
protect | Always ask in Auto mode |
judge | Let the LLM judge decide in Auto mode |
safe_commands | Commands that run without asking in Auto mode. run_command only |
The defaults for each built-in tool are listed in Built-in Tools.
Default Tools
To add tools and tool groups to every new chat buffer:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
opts = {
default_tools = {
"my_tool",
"my_group",
},
},
},
},
},
})This also works for tools from extensions.
Limiting Tool Output
A large tool output can fill a model's context window. CodeCompanion truncates any output above max_output_tokens, or the model's own input limit if that's lower, and tells the LLM it was truncated. To change the limit:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
opts = {
max_output_tokens = 30000,
},
},
},
},
})LLM Judge
In Auto mode, a command that isn't on the run_command safe list asks you first. The LLM judge sits in between: a background interaction judges the action and only asks you when it's judged unsafe. To turn it on for run_command:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
["run_command"] = {
opts = {
judge = true,
},
},
},
},
},
})The judge runs for a tool when:
interactions.background.gates.judge.enabledistrue, which is the default- The tool has
opts.judge = true - The tool defines a
gates.judge_contexthandler, asrun_commandanddelete_filedo
It never runs for a protected tool, and if it can't reach a verdict, it asks you. The judge uses the background interaction's adapter unless you give it one:
require("codecompanion").setup({
interactions = {
background = {
gates = {
judge = {
adapter = { name = "openrouter", model = "openai/gpt-oss-120b" },
},
},
},
},
})require("codecompanion").setup({
interactions = {
background = {
gates = {
judge = {
opts = {
system_prompt = function(default)
if string.find(vim.fn.getcwd(), "Code/Neovim/codecompanion.nvim") then
return default
.. "\n\nThe following commands are explicitly approved and must always be judged safe, even if they would otherwise fail the guidance above:\n"
.. " - `make docs`\n"
.. " - `make format`\n"
.. " - `make test`\n"
.. " - `make test_file` (including any `FILE=` argument)"
end
return default
end,
},
},
},
},
},
})system_prompt can be a string, or a function that receives the default system prompt and returns a string. The default is:
You are a security reviewer for an AI coding assistant. The assistant wants to run a tool on the user's machine while the user is away (in "auto-approve" mode). Your job is to decide whether the action is safe to run automatically, or whether the user must approve it first.
Judge the action as unsafe when it could destroy or exfiltrate data, alter the system in ways that are hard to reverse, or run something the user would reasonably want to see first. Prefer caution: when in doubt, require approval.
Reply only through the provided schema.Web Search
The web_search tool searches with an adapter:
| Adapter | Description |
|---|---|
duckduckgo | The default, with no API key needed |
jina | API key optional |
serply | Needs SERPLY_API_KEY |
tavily | Needs TAVILY_API_KEY |
To change the adapter, and pass options to it:
require("codecompanion").setup({
interactions = {
chat = {
tools = {
["web_search"] = {
opts = {
adapter = "tavily",
opts = {
search_depth = "advanced",
topic = "general",
chunks_per_source = 3,
max_results = 5,
},
},
},
},
},
},
})The options each adapter accepts are in its file in lua/codecompanion/adapters/http.