Skip to main content

Tools & Permissions

Built-in tools

All tools are registered automatically. Control access with allowed_tools / disallowed_tools.

ToolCategoryRead-OnlyDescription
BashbashNoRun shell commands, view/create/edit files
ReadfilesystemYesRead file contents or list a directory
WritefilesystemNoWrite file contents
EditfilesystemNoEdit files with search/replace
GlobfilesystemYesFind files by pattern
GrepfilesystemYesSearch file contents
WebSearchwebYesSearch the web (Tavily/Brave/SerpAPI/DuckDuckGo)
WebFetchwebYesFetch and extract web page text; SSRF-protected
TodoWritetodoNoManage a structured task list
AskUserQuestionuser_inputNoAsk the user a question
ToolSearchmetaYesSearch available tools by keyword
TaskorchestrationNoDelegate work to a registered sub-agent

Task is only registered when agents are configured on the options object.

Restricting tools

from agentix import AgentixAgentOptions

# Allow only specific tools
options = AgentixAgentOptions(
allowed_tools=["Read", "Glob", "Grep", "WebSearch"],
)

# Block specific tools
options = AgentixAgentOptions(
disallowed_tools=["Bash", "Write"],
)

When allowed_tools is empty (the default), all tools are available. When non-empty, only the listed tools are available. disallowed_tools always overrides allowed_tools.

Permission modes

ModeBehavior
"default"Checks allowed_tools / disallowed_tools + can_use_tool callback
"acceptEdits"Auto-approves all file edits
"bypassPermissions"Allows everything except disallowed_tools
"plan"No tools execute; planning mode only
"dontAsk"Denies any tool not in allowed_tools
options = AgentixAgentOptions(permission_mode="bypassPermissions")

# Or change at runtime
client.set_permission_mode("plan")

Permission callback

The can_use_tool callback is the single control point for all tool interactions — permission gating, input sanitisation, and user-input delivery.

from agentix import (
AgentixAgentOptions,
PermissionResultAllow,
PermissionResultDeny,
ToolPermissionContext,
)

async def my_permission_handler(
tool_name: str,
tool_input: dict,
context: ToolPermissionContext,
):
# Hard deny — aborts the run immediately
if tool_name == "Bash" and "rm -rf" in tool_input.get("command", ""):
return PermissionResultDeny(message="Destructive command blocked.", interrupt=True)

# Soft deny — agent sees the message and may try a different approach
if tool_name == "Bash":
return PermissionResultDeny(message="Shell access is disabled.")

# Allow with no modifications (most common)
return PermissionResultAllow()

options = AgentixAgentOptions(
name="safe-agent",
can_use_tool=my_permission_handler,
)

Permission result fields

ClassFieldDefaultDescription
PermissionResultAllowupdated_inputNoneReplacement input dict; None = use original
PermissionResultAllowupdated_permissionsNoneAdvisory rule changes to the session's permission state
PermissionResultDenymessage""Reason shown to the agent
PermissionResultDenyinterruptFalseTrue = hard abort; False = soft deny

When PermissionResultAllow.updated_input is provided, the modified input is what gets executed and persisted to the session log, keeping audit trails accurate.

Handling AskUserQuestion

When the agent calls AskUserQuestion, handle it inside can_use_tool by returning PermissionResultAllow with the user's answer in updated_input:

async def my_permission_handler(tool_name, tool_input, context):
if tool_name == "AskUserQuestion":
answer = await your_ui.ask(tool_input["question"])
return PermissionResultAllow(updated_input={**tool_input, "answer": answer})
return PermissionResultAllow()

When no can_use_tool is set, AskUserQuestion falls back to a blocking input() call — suitable for CLI scripts but will hang in headless deployments.

Web search backends

Set one of these environment variables to auto-select the web search backend:

export TAVILY_API_KEY=tvly-...     # recommended
export BRAVE_API_KEY=BSA...
export SERPAPI_API_KEY=...
# (no key) → DuckDuckGo fallback

SSRF protection

WebFetch blocks requests to private/internal IPs (RFC-1918, link-local, loopback) by default. To allow specific internal ranges:

from agentix import AgentixAgentOptions, SandboxSettings, SandboxNetworkConfig

options = AgentixAgentOptions(
sandbox=SandboxSettings(
enabled=True,
network=SandboxNetworkConfig(
allow_network_ranges=["10.100.0.0/16"],
),
),
)

Secure subprocess environment

The Bash tool spawns subprocesses with a minimal environment (safe PATH only) by default, so ambient credentials are not leaked to subprocesses. To forward specific variables:

options = AgentixAgentOptions(
env_pass_through=["DATABASE_URL", "REDIS_URL"], # opt-in
env={"MY_APP_ENV": "production"}, # explicit extras
)