Skip to content

select_builtins

Python · package toolnexus · SPEC §4A · python/src/toolnexus/builtin.py

BuiltinsConfig = bool | dict[str, Any]
def select_builtins(cfg: BuiltinsConfig | None) -> list[Tool]

Resolves a builtins config value into the actual list of active built-in Tools — the ten opencode-style tools toolnexus ships (bash, read, write, edit, grep, glob, webfetch, question, apply_patch, todowrite). This is what create_toolkit calls internally when you pass its builtins= option; select_builtins is the same logic exposed directly, for when you want the resolved list without a toolkit around it.

  • You want the built-in tools without MCP, skills, or a toolkit — just the file/shell/web set, as a plain list[Tool] to hand to an adapter or a client.
  • You are implementing your own toolkit-like aggregator and need the same builtins config semantics (whole-source toggle, per-tool tools map) that create_toolkit uses.
  • You want to turn a config valueTrue, False, or a dict a user typed — into a tool list, the same way create_toolkit(builtins=...) would, to preview or validate it.

For the unconditional full ten with no config to interpret, use create_builtin_toolsselect_builtins(True) and create_builtin_tools() return the same tools, but select_builtins is the one that understands False and a tools map.

1. Default on, and the two ways to turn it fully off

Section titled “1. Default on, and the two ways to turn it fully off”
import asyncio
from toolnexus import select_builtins
# None and True both mean "all ten, on".
all_default = select_builtins(None)
all_true = select_builtins(True)
assert len(all_default) == 10
assert [t.name for t in all_default] == [t.name for t in all_true]
# False (or {"disabled": True}, or {"enabled": False}) turns the whole source off.
off_bool = select_builtins(False)
off_disabled = select_builtins({"disabled": True})
off_enabled_false = select_builtins({"enabled": False})
assert off_bool == off_disabled == off_enabled_false == []
print("ok:", len(all_default), "builtins ->", [t.name for t in all_default][:3], "...")
async def main():
bash = next(t for t in all_default if t.name == "bash")
res = await bash.execute({"command": "echo hi"})
assert res.is_error is False
assert res.output.strip() == "hi"
asyncio.run(main())

2. A tools map drops specific tools, all-on baseline

Section titled “2. A tools map drops specific tools, all-on baseline”

{"tools": {name: False}} starts from “all ten on” and removes just the named ones — unlike the skills/MCP filter, there is no allowlist form here: a tools map only ever drops.

from toolnexus import select_builtins
selected = select_builtins({"tools": {"bash": False, "apply_patch": False}})
names = {t.name for t in selected}
assert "bash" not in names
assert "apply_patch" not in names
# Everything else stays on.
assert names == {"read", "write", "edit", "grep", "glob", "webfetch", "question", "todowrite"}
# An unknown name in the map is simply ignored — no error, nothing removed for it.
minus_bash_only = select_builtins({"tools": {"bash": False, "not-a-real-tool": False}})
assert {t.name for t in minus_bash_only} == names | {"apply_patch"}
print("ok: dropped ->", {"bash", "apply_patch"} - names == {"bash", "apply_patch"})

3. A config value round-tripped from user input

Section titled “3. A config value round-tripped from user input”

The realistic shape: cfg came from somewhere external (a parsed JSON config, an env-derived dict) and might be any of the accepted shapes — validate it once with select_builtins rather than hand-rolling the same disabled/tools-map logic.

import asyncio
import json
from toolnexus import select_builtins, to_anthropic
raw_config = json.loads('{"tools": {"bash": false, "webfetch": false}}')
tools = select_builtins(raw_config)
assert {t.name for t in tools} == {
"read", "write", "edit", "grep", "glob", "question", "apply_patch", "todowrite",
}
# The resolved list is ordinary Tools — adapters don't care where they came from.
schema = to_anthropic(tools)
assert {s["name"] for s in schema} == {t.name for t in tools}
async def main():
read_tool = next(t for t in tools if t.name == "read")
res = await read_tool.execute({"path": "examples/mcp.json"})
assert res.is_error is False
assert "mcpServers" in res.output
print("ok:", sorted(t.name for t in tools))
asyncio.run(main())
cfg Result
None All ten, on (default).
True All ten, on.
False [] — whole source off.
{"disabled": True} [] — whole source off (checked before enabled).
{"enabled": False} [] — whole source off.
{"tools": {name: False, ...}} All ten minus the named ones. Unknown names ignored. True/absent stays on — there is no allowlist form.
  • create_builtin_tools — Construct all ten unconditionally, with no config to interpret.
  • create_toolkit — Takes builtins in exactly this shape, merged with MCP/skills/your own tools.