Tools¶
Keen Code provides a set of built-in tools that the LLM can use to interact with the codebase. Tools are registered in a central registry and exposed to the LLM with their schemas.
Tool Registry¶
// internal/tools/tool.go
type Tool interface {
Name() string
Description() string
InputSchema() map[string]any
Execute(ctx context.Context, input any) (any, error)
}
type Registry struct {
tools map[string]Tool
}
The registry manages all available tools and converts them to provider-specific tool formats (Anthropic, OpenAI, etc.).
Available Tools¶
| Tool | Purpose | Key Parameters |
|---|---|---|
read_file |
Read file contents | path, offset, limit |
write_file |
Create/overwrite files | path, content |
edit_file |
Targeted string replacement | path, oldString, newString |
glob |
Find files by pattern | pattern, path |
grep |
Search file contents | pattern, path, include |
bash |
Execute shell commands | command, isDangerous, summary |
web_fetch |
Fetch content from a URL | url |
call_mcp_tool |
Call a tool on an MCP server | server, tool, arguments |
read_file¶
Reads a UTF-8 text file with permission checks and validation.
type ReadFileTool struct {
guard *filesystem.Guard
permissionRequester PermissionRequester
}
Parameters:
- path (string, required): Absolute or relative path to the file
- offset (integer, optional): 1-based line number to start reading from (defaults to 1)
- limit (integer, optional): Maximum number of lines to return (defaults to 1000)
Validation:
- File must be valid UTF-8 text
- File must be under 25MB; offset and limit bound returned lines, not the initial file-size check
- Binary files are rejected
- Long lines are truncated to 1000 runes to keep tool results bounded
Returns:
{
"path": "/absolute/path/to/file",
"content": "1: file contents...",
"bytes_read": 1234,
"offset": 1,
"limit": 1000,
"total_lines": 10,
"truncated": false
}
Files under ~/.keen/bash/ can be read without an extra permission prompt. Use the returned stdout_file and stderr_file paths from bash rather than guessing artifact names.
write_file¶
Creates a new file or overwrites existing content.
type WriteFileTool struct {
guard *filesystem.Guard
diffEmitter DiffEmitter
permissionRequester PermissionRequester
}
Parameters:
- path (string, required): Target file path
- content (string, required): Content to write
Behavior:
- Creates parent directories if needed
- Overwrites existing files completely
- Emits diff for display via DiffEmitter
Returns:
{
"path": "/absolute/path/to/file",
"bytes_written": 1234,
"created": true
}
edit_file¶
Performs targeted string replacement in existing files.
type EditFileTool struct {
guard *filesystem.Guard
diffEmitter DiffEmitter
permissionRequester PermissionRequester
}
Parameters:
- path (string, required): Target file path
- oldString (string, required): Exact text to find and replace
- newString (string, required): Replacement text
- shouldReplaceAll (boolean, optional): Replace all occurrences (default: false)
Behavior:
- File must already exist
- oldString must match exactly (including whitespace)
- Uses go-udiff for unified diff output
- Emits diff via DiffEmitter
Returns:
{
"success": true,
"path": "/absolute/path/to/file",
"replacementCount": 1
}
glob¶
Finds files matching a glob pattern.
type GlobTool struct {
guard *filesystem.Guard
permissionRequester PermissionRequester
}
Parameters:
- pattern (string, required): Glob pattern (e.g., *.go, **/*.md)
- path (string, optional): Base directory (defaults to working directory)
Limits: - Maximum 1000 files returned
Returns:
{
"pattern": "*.go",
"base_path": "/project",
"files": ["/project/main.go", "/project/pkg/foo.go"],
"count": 2
}
grep¶
Searches file contents using regular expressions.
type GrepTool struct {
guard *filesystem.Guard
permissionRequester PermissionRequester
}
Parameters:
- pattern (string, required): Regex pattern (Go/RE2 syntax)
- path (string, optional): Base directory
- include (string, optional): Glob filter for file types
- output_mode (string, optional): "file" or "content" (default)
Limits: - Maximum 1000 matches
Returns (content mode):
{
"pattern": "func foo",
"base_path": "/project",
"output_mode": "content",
"matches": [
{"file": "/project/main.go", "line_number": 10, "line": "func foo() {"},
{"file": "/project/main.go", "line_number": 25, "line": "func foo() error {"}
],
"count": 2
}
bash¶
Executes shell commands with timeout and bounded inline output. Large stdout is saved to an artifact file so the model can inspect it later without flooding the prompt.
type BashTool struct {
guard *filesystem.Guard
permissionRequester PermissionRequester
}
Parameters:
- command (string, required): Bash command to execute
- isDangerous (boolean, optional): Always prompts for permission if true
- summary (string, optional): Brief description for the UI
Limits:
- Timeout: 300 seconds
- Inline output: 64KB max per stream before truncation
- Truncated output preview: head/tail excerpt with omitted-byte count
- Full truncated stdout is written to randomly named files under ~/.keen/bash/, such as keen-bash-*.stdout
- Stderr is returned only when the command exits non-zero; large captured stderr may be saved to stderr_file
When truncated is true, the agent should not rerun the same broad command just to see more output. It should inspect any returned stdout_file or stderr_file with read_file using targeted offset/limit values, or use grep for targeted follow-up.
Dangerous commands (always prompt):
- File removal (rm, rm -rf)
- Git operations that modify repo (git commit, git push, git reset, git rebase)
- Process termination (kill)
- System modifications
Returns:
{
"command": "go test ./...",
"exit_code": 0,
"stdout": "PASS\nok github.com/user/keen-code 0.015s",
"truncated": false,
"summary": "Run Go tests"
}
Returns (truncated output):
{
"command": "grep -R plan.md ~/.keen/sessions",
"exit_code": 0,
"stdout": "first preview...\n\n... (1048576 bytes omitted; full stdout saved to /Users/alice/.keen/bash/keen-bash-abc123.stdout) ...\n\nlast preview...",
"truncated": true,
"stdout_file": "/Users/alice/.keen/bash/keen-bash-abc123.stdout"
}
web_fetch¶
Fetches content from a URL and returns it as text.
type WebFetchTool struct{}
Parameters:
- url (string, required): The URL to fetch
Behavior: - HTML pages are automatically converted to Markdown for readability - Other content types (JSON, plain text, XML) are returned as-is - JavaScript-rendered pages (SPAs) return the pre-JS skeleton only
Limits: - Timeout: 30 seconds - Maximum response size: 128KB (truncated if exceeded)
Returns:
{
"url": "https://example.com",
"status_code": 200,
"content": "markdown or raw content..."
}
call_mcp_tool¶
Calls a tool on a connected MCP (Model Context Protocol) server.
type CallMCPTool struct {
manager keenmcp.Runtime
permissionRequester PermissionRequester
}
Parameters:
- server (string, required): The MCP server name as configured
- tool (string, required): The exact tool name to call on the server
- arguments (object, optional): Key-value arguments matching the tool's input schema
- checkCache (boolean, optional): Reserved for future caching; set to false or omit
Behavior:
- Requires user permission before execution
- Server name must match a configured MCP server
- Arguments must match the tool's input schema exactly
- Skill file at ~/.keen/skills/mcp:<server>/SKILL.md describes available tools
- Schema file at ~/.keen/skills/mcp:<server>/schemas/<tool>.json describes required arguments
Returns:
{
"server": "server-name",
"tool": "tool-name",
"content": "tool output text"
}
DiffEmitter¶
The DiffEmitter interface allows tools to emit diff output for display:
// internal/tools/diff.go
type DiffEmitter interface {
EmitDiff(lines []EditDiffLine)
}
type EditDiffLine struct {
Kind EditDiffLineKind
OldLineNum int
NewLineNum int
Content string
}
const (
DiffLineContext EditDiffLineKind = iota
DiffLineAdded
DiffLineRemoved
DiffLineHunk
)
Permission Integration¶
All tools integrate with the permission system through PermissionRequester:
// internal/tools/permission.go
type PermissionRequester interface {
RequestPermission(ctx context.Context, toolName, path, resolvedPath string, isDangerous bool) (bool, error)
}
Tools check permissions before execution and may request user approval for:
- Paths outside the working directory
- Dangerous operations (marked with isDangerous=true)
- First-time access to certain paths