Tool
A Tool is an external capability that agents can invoke during execution. Orloj provides a standardized tool contract, multiple isolation backends, and runtime controls for timeout, retry, and risk classification.
Defining a Tool
Tools are declared as resources that describe the tool's endpoint, auth requirements, risk level, and runtime configuration.
apiVersion: orloj.dev/v1
kind: Tool
metadata:
name: web_search
spec:
type: http
endpoint: https://api.search.com
auth:
secretRef: search-api-keyFor tools that require isolation and runtime controls:
apiVersion: orloj.dev/v1
kind: Tool
metadata:
name: wasm-echo
spec:
type: wasm
wasm:
module: path/to/echo.wasm
entrypoint: run
max_memory_bytes: 16777216
fuel: 100000
enable_wasi: true
capabilities:
- wasm.echo.invoke
risk_level: low
runtime:
isolation_mode: wasm
timeout: 5sKey Fields
| Field | Description |
|---|---|
type | Tool type. Determines the transport and execution model. See below. |
endpoint | The tool's network endpoint. |
capabilities | Declared operations this tool provides. Used for permission matching. |
risk_level | low, medium, high, or critical. Affects default isolation mode. |
runtime.isolation_mode | Execution isolation backend (see below). |
runtime.timeout | Maximum execution time. Defaults to 30s. |
runtime.retry | Retry policy for failed invocations. |
auth.secretRef | Reference to a Secret resource for tool authentication. |
Tool Types
Tool.spec.type determines how the runtime communicates with the tool. Seven types are supported:
| Type | Transport | Contract | Use case |
|---|---|---|---|
http | HTTP POST to endpoint | Raw body or ToolExecutionResponse | Simple API integrations. Default when omitted. |
external | HTTP POST to endpoint | Strict ToolExecutionRequest / ToolExecutionResponse | Tools running as standalone microservices that need the full execution context. |
grpc | Unary gRPC call to endpoint | ToolExecutionRequest / ToolExecutionResponse as JSON over orloj.tool.v1.ToolService/Execute | Teams that prefer gRPC for tool communication. |
webhook-callback | HTTP POST to endpoint, then poll {endpoint}/{request_id} | ToolExecutionRequest / ToolExecutionResponse | Long-running tools, batch jobs, or tools that require human-in-the-loop steps. |
mcp | JSON-RPC 2.0 via stdio or HTTP | MCP tools/call / tools/list | Tools exposed by MCP servers. Auto-generated by the McpServer controller. |
cli | execve (container or direct) | stdout / stderr captured as result | Local binaries (kubectl, gh, aws, etc.) run under Orloj's isolation model. See CLI Tools. |
wasm | Embedded wazero runtime | stdin/stdout JSON contract | Tools compiled to WebAssembly, executed in-process with host-enforced resource limits. |
All types flow through the same governed runtime pipeline -- policy enforcement, retry, timeout, auth injection, and error taxonomy behave identically regardless of tool type. Unknown type values are rejected at apply time.
MCP
The mcp type represents tools provided by an MCP (Model Context Protocol) server. These tools are auto-generated by the McpServer controller -- you do not create them manually. Each type=mcp tool carries mcp_server_ref (the McpServer that owns it) and mcp_tool_name (the tool name on the MCP server).
At invocation time, the MCPToolRuntime resolves the server reference, obtains a session from the McpSessionManager, and sends a tools/call JSON-RPC 2.0 request through the appropriate transport (stdio or Streamable HTTP).
MCP tools also carry description and input_schema from the MCP server's tools/list response. These are propagated to the model gateway so the LLM receives rich, structured tool definitions instead of generic parameter schemas.
See the Connect an MCP Server guide for a complete walkthrough.
CLI
The cli type invokes a local binary (kubectl, gh, aws, jq, etc.) using an execve-style call — no shell involved. Each entry in cli.args is a Go text/template evaluated against the agent's JSON input; each template produces exactly one argv element.
CLI tools default to container isolation regardless of risk_level, running the binary inside a Docker container specified by cli.image. Credentials are injected as environment variables via cli.env_from (referencing Secret resources); spec.auth is not supported for this type.
See Define a CLI Tool for a complete reference and examples.
HTTP (default)
The http type sends the agent's tool input as an HTTP POST body to spec.endpoint. The runtime accepts both raw text responses and structured ToolExecutionResponse JSON envelopes. Auth is injected as an Authorization: Bearer header when auth.secretRef is configured.
External
The external type sends the full ToolExecutionRequest contract envelope as JSON to spec.endpoint and expects a ToolExecutionResponse back. This gives the external service access to the full execution context (task ID, agent, namespace, trace IDs, attempt number). Use this when your tool needs to be aware of the Orloj execution context.
gRPC
The grpc type calls orloj.tool.v1.ToolService/Execute as a unary gRPC method on spec.endpoint, using a JSON codec. The request and response payloads are the same ToolExecutionRequest / ToolExecutionResponse envelopes as external. Use this when your tool infrastructure is gRPC-native.
Webhook-Callback
The webhook-callback type supports asynchronous tool execution:
- The runtime POSTs a
ToolExecutionRequesttospec.endpoint. - The tool returns
202 Accepted(or200 OKwith an immediate result). - If
202: the runtime polls{endpoint}/{request_id}at regular intervals until aToolExecutionResponsewith a terminal status arrives, or the configured timeout expires.
This is useful for tools that take minutes to complete (e.g., batch processing, code review, CI pipeline triggers) or that require external approval before returning a result.
Isolation Modes
Isolation modes control the execution boundary of a tool, independent of tool type.
| Mode | Description | Default for |
|---|---|---|
none | Direct execution in the worker process. The http type makes real HTTP calls; other types use their respective transports. | low and medium risk tools |
sandboxed | Restricted container execution with secure defaults: read-only filesystem, no capabilities, no privilege escalation, no network, non-root user, memory/CPU/pids limits. | high and critical risk tools |
container | Each tool invocation runs in an isolated container. Full filesystem and network isolation. | Explicitly configured |
kubernetes | Executes the tool as an ephemeral Kubernetes Job. Requires --tool-k8s-enabled=true. The tool's spec.cli.image, command, args, and resources are mapped to the Job's Pod spec. | Explicitly configured |
wasm | Tool runs as a WebAssembly module with a host-guest stdin/stdout contract. Memory-safe and deterministic. | Explicitly configured |
The isolation mode defaults are based on risk_level:
lowormediumrisk: defaults tononehighorcriticalrisk: defaults tosandboxed
You can always override the default by setting runtime.isolation_mode explicitly.
Sandboxed Defaults
When isolation_mode is sandboxed, the container backend enforces these secure defaults:
--read-onlyfilesystem--cap-drop=ALL(no Linux capabilities)--security-opt no-new-privileges--network none(no network access)--user 65532:65532(non-root)--memory 128m--cpus 0.50--pids-limit 64
These defaults can be overridden with --tool-container-* flags on orlojd and orlojworker, but the default posture is restrictive.
Tool Contract v1
Every tool interaction follows a standardized request/response envelope. This contract ensures tools are portable, testable, and observable regardless of the isolation backend.
Request envelope (sent to the tool):
{
"request_id": "req-abc-123",
"tool": "web_search",
"action": "invoke",
"parameters": {
"query": "enterprise AI adoption trends"
},
"auth": {
"type": "bearer",
"token": "sk-..."
},
"context": {
"task": "weekly-report",
"agent": "research-agent",
"attempt": 1
}
}Response envelope (returned from the tool):
{
"request_id": "req-abc-123",
"status": "success",
"result": {
"data": "..."
}
}{
"request_id": "req-abc-123",
"status": "error",
"error": {
"tool_code": "rate_limited",
"tool_reason": "API rate limit exceeded",
"retryable": true
}
}The tool contract defines a canonical error taxonomy with tool_code, tool_reason, and retryable fields, enabling the runtime to make intelligent retry decisions.
WASM Tools
WASM tools run as WebAssembly modules inside an embedded wazero runtime. No external binary is required -- the runtime is pure Go and always available regardless of --tool-isolation-backend.
WASM tools communicate over stdin/stdout using a JSON contract (v1). The host writes the request to the module's stdin and reads a JSON response from stdout. This provides memory-safe, sandboxed execution with no filesystem or network access unless WASI is explicitly enabled.
Per-Tool Configuration
Each WASM tool declares its module and resource limits in spec.wasm. The module field accepts a local path, HTTPS URL, or OCI artifact reference:
apiVersion: orloj.dev/v1
kind: Tool
metadata:
name: wasm-echo
spec:
type: wasm
wasm:
module: path/to/echo.wasm # Local path, HTTPS URL, or oci://... reference
entrypoint: run # Exported function (default: run)
max_memory_bytes: 67108864 # 64 MB (default)
fuel: 1000000 # Execution fuel limit (default: 1M)
enable_wasi: true # Enable stdin/stdout/stderr via WASI
image_pull_secret: ghcr-creds # Optional: Secret for private OCI registries
capabilities:
- wasm.echo.invoke
risk_level: low
runtime:
isolation_mode: wasm
timeout: 5sWriting a WASM Tool
Any language that compiles to WebAssembly can produce a WASM tool. The simplest path is Go with GOOS=wasip1 GOARCH=wasm:
package main
import (
"encoding/json"
"io"
"os"
)
func main() {
data, _ := io.ReadAll(os.Stdin)
var req struct {
Input string `json:"input"`
}
_ = json.Unmarshal(data, &req)
resp := map[string]string{
"contract_version": "v1",
"status": "ok",
"output": req.Input,
}
_ = json.NewEncoder(os.Stdout).Encode(resp)
}Build with: GOOS=wasip1 GOARCH=wasm go build -o tool.wasm tool.go
Resource Limits
Memory and fuel limits are enforced by the host runtime, not by the guest module:
- Memory:
max_memory_byteslimits the WASM linear memory. Defaults to 64 MB. - Fuel:
fuellimits execution steps. Prevents runaway modules from consuming unbounded CPU. - WASI: When
enable_wasiis false, the module has no access to stdin/stdout/stderr. Most tools require WASI enabled.
Coexistence with Containers
WASM tools have their own dedicated runtime slot and work independently of --tool-isolation-backend. You can run WASM tools and container-isolated tools in the same agent system without conflict.
CLI Development Tools
orlojctl provides scaffold and test commands for WASM tool development:
orlojctl tool scaffold <name> --lang go|rustgenerates a ready-to-build project with guest code, Makefile, tool manifest, and test fixtures.orlojctl tool test <module.wasm> --fixtures <dir>runs the module against JSON test fixtures and validates contract compliance, expected output, and resource budgets.
For a complete walkthrough including the full stdin/stdout contract specification, error handling, multi-language examples, and local testing, see the Build a WASM Tool guide.
Error Taxonomy
Tool failures use a canonical error taxonomy with three fields:
| Field | Purpose |
|---|---|
tool_code | Machine-readable error code (e.g. rate_limited, unsupported_tool, secret_resolution_failed) |
tool_reason | Human-readable explanation |
retryable | Whether the runtime should retry the invocation |
HTTP status codes are mapped automatically: 429 and 5xx are retryable, 4xx are not. HTTP 401 maps to auth_invalid and 403 maps to auth_forbidden -- both non-retryable. All tool types share the same taxonomy, so policy and observability behave consistently.
Auth Profiles
Tools support four authentication profiles via spec.auth.profile:
| Profile | Secret format | Injection |
|---|---|---|
bearer (default) | Single token value | Authorization: Bearer <token> |
api_key_header | Single key value | Custom header via spec.auth.headerName |
basic | username:password | Authorization: Basic <base64> |
oauth2_client_credentials | Multi-key secret with client_id and client_secret | Token exchange at spec.auth.tokenURL, then bearer injection |
When spec.auth.secretRef is set without an explicit profile, the default is bearer for backward compatibility.
Secret Rotation
Secret resolution is performed fresh per tool invocation -- there is no caching of raw secret values. If a secret is rotated between invocations, the new value takes effect on the next call without requiring a restart.
For oauth2_client_credentials, access tokens are cached with a TTL derived from the token endpoint's expires_in response. Tokens are evicted on expiry or when the tool endpoint returns HTTP 401, triggering a fresh token exchange.
Retry and Timeout
Each tool can configure its own retry policy independently of the task-level retry:
runtime:
timeout: 5s
retry:
max_attempts: 3
backoff: 1s
max_backoff: 30s
jitter: fullRetry uses capped exponential backoff. The jitter field controls randomization: none (deterministic), full (random between 0 and backoff), or equal (half deterministic, half random).
Operation Classes
Tools can declare operation classes via spec.operation_classes (e.g. read, write, delete, admin). When omitted, the default is ["read"] for low/medium risk tools and ["write"] for high/critical risk tools.
Operation classes are used by ToolPermission.spec.operation_rules to define per-class policy verdicts:
- allow: proceed with the tool call (default).
- deny: block the call with a
permission_deniederror. - approval_required: pause the task and create a ToolApproval resource. An external actor (human or system) must approve or deny the request before the task can continue.
When multiple rules match, the most restrictive verdict wins: deny > approval_required > allow.
Governance Integration
Tool invocations are gated by the governance layer. An agent must have the required permissions (via AgentRole) to invoke a tool, and the tool must not be blocked by any applicable AgentPolicy. Unauthorized calls fail closed with a tool_permission_denied error.
Related
- McpServer -- auto-discover tools from MCP servers
- Secret -- store credentials for tool auth
- ModelEndpoint -- model provider configuration
- Resource Reference: Tool
- Guide: Build a Custom Tool
- Guide: Build a WASM Tool
- Guide: Connect an MCP Server