initializdocs
DeveloperForge runtimeCore concepts

Tools & Builtins

Built-in tools, adapter tools, and the pluggable tool system.

Tools are capabilities that an LLM agent can invoke during execution. Forge provides a pluggable tool system with built-in tools, adapter tools, development tools, and custom tools.

Tool Categories

CategoryCodeDescription
BuiltinbuiltinCore tools shipped with Forge
AdapteradapterExternal service integrations via webhook, MCP, or OpenAPI
DevdevDevelopment-only tools, filtered in production builds
CustomcustomUser-defined tools discovered from the project

Built-in Tools

ToolDescription
http_requestMake HTTP requests (GET, POST, PUT, DELETE). Strips credentials on cross-origin redirects
json_parseParse and query JSON data
csv_parseParse CSV data into structured records
datetime_nowGet current date and time
uuid_generateGenerate UUID v4 identifiers
math_calculateEvaluate mathematical expressions
web_searchSearch the web for quick lookups and recent information
web_fetchFetch a URL and return its main content as clean, readable text/markdown (strips nav/scripts/styling; preserves <pre>/<code> and transcodes non-UTF-8 charsets). Read-only GET, egress-controlled (refuses if no egress client is present — no DefaultTransport fallback), with redirect + size caps and a content-type guard. A non-2xx response still returns the error page's content with its status. Use to read a page; web_search finds pages, http_request returns raw bytes
file_createCreate a downloadable file, written to the agent's .forge/files/ directory
read_skillLoad full instructions for an available skill on demand
memory_searchSearch long-term memory (when enabled)
memory_getRead memory files (when enabled)
context_expandRetrieve the original content behind a <<ctxzip:...>> compression marker (when compression is enabled)
cli_executeExecute pre-approved CLI binaries
schedule_setCreate or update a recurring cron schedule
schedule_listList all active and inactive schedules
schedule_deleteRemove an LLM-created schedule
schedule_historyView execution history for scheduled tasks

Register all builtins with builtins.RegisterAll(registry).

File & Search Tools

Every Forge agent gets a file read/write/edit/patch surface plus search, registered by default and confined to the agent's working directory (WorkDir) via a PathValidator. All resolved paths are confined within the working directory, preventing directory-traversal attacks.

ToolDescription
file_readRead file contents with optional line offset/limit, or list directory entries
file_writeCreate or overwrite files in the working directory
file_editEdit files by exact string matching with unified diff output
file_patchBatch file operations (add, update, delete, move) in a single call
glob_searchFind files by glob pattern (e.g., **/*.go), sorted by modification time
grep_searchSearch file contents with regex; uses rg if available, falls back to Go
directory_treeDisplay tree-formatted directory listing (default max depth: 3)

file_read / file_write / file_edit / file_patch are registered for general agents by the runtime (#268); previously they were reachable only when a skill wired them up. They give a general agent a real file-editing surface — not just file_create + search.

General file tools vs. the code-agent skill

There are two distinct file surfaces, and they do not collide:

SurfaceToolsScopeWhen
General builtins (#268)file_read / file_write / file_edit / file_patchWorkDirEvery agent, except when the code-agent skill is active
Code-agent skillcode_agent_read / code_agent_write / code_agent_run (from the skill's SKILL.md)the skill's project_dirOnly when the code-agent skill is active

When the code-agent skill is active, the general file_* builtins are skipped — the skill's project-scoped code_agent_* tools are the specialized file surface (skill tools win), so the LLM never sees two overlapping file surfaces. Search tools (grep_search / glob_search / directory_tree) are registered in both cases, scoped to workspace/ under the code-agent skill and to WorkDir otherwise.

Registration Groups

These tools are constructed and registered in layered groups (forge-core/tools/builtins/register.go), so the runtime and skills can request only the capabilities they need:

GroupToolsPurpose
FileToolsfile_read, file_write, file_edit, file_patchGeneral file surface — registered for non-code-agent agents (#268)
CodeAgentSearchToolsgrep_search, glob_search, directory_treeRead-only exploration — registered for every Forge agent
CodeAgentReadToolsfile_read + search toolsSafe reading
CodeAgentWriteToolsfile_write, file_edit, file_patchModification
CodeAgentToolsAll read + write toolsFull code-agent capability

Path Validation

All file tools use PathValidator (from pathutil.go):

  • All resolved paths must stay within the configured workDir
  • Directory traversal via .. is caught after symlink resolution
  • Standard directories are excluded from search: .git, node_modules, vendor, __pycache__, .venv, dist, build

Adapter Tools

AdapterDescription
webhook_callPOST JSON payloads to webhook URLs. Strips credentials on cross-origin redirects

Adapter tools bridge external services into the agent's tool set.

Per-operation API tools (apis.servers)

Instead of one generic openapi_call tool, Forge registers one typed tool per admitted OpenAPI operation (issue #400 — the generic openapi_call stub was removed). Configure them under the top-level apis: block: each operations[] entry of a server registers as a namespaced <name>__<op> tool (e.g. server memberservice op reverse_fee → tool memberservice__reverse_fee) with the operation's own typed input schema, so the LLM (and the PDP) see a distinct, argument-typed tool per endpoint rather than a free-form HTTP call.

apis:
  servers:
    - name: memberservice
      base_url: https://member-service.internal   # host must be on the egress allowlist
      auth:
        token_env: MEMBERSERVICE_TOKEN             # bearer/static only (oauth rejected for apis)
      operations:
        - name: reverse_fee
          method: POST
          path: /accounts/{account_id}/reversals   # {…} segments filled from typed args
          description: Reverse a fee on an account

apis.servers[] entries are typically platform-materialized from admitted OpenAPI specs. Because each operation is its own <name>__<op> tool, the managed PDP keys authorization rules per operation. See the apis: schema.

MCP tools are not listed in this table. Configure MCP servers under the top-level mcp: block in forge.yaml; each server's discovered tools are registered as namespaced <server>__<tool> entries automatically. See docs/mcp/ for the configuration reference. The previous mcp_call adapter tool was removed in v0.12.0 — the new block is strictly more capable.

Web Search Providers

The web_search tool supports two providers:

ProviderAPI Key Env VarEndpoint
Tavily (recommended)TAVILY_API_KEYapi.tavily.com/search
PerplexityPERPLEXITY_API_KEYapi.perplexity.ai/chat/completions

Provider selection: WEB_SEARCH_PROVIDER env var, or auto-detect from available API keys (Tavily first).

CLI Execute

The cli_execute tool provides security-hardened command execution with 13 security layers:

tools:
  - name: cli_execute
    config:
      allowed_binaries: ["git", "curl", "jq", "python3"]
      env_passthrough: ["GITHUB_TOKEN"]
      timeout: 120
      max_output_bytes: 1048576
#LayerDetail
1Shell denylistShell interpreters (bash, sh, zsh, dash, ksh, csh, tcsh, fish) are filtered out at construction time and unconditionally blocked at execution — they defeat the no-shell design
2Binary allowlistOnly pre-approved binaries can execute
3Binary resolutionBinaries are resolved to absolute paths via exec.LookPath at startup
4Argument validationRejects arguments containing $(, backticks, newlines, or file:// URLs
5File protocol blockingArguments containing file:// (case-insensitive) are blocked to prevent filesystem traversal via curl file:///etc/passwd (see File Protocol Blocking)
6Path confinementPath arguments inside $HOME but outside workDir are blocked (see Path Containment)
7TimeoutConfigurable per-command timeout (default: 120s)
8No shellUses exec.CommandContext directly — no shell expansion
9Working directorycmd.Dir set to workDir so relative paths resolve within the agent directory
10Environment isolationOnly PATH, HOME, LANG, explicit passthrough vars, proxy vars, OPENAI_ORG_ID (when set), GH_CONFIG_DIR (auto-set to real ~/.config/gh only for gh), and KUBECONFIG/NO_PROXY (only for kubectl/helm — see below). HOME is overridden to workDir to prevent ~ expansion from reaching the real home directory
11Output limitsConfigurable max output size (default: 1MB) to prevent memory exhaustion
12Skill guardrailsSkill-declared deny_commands and deny_output patterns block/redact command inputs and outputs (see Skill Guardrails)
13Custom tool entrypoint validationCustom tool entrypoints are validated: rejects empty, absolute, or ..-containing paths; resolves symlinks and verifies the target stays within the project directory and is a regular file

KUBECONFIG and NO_PROXY Scoping

When HOME is overridden to workDir, kubectl and helm lose access to ~/.kube/config. For these two binaries only, cli_execute auto-sets:

Env VarValuePurpose
KUBECONFIGExplicit KUBECONFIG if set, else <real-home>/.kube/configPasses through the active kubeconfig
NO_PROXYK8s API server hostname(s)Bypasses the egress proxy for cluster connections

If KUBECONFIG is explicitly set in the environment (e.g., via docker run -e KUBECONFIG=... or after KUBECONFIG materialization), that value is passed through directly. Otherwise, cli_execute falls back to the real ~/.kube/config. NO_PROXY is extracted from the kubeconfig's clusters[].cluster.server field. Other binaries do not receive these variables.

File Create

The file_create tool generates downloadable files that are both written to disk and uploaded to the user's channel (Slack/Telegram).

FieldDescription
filenameName with extension (e.g., patches.yaml, report.json)
contentFull file content as text

Output JSON includes filename, content, mime_type, and path. The path field contains the absolute disk location, allowing other tools (e.g., kubectl apply -f <path>) to reference the file.

File location: Files are written to the agent's .forge/files/ directory (under WorkDir). The runtime injects this path via FilesDir in the executor context. When running outside the full runtime (e.g., tests), falls back to $TMPDIR/forge-files/.

Allowed extensions:

ExtensionMIME Type
.mdtext/markdown
.jsonapplication/json
.yaml, .ymltext/yaml
.txt, .logtext/plain
.csvtext/csv
.shtext/x-shellscript
.xmltext/xml
.htmltext/html
.pytext/x-python
.tstext/typescript

Filenames with path separators (/, \) or traversal patterns (..) are rejected.

Memory Tools

When long-term memory is enabled, two additional tools are registered:

  • memory_search — Hybrid vector + keyword search across stored memory files
  • memory_get — Read specific memory files by path

These tools allow the agent to recall information from previous sessions.

Context Expansion Tool

When context compression is enabled, the context_expand tool is registered. Compressed tool outputs carry inline <<ctxzip:HASH note>> markers; the model calls context_expand with the hash to retrieve the offloaded original from the local store. The tool tolerates imperfect input — a whole marker pasted as the hash, or a truncated hash that uniquely prefixes a recently emitted one — and a miss (expired/evicted entry) returns guidance to re-run the producing tool rather than an error.

Development Tools

Development tools (local_shell, local_file_browser, debug_console, test_runner) are available during forge run --dev but are automatically filtered out in production builds by the ToolFilterStage.

Tool Interface

All tools implement the tools.Tool interface:

type Tool interface {
    Name() string
    Description() string
    Category() Category
    InputSchema() json.RawMessage
    Execute(ctx context.Context, args json.RawMessage) (string, error)
}

Writing a Custom Tool

Custom tools are discovered from the project directory. Create a Python or TypeScript file with a docstring schema:

"""
Tool: my_custom_tool
Description: Does something useful.

Input:
  query (str): The search query.
  limit (int): Maximum results.

Output:
  results (list): The search results.
"""

import json
import sys

def execute(args: dict) -> str:
    query = args.get("query", "")
    return json.dumps({"results": [f"Result for: {query}"]})

if __name__ == "__main__":
    input_data = json.loads(sys.stdin.read())
    print(execute(input_data))

Custom tools can also be added by placing scripts in a tools/ directory in your project. TypeScript tools run via npx --no-install ts-node to prevent automatic package downloads.

Custom Tool Entrypoint Validation

Custom tool entrypoints are validated at registration time:

  • Empty or absolute paths are rejected
  • Paths containing .. after filepath.Clean are rejected
  • Symlinks are resolved and the target must remain within the project directory
  • The entrypoint must be a regular file (not a directory or device)

Tool Commands

# List all registered tools
forge tool list

# Show details for a specific tool
forge tool describe web_search

Build Pipeline

The ToolFilterStage runs during forge build:

  1. Annotates each tool with its category (builtin, adapter, dev, custom)
  2. Sets tool_interface_version to "1.0" on the AgentSpec
  3. In production mode (--prod), removes all dev-category tools
  4. Counts tools per category for the build manifest

On this page