Phase 3.2: Built-in Tools Module
Phase 3.2: Built-in Tools Module
Status: Complete Last Updated: 2025-11-26 Related: Agent Framework Roadmap
Executive Summary
Phase 3.2 adds a set of production-ready built-in tools to llm4s, enabling:
- Web search capabilities without external setup
- File system operations for agent workflows
- HTTP request tools for API integration
- Shell command execution (sandboxed)
- Date/time utilities
- Calculator/math operations
Motivation
Current Limitations
Users must implement common tools from scratch:
- Inconsistent quality across implementations
- Longer time-to-production
- Repeated boilerplate for standard operations
Benefits of Built-in Tools
- Faster Development: Common tools out of the box
- Best Practices: Proper error handling, safety limits
- Consistency: Well-tested, documented tools
- Security: Sandboxed operations where appropriate
Architecture
Design Principles
- Opt-in: All tools are imported explicitly, not auto-registered
- Configurable: Tools accept configuration for limits, timeouts, etc.
- Safe by Default: Filesystem and shell tools have safety limits
- No External Dependencies: Core tools work without API keys
- Extensible: Easy to customize or extend built-in tools
Tool Categories
1. Core Utilities (No API Keys Required)
- DateTimeTool: Current date/time, timezone conversion, formatting
- CalculatorTool: Math operations (add, subtract, multiply, divide, sqrt, power, percentage)
- JSONTool: Parse, format, query, validate JSON data
- UUIDTool: Generate UUIDs (standard or compact format)
2. File System Tools
- ReadFileTool: Read file contents (with size limits)
- WriteFileTool: Write/append to files (with path restrictions)
- ListDirectoryTool: List directory contents
- FileInfoTool: Get file metadata (size, modified date, etc.)
3. HTTP Tools
- HTTPTool: General HTTP client (GET, POST, PUT, DELETE)
- Domain allowlist/blocklist
- Method restrictions
- Timeout configuration
4. Shell Tools (Sandboxed)
- ShellTool: Execute shell commands with restrictions
- Configurable allowed commands (allowlist)
- Timeout limits
- Output size limits
- Working directory configuration
5. Search Tools (No API Keys Required)
- WebSearchTool: Web search via DuckDuckGo Instant Answer API
Implementation
Package Structure
modules/core/src/main/scala/org/llm4s/toolapi/builtin/
├── package.scala # Package object with tool bundles
├── BuiltinTools.scala # All tools registry with factory methods
├── core/
│ ├── package.scala # Core utilities package
│ ├── DateTimeTool.scala
│ ├── CalculatorTool.scala
│ ├── JSONTool.scala
│ └── UUIDTool.scala
├── filesystem/
│ ├── package.scala # FileConfig, WriteConfig
│ ├── ReadFileTool.scala
│ ├── WriteFileTool.scala
│ ├── ListDirectoryTool.scala
│ └── FileInfoTool.scala
├── http/
│ ├── HTTPTool.scala
│ └── HttpConfig.scala
├── shell/
│ ├── ShellTool.scala
│ └── ShellConfig.scala
└── search/
└── WebSearchTool.scala
Tool Bundles
The BuiltinTools object provides pre-configured bundles for common use cases:
import org.llm4s.toolapi.builtin.BuiltinTools
// Core utilities only (always safe)
val coreTools = BuiltinTools.core
// Core + safe network tools (web search, HTTP)
val safeTools = BuiltinTools.safe()
// Safe + read-only file access
val fileTools = BuiltinTools.withFiles()
// All tools for development (use with caution in production)
val devTools = BuiltinTools.development()
// Custom configuration
val customTools = BuiltinTools.custom(
includeSearch = true,
httpConfig = Some(HttpConfig.readOnly()),
fileConfig = Some(FileConfig()),
writeConfig = Some(WriteConfig(allowedPaths = Seq("/tmp"))),
shellConfig = Some(ShellConfig.readOnly())
)
Usage Examples
Basic Usage with Core Tools
import org.llm4s.toolapi.ToolRegistry
import org.llm4s.toolapi.builtin._
// Get core tools (DateTime, Calculator, UUID, JSON)
val tools = new ToolRegistry(BuiltinTools.core)
// Execute directly
val dateRequest = ToolCallRequest(
functionName = "get_current_datetime",
arguments = ujson.Obj("timezone" -> "America/New_York")
)
tools.execute(dateRequest) match {
case Right(result) => println(result.render())
case Left(error) => println(s"Error: $error")
}
With File System Tools
import org.llm4s.toolapi.builtin.filesystem._
val fileConfig = FileConfig(
allowedPaths = Some(Seq("/tmp", System.getProperty("user.home"))),
blockedPaths = Seq("/etc", "/var", "/sys", "/proc")
)
val writeConfig = WriteConfig(
allowedPaths = Seq("/tmp"),
allowOverwrite = true
)
val tools = BuiltinTools.custom(
includeSearch = false,
fileConfig = Some(fileConfig),
writeConfig = Some(writeConfig)
)
With Shell Tools
import org.llm4s.toolapi.builtin.shell._
// Read-only shell (safe commands only)
val readOnlyConfig = ShellConfig.readOnly()
// Development shell (common dev tools)
val devConfig = ShellConfig.development()
// Custom allowlist
val customConfig = ShellConfig(
allowedCommands = Seq("ls", "cat", "pwd", "echo"),
timeoutMs = 5000,
maxOutputSize = 10000,
workingDirectory = Some("/home/user/project")
)
With HTTP Tools
import org.llm4s.toolapi.builtin.http._
// Read-only HTTP (GET, HEAD, OPTIONS only)
val readOnlyConfig = HttpConfig.readOnly()
// Restricted to specific domains
val restrictedConfig = HttpConfig.restricted(Seq("api.example.com"))
// Full HTTP with custom settings
val customConfig = HttpConfig(
allowedMethods = Seq("GET", "POST"),
allowedDomains = Some(Seq("api.example.com")),
blockedDomains = Seq("localhost", "127.0.0.1"),
timeoutMs = 30000,
maxResponseSize = 1024 * 1024
)
With Agent
import org.llm4s.agent.Agent
import org.llm4s.llmconnect.LLMConnect
import org.llm4s.toolapi.ToolRegistry
import org.llm4s.toolapi.builtin._
for {
client <- LLMConnect.fromEnv()
tools = new ToolRegistry(BuiltinTools.safe())
agent = new Agent(client)
state <- agent.run("What is 15% of 850?", tools)
} yield state
Safety Considerations
File System Safety
- Size Limits: Default 1MB read limit prevents memory exhaustion
- Path Restrictions: Configurable allowed/blocked paths
- Blocked by Default: System paths (/etc, /var, /sys, /proc, /dev) blocked
- Write Restrictions: Write tool requires explicit path allowlist
Shell Safety
- Command Allowlist: Only explicitly allowed commands can run
- Timeout: Commands timeout after configurable duration (default 30s)
- Output Limits: Command output is truncated to prevent memory issues
- Working Directory: Can be restricted to specific directory
- No Dangerous Commands by Default:
readOnly()only allows safe commands
HTTP Safety
- Domain Allowlist/Blocklist: Prevent SSRF attacks
- Localhost Blocked: By default, localhost and internal IPs are blocked
- Response Size Limits: Prevent memory exhaustion
- Timeout: Configurable request timeout
- Method Restrictions: Can limit to read-only methods (GET, HEAD, OPTIONS)
Testing
All tools have comprehensive tests:
- CoreToolsSpec: Tests for DateTime, Calculator, UUID, JSON tools
- FileSystemToolsSpec: Tests for file read/write/list/info with path restrictions
- HttpToolsSpec: Tests for HTTP requests with domain/method restrictions
- ShellToolsSpec: Tests for shell execution with allowlist, timeout, truncation
- BuiltinToolsSpec: Tests for tool bundle factory methods
Total: 73 tests across 5 test suites, all passing on Scala 2.13 and 3.x.
Samples
Two sample applications demonstrate built-in tools usage:
- BuiltinToolsExample: Demonstrates direct tool usage without an LLM
sbt "samples/runMain org.llm4s.samples.toolapi.BuiltinToolsExample" - BuiltinToolsAgentExample: Demonstrates tools with an LLM agent
export LLM_MODEL=openai/gpt-4o export OPENAI_API_KEY=sk-... sbt "samples/runMain org.llm4s.samples.agent.BuiltinToolsAgentExample"
Success Criteria (All Complete)
- All core utility tools implemented and tested
- File system tools with proper safety limits
- HTTP tools with SSRF protection
- Shell tool with command allowlist
- Web search tool (DuckDuckGo)
- Documentation for each tool
- Sample applications demonstrating usage
- All tests passing on Scala 2.13 and 3.x