A Go library for HTTP-based tool calling with Ollama integration and a simple test UI.
- HTTP Tool Client: Call HTTP endpoints as tools with JSON configuration
- Ollama Integration: Seamlessly integrate tools with Ollama's chat API
- System Prompts: Configure custom system prompts for consistent AI behavior
- Structured Logging: Configurable log levels and formats (JSON/text) using Go's slog
- Test UI: Simple web interface for testing tools and chat interactions
- Flexible Configuration: JSON-based tool definitions
# Quick setup - install deps and build
make quick-start
# Start development server
make dev
# View all available commands
make help# 1. Run the development setup script
./scripts/dev-setup.sh
# 2. Start the server
go run cmd/server/main.go
# 3. Open your browser
open http://localhost:8080# Build and run with Docker
make docker-build
make docker-run
# Or use docker-compose (includes Ollama)
docker-compose upCopy config.example.json to tooly.json and customize:
- Server settings: Port, host, timeouts
- Ollama integration: Model, URL, iterations, system prompt
- Tool configuration: File paths, timeouts
- Logging: Log levels, output format
package main
import (
"context"
"fmt"
"time"
"tooly/pkg/tooly"
)
func main() {
// Create client using simple constructor
client := tooly.NewClient(30 * time.Second)
// Or create client using options pattern for more flexibility
client := tooly.NewClientWithOptions(
tooly.WithTimeout(60 * time.Second),
tooly.WithVersion("1.0.0"),
)
// Load tools from configuration
err := client.LoadToolsFromFile("examples/tools.json", 30 * time.Second)
if err != nil {
panic(err)
}
// Call a tool
call := tooly.ToolCall{
Name: "get_weather",
Arguments: map[string]interface{}{
"q": "London,UK",
"appid": "your-api-key",
},
}
result, err := client.CallTool(context.Background(), call)
if err != nil {
panic(err)
}
fmt.Printf("Result: %+v\n", result)
}package main
import (
"context"
"fmt"
"time"
"tooly/pkg/ollama"
"tooly/pkg/tooly"
)
func main() {
// Setup tool client
toolClient := tooly.NewClientWithOptions(
tooly.WithTimeout(30 * time.Second),
tooly.WithVersion("1.0.0"),
)
toolClient.LoadToolsFromFile("examples/tools.json", 30 * time.Second)
// Setup Ollama client using options pattern (recommended)
ollamaClient := ollama.NewClientWithOptions(toolClient,
ollama.WithBaseURL("http://localhost:11434"),
ollama.WithTimeout(60 * time.Second),
ollama.WithMaxIterations(10),
ollama.WithAvailableModels([]string{"llama3.2", "qwen2.5:3b", "codellama:7b"}),
ollama.WithDefaultModel("llama3.2"),
ollama.WithSystemPrompt("You are a helpful assistant. Be concise and use tools when appropriate."),
)
// Or use the traditional constructor
ollamaClient = ollama.NewClient("http://localhost:11434", toolClient, 60 * time.Second, 10,
[]string{"llama3.2", "qwen2.5:3b"}, "llama3.2",
"You are a helpful assistant.")
// Chat with tool execution
messages := []ollama.Message{
{Role: "user", Content: "What's the weather in London?"},
}
result, err := ollamaClient.ChatWithToolExecution(
context.Background(),
"llama3.2",
messages,
)
if err != nil {
panic(err)
}
for _, msg := range result {
fmt.Printf("[%s]: %s\n", msg.Role, msg.Content)
}
}Tools are defined in JSON format with support for system parameters that are managed automatically:
{
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a location",
"url": "https://api.openweathermap.org/data/2.5/weather",
"method": "GET",
"headers": {
"User-Agent": "Tooly/1.0"
},
"parameters": {
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "City name"
},
"appid": {
"type": "string",
"description": "API key for OpenWeatherMap",
"default": "your-api-key-here",
"system": true
},
"units": {
"type": "string",
"description": "Temperature units",
"enum": ["standard", "metric", "imperial"],
"default": "metric"
}
},
"required": ["q"]
}
}
]
}name: Unique identifier for the tooldescription: Human-readable description sent to the LLMurl: HTTP endpoint URLmethod: HTTP method (GET, POST, PUT, PATCH, DELETE)headers: Optional HTTP headers to include in requestsparameters: JSON Schema defining expected parameterstimeout: Optional timeout duration (defaults to 30s)
Each parameter in the properties object supports:
type: Parameter data type (string,number,boolean,object,array)description: Human-readable description sent to the LLMdefault: Default value used when parameter is not providedsystem: Boolean - iftrue, parameter is managed by system config (see System Parameters)enum: Array of allowed values for the parameterrequired: Specify in the parentrequiredarray if parameter must be provided
System parameters are a powerful feature that allows you to manage sensitive configuration (like API keys) automatically while keeping them hidden from the LLM:
{
"parameters": {
"properties": {
"api_key": {
"type": "string",
"default": "sk-your-actual-api-key-here",
"system": true
},
"query": {
"type": "string",
"description": "Search query"
}
},
"required": ["query"]
}
}How System Parameters Work:
- Hidden from LLM: System parameters are completely removed from the tool schema sent to the LLM
- Automatic injection: The configured
defaultvalue is automatically used for all requests - LLM cannot override: Even if the LLM tries to provide a value, it will be ignored
- Security focused: Prevents LLM from generating placeholder API keys or sensitive values
- No required constraint: System parameters should not be listed in
requiredsince the LLM cannot provide them
Benefits:
- API Key Management: Store real API keys in configuration, LLM never sees or generates them
- Consistent Authentication: Guaranteed use of proper credentials for all requests
- Cleaner Tool Calls: LLM focuses only on user-relevant parameters
- Security: Eliminates risk of LLM generating invalid or placeholder credentials
Example System Parameter Log Output:
Applied system parameter value (always overrides LLM)
param_name=api_key system_value=sk-real-key-here llm_value=<nil>
The test server provides the following endpoints:
GET /- Test UI interfaceGET /api/tools- List available toolsPOST /api/tools/call- Execute a toolGET /api/models- List available modelsPOST /api/chat- Chat with Ollama using tools
curl -X POST http://localhost:8080/api/tools/call \
-H "Content-Type: application/json" \
-d '{
"name": "get_weather",
"arguments": {
"q": "London,UK",
"appid": "your-api-key"
}
}'curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.2",
"message": "What is the weather in London?"
}'Common development tasks:
make dev # Start development server
make build # Build binary
make test # Run tests
make fmt # Format code
make lint # Run linter
make check # Run all checks
make clean # Clean build artifactsBuild for different platforms:
make build-linux # Linux AMD64
make build-macos # macOS AMD64
make build-macos-arm # macOS ARM64
make build-windows # Windows AMD64
make build-all # All platformsDocker development:
make docker-build # Build Docker image
make docker-run # Run container
make docker-dev # Development containerscripts/dev-setup.sh- Set up development environmentscripts/release.sh- Create release builds
tooly/
├── pkg/
│ ├── config/ # Configuration management
│ ├── tooly/ # Core HTTP tool client
│ └── ollama/ # Ollama integration
├── cmd/
│ └── server/ # Test server
├── web/
│ └── static/ # Test UI
├── examples/ # Configuration examples
├── scripts/ # Development scripts
├── build/ # Build output
├── Makefile # Development tasks
├── Dockerfile # Container definition
└── docker-compose.yml # Multi-service setup
The application can be configured using a JSON configuration file. By default, it looks for:
tooly.jsonin the current directoryconfig.jsonin the current directoryconfig/tooly.json- File specified in
TOOLY_CONFIGenvironment variable
{
"server": {
"port": "8080",
"host": "localhost",
"read_timeout": "30s",
"write_timeout": "30s",
"static_dir": "web/static"
},
"ollama": {
"base_url": "http://localhost:11434",
"default_model": "llama3.2",
"available_models": [
"llama3.2",
"llama3.2:3b",
"qwen2.5:3b",
"codellama:7b",
"mistral:7b"
],
"timeout": "60s",
"max_iterations": 10,
"system_prompt": "You are a helpful assistant. Be concise and professional in your responses. Always use available tools when they can help answer questions."
},
"tools": {
"config_file": "examples/tools.json",
"default_timeout": "30s"
},
"logging": {
"level": "info",
"format": "text"
}
}Configure which models users can select in the UI:
- available_models: Array of model names that can be used
- default_model: Model selected by default (must be in available_models)
- Models are validated - requests with unlisted models will be rejected
- The UI automatically populates a dropdown with available models
Configure custom system prompts to provide consistent AI behavior and guardrails:
{
"ollama": {
"system_prompt": "You are a helpful assistant specialized in technical support. Be concise, accurate, and always suggest using available tools when appropriate."
}
}The system prompt is automatically prepended to all chat conversations. If a conversation already starts with a system message, the configured prompt is ignored to avoid duplication.
Control logging output with configurable levels and formats:
{
"logging": {
"level": "info", // debug, info, warn, error
"format": "text" // text, json
}
}Log Levels:
debug: Verbose output including HTTP requests, tool execution details, and internal operationsinfo: General application information including startup, configuration, and major operationswarn: Warning messages about potential issueserror: Error messages only (minimal output)
Log Formats:
text: Human-readable format for development and debuggingjson: Structured JSON format for production log aggregation and analysis
Examples:
Text format (info level):
2025/09/11 18:46:19 INFO Server configuration host=localhost port=8080
2025/09/11 18:46:19 INFO Ollama configuration base_url=http://localhost:11434
JSON format (debug level):
{"time":"2025-09-11T18:46:19.145667482-07:00","level":"INFO","msg":"Server configuration","host":"localhost","port":"8080"}
{"time":"2025-09-11T18:46:19.145676426-07:00","level":"DEBUG","msg":"HTTP request received","method":"POST","path":"/api/chat","remote_addr":"127.0.0.1:54321"}Server:
port: Server port (default: 8080)host: Server host (default: localhost)read_timeout: HTTP read timeoutwrite_timeout: HTTP write timeoutstatic_dir: Directory containing static files
Ollama:
base_url: Ollama API base URLdefault_model: Default model for chat requestsavailable_models: List of models that can be used via the APItimeout: Request timeout for Ollama callsmax_iterations: Maximum tool execution iterations per chatsystem_prompt: System prompt to include in all chat requests (optional)
Tools:
config_file: Path to tools configuration filedefault_timeout: Default timeout for tool HTTP requests
Logging:
level: Log level (debug,info,warn,error)format: Log output format (text,json)
- Go 1.19+
- Ollama running locally (for chat features)
- Network access for HTTP tools
GNU GPLv3