The tool block declares a tool, its source, capabilities, and configuration.
The tool block declares a tool that the agent can call during a run. A manifest can contain any
number of tool blocks. The kind field determines the tool type and which additional fields apply.
tool "adder" {
kind = "javascript"
source = "./tools/adder"
capabilities = ["math::add"]
enabled = runtime("TOOL_ADDER_ENABLED", true)
}These fields apply to all tool kinds. For javascript, python and bash tools, the block label
is the tool's name, the name the model calls it by.
| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
kind | javascript | python | bash | mcp | a2a | yes | no | … | Execution or connection mechanism used for the tool. |
description | string | no | no | unset | What the tool does, as the model reads it. Unset, a javascript or python tool uses its class's static description, and a bash tool uses a built-in description. A javascript or python tool fails to load when neither sets one. |
capabilities | array<string> | no | no | [] | Capability tags required to invoke this tool. See Tools and capabilities. |
enabled | boolean | no | yes | true | Whether the tool is registered at startup. |
config | map<string, string> | no | yes | {} | Named string values made available to the tool at runtime, each of which may be a constant or a runtime() expression. |
A JavaScript tool is a TypeScript or JavaScript package bundled by esbuild at compile time and
executed in the runtime's embedded engine. See runtime libraries for the
environment that engine provides. The bundle is embedded in the binary; no Node.js installation is
required at runtime. The selected export must be a class extending Tool from agentc:tools.
Its static parameters must be a Schema instance.
tool "adder" {
kind = "javascript"
source = "./tools/adder"
export = "AdderTool"
capabilities = ["math::add"]
enabled = runtime("TOOL_ADDER_ENABLED", true)
config = {
API_ENDPOINT = runtime("ADDER_API_ENDPOINT", "http://localhost:3000")
}
}| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
source | string | yes | no | … | Path to the tool's package directory, relative to the manifest. Must contain a package.json. |
export | string | no | no | tool block name | Name of the exported Tool subclass. |
See Write a tool for how to write the tool source.
A Python tool is a uv-managed Python package. Dependencies are installed at compile time and
the package source and site-packages are embedded in the binary. The selected interpreter
determines whether the embedded package runs through RustPython or CPython. The selected export
must be a class subclassing Tool from agentc_tools.
tool "weather" {
kind = "python"
source = "./tools/weather"
export = "Weather"
interpreter = "embedded"
capabilities = ["weather::get"]
}| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
source | string | yes | no | … | Path to the tool's directory, relative to the manifest. Must contain a pyproject.toml. |
export | string | no | no | tool block name | Name of the exported Tool subclass. |
interpreter | embedded | static | no | no | "embedded" | Python runtime backend. "embedded" uses RustPython, supports pure-Python packages, and produces a self-contained binary. "static" uses CPython 3.14 and supports packages with C extensions, but requires a compatible shared CPython installation and libpython available at runtime. |
See Write a tool for how to write the tool source.
A Bash tool provides the agent with a shell interpreter. It uses the top-level
filesystem and network
configuration. The agent must hold the built-in bash capability in addition to any capabilities
declared on the tool.
tool "shell" {
kind = "bash"
commands = ["git"]
cwd = "/workspace"
shared = false
env {
kind = "allow"
vars = ["HOME", "PATH"]
}
limits {
max_execution_time_secs = 30
max_output_size = 10485760
max_command_count = 10000
max_loop_iterations = 10000
}
}| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
commands | array<string> | no | no | [] | Additional host programs to register as passthrough commands. Each program must be available on the host at runtime. |
cwd | string | no | no | "/home/agent" | Initial directory in the agent filesystem. The value is always a POSIX path. |
env | BashEnv | no | defaults below | Controls which host environment variables Bash commands receive. | |
limits | BashLimits | no | defaults below | Resource limits applied to each invocation. | |
shared | boolean | no | no | false | Whether invocations share one interpreter. Shared invocations retain shell-local state and execute serially. |
| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
kind | empty | inherit | allow | deny | no | no | "empty" | Rule for selecting host environment variables available to Bash commands. |
vars | array<string> | no | no | [] | Variable names used by the "allow" and "deny" policies. |
Supported policies are "empty" (no variables), "inherit" (all variables), "allow" (only vars), and "deny" (all except vars).
All fields are optional and use the following defaults when absent.
| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
max_execution_time_secs | integer | no | no | 30 | Maximum wall-clock execution time in seconds. |
max_output_size | integer | no | no | 10485760 | Maximum combined output size in bytes. |
max_command_count | integer | no | no | 10000 | Maximum number of commands that may be dispatched. |
max_loop_iterations | integer | no | no | 10000 | Maximum number of loop iterations. |
With shared = false, each invocation gets a fresh interpreter. With shared = true, variables,
functions, aliases, and working directory changes persist between invocations. Filesystem changes
follow the configured filesystem backend in either mode.
The built-in curl command uses the agent's HTTP client and cannot bypass its network policy or
limits. See Use the Bash tool for configuration and lifecycle examples.
An MCP tool connects the agent to a Model Context Protocol server. Two transports are supported:
stdio spawns a local subprocess; http connects to a remote server over streamable HTTP.
# stdio transport
tool "time_server" {
kind = "mcp"
transport = "stdio"
command = "uvx"
args = ["mcp-server-time"]
}
# HTTP transport
tool "remote_tools" {
kind = "mcp"
transport = "http"
url = runtime("MCP_URL", "https://tools.example.com")
auth_token = secret(runtime("MCP_TOKEN"))
headers = {
"X-Client-ID" = runtime("CLIENT_ID")
}
}stdio fields:
| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
transport | stdio | yes | no | … | Launches the MCP server as a subprocess and exchanges messages over standard input and output. |
command | string | yes | yes | … | Executable used to spawn the MCP server subprocess. |
args | array<string> | no | yes | [] | Arguments passed to the command. Each element may be a runtime() expression. |
config | map<string, string> | no | yes | {} | Environment variables forwarded to the subprocess. Each value may be a runtime() expression. |
HTTP fields:
| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
transport | http | yes | no | … | Connects to the MCP server over HTTP. |
url | string | yes | yes | … | Base URL of the MCP server. |
auth_token | string | no | yes | unset | Bearer token sent in the Authorization header. Use secret(runtime(...)). |
headers | map<string, string> | no | yes | {} | Additional HTTP headers sent with every request. Each value may be a runtime() expression. |
See Connect external tools via MCP for more on Model Context Protocol integration.
An A2A tool delegates work to another agent through the Agent2Agent protocol. Each configured target is a fixed downstream server, not a model-visible arbitrary URL. Every A2A target registers the same four operation tools:
a2a_{target_id}_senda2a_{target_id}_streama2a_{target_id}_get_taska2a_{target_id}_cancel_tasktool "planner" {
kind = "a2a"
description = "Delegate planning subtasks to the planning agent."
url = runtime("PLANNER_A2A_URL", "https://planner.example.com")
auth_token = secret(runtime("PLANNER_A2A_TOKEN"))
headers = {
"X-Client-ID" = runtime("PLANNER_CLIENT_ID", "assistant")
}
tenant = {
policy = "inherit"
}
timeout_secs = runtime("PLANNER_A2A_TIMEOUT", 90)
default_accepted_output_modes = ["text/plain"]
capabilities = ["a2a::planner"]
enabled = runtime("PLANNER_A2A_ENABLED", true)
}| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
url | string | yes | yes | … | Base URL of the downstream A2A server. |
auth_token | string | no | yes | unset | Bearer token sent in the Authorization header. Use secret(runtime(...)). |
headers | map<string, string> | no | yes | {} | Additional HTTP headers sent with every request. Each value may be a runtime() expression. |
tenant | TenantPolicy | no | no | inherit | Controls the tenant ID sent to the downstream A2A server. |
timeout_secs | integer | no | yes | unset | Request timeout in seconds. |
default_accepted_output_modes | array<string> | no | no | [] | Default output formats accepted from downstream A2A operations. |
There is no operation-selection field. Every target always gets send, stream, get-task, and cancel-task tools.
The tenant block controls the X-Tenant-Id value sent to the downstream A2A server.
tenant = {
policy = "inherit"
}| Field | Type | Required | runtime() | Default | Description |
|---|---|---|---|---|---|
policy | inherit | fixed | none | yes | no | … | Rule for inheriting, fixing, or omitting the downstream tenant ID. |
id | string | required for fixed | yes | … | Tenant ID sent when policy = "fixed". |
inherit forwards the effective tenant from the parent run. fixed sends the configured id.
none omits the A2A tenant header.
The stream operation emits stateless activity deltas while the downstream task runs. These deltas are A2A-specific and are delivered through the normal tool activity event channel.
Activity types are:
a2a_taska2a_task_statusa2a_artifacta2a_messageThe accumulated activity state has this shape:
{
"target_id": "planner",
"task_id": "task-123",
"context_id": "ctx-123",
"state": "TASK_STATE_WORKING",
"latest_message": "Drafting plan.",
"artifacts": []
}See Connect agents via A2A for a complete guide to outbound A2A delegation.
© 2026 pogue.dev. All rights reserved.
CC BY 4.0