These tools cover reading, listing, searching, and locating files. They are read-only — no file is modified. For writing and editing, see Editing.
All paths are relative to the active project root unless you supply an absolute path. Reads are subject to the project's security deny-list (e.g. SSH keys and credential files are blocked by default).
See also: Output Buffers — how large file reads are stored as
@file_idrefs rather than dumped into context.
Purpose: Read the contents of a file, optionally restricted to a line range.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | yes | — | File path relative to project root |
start_line |
integer | no | — | First line to return (1-indexed) |
end_line |
integer | no | — | Last line to return (1-indexed, inclusive) |
Example — read an entire file:
{
"path": "src/main.rs"
}Output:
{
"content": "fn main() {\n println!(\"Hello\");\n}\n",
"total_lines": 3
}Example — read a specific range:
{
"path": "src/main.rs",
"start_line": 10,
"end_line": 25
}When you supply both start_line and end_line, the tool returns exactly those lines with no overflow cap applied. When neither is supplied and the file exceeds 200 lines, only the first 200 lines are returned and an overflow field tells you how to retrieve the rest:
{
"content": "... first 200 lines ...",
"total_lines": 850,
"overflow": {
"shown": 200,
"total": 850,
"hint": "File has 850 lines. Use start_line/end_line to read specific ranges"
}
}Tips:
- Use
list_symbolsorfind_symbolto locate the line range of a function before callingread_file— this lets you fetch exactly what you need without reading the whole file. - For large files, prefer reading in chunks with explicit
start_line/end_lineover reading the whole file. - If you want to search for a pattern rather than read, use
search_patterninstead.
Purpose: List files and directories under a path. Pass recursive=true for a full tree.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | yes | — | Directory path relative to project root |
recursive |
boolean | no | false |
Descend into subdirectories |
detail_level |
string | no | compact | "full" to show all entries without the exploring-mode cap |
offset |
integer | no | 0 |
Skip this many entries (focused-mode pagination) |
limit |
integer | no | 50 |
Max entries per page in focused mode |
Example — shallow listing:
{
"path": "src"
}Output:
{
"entries": [
"/home/user/project/src/main.rs",
"/home/user/project/src/lib.rs",
"/home/user/project/src/tools/"
]
}Directories are suffixed with /. In exploring mode the output is capped at 200 entries; if the directory has more, an overflow field appears with guidance.
Example — full recursive tree:
{
"path": "src",
"recursive": true
}Hidden files and paths matched by .gitignore are excluded automatically.
Tips:
- Start with a shallow listing to understand the top-level structure, then drill into subdirectories of interest.
- Use
recursive=trueonly when you need the full tree. On large repositories a recursive walk can produce many entries; narrow it with a more specificpathif you hit the overflow cap. - To find files by name pattern use
find_fileinstead — it supports glob patterns and is faster for targeted searches.
Purpose: Search the codebase for a regex pattern. Returns matching lines with file path and line number.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
pattern |
string | yes | — | Regular expression to search for |
path |
string | no | project root | Directory to restrict the search to |
max_results |
integer | no | 50 |
Maximum number of matching lines to return |
Example:
{
"pattern": "fn\\s+validate_\\w+",
"path": "src",
"max_results": 20
}Output:
{
"matches": [
{
"file": "/home/user/project/src/util/path_security.rs",
"line": 14,
"content": "pub fn validate_read_path("
},
{
"file": "/home/user/project/src/util/path_security.rs",
"line": 38,
"content": "pub fn validate_write_path("
}
],
"total": 2
}The search walks the directory tree using the same .gitignore-aware walker as list_dir. Binary files that cannot be decoded as UTF-8 are silently skipped. The regex engine enforces size limits to prevent pathological patterns from hanging.
Tips:
- Use
pathto narrow the search when you already know which part of the codebase is relevant — this is significantly faster on large repos. - Increase
max_resultsif you expect many matches and need to see them all. - When you know a symbol name,
find_symbolis more precise than a regex search because it uses the LSP index. Usesearch_patternwhen you are looking for text patterns, string literals, comments, or constructs that the LSP does not model as symbols. - To find files by name (not content), use
find_file.
Purpose: Find files matching a glob pattern. Respects .gitignore.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
pattern |
string | yes | — | Glob pattern (e.g. **/*.rs, src/**/mod.rs) |
path |
string | no | project root | Directory to search within |
max_results |
integer | no | 100 |
Maximum number of file paths to return |
Example:
{
"pattern": "**/*.toml",
"path": "."
}Output:
{
"files": [
"/home/user/project/Cargo.toml",
"/home/user/project/.codescout/project.toml"
],
"total": 2
}Example — find all test files in a subdirectory:
{
"pattern": "**/test_*.py",
"path": "tests"
}The glob is matched against the path relative to the search directory, so **/*.rs will match files at any depth. The walker respects .gitignore, so build artifacts, vendored dependencies, and other ignored paths are excluded.
Tips:
- Prefer
find_fileoverlist_dirwhen you are looking for files by name — the glob match is more expressive than scanning a directory tree manually. - Use
search_patternwhen you need to find files by their contents rather than their names. - The
**wildcard matches across directory boundaries. Use it for language-wide searches like**/*.rsor to locate files with a specific name anywhere in the tree:**/Makefile.
Purpose: Create a new file or overwrite an existing file with given content.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | yes | — | File path relative to project root |
content |
string | yes | — | Full file content to write |
Example:
{
"tool": "create_file",
"arguments": {
"path": "src/utils/helpers.rs",
"content": "pub fn clamp(v: f64, min: f64, max: f64) -> f64 {\n v.max(min).min(max)\n}\n"
}
}Output: "ok"
Tips:
- Creates parent directories if they don't exist.
- Overwrites without warning — check that the path is correct before writing.
- For editing existing files, use
edit_fileinstead.
See Editing for more usage guidance.
Purpose: Find-and-replace editing within an existing file. Matches an exact string and replaces it — whitespace-sensitive.
Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | yes | — | File path relative to project root |
old_string |
string | yes | — | Exact text to find (must match including whitespace) |
new_string |
string | yes | — | Replacement text |
replace_all |
boolean | no | false |
Replace every occurrence instead of just the first |
insert |
string | no | — | "prepend" or "append" — add text at the start/end of the file |
Example — change an import:
{
"tool": "edit_file",
"arguments": {
"path": "src/main.rs",
"old_string": "use crate::utils::old_helper;",
"new_string": "use crate::utils::new_helper;"
}
}Output: "ok"
Tips:
old_stringmust match exactly — including indentation and line endings.- Use for imports, constants, config values, and small literal changes.
- For changes to a function or struct body, prefer
replace_symbol— it's robust to line number shifts.
See Editing for full parameter details and more examples.