Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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-key

For 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: 5s

Key Fields

FieldDescription
typeTool type. Determines the transport and execution model. See below.
endpointThe tool's network endpoint.
capabilitiesDeclared operations this tool provides. Used for permission matching.
risk_levellow, medium, high, or critical. Affects default isolation mode.
runtime.isolation_modeExecution isolation backend (see below).
runtime.timeoutMaximum execution time. Defaults to 30s.
runtime.retryRetry policy for failed invocations.
auth.secretRefReference to a Secret resource for tool authentication.

Tool Types

Tool.spec.type determines how the runtime communicates with the tool. Seven types are supported:

TypeTransportContractUse case
httpHTTP POST to endpointRaw body or ToolExecutionResponseSimple API integrations. Default when omitted.
externalHTTP POST to endpointStrict ToolExecutionRequest / ToolExecutionResponseTools running as standalone microservices that need the full execution context.
grpcUnary gRPC call to endpointToolExecutionRequest / ToolExecutionResponse as JSON over orloj.tool.v1.ToolService/ExecuteTeams that prefer gRPC for tool communication.
webhook-callbackHTTP POST to endpoint, then poll {endpoint}/{request_id}ToolExecutionRequest / ToolExecutionResponseLong-running tools, batch jobs, or tools that require human-in-the-loop steps.
mcpJSON-RPC 2.0 via stdio or HTTPMCP tools/call / tools/listTools exposed by MCP servers. Auto-generated by the McpServer controller.
cliexecve (container or direct)stdout / stderr captured as resultLocal binaries (kubectl, gh, aws, etc.) run under Orloj's isolation model. See CLI Tools.
wasmEmbedded wazero runtimestdin/stdout JSON contractTools 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:

  1. The runtime POSTs a ToolExecutionRequest to spec.endpoint.
  2. The tool returns 202 Accepted (or 200 OK with an immediate result).
  3. If 202: the runtime polls {endpoint}/{request_id} at regular intervals until a ToolExecutionResponse with 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.

ModeDescriptionDefault for
noneDirect execution in the worker process. The http type makes real HTTP calls; other types use their respective transports.low and medium risk tools
sandboxedRestricted 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
containerEach tool invocation runs in an isolated container. Full filesystem and network isolation.Explicitly configured
kubernetesExecutes 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
wasmTool 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:

  • low or medium risk: defaults to none
  • high or critical risk: defaults to sandboxed

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-only filesystem
  • --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": "..."
  }
}
Error response:
{
  "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: 5s

Writing 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_bytes limits the WASM linear memory. Defaults to 64 MB.
  • Fuel: fuel limits execution steps. Prevents runaway modules from consuming unbounded CPU.
  • WASI: When enable_wasi is 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|rust generates 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:

FieldPurpose
tool_codeMachine-readable error code (e.g. rate_limited, unsupported_tool, secret_resolution_failed)
tool_reasonHuman-readable explanation
retryableWhether 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:

ProfileSecret formatInjection
bearer (default)Single token valueAuthorization: Bearer <token>
api_key_headerSingle key valueCustom header via spec.auth.headerName
basicusername:passwordAuthorization: Basic <base64>
oauth2_client_credentialsMulti-key secret with client_id and client_secretToken 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: full

Retry 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_denied error.
  • 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