onetool Configuration¶
Complete reference for onetool.yaml configuration.
Two-Tier System¶
┌──────────────────┐ ┌──────────────────┐
│ Global │ --> │ Project │
│ (~/.onetool/) │ │ (cwd/.onetool/) │
└──────────────────┘ └──────────────────┘
User preferences Project overrides
| Location | Purpose | Scope |
|---|---|---|
~/.onetool/config/onetool.yaml |
Global config | User |
.onetool/config/onetool.yaml |
Project config | Project |
Resolution order: CLI flags → ONETOOL_CONFIG env var → project → global
Windows paths: Replace ~/.onetool/ with %USERPROFILE%\.onetool\
First Run Bootstrap¶
On first onetool invocation, OneTool creates ~/.onetool/ with default configs:
$ onetool --help
Creating ~/.onetool/
✓ config/
✓ logs/
✓ stats/
✓ tools/
✓ config/onetool.yaml
✓ config/snippets.yaml
✓ config/servers.yaml
✓ config/secrets.yaml
✓ config/prompts.yaml
✓ config/security.yaml
✓ config/diagram.yaml
✓ config/bench.yaml
✓ config/bench-secrets.yaml
✓ config/diagram-templates/
Manage manually:
onetool init # Create ~/.onetool/ (if missing)
onetool init reset # Reset to defaults (prompts per file, offers backups)
onetool init validate # Check for errors
Configuration Inheritance¶
Project configs can inherit from global config:
| Value | Behaviour |
|---|---|
global (default) |
Merge global config first, project overrides |
none |
No inheritance, use project config as-is |
Merge semantics:
- Nested dicts are deep-merged (partial overrides work)
- Lists and scalars are replaced entirely
YAML Schema¶
version: 1 # Config schema version (required)
include: # External config files to merge
- prompts.yaml
- snippets.yaml
- servers.yaml
tools_dir: # Tool discovery patterns
- src/ot_tools/*.py
log_level: INFO # DEBUG, INFO, WARNING, ERROR
security: # Code validation settings
validate_code: true
enabled: true
projects: {} # Named project paths
servers: {} # External MCP servers
tools: {} # Pack-specific configuration
alias: {} # Function aliases
snippets: {} # Reusable code templates
prompts: {} # Inline prompts (overrides included)
Config Includes¶
Compose configuration from multiple files:
version: 1
include:
- config/prompts.yaml # Relative to OT_DIR (.onetool/)
- config/snippets.yaml
- local-snippets.yaml # Project-only additions
servers:
local_dev:
type: stdio
command: python
Include resolution (two-tier fallback):
- OT_DIR (project
.onetool/or wherever the config is) - Global (
~/.onetool/)
Merge behaviour:
- Files are merged left-to-right (later files override earlier)
- Inline content in the main file overrides everything
Pack Configuration Reference¶
| Pack | Field | Type | Default | Range | Description |
|---|---|---|---|---|---|
| brave | timeout | float | 60.0 | 1-300 | API request timeout (seconds) |
| code | base_url | string | https://openrouter.ai/api/v1 | - | Embedding API base URL |
| code | content_limit | int | 500 | 100-10K | Content preview character limit |
| code | content_limit_expanded | int | 2000 | 500-20K | Expanded content character limit |
| code | db_path | string | .chunkhound/chunks.db | - | Chunks database path |
| code | dimensions | int | 1536 | - | Embedding dimensions |
| code | limit | int | 10 | 1-100 | Max search results |
| code | model | string | text-embedding-3-small | - | Embedding model |
| code | provider | string | openai | - | Embedding provider |
| context7 | docs_limit | int | 10 | 1-20 | Max documentation results |
| context7 | timeout | float | 30.0 | 1-120 | API request timeout (seconds) |
| db | max_chars | int | 4000 | 100-100K | Query output truncation |
| ground | model | string | gemini-2.5-flash | - | Gemini model for grounding |
| package | timeout | float | 30.0 | 1-120 | Registry request timeout |
| ripgrep | relative_paths | bool | true | - | Use relative paths in output |
| ripgrep | timeout | float | 60.0 | 1-300 | Search timeout (seconds) |
| stats | chars_per_token | float | 4.0 | ≥1.0 | Characters per token estimate |
| stats | context_per_call | int | 30000 | ≥0 | Context tokens saved per call |
| stats | cost_per_million_input_tokens | float | 15.0 | ≥0 | Input token cost (USD) |
| stats | cost_per_million_output_tokens | float | 75.0 | ≥0 | Output token cost (USD) |
| stats | enabled | bool | true | - | Enable statistics collection |
| stats | flush_interval_seconds | int | 30 | 1-300 | Disk flush interval |
| stats | model | string | anthropic/claude-opus-4.5 | - | Model for cost estimation |
| stats | persist_dir | string | stats | - | Stats directory path |
| stats | persist_path | string | stats.jsonl | - | Stats file path |
| stats | time_overhead_per_call_ms | int | 4000 | ≥0 | Time overhead saved (ms) |
| transform | base_url | string | "" | - | OpenAI-compatible API base URL |
| transform | max_tokens | int | null | - | Max output tokens (null=no limit) |
| transform | model | string | "" | - | Model for transformations |
| transform | timeout | int | 30 | - | API timeout in seconds |
| web | max_length | int | 50000 | 1K-500K | Max content characters |
| web | timeout | float | 30.0 | 1-120 | Page fetch timeout (seconds) |
Example:
Projects Configuration¶
Named projects for path resolution and attributes:
projects:
myapp:
path: /path/to/myapp
attrs:
db_url: postgresql://localhost/myapp
api_key: ${MY_API_KEY}
demo:
path: .
attrs:
db_url: sqlite:///demo/db/northwind.db
Secrets Configuration¶
API keys stored separately in secrets.yaml (gitignored):
| Location | Purpose | Scope |
|---|---|---|
.onetool/config/secrets.yaml |
Project secrets | Project |
~/.onetool/config/secrets.yaml |
Global secrets | User |
Resolution: OT_SECRETS_FILE env var → project → global
# API keys for tools (values are literal, no ${VAR} expansion)
BRAVE_API_KEY: "your-brave-api-key"
OPENAI_API_KEY: "sk-..."
CONTEXT7_API_KEY: "your-context7-key"
GEMINI_API_KEY: "your-gemini-key"
DATABASE_URL: "postgresql://user:pass@localhost/db"
Accessing Secrets in Tools¶
from ot.config.secrets import get_secret
api_key = get_secret("BRAVE_API_KEY")
if not api_key:
return "Error: BRAVE_API_KEY not configured in secrets.yaml"
External MCP Servers¶
Proxy external MCP servers through OneTool. Supports both stdio (local process) and HTTP (remote server) transports.
Stdio Servers¶
Local MCP servers running as subprocesses:
servers:
github:
type: stdio
command: npx
args: ["-y", "@anthropic-ai/github-mcp-server@latest"]
timeout: 30
chrome-devtools:
type: stdio
command: npx
args: ["-y", "@anthropic-ai/chrome-devtools-mcp@latest"]
HTTP Servers¶
Remote MCP servers accessed via HTTP/HTTPS:
servers:
# HTTP server without authentication
local_dev:
type: http
url: https://localhost:3000/mcp
timeout: 30
# HTTP server with Bearer token authentication
github:
type: http
url: https://api.githubcopilot.com/mcp/
auth:
type: bearer
token: ${GITHUB_TOKEN} # Expands from secrets.yaml
headers:
Accept: "application/json, text/event-stream"
timeout: 120
# HTTP server with OAuth 2.1 + PKCE
context7:
type: http
url: https://mcp.context7.com/mcp
auth:
type: oauth
scopes: [tools:read, tools:write]
timeout: 60
Authentication Types:
- None (default): No authentication required
- bearer: Static token authentication (use
${VAR}for secrets) - oauth: OAuth 2.1 with PKCE flow (browser-based authorization)
Aliases¶
Short names for common tool functions:
Snippets¶
Reusable code templates with Jinja2 substitution:
snippets:
multi_search:
description: Search multiple queries
params:
queries: { required: true, description: "List of queries" }
body: |
results = []
for q in {{ queries }}:
results.append(brave.web_search(query=q))
"\n---\n".join(results)
External snippet files:
include:
- config/snippets.yaml # Falls back to global
- local-snippets.yaml # Project-specific additions
snippets:
custom:
body: "demo.foo()" # Inline overrides included
Statistics Configuration¶
Track runtime statistics:
stats:
enabled: true
persist_dir: stats
persist_path: stats.jsonl
flush_interval_seconds: 30
context_per_call: 30000
time_overhead_per_call_ms: 4000
model: anthropic/claude-opus-4.5
cost_per_million_input_tokens: 15.0
cost_per_million_output_tokens: 75.0
chars_per_token: 4.0
View with ot.stats():
ot.stats() # All-time
ot.stats(period="day") # Last 24 hours
ot.stats(period="week", tool="brave.search")
ot.stats(output="stats_report.html") # HTML report
Transform Configuration¶
Configure the llm.transform() tool for LLM-powered text transformations:
tools:
transform:
model: "gpt-4o-mini" # Model for transformations
base_url: "https://api.openai.com/v1" # OpenAI-compatible API endpoint
max_tokens: 4096 # Max output tokens
Requires OPENAI_API_KEY in secrets.yaml (or compatible provider key).
Message Configuration¶
Configure ot.notify() topic-to-file routing:
tools:
msg:
topics:
- pattern: "status:*" # Glob-style topic pattern
file: "~/.onetool/status.log" # Output file (supports ~ and ${VAR})
- pattern: "doc:*"
file: "./docs/notes.md"
Messages are appended to matching files. First pattern match wins.
Output Configuration¶
Control large output handling:
output:
max_inline_size: 50000 # Threshold in bytes (0 to disable)
result_store_dir: tmp # Directory for stored results
result_ttl: 3600 # Time-to-live in seconds
preview_lines: 10 # Lines in summary preview
When output exceeds max_inline_size, OneTool stores the result and returns a handle. Query with ot.result(handle="...").
Security Configuration¶
OneTool uses an allowlist-based security model: everything is blocked by default, you explicitly allow what's safe. Tool namespaces (ot.*, brave.*, etc.) are auto-allowed.
Security rules are defined in security.yaml (included by default):
security:
validate_code: true # Enable AST validation
enabled: true # Enable security checks
builtins:
allow:
- [str, int, float, list, dict, set, tuple] # Types
- [len, range, enumerate, zip, sorted] # Iteration
- [print, repr, format] # Output
imports:
allow: [json, re, math, datetime, collections, itertools]
warn: [yaml] # Allowed but logs warning
calls:
block: [pickle.*, yaml.load] # Blocked qualified calls
warn: [random.seed] # Warned qualified calls
dunders:
allow: [__format__, __sanitize__] # Allowed magic variables
sanitize:
enabled: true # Output sanitization (prompt injection protection)
Compact array format: Group related items for readability:
Security Introspection¶
Check what's allowed at runtime:
ot.security() # Summary of all rules
ot.security(check="json") # Check specific pattern
ot.security(check="pickle.load") # Check qualified call
Output Sanitization¶
The security.sanitize subsection protects against indirect prompt injection by sanitizing tool outputs:
- Trigger sanitization: Replace
__ot,mcp__onetoolpatterns - Tag sanitization: Remove
<external-content-*>patterns - GUID-tagged boundaries: Wrap content in unpredictable tags
Disable per-call with __sanitize__ = False prefix.
See Security Model for full documentation.
Environment Variables¶
| Variable | Description |
|---|---|
ONETOOL_CONFIG |
Config file path override |
OT_LOG_LEVEL |
Log level (DEBUG/INFO/WARNING/ERROR) |
OT_LOG_VERBOSE |
Enable verbose logging (true/false) |
OT_LOG_DIR |
Log directory path |
OT_SECRETS_FILE |
Secrets file path override |
OT_COMPACT_MAX_LENGTH |
Max value length in compact output |
Environment Variable Expansion¶
Config values support ${VAR} and ${VAR:-default} syntax:
Validation¶
Invalid values are rejected at load time:
ValueError: Invalid configuration in config/onetool.yaml:
tools.brave.timeout: Input should be greater than or equal to 1