Agent Substrate

1 · Tools

Three kinds of tool

The kernel defines three concrete types in kernel/tools/tools.py. Everything in L2 and L1 branches on this taxonomy.

diagram
Rendering diagram…
TypeDispatchExample
Tool (LOCAL)ToolInvoker calls execute() in-processWebSearchTool, CalculatorTool
HostedToolIncluded in tools= array; provider runs itCode execution on OpenAI
ProviderDefinedToolLLM calls a provider-defined shape; handle_call() runs locallyComputerUseTool

Use is_hosted_tool() / is_provider_defined_tool() from kernel.tools to branch at dispatch.

CapabilityDiscovery — auto-scan at startup

CapabilityDiscovery (capabilities/tools/discovery.py) walks three directories at boot:

  • capabilities/tools/ — tool packages (any subdirectory with tool.py)
  • capabilities/tools/skills/ — skill packages (any subdirectory with SKILL.md)
  • capabilities/tools/connectors/ — connector packages (any with connector.py)
diagram
Rendering diagram…

First-occurrence wins — earlier directories take priority if the same package name appears in multiple locations.

Built-in tools

PackageClassWhat it does
web/search.pyWebSearchToolTavily web search
web/surfer.pyWebSurferToolHeadless browser page fetch
web/read_url.pyReadUrlToolFetch + extract text from URL
web/wikipedia.pyWikipediaToolWikipedia article lookup
files/document_analyzer.pyDocumentAnalyzerToolExtract text from PDF/DOCX/etc
files/invoice_extractor.pyInvoiceExtractorToolStructured invoice data extraction
communication/email_sender.pyEmailSenderToolSend email via SMTP
communication/http_request.pyHttpRequestToolArbitrary HTTP requests
compute/calculator.pyCalculatorToolSafe math expression evaluator
database/postgres_query.pyPostgresQueryToolRun SQL on a user-configured DB
ai/image_generator.pyImageGeneratorToolGenerate images via DALL-E / compatible
ai/knowledge_search.pyKnowledgeSearchToolSearch a KnowledgeBase via RAGPipeline
task_manager/tool.pyTaskManagerToolKanban board (create/update/list tasks)
utils/current_time.pyCurrentTimeToolCurrent UTC timestamp
utils/tool_search.pyToolSearchToolSearch the Toolbox by name/description
code_interpreter/code_interpreter/tool.pyCodeInterpreterToolExecute Python or shell in an isolated sandbox — backend chosen by SANDBOX_RUNTIME (bubblewrap: Linux namespaces on this host, the single-node default; k8s: one agent-sandbox pod per session; inprocess: no isolation, tests only) via runtimes/factory.py::build_runtime
skills/tool.pySkillToolDiscover and activate agent skills
chain/tool.pyToolChainToolScript-driven multi-tool chaining (see page 3)

Writing a tool

Drop a tool.py in any subdirectory under capabilities/tools/:

python
from substrate.kernel.tools import ToolExecutionResult
from substrate.kernel.core.content import TextBlock

class MyTool:
    name = "my_tool"
    description = "What it does — shown to the LLM"
    input_schema = {
        "type": "object",
        "properties": {"query": {"type": "string"}},
        "required": ["query"],
    }

    async def execute(self, *, ctx=None, query: str, **_) -> ToolExecutionResult:
        result = do_work(query)
        return ToolExecutionResult(content=[TextBlock(text=result)])

CapabilityDiscovery finds it automatically at next startup — no registration step needed.

Risk annotation

Tag tools that modify state or call external APIs:

python
from substrate.kernel.tools import ToolRisk

class DangerousTool:
    risk: ToolRisk = ToolRisk.HIGH   # SAFE | LOW | MEDIUM | HIGH | CRITICAL
    ...

ToolInvoker (L1) enforces approval gates for HIGH and CRITICAL tools before calling execute().

ToolExecutionResult fields

python
@dataclass
class ToolExecutionResult:
    content: list[ContentBlock]       # TextBlock, ImageBlock, …
    is_error: bool = False
    structured_content: dict | None = None   # machine-readable output
    app_data: dict | None = None             # metadata not shown to LLM