Toolnexus.Builtin.tools
Elixir · package toolnexus · SPEC §4A · elixir/lib/toolnexus/builtin.ex
@spec tools() :: [Toolnexus.Tool.t()]def tools()Builds all ten built-in tools — bash, read, write, edit, grep, glob, webfetch,
question, apply_patch, todowrite — in fixed order, every time, with no config and no
filtering. This is what Toolnexus.Builtin.load/1 calls
internally before applying the on/off and per-tool toggles.
When to use it
Section titled “When to use it”- You want the full set, unconditionally — no
enabled/disabledcheck, notoolsallowlist to reason about, just the ten tools. - Testing or inspecting a single builtin —
tools/0is the shortest path to a realbashorreadtool struct you can callexecute.(args, ctx)on directly. - Building your own selection logic — if
Builtin.load/1’s toggle shape doesn’t fit your config format, calltools/0and filter the list yourself.
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”1. All ten, in fixed order, every call
Section titled “1. All ten, in fixed order, every call”alias Toolnexus.Builtin
tools = Builtin.tools()
true = Enum.map(tools, & &1.name) == ["bash", "read", "write", "edit", "grep", "glob", "webfetch", "question", "apply_patch", "todowrite"]
true = Enum.all?(tools, &(&1.source == "builtin"))# Calling it again returns the same shape — no memoization surprises, no shared state.true = Enum.map(Builtin.tools(), & &1.name) == Enum.map(tools, & &1.name)
IO.puts("ok: #{length(tools)} builtins, fixed order")2. Executing one directly — no toolkit, no agent, just the tool
Section titled “2. Executing one directly — no toolkit, no agent, just the tool”alias Toolnexus.{Builtin, Context}
tools = Builtin.tools()write = Enum.find(tools, &(&1.name == "write"))read = Enum.find(tools, &(&1.name == "read"))
dir = Path.join(System.tmp_dir!(), "toolnexus-docs-builtins-#{System.unique_integer([:positive])}")path = Path.join(dir, "note.txt")
wrote = write.execute.(%{"path" => path, "content" => "hello from tools/0"}, %Context{})false = wrote.is_errortrue = wrote.metadata.bytes == byte_size("hello from tools/0")
got = read.execute.(%{"path" => path}, %Context{})false = got.is_errortrue = got.output == "hello from tools/0"
File.rm_rf!(dir)
IO.puts("ok: wrote #{wrote.metadata.bytes} bytes, read back #{byte_size(got.output)}")3. tools/0 ignores config entirely — unlike load/1
Section titled “3. tools/0 ignores config entirely — unlike load/1”Same underlying ten tools, but tools/0 never looks at a config map — passing one to load/1
still narrows the set; there is no equivalent argument to tools/0.
alias Toolnexus.Builtin
full = Builtin.tools()10 = length(full)
# load/1 with a `tools` toggle narrows the set...narrowed = Builtin.load(%{"tools" => %{"bash" => false}})9 = length(narrowed)
# ...but tools/0 has no config argument at all — always the full ten.true = length(Builtin.tools()) == length(full)true = Enum.map(Builtin.tools(), & &1.name) == Enum.map(full, & &1.name)
IO.puts("ok: tools/0 always #{length(full)}, load/1 narrowed to #{length(narrowed)}")See also
Section titled “See also”Toolnexus.Builtin.load— the config-driven on/off and per-tool toggleToolnexus.create_toolkit— where builtins normally get wired into an agentToolnexus.Tool— the struct every builtin returns