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.
When to use it
Section titled “When to use 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
builtinsconfig semantics (whole-source toggle, per-tooltoolsmap) thatcreate_toolkituses. - You want to turn a config value —
True,False, or a dict a user typed — into a tool list, the same waycreate_toolkit(builtins=...)would, to preview or validate it.
Why this and not the alternative
Section titled “Why this and not the alternative”For the unconditional full ten with no config to interpret, use
create_builtin_tools — select_builtins(True) and
create_builtin_tools() return the same tools, but select_builtins is the one that understands
False and a tools map.
Examples
Section titled “Examples”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 asynciofrom 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) == 10assert [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 namesassert "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 asyncioimport jsonfrom 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())Accepted cfg shapes
Section titled “Accepted cfg shapes”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. |
See also
Section titled “See also”create_builtin_tools— Construct all ten unconditionally, with no config to interpret.create_toolkit— Takesbuiltinsin exactly this shape, merged with MCP/skills/your own tools.