filesystem-mcp
The server provides guarded filesystem access for AI assistants, scoped to explicitly allowed directories.
Navigation & discovery:
list_rootsshows allowed workspaces;listshows directory entries and trees with pagination;find_fileslocates files by glob patterns.Inspection:
statreturns metadata (size, MIME, permissions, timestamps, token estimate);search_textgreps file contents with context, regex, and pagination;diffcompares two files.Reading:
readsupports single or batch file reads, head/tail/line-range partial reads, and optional SHA-256 hashes.Writing:
createwrites/overwrites/appends files;editapplies sequential literal replacements;movemoves/renames/copies;deleteremoves files/dirs (recursive, with confirmation);patchapplies unified diffs;replace_textdoes bulk glob-based search-and-replace.Batch operations: Most tools accept arrays of paths/files (up to 100–1000 per call) for parallel execution.
Safety & guards: Every path is validated against allowed roots; sensitive patterns (
.env,*.pem,*id_rsa*) are denied by default;--read-onlydisables write tools; RE2 regex prevents ReDoS.Transports & clients: Runs over stdio by default, or Streamable HTTP with
--port; supports confirmations on capable clients.Extras: Resource subscriptions for file changes, cached tool results, and a
get-helpprompt.
Provides filesystem access for AI assistants within the Amp development environment, enabling reading, writing, searching, diffing, and patching files through structured MCP tools.
Integrates with Codeium's Windsurf editor to give AI coding assistants secure filesystem access for reading, writing, and managing project files through structured MCP tools.
Provides containerized filesystem access for AI assistants, allowing secure file operations within Docker containers through volume mounting and isolated execution environments.
Enables AI assistants to perform filesystem operations on Node.js projects, including reading, writing, and managing JavaScript/TypeScript files with structured output for reliable parsing.
Filesystem MCP Server
Overview
Filesystem-MCP is a Model Context Protocol server that lets AI assistants read and write files within explicitly allowed directories. Sensitive file patterns (.env, *.pem, *id_rsa*) are blocked by default. It exposes filesystem tools, resources, and prompts over stdio or Streamable HTTP transport.
Aspect | Details |
Status | Active (see npm badge for the current version) |
Language | TypeScript (strict) |
Runtime | Node.js >= 24 |
Package | npm |
License | MIT |
Related MCP server: Filesys
Features
Feature | Description |
Path guarding | Every path is validated against allowed roots; |
Filesystem tools | Navigate, inspect, read, and write across all major file operations |
Batch operations | Most tools accept |
Dual transport | stdio by default; |
File subscriptions | Resource subscriptions push change notifications when watched files update |
Regex safety | RE2 in all search tools: linear-time matching, so no pattern can ReDoS the server |
Built with
Layer | Technology |
Protocol | MCP SDK v2 ( |
Runtime | Node.js >= 24 · TypeScript 6 · ESM |
Transport | stdio (default) · Streamable HTTP ( |
Regex | RE2 ( |
Container | Docker alpine · multi-stage build · non-root user |
Table of Contents
Quick start
Requires Node.js ≥ 24.
Prerequisites
Requirement | Version / Notes |
Node.js | ≥ 24 |
npm | Bundled with Node.js |
Docker | Optional — for container use |
Install via npx
npx -y @j0hanz/filesystem-mcp /path/to/allowed/dirOr install globally:
npm install -g @j0hanz/filesystem-mcp
filesystem-mcp /path/to/allowed/dirInstall via Docker
docker run -i --rm \
-v /path/to/project:/workspace:ro \
ghcr.io/j0hanz/filesystem-mcp:latest \
--read-only /workspaceConfigure in VS Code
Add to .vscode/mcp.json:
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}Or install via CLI:
code --add-mcp '{"name":"filesystem","command":"npx","args":["-y","@j0hanz/filesystem-mcp@latest","/path/to/project"]}'Configure in Visual Studio
Add to .vs\mcp.json in your solution directory, or %USERPROFILE%\.mcp.json for a global configuration:
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}Configure in Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}Install in Cursor
Add to .cursor/mcp.json in your project root (project-scoped), or ~/.cursor/mcp.json for a global configuration:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}Docker configuration
VS Code (.vscode/mcp.json) and Visual Studio (.vs\mcp.json):
{
"servers": {
"filesystem": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"/path/to/project:/workspace",
"ghcr.io/j0hanz/filesystem-mcp:latest",
"/workspace"
]
}
}
}Claude Desktop (claude_desktop_config.json) and Cursor (mcp.json):
{
"mcpServers": {
"filesystem": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"/path/to/project:/workspace",
"ghcr.io/j0hanz/filesystem-mcp:latest",
"/workspace"
]
}
}
}For least privilege, use both controls::ro makes the container mount
read-only at the operating-system boundary, while the server's --read-only
flag removes mutating tools (create, edit, move, delete, patch,
replace_text) from tools/list.
Usage
Tools
All tools are scoped to the configured roots. Call list_roots first to discover what is allowed.
Navigate
Tool | Description |
| List allowed workspace roots. Call this first — all other tools scope to these. |
| List directory contents. Returns entries (dirs-first, alphabetical) and an ASCII tree. |
| Find files by glob pattern (e.g. |
Inspect
Tool | Description |
| Get file/directory metadata: size, modified time, permissions, MIME type, token estimate. |
| Search file contents for text (grep-like). Returns matching lines with context. |
| Compare two files and return a unified diff with added/removed line counts. |
Read
Tool | Description |
| Read a text file. Supports head/tail and line ranges. Accepts |
Write
Tool | Description |
| Create one or more files, creating parent directories as needed. An existing file prompts the user to confirm the overwrite; |
| Apply sequential literal string replacements to one or more files (max 5 per call). |
| Move, rename, or copy ( |
| Permanently delete one or more files or directories. This action is irreversible. |
| Bulk search-and-replace across files matching a glob pattern. |
| Apply a single-file unified diff and write the result. |
Resources
URI | Description |
| Server navigation guide — tools overview, constraints, and error recovery. |
| Read a workspace file. Subscribe to receive push notifications on change. |
| Ephemeral cached tool output. Expires after ~60 seconds, eviction, or server restart. |
Prompts
Prompt | Description |
| Return usage instructions, optionally filtered to a specific section. |
Project structure
filesystem-mcp/
├── __tests__/ Test suites
├── src/
│ ├── core/ Path guarding, filesystem abstraction, concurrency, observability
│ ├── tools/ Tool definitions and registration
│ ├── index.ts Process entrypoint and transport selection
│ ├── server.ts Server factory and registrar composition
│ ├── transport/ stdio and Streamable HTTP transport setup
│ ├── prompts.ts Prompt definitions and registration
│ └── resources.ts Resource definitions and registration
└── Dockerfile Multi-stage alpine build, non-root userRuntime composition flows from src/index.ts to src/transport.ts, then to
src/server.ts, the registrars, and finally src/core/. Each registrar owns
the narrow dependency contract it consumes.
Path | Purpose |
|
|
|
|
| Tool registration and execution framework |
| Batch helpers (runOverPaths, isTotalFailure) |
| Builds shared dependencies and invokes the three registrars |
| Owns stdio and Streamable HTTP setup around the server factory |
Configuration
The server starts with allowed directories from explicit startup configuration:
Positional directories passed to
filesystem-mcp.Environment variable
FS_ALLOWED_DIRS(separated by:on POSIX or;on Windows).Current working directory when
--allow-cwdis enabled.
Legacy MCP connections may additionally seed roots through the deprecated
roots/list flow. Modern 2026-07-28 connections do not automatically send
workspace roots. They can add access after startup by calling a tool with a
concrete path and approving the elicitation-backed grant. list_roots reports
the roots already configured or accepted; it cannot discover an unknown
workspace by itself.
Over HTTP, 2025-era clients are served statelessly: tools, resources and
prompts work. Confirmations (recursive delete, overwrite, access grants) need a
2026-07-28 client or stdio and answer with a tool error saying so; file
subscriptions are not advertised on that leg, and a resources/subscribe sent
anyway is refused with method-not-found.
Recommended global recipes
VS Code / Cursor / Claude Code (primary recipe)
Configure the project directory explicitly:
Add to your global or project-scoped configuration:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest", "/path/to/project"]
}
}
}Claude Desktop (fallback recipe via environment variable)
Claude Desktop and similar clients don't support the MCP Roots protocol. Use the FS_ALLOWED_DIRS environment variable to configure allowed folders.
Add to your claude_desktop_config.json:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@j0hanz/filesystem-mcp@latest"],
"env": {
"FS_ALLOWED_DIRS": "/path/to/project1:/path/to/project2"
}
}
}
}(On Windows, separate directories with a semicolon ; instead of a colon :).
Advanced / per-project positional arguments
You can also restrict access to specific directories by passing positional arguments directly:
# Start with explicit positional paths
filesystem-mcp /path/to/project1 /path/to/project2Configuration reference
CLI flags
Flag | Default | Purpose |
| — | One or more allowed root directories (positional) |
|
| Also allow the current working directory as a root |
|
| Walk up from CWD to find a project root; implies |
|
| Start even if configured allowed directories do not exist |
| — | Enable Streamable HTTP transport on the given port (env: |
| — | HTTP server bind address (env: |
| — | Require this API key on HTTP requests (env: |
|
| Disable write tools: |
| — | Block paths matching this pattern; repeatable |
| — | Exempt a pattern from the built-in sensitive denylist; repeatable (env: |
|
| Allow access to sensitive system paths (env: |
| — | Require all allowed roots to fall under this path (env: |
| — | Maximum file size for reads in bytes (env: |
|
| RFC 5424 log level, |
|
| Print the active configuration as JSON and exit |
--deny and --allow patterns support * (any run within a segment),
** (any run of segments), ?, [...] classes, and {a,b} alternation.
Dot-leading (hidden) names match like any other — secrets/** denies
secrets/.env, *id_rsa* denies .id_rsa.
Environment variables
All boolean variables accept true or 1 to enable and false, 0, or
unset to disable; any other value logs a warning and reads as disabled.
Flags take precedence when both are set.
Variable | Purpose |
| Colon-separated (POSIX) or semicolon-separated (Windows) list of directories to allow. |
| Path prefix all allowed roots must fall under (mirrors |
| Walk up from CWD to find a project root (mirrors |
| Start even if configured directories do not exist (mirrors |
| Allow access to sensitive system paths (mirrors |
| Comma-separated list of paths or patterns to block (mirrors |
| Comma-separated patterns exempted from the built-in sensitive denylist (mirrors |
| Maximum file size for reads in bytes (mirrors |
| RFC 5424 log level: |
| Start the Streamable HTTP transport on this port; unset = stdio (mirrors |
| HTTP server bind address (mirrors |
| API key required on HTTP requests (mirrors |
| Express |
| Comma-separated Host header values to accept (HTTP transport). |
| Comma-separated origin hostnames for CORS. |
| Bind a wildcard host with no Host validation (accepts the risk). |
| Resource identifier URL for RFC 9728 discovery. |
| Per-client-IP requests/minute (default 120 with API-key authentication, 6,000 for keyless loopback; range 1–100000). |
| Max concurrent file watchers (default 256, 1–4096). |
| Any value disables ANSI color output. |
| HMAC key sealing |
Examples
# Allow current working directory
filesystem-mcp --allow-cwd
# HTTP transport on port 3000
filesystem-mcp --port 3000Scripts
Mode | Command | Description |
Full check |
| Run build, type check, lint, format, knip, and tests |
Auto-fix + check |
| Auto-fix formatting/linting and run the full check |
Static only |
| Run static analysis without tests |
Tests only |
| Run tests; accepts native |
Security
Report vulnerabilities privately viaGitHub Security Advisories. Do not open public issues for security reports.
Topic | Detail |
Path traversal | Every path is resolved and validated against allowed roots before any operation |
Sensitive files |
|
Regex safety | RE2 cannot backtrack, so a hostile pattern cannot hang the server (ReDoS) |
Container | Runs as non-root |
Contributing
Fork the repository.
Create a feature branch:
git checkout -b feat/your-feature.Commit your changes with a clear message.
Run
npm run checkto confirm tests, types, lint, formatting, and knip all pass.Open a pull request.
License
Released under the MIT License. See LICENSE for details.
Available Tools
13 toolscreateCreate FilesADestructive
Create one or more files (max 100), creating parent directories as needed. Pass files: [{ path, content }] — there is no single-path form. An existing file prompts the user to confirm the overwrite, so the call returns without writing anything until that confirmation comes back; set overwrite: true on an entry to replace it without the prompt. Set append: true on an entry to add to the end of an existing file (created if missing) instead of overwriting.
| Name | Required | Description | Default |
|---|---|---|---|
| files | Yes | List of files to create (max 100); each entry requires path and content |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructiveHint annotation by explaining the confirmation flow: an existing file pauses the call and returns without writing until the user confirms. It also discloses parent-directory creation and append behavior, which are expected side effects an agent needs to anticipate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences front-load the core action and limits, then cover confirmation, overwrite, and append. Every sentence earns its place and no information is repeated unnecessarily.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a batch file-creation tool with a destructive hint and serious side effects, coverage is strong: max batch size, required fields, parent-directory creation, single-path absence, confirmation behavior, and overwrite/append variants are all stated. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents each field; baseline is 3. The description adds meaningful value by clarifying array-only input, the no-single-path restriction, and the overwrite confirmation behavior, which in turn explains the effect of overwrite: false.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific operation ('create one or more files') with explicit limits (max 100) and behavior (creates parent directories). The note that there is no single-path form sharpens the contract and distinguishes it from file-editing siblings like edit and replace_text.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear guidance on how to invoke the tool: pass an array of { path, content }, use overwrite: true to bypass the confirmation prompt, and append: true to add to an existing file. It does not explicitly name sibling alternatives or state when not to use create, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
deleteDelete FileADestructive
Permanently delete one or more files, directories, or symlinks (max 1000 per call). This action is irreversible. Pass paths: [...] — there is no single-path form. Non-empty directories require recursive=true and additionally prompt the user to confirm each one, so the call returns without deleting anything until that confirmation comes back; a client that cannot prompt gets an error naming the alternative. Workspace root directories cannot be deleted.
| Name | Required | Description | Default |
|---|---|---|---|
| paths | Yes | Paths to delete (max 1000); accepts files, directories, or symlinks | |
| recursive | No | Delete directory contents recursively (required for non-empty directories) | |
| ignoreIfNotExists | No | Silently succeed if a path does not exist instead of returning an error |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, but the description goes well beyond that by disclosing irreversibility, the confirmation prompt for non-empty directories, the fact that deletion is deferred until confirmation, the error behavior for non-prompting clients, and workspace root protection. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence carries essential information, and the most critical facts (permanent deletion, max 1000, irreversibility) are front-loaded. The paragraph is appropriately sized for a destructive multi-option operation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive tool with no output schema, the description covers the key behavioral consequences: what gets deleted, the max batch size, irreversibility, recursive and confirmation requirements, the client-prompting failure mode, and workspace root restrictions. An agent has enough information to use the tool safely and correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining that there is no single-path form, that non-empty directories require recursive=true and trigger a confirmation flow, and that workspace root directories cannot be deleted. This enriches the semantics of both paths and recursive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Permanently delete') and names the exact resources ('files, directories, or symlinks'), plus a clear cap of 1000 per call. It also distinguishes itself from a hypothetical single-path form, leaving no ambiguity about what this tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear operational guidance: paths must be passed as an array, there is no single-path form, non-empty directories require recursive=true, and workspace roots cannot be deleted. It does not explicitly name sibling alternatives like move or rename as the right choice for non-deletion operations, but deletion intent is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
diffDiffARead-only
Compare two files and return a unified diff with line counts. Pass the two paths as a and b. Use after an edit dry-run to compare against another file, or to inspect changes between two paths.
| Name | Required | Description | Default |
|---|---|---|---|
| a | Yes | First file to compare | |
| b | Yes | Second file to compare | |
| context | No | Number of context lines surrounding each change (default: 3) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool as read-only and closed-world. The description adds useful behavioral detail by specifying the output form ('unified diff with line counts'). It does not contradict the annotations, and it offers enough beyond them for a safe read operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences cover purpose, output format, parameter mapping, and usage context with no filler. The core action is front-loaded, and every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple diff tool with read-only annotations, the description covers what it does, what it returns, and when to use it. There is no output schema, so the mention of unified diff and line counts provides the necessary return-value context. Minor gaps like failure behavior or path restrictions are not critical here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameters are already well documented. The description adds the instruction 'Pass the two paths as a and b,' which is a mild reinforcement but not substantial new meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Compare two files and return a unified diff with line counts.' This clearly differentiates it from sibling read/list/stat tools, which inspect single paths rather than compare two files. The purpose is immediately recognizable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states explicit usage contexts: 'Use after an edit dry-run to compare against another file, or to inspect changes between two paths.' It does not name alternatives or exclusions, but the guidance is clear enough for an agent to decide when to invoke this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
editEdit FilesADestructive
Apply sequential literal string replacements to one or more files (max 5 files per call). Modes: single-file { path, edits } or per-file { files: [{ path, edits }] }. oldText must match file content exactly and only once; include 3-5 lines of surrounding context, or the edit fails listing the lines it matched. Set dryRun=true to preview diffs without writing. For glob-based bulk regex replacement across many files, use replace_text instead.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Single file path; mutually exclusive with files | |
| edits | No | Replacements applied to path; not allowed when using files | |
| files | No | Per-file entries (batch mode) | |
| dryRun | No | Preview diffs without writing to disk (default: false = apply edits) | |
| ignoreWhitespace | No | Ignore leading/trailing whitespace differences when matching oldText |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal destructive/write behavior, and the description reinforces this by stating edits are applied and that dryRun can preview without writing. It adds useful behavioral details beyond annotations: exact-once matching, failure with matched line numbers, and the 5-file cap. It does not contradict annotations, though it could mention atomicity or partial-apply behavior on multi-edit failures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: core behavior, modes, matching constraints, dry-run option, and the alternative tool. It is front-loaded with the most important operational fact and avoids unnecessary filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive 5-parameter tool with no output schema, the description covers the critical invocation aspects: files cap, mode shapes, matching failure behavior, dryRun, and the main alternative. It does not describe the success return format or behavior across multiple files when one edit fails, which would make it fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds value beyond the schema by explaining the oldText uniqueness requirement, recommending 3-5 lines of context, and clarifying the two invocation modes. It does not cover ignoreWhitespace, but the schema already documents that parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific action ('apply sequential literal string replacements'), identifies the resource ('one or more files'), and scopes it with a clear limit (max 5 files per call). It also distinguishes itself from the sibling replace_text, so an agent can tell them apart immediately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly routes alternative usage: 'For glob-based bulk regex replacement across many files, use replace_text instead.' It also explains when to use dryRun and lays out the single-file vs per-file modes, giving the agent clear selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_filesFind FilesARead-only
Find files matching a glob pattern. Returns matched paths with optional metadata. Pagination cursors reference a query-bound snapshot that expires after 60 seconds. For content search use search_text; for bulk regex replacements use replace_text with the same glob.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Base directory to search under (default: first allowed root) | |
| cursor | No | Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot taken on the first call; it expires after ~60s — re-request without a cursor if rejected. | |
| sortBy | No | Sort order: path = full path (default), name = basename only | path |
| pattern | Yes | Glob pattern to match file paths (e.g. **/*.ts, src/**/*.js) | |
| maxDepth | No | Max directory depth to scan; 0 = base directory only, omit for unlimited | |
| maxResults | No | Maximum number of matching files to return per page | |
| includeHidden | No | Include hidden items (starting with .) | |
| includeIgnored | No | Include ignored items (node_modules, .git, etc). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the operation as read-only and non-open-world, and the description adds useful behavioral context beyond that: pagination cursors reference a query-bound snapshot that expires after 60 seconds. This is valuable operational detail an agent would not otherwise know, though it does not describe output formatting or error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler: the core function is front-loaded, followed by a key pagination caveat and sibling routing. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's purpose, output shape at a high level, pagination behavior, and when to use alternatives. However, 'optional metadata' is vague and there is no output schema to clarify what the response contains, so a small completeness gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema carries most parameter documentation. The description adds meaningful context by explaining the pagination cursor's snapshot semantics and expiration, which directly affects how the cursor parameter should be used. It also reinforces that pattern is a glob.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise verb and resource: 'Find files matching a glob pattern' and explicitly says it returns matched paths with optional metadata. It also names sibling tools it is not (search_text for content, replace_text for replacements), making it easily distinguishable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: use find_files for path/glob-based file discovery, use search_text for content search, and use replace_text with the same glob for bulk replacements. This clearly tells an agent when to choose this tool over its most relevant siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
listListARead-only
List sorted directory entries and an ASCII tree. maxDepth=1 is top-level. maxEntries sets page size; continue with nextCursor. An incomplete first page also carries resourceUri for the whole list.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Directory to list (default: first allowed root) | |
| cursor | No | Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot taken on the first call; it expires after ~60s — re-request without a cursor if rejected. | |
| maxDepth | No | Max directory depth to traverse (default: 1 = top-level only; increase to recurse deeper) | |
| maxEntries | No | Page size (default: 1000). Continue with nextCursor; an incomplete first page also carries resourceUri for the whole list. | |
| includeHidden | No | Include hidden items (starting with .) | |
| includeIgnored | No | Include ignored items (node_modules, .git, etc). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the agent knows this is a safe read operation. The description adds valuable behavioral context: pagination via nextCursor, snapshot expiration (~60s), and the resourceUri on incomplete first pages. It also explains maxDepth semantics. This goes beyond the annotations and helps the agent understand the tool's behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the core purpose. The first sentence states what the tool does, and the following sentences add key behavioral details. It's efficient, though the pagination details could be slightly more structured. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only listing tool with 100% schema coverage and no output schema, the description covers the essential behavior: pagination, snapshot expiration, and depth semantics. It doesn't describe the exact output format of the ASCII tree, but the description says 'ASCII tree' and the tool is a list operation, so an agent can infer the return. The snapshot expiration and resourceUri details are valuable context that make it complete enough.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 6 parameters. The description adds some value by explaining pagination behavior (nextCursor, resourceUri) and maxDepth semantics, but most parameter meaning is already in the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'List sorted directory entries and an ASCII tree.' It also clarifies the tool's scope (directory listing) and distinguishes it from siblings like read, find_files, and search_text. The mention of maxDepth=1 as top-level and pagination behavior makes the purpose concrete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use this tool: to list directory entries and an ASCII tree. It doesn't explicitly name alternatives or exclusions, but the sibling list and the description's focus on directory listing imply when it's appropriate. It could be improved by explicitly saying 'use read for file contents, find_files for searching' but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_rootsWorkspace RootsARead-only
List the allowed workspace root directories. Call this first to discover what paths are accessible; all other tools are scoped to these roots. Allowed directories are configured via CLI arguments, the FS_ALLOWED_DIRS environment variable, or --allow-cwd.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=trueabb, so the description adds value by explaining the scoping relationship among tools and how allowed directories are configured. It does not describe return format, but the zero-parameter nature makes the behavior straightforward.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler. The core purpose is front-loaded, followed by usage guidance and configuration sources, all concisely.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only discovery tool, the description fully covers what the agent needs: what the tool lists, why to call it first, how access is configured, and that all sibling file tools are scoped to these roots.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are no parameters, so the schema needs no additional explanation. The description adds useful context about how directory access is configured, more than the schema alone would provide.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action ('List') and resource ('allowed workspace root directories'), and immediately establishes its unique role as the entry point for path discovery. The distinction from the sibling 'list' tool is clear through the explicit workspace-root scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs the agent to call this tool first and explains the context: all other tools are scoped to these roots. It does not name a specific alternative or when-not-to-use case, but for a discovery tool this guidance is clear and sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
moveMove or Copy FilesADestructive
Move, rename, or copy files and directories to explicit destination paths (max 100 operations per call). Pass moves: [{ source, destination }] — there is no single-pair form. Parent directories are created automatically. Set copy=true to copy instead of move (sources are kept). An existing destination prompts the user to confirm the overwrite, so the call returns without moving anything until that confirmation comes back; copy=true with overwrite=true skips the prompt, move has no such bypass. Self-moves are silently skipped.
| Name | Required | Description | Default |
|---|---|---|---|
| copy | No | Copy instead of move; sources are left in place | |
| moves | Yes | Operations to perform (max 100) | |
| overwrite | No | Copy mode only: overwrite existing destinations without confirmation |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate destructiveHint=true and readOnlyHint=false, so the description goes beyond by disclosing critical behaviors: overwrite confirmation prompts, the lack of a bypass for move operations, self-move skipping, and the effect of copy=true with overwrite=true. This adds significant context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with the core purpose, then efficiently covers key behaviors in a single paragraph. There is no fluff; every sentence contributes essential information, and the structure flows logically from purpose to usage details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (array of operations, max 100, overwrite logic, copy mode), the description covers all necessary aspects for correct invocation: format, limits, parent directory handling, confirmation behavior, and edge cases like self-moves. No output schema exists, so return values are not required. The description is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with each parameter described. The description adds value by explaining the interaction between copy and overwrite, the array-only format, and the absence of a single-pair form, which the schema does not convey. This goes beyond mere repetition and clarifies parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool moves, renames, or copies files and directories to explicit paths. It distinguishes from siblings by focusing on move/copy operations, while siblings like create, delete, and edit have different purposes. The mention of 'max 100 operations' and the array format adds specificity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides detailed usage instructions, such as the mandatory array format, parent directory auto-creation, and copy vs. move behavior. However, it does not explicitly state when to choose this tool over alternatives (e.g., delete vs. move, create vs. copy), leaving the agent to infer the appropriate context from the tool name and purpose.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
patchPatchADestructive
Apply a single-file unified diff to one file and write the result. Pass { path, diff }. Use after inspecting a diff tool dry-run: pass the diff blob directly instead of re-expressing it as line edits. Rejects multi-file diffs and diffs whose hunk context does not match the file. Set dryRun=true to preview the result without writing.
| Name | Required | Description | Default |
|---|---|---|---|
| diff | Yes | Single-file unified diff to apply (as produced by the diff tool or edit dry-run) | |
| path | Yes | File to apply the diff to | |
| dryRun | No | Preview the result without writing (default: false) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true, and the description reinforces that it writes the result. It discloses rejection behaviors (multi-file, context mismatch) and the dryRun preview option, which are not covered by annotations. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler. The first sentence front-loads the core action and inputs, the second gives usage guidance, and the third lists constraints and the dryRun option. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive file-patch tool, the description covers what it does, when to use it, its limitations, and the dryRun option. With annotations providing the destructive hint and no output schema, nothing essential is missing for an agent to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and describes each parameter. The description adds context to the diff parameter by clarifying it should come from a diff tool dry-run and that it rejects multi-file diffs and context mismatches, which goes beyond the schema's basic description. It also briefly mentions dryRun behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (apply), a precise resource (a single-file unified diff to one file), and the result (write the result). It clearly differentiates from siblings like edit or replace_text by specifying the diff input and the rejection of multi-file diffs, making it distinct.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to use after a diff tool dry-run and to pass the diff blob directly rather than re-expressing it as line edits, which contrasts with edit/replace_text. It also states rejection conditions (multi-file diffs, mismatched context), effectively saying when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
readRead FileARead-only
Read one or more text files and return content. Partial reads: head (first N lines), tail (last N lines), startLine/endLine (line range). Batch mode: pass paths[] instead of path; line params are shared across all files. head, tail, and startLine/endLine are mutually exclusive — use exactly one.
| Name | Required | Description | Default |
|---|---|---|---|
| head | No | Return first N lines | |
| path | No | Single file path; mutually exclusive with paths | |
| tail | No | Return last N lines | |
| paths | No | Array of file paths for batch mode (max 1000); mutually exclusive with path | |
| endLine | No | End line (1-indexed) | |
| startLine | No | Start line (1-indexed) | |
| includeHash | No | Include SHA-256 hash of the returned content in the response |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the agent knows this is a safe read operation. The description adds key behavioral constraints: mutual exclusivity of line parameters, shared line params across batch files, and a limit of 1000 paths. It also implies content is returned but does not detail format, which is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact, front-loads the core function, and clearly bundles related partial-read modes. It uses bullet-like lists and short sentences, with no fluff. Each clause serves a purpose, making it easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (7 params, partial reads, batch mode) and no output schema, the description covers the essential operation modes and constraints. It explains when to use batch, mutual exclusivity, and limits like max 1000 paths (though max limits are in schema, not description). It doesn't detail return format, but that is a minor gap given the read nature.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all parameters are documented, but the description adds crucial semantics about mutual exclusivity and batch behavior not fully captured in the schema. It clarifies how head, tail, and startLine/endLine are alternatives and how paths[] works with line params, going beyond the schema's static descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reads one or more text files and returns content. It explicitly covers partial reads (head, tail, startLine/endLine) and batch mode via paths[]. This distinguishes it from siblings like find_files or search_text, which are for locating/searching rather than reading content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states that head, tail, and startLine/endLine are mutually exclusive and instructs to use exactly one. It also clearly explains batch mode when to pass paths[] instead of path. This provides explicit when-to-use guidance and exclusion of alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
replace_textSearch and ReplaceADestructive
Bulk search-and-replace across files matching a glob pattern. Replaces ALL occurrences per file (unlike edit, which replaces only the first match). Set returnDiff=true to preview changes as a unified diff before or after writing. Literal matching by default; set isRegex=true to enable RE2 regex with capture groups ($1, $2).
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | File to rewrite, or directory to rewrite under. Omitting it targets the ENTIRE first allowed root — scope it deliberately, and pair a wide scope with dryRun=true first | |
| dryRun | No | Preview replacements without writing to disk (default: false = apply changes) | |
| isRegex | No | Treat searchPattern as a RE2 regex (default: literal text match) | |
| pattern | No | Glob to restrict replacements to specific file types (e.g. **/*.ts); default: all text files | |
| maxDepth | No | Max directory depth to scan; 0 = base directory only, omit for unlimited | |
| maxFiles | No | Maximum number of files to process | |
| wholeWord | No | Match whole words only (word boundary anchoring) | |
| maxResults | No | Maximum total match count across all files before stopping | |
| returnDiff | No | Include a unified diff of all changes in the response | |
| replacement | Yes | Replacement text. Use capture group references ($1, $2, etc.) when isRegex=true. Use an empty string to delete all matches. | |
| caseSensitive | No | Enable case-sensitive matching (default: case-insensitive) | |
| includeHidden | No | Include hidden items (starting with .) | |
| searchPattern | Yes | Exact literal text or RE2 regex pattern to search for. When isRegex=true, uses RE2 syntax (no lookahead, lookbehind, or backreferences are supported). Cannot be empty or whitespace-only. | |
| includeIgnored | No | Include ignored items (node_modules, .git, etc). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and readOnlyHint=false. The description adds key behaviors: 'Replaces ALL occurrences per file', literal-by-default matching, and RE2 regex support. However, the wording 'Set returnDiff=true to preview changes... before or after writing' is slightly ambiguous because `returnDiff` alone does not prevent writing; `dryRun` is the true preview-without-write parameter, so this could mislead an agent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tightly focused sentences, front-loaded with the tool's core purpose and key distinction from `edit`. Every sentence earns its place; there is no filler or redundant restating of the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the most critical context for a destructive bulk operation: it replaces all occurrences, can be scoped by glob, and can return a diff. Given a rich 100%-coverage schema and the destructiveHint annotation, it is nearly complete. The only gap is the ambiguous `returnDiff`/`dryRun` preview guidance.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds value by explaining `isRegex` and `replacement` semantics: 'Literal matching by default; set isRegex=true to enable RE2 regex with capture groups ($1, $2).' This synthesizes parameter behavior beyond the schema's individual descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description states a specific verb and resource: 'Bulk search-and-replace across files matching a glob pattern.' It clearly distinguishes itself from a sibling: 'unlike edit, which replaces only the first match.' An agent can immediately tell this tool replaces all occurrences.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly contrasts with the `edit` tool ('unlike edit, which replaces only the first match'), telling the agent when this tool is the right choice. It also advises using `returnDiff=true` to preview changes, giving practical guidance on how to use it safely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_textSearch ContentARead-only
Search file contents by text or regex (grep-style). Returns matching lines with file path, 1-indexed line number and 0-indexed column offset. Set context=N to also return N lines either side of each match (grep -C). Scope to specific file types with pattern (e.g. **/*.ts). Set includeHidden=true to include dotfiles. Use find_files to search by filename instead.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | File to search, or directory to search under (default: the whole first allowed root). Naming a file searches that file alone: pattern is ignored and hidden/ignored filtering does not apply. | |
| cursor | No | Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot taken on the first call; it expires after ~60s — re-request without a cursor if rejected. | |
| context | No | Lines of context to return either side of each match, like grep -C (default: 0, max: 10) | |
| isRegex | No | Treat searchPattern as a regex (default: literal text match) | |
| pattern | No | Glob to restrict search to specific file types (e.g. **/*.ts); default: all text files | |
| maxDepth | No | Max directory depth to scan; 0 = base directory only, omit for unlimited | |
| maxResults | No | Maximum number of matching lines to return per page | |
| caseSensitive | No | Enable case-sensitive matching (default: case-insensitive) | |
| includeHidden | No | Include hidden items (starting with .) | |
| searchPattern | Yes | Exact literal text or RE2 regex pattern to search for in file contents. When isRegex=true, uses RE2 syntax (no lookahead, lookbehind, or backreferences). Cannot be empty or whitespace-only. | |
| includeIgnored | No | Include ignored items (node_modules, .git, etc). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true, and the description adds meaningful behavior beyond that: it specifies return shape ('matching lines with file path, 1-indexed line number and 0-indexed column offset') and context behavior. It does not mention pagination or snapshot expiration, but the cursor parameter schema covers those details, so the description is reasonably transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and output format, then gives targeted usage examples. Each sentence serves a purpose, though a few points about context and hidden files slightly echo the schema. It remains appropriately sized for an 11-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no output schemaanding many parameters, the description covers the key invocation details: what is searched, what the output looks like, how to get context, how to restrict by file type, and which sibling to use instead. Remaining details like pagination and matching rules live in the schema, which is fully described.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 11 parameters. The description repeats a few useful parameter semantics, such as context=N returning N lines and pattern scoping with '**/*.ts', but it does not add meaning beyond what the schema already provides. A baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Search file contents by text or regex (grep-style).' It clearly distinguishes the tool from find_files by stating 'Use find_files to search by filename instead.' This makes the tool's scope unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly names the closest sibling alternative and the condition that routes to it: 'Use find_files to search by filename instead.' It also gives concrete usage direction for context, file-type scoping, and hidden-file inclusion, making when-to-use unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statGet File InfoARead-only
Get metadata for one or more files or directories: size, type, permissions, MIME type, timestamps, and tokenEstimate. Use tokenEstimate to pre-screen read cost before calling read. Single path: pass path. Batch mode: pass paths[].
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Single file path; mutually exclusive with paths | |
| paths | No | Array of file paths for batch mode (max 1000); mutually exclusive with path |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the agent knows this is a safe read operation. The description adds value by disclosing that the tool returns tokenEstimate, which is a behavioral detail beyond the schema, and by explaining that it can operate in batch mode on up to 1000 paths. It doesn't describe error behavior or permission requirements, but for a read-only metadata tool with readOnlyHint=true, the description covers the key behavioral aspects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with zero waste. The core purpose and return fields are front-loaded, the usage guidance is concise, and the two invocation modes are stated in a compact, scannable format. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only metadata tool with 100% schema coverage and readOnlyHint=true, the description is nearly complete. It covers what the tool returns, when to use it, and how to invoke it in both modes. The only minor gap is that it doesn't describe the output structure or error behavior, but since there's no output schema and the tool is simple, this is a small omission rather than a critical one.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both parameters (path and paths) including their mutual exclusivity and constraints. The description adds the semantic context that path is for single-file mode and paths is for batch mode, which is helpful but largely mirrors what the schema already states. Baseline 3 is appropriate since the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Get metadata') and resource ('one or more files or directories'), and enumerates the exact metadata fields returned: size, type, permissions, MIME type, timestamps, and tokenEstimate. This clearly distinguishes it from sibling tools like read, list, and find_files, which have different purposes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when to use this tool: 'Use tokenEstimate to pre-screen read cost before calling read.' This is a clear usage directive that positions stat as a precursor to read. It also explains the two invocation modes (single path vs batch paths), which is practical guidance for choosing how to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
13 tool updates
v2.4.1- Changed
create1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
delete1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
diff1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
edit3 fields changed- added
Input schema / $defs / EditSpec / properties / newText / examplesAdded value: +[ + "const x = 2;", + "function newName(", + "" +] - added
Input schema / $defs / EditSpec / properties / oldText / examplesAdded value: +[ + "const x = 1;", + "function oldName(" +] - added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
find_files2 fields changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / pattern / examplesAdded value: +[ + "**/*.ts", + "src/**/*.js", + "*.{ts,tsx}" +]
- Changed
list1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
list_roots1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
move1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
patch1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
read1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
replace_text4 fields changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / pattern / examplesAdded value: +[ + "**/*.ts", + "src/**/*.js", + "*.{ts,tsx}" +] - added
Input schema / properties / replacement / examplesAdded value: +[ + "$1_renamed", + "", + "TODO: fix" +] - added
Input schema / properties / searchPattern / examplesAdded value: +[ + "TODO", + "function\\s+(\\w+)", + "import.*from" +]
- Changed
search_text3 fields changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / pattern / examplesAdded value: +[ + "**/*.ts", + "src/**/*.js", + "*.{ts,tsx}" +] - added
Input schema / properties / searchPattern / examplesAdded value: +[ + "TODO", + "function\\s+(\\w+)", + "import.*from" +]
- Changed
stat1 field changed- added
Input schema / $schemaAdded value: +"https://json-schema.org/draft/2020-12/schema"
3 tool updates
v2.3.0- Changed
create2 fields changed- added
Input schema / properties / files / items / properties / appendAdded value: +{ + "description": "Append the content to the end of the file instead of overwriting; creates the file if it does not exist", + "type": "boolean" +} - added
Input schema / properties / files / items / properties / overwriteAdded value: +{ + "description": "Replace an existing file without asking the user; without it an existing file prompts for confirmation", + "type": "boolean" +}
- Changed
edit1 field changed- changed
Input schema / $defs / EditSpec / properties / oldText / descriptionPrevious value: -"Exact literal text to locate in the file. Must include 3-5 lines of context to ensure uniqueness and avoid matching the wrong block."New value: +"Exact literal text to locate in the file; it must match exactly once. Include 3-5 lines of context so it does — an oldText found in several places fails with their line numbers."
- Changed
search_text1 field changed- added
Input schema / properties / contextAdded value: +{ + "default": 0, + "description": "Lines of context to return either side of each match, like grep -C (default: 0, max: 10)", + "maximum": 10, + "minimum": 0, + "type": "integer" +}
5 tool updates
v2.1.5- Changed
delete1 field changed- changed
Output schema / (root)Previous value: -{ - "additionalProperties": false, - "properties": { - "results": { - "description": "Per-path results ordered to match the input paths", - "items": { - "additionalProperties": false, - "properties": { - "error": { - "additionalProperties": false, - "description": "Error details; present on failure", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "path": { - "type": "string" - }, - "suggestion": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - }, - "path": { - "description": "Requested path", - "type": "string" - }, - "value": { - "additionalProperties": false, - "description": "Delete outcome; present on success", - "properties": { - "deleted": { - "description": "True when the path was removed; false when the user chose Skip", - "type": "boolean" - } - }, - "required": [ - "deleted" - ], - "type": "object" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "type": "array" - }, - "summary": { - "additionalProperties": false, - "properties": { - "failed": { - "minimum": 0, - "type": "integer" - }, - "succeeded": { - "minimum": 0, - "type": "integer" - }, - "total": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "total", - "succeeded", - "failed" - ], - "type": "object" - } - }, - "required": [ - "results", - "summary" - ], - "type": "object" -}New value: +null
- Changed
edit1 field changed- changed
Output schema / (root)Previous value: -{ - "additionalProperties": false, - "properties": { - "results": { - "description": "Per-path edit results ordered to match the input paths", - "items": { - "additionalProperties": false, - "properties": { - "error": { - "additionalProperties": false, - "description": "Error details; present on failure", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "path": { - "type": "string" - }, - "suggestion": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - }, - "path": { - "description": "Requested file path", - "type": "string" - }, - "value": { - "additionalProperties": false, - "description": "Edit result; present on success", - "properties": { - "appliedEdits": { - "description": "Number of edits successfully applied", - "minimum": 0, - "type": "integer" - }, - "diff": { - "description": "Unified diff of all changes (present only in dryRun mode)", - "type": "string" - }, - "kind": { - "description": "Broad file kind: text, binary, image, audio, or pdf", - "enum": [ - "text", - "binary", - "image", - "audio", - "pdf" - ], - "type": "string" - }, - "lineCount": { - "description": "Number of lines in the file after edits", - "minimum": 0, - "type": "integer" - }, - "lineRange": { - "description": "Line range [firstLine, lastLine] covering all applied edits", - "prefixItems": [ - { - "exclusiveMinimum": 0, - "type": "integer" - }, - { - "exclusiveMinimum": 0, - "type": "integer" - } - ], - "type": "array" - }, - "linesAdded": { - "description": "Net lines added by all applied edits", - "minimum": 0, - "type": "integer" - }, - "linesRemoved": { - "description": "Net lines removed by all applied edits", - "minimum": 0, - "type": "integer" - }, - "mimeType": { - "description": "Detected MIME type of the file", - "type": "string" - }, - "modified": { - "description": "Last modification timestamp after edits (ISO 8601 UTC)", - "format": "date-time", - "type": "string" - }, - "path": { - "description": "Resolved absolute path of the edited file", - "type": "string" - }, - "resourceUri": { - "description": "Resource URI pointing to the updated file content; omitted when no edit matched (appliedEdits is 0) and the file was left untouched", - "type": "string" - }, - "size": { - "description": "File size in bytes after edits", - "minimum": 0, - "type": "integer" - }, - "unmatchedEdits": { - "description": "oldText values that did not match any content in the file", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "path", - "size", - "lineCount", - "mimeType", - "kind", - "modified", - "appliedEdits" - ], - "type": "object" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "type": "array" - }, - "summary": { - "additionalProperties": false, - "properties": { - "failed": { - "minimum": 0, - "type": "integer" - }, - "succeeded": { - "minimum": 0, - "type": "integer" - }, - "total": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "total", - "succeeded", - "failed" - ], - "type": "object" - } - }, - "required": [ - "results", - "summary" - ], - "type": "object" -}New value: +null
- Changed
list1 field changed- changed
Input schema / properties / maxEntries / descriptionPrevious value: -"Page size (default: 1000). Continue with nextCursor; resourceUri is only for hard-cap overflow."New value: +"Page size (default: 1000). Continue with nextCursor; an incomplete first page also carries resourceUri for the whole list."
- Changed
read1 field changed- changed
Output schema / (root)Previous value: -{ - "additionalProperties": false, - "properties": { - "results": { - "description": "Per-path results ordered to match the input paths", - "items": { - "additionalProperties": false, - "properties": { - "error": { - "additionalProperties": false, - "description": "Error details; present on failure", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "path": { - "type": "string" - }, - "suggestion": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - }, - "path": { - "description": "Requested file path", - "type": "string" - }, - "value": { - "additionalProperties": false, - "description": "Read result; present on success", - "properties": { - "contentHash": { - "description": "SHA-256 hex digest of the returned content (present when includeHash=true)", - "pattern": "^[0-9a-f]{64}$", - "type": "string" - }, - "continuation": { - "additionalProperties": false, - "description": "Next-read arguments; present when content was truncated due to size limits", - "properties": { - "args": { - "additionalProperties": false, - "description": "Ready-to-use arguments for the next call; pass verbatim", - "properties": { - "endLine": { - "exclusiveMinimum": 0, - "type": "integer" - }, - "path": { - "type": "string" - }, - "startLine": { - "exclusiveMinimum": 0, - "type": "integer" - } - }, - "required": [ - "path", - "startLine", - "endLine" - ], - "type": "object" - }, - "hint": { - "description": "One-sentence description of the data still remaining to be read", - "type": "string" - }, - "tool": { - "description": "Tool name to call for the next chunk", - "type": "string" - } - }, - "required": [ - "tool", - "args", - "hint" - ], - "type": "object" - }, - "endLine": { - "description": "End line", - "exclusiveMinimum": 0, - "type": "integer" - }, - "hasMoreLines": { - "description": "True when additional lines remain beyond what was returned", - "type": "boolean" - }, - "head": { - "description": "Head lines requested", - "exclusiveMinimum": 0, - "type": "integer" - }, - "kind": { - "description": "Broad file kind: text, binary, image, audio, or pdf", - "enum": [ - "text", - "binary", - "image", - "audio", - "pdf" - ], - "type": "string" - }, - "linesRead": { - "description": "Number of lines returned in this response", - "minimum": 0, - "type": "integer" - }, - "mimeType": { - "description": "Detected MIME type (e.g. text/typescript)", - "type": "string" - }, - "resourceUri": { - "description": "Resource URI for externalized content (present when file is stored in resource store)", - "type": "string" - }, - "startLine": { - "description": "Start line", - "exclusiveMinimum": 0, - "type": "integer" - }, - "tail": { - "description": "Tail lines requested", - "exclusiveMinimum": 0, - "type": "integer" - }, - "totalLines": { - "description": "Total line count in the full file", - "minimum": 0, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "type": "array" - }, - "summary": { - "additionalProperties": false, - "properties": { - "failed": { - "minimum": 0, - "type": "integer" - }, - "succeeded": { - "minimum": 0, - "type": "integer" - }, - "total": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "total", - "succeeded", - "failed" - ], - "type": "object" - } - }, - "required": [ - "results", - "summary" - ], - "type": "object" -}New value: +null
- Changed
replace_text1 field changed- changed
Output schema / (root)Previous value: -{ - "additionalProperties": false, - "properties": { - "diff": { - "description": "Unified diff of all changes (present when returnDiff=true or dryRun=true)", - "type": "string" - }, - "diffTruncated": { - "description": "True when the diff was cut due to the size limit", - "type": "boolean" - }, - "filesScanned": { - "description": "Total number of files examined", - "minimum": 0, - "type": "integer" - }, - "results": { - "description": "Per-file results: modified files, then any that could not be processed", - "items": { - "additionalProperties": false, - "properties": { - "error": { - "additionalProperties": false, - "description": "Error details; present on failure", - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "path": { - "type": "string" - }, - "suggestion": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - }, - "path": { - "description": "File path relative to the search root", - "type": "string" - }, - "value": { - "additionalProperties": false, - "description": "Replacement outcome; present on success", - "properties": { - "matches": { - "description": "Replacements applied in this file", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "matches" - ], - "type": "object" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "type": "array" - }, - "resultsTruncated": { - "description": "True when the results list holds fewer entries than summary.total: the changed-file or failed-file cap was hit. Trust summary over results.length.", - "type": "boolean" - }, - "stoppedReason": { - "description": "Why enumeration stopped early: maxResults = match cap reached, maxFiles = file cap reached, timeout = time limit hit or cancelled. Absent when every matching file was enumerated. Marks the sweep incomplete, not the writes; files already dispatched still complete.", - "enum": [ - "maxResults", - "maxFiles", - "timeout" - ], - "type": "string" - }, - "summary": { - "additionalProperties": false, - "properties": { - "failed": { - "minimum": 0, - "type": "integer" - }, - "succeeded": { - "minimum": 0, - "type": "integer" - }, - "total": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "total", - "succeeded", - "failed" - ], - "type": "object" - }, - "totalMatches": { - "description": "Total number of replacements made across all files", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "results", - "summary", - "totalMatches", - "filesScanned" - ], - "type": "object" -}New value: +null
28 tool updates
v2.0.0- Removed
apply_patch - Removed
calculate_hash - Added
create - Added
delete - Added
diff - Removed
diff_files - Changed
edit27 fields changed- added
Input schema / $defsAdded value: +{ + "EditSpec": { + "additionalProperties": false, + "properties": { + "newText": { + "description": "Replacement text. Use an empty string to delete the matched oldText.", + "type": "string" + }, + "oldText": { + "description": "Exact literal text to locate in the file. Must include 3-5 lines of context to ensure uniqueness and avoid matching the wrong block.", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "oldText", + "newText" + ], + "type": "object" + } +} - removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Input schema / oneOfAdded value: +[ + { + "required": [ + "path", + "edits" + ] + }, + { + "required": [ + "files" + ] + } +] - changed
Input schema / properties / dryRun / descriptionPrevious value: -"Preview edits without writing. Check `unmatchedEdits` in response."New value: +"Preview diffs without writing to disk (default: false = apply edits)" - changed
Input schema / properties / edits / descriptionPrevious value: -"List of replacements to apply sequentially. Each edit replaces the first occurrence of oldText."New value: +"Replacements applied to path; not allowed when using files" - added
Input schema / properties / edits / items / $refAdded value: +"#/$defs/EditSpec" - removed
Input schema / properties / edits / items / additionalPropertiesRemoved value: -false - removed
Input schema / properties / edits / items / propertiesRemoved value: -{ - "newText": { - "description": "Replacement string. Preserve surrounding indentation style.", - "type": "string" - }, - "oldText": { - "description": "Exact literal string to replace (character-for-character). Include 3–5 lines of context for unique targeting.", - "maxLength": 102400, - "minLength": 1, - "type": "string" - } -} - removed
Input schema / properties / edits / items / requiredRemoved value: -[ - "oldText", - "newText" -] - removed
Input schema / properties / edits / items / typeRemoved value: -"object" - added
Input schema / properties / edits / maxItemsAdded value: +100 - added
Input schema / properties / filesAdded value: +{ + "description": "Per-file entries (batch mode)", + "items": { + "additionalProperties": false, + "properties": { + "edits": { + "description": "Replacements to apply to this specific file", + "items": { + "$ref": "#/$defs/EditSpec" + }, + "maxItems": 100, + "minItems": 1, + "type": "array" + }, + "path": { + "description": "File or directory path inside an allowed workspace root.", + "maxLength": 4096, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "path", + "edits" + ], + "type": "object" + }, + "maxItems": 5, + "minItems": 1, + "type": "array" +} - changed
Input schema / properties / ignoreWhitespace / descriptionPrevious value: -"Treat all whitespace sequences as equivalent when matching oldText."New value: +"Ignore leading/trailing whitespace differences when matching oldText" - changed
Input schema / properties / path / descriptionPrevious value: -"Absolute path to file or directory."New value: +"Single file path; mutually exclusive with files" - removed
Input schema / requiredRemoved value: -[ - "path", - "edits" -] - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - removed
Output schema / properties / appliedEditsRemoved value: -{ - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" -} - removed
Output schema / properties / diffRemoved value: -{ - "description": "Unified diff of changes (dryRun)", - "type": "string" -} - removed
Output schema / properties / lineRangeRemoved value: -{ - "description": "Line range modified [start, end] (1-based)", - "prefixItems": [ - { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - ], - "type": "array" -} - removed
Output schema / properties / linesAddedRemoved value: -{ - "description": "Lines added", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" -} - removed
Output schema / properties / linesRemovedRemoved value: -{ - "description": "Lines removed", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" -} - removed
Output schema / properties / okRemoved value: -{ - "type": "boolean" -} - removed
Output schema / properties / pathRemoved value: -{ - "type": "string" -} - added
Output schema / properties / resultsAdded value: +{ + "description": "Per-path edit results ordered to match the input paths", + "items": { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "description": "Error details; present on failure", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "path": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + "path": { + "description": "Requested file path", + "type": "string" + }, + "value": { + "additionalProperties": false, + "description": "Edit result; present on success", + "properties": { + "appliedEdits": { + "description": "Number of edits successfully applied", + "minimum": 0, + "type": "integer" + }, + "diff": { + "description": "Unified diff of all changes (present only in dryRun mode)", + "type": "string" + }, + "kind": { + "description": "Broad file kind: text, binary, image, audio, or pdf", + "enum": [ + "text", + "binary", + "image", + "audio", + "pdf" + ], + "type": "string" + }, + "lineCount": { + "description": "Number of lines in the file after edits", + "minimum": 0, + "type": "integer" + }, + "lineRange": { + "description": "Line range [firstLine, lastLine] covering all applied edits", + "prefixItems": [ + { + "exclusiveMinimum": 0, + "type": "integer" + }, + { + "exclusiveMinimum": 0, + "type": "integer" + } + ], + "type": "array" + }, + "linesAdded": { + "description": "Net lines added by all applied edits", + "minimum": 0, + "type": "integer" + }, + "linesRemoved": { + "description": "Net lines removed by all applied edits", + "minimum": 0, + "type": "integer" + }, + "mimeType": { + "description": "Detected MIME type of the file", + "type": "string" + }, + "modified": { + "description": "Last modification timestamp after edits (ISO 8601 UTC)", + "format": "date-time", + "type": "string" + }, + "path": { + "description": "Resolved absolute path of the edited file", + "type": "string" + }, + "resourceUri": { + "description": "Resource URI pointing to the updated file content; omitted when no edit matched (appliedEdits is 0) and the file was left untouched", + "type": "string" + }, + "size": { + "description": "File size in bytes after edits", + "minimum": 0, + "type": "integer" + }, + "unmatchedEdits": { + "description": "oldText values that did not match any content in the file", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "path", + "size", + "lineCount", + "mimeType", + "kind", + "modified", + "appliedEdits" + ], + "type": "object" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / summaryAdded value: +{ + "additionalProperties": false, + "properties": { + "failed": { + "minimum": 0, + "type": "integer" + }, + "succeeded": { + "minimum": 0, + "type": "integer" + }, + "total": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "total", + "succeeded", + "failed" + ], + "type": "object" +} - removed
Output schema / properties / unmatchedEditsRemoved value: -{ - "description": "Edits that could not be applied", - "items": { - "type": "string" - }, - "type": "array" -} - changed
Output schema / requiredPrevious value: -[ - "ok" -]New value: +[ + "results", + "summary" +]
- Removed
find - Added
find_files - Removed
grep - Added
list - Added
list_roots - Removed
ls - Removed
mkdir - Added
move - Removed
mv - Added
patch - Changed
read31 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Input schema / dependentRequiredAdded value: +{ + "endLine": [ + "startLine" + ] +} - removed
Input schema / descriptionRemoved value: -"Use one read mode only: 'head', 'tail', or 'startLine'/'endLine'." - added
Input schema / oneOfAdded value: +[ + { + "required": [ + "path" + ] + }, + { + "required": [ + "paths" + ] + } +] - changed
Input schema / properties / endLine / descriptionPrevious value: -"End line (1-based, inclusive). Defaults to last line when startLine is set."New value: +"End line (1-indexed)" - changed
Input schema / properties / endLine / maximumPrevious value: -9007199254740991New value: +100000 - changed
Input schema / properties / head / descriptionPrevious value: -"Read first N lines (preview)"New value: +"Return first N lines" - changed
Input schema / properties / includeHash / descriptionPrevious value: -"Include SHA-256 hash of full file content"New value: +"Include SHA-256 hash of the returned content in the response" - changed
Input schema / properties / path / descriptionPrevious value: -"Absolute path to file or directory."New value: +"Single file path; mutually exclusive with paths" - added
Input schema / properties / pathsAdded value: +{ + "description": "Array of file paths for batch mode (max 1000); mutually exclusive with path", + "items": { + "description": "File or directory path inside an allowed workspace root.", + "maxLength": 4096, + "minLength": 1, + "type": "string" + }, + "maxItems": 1000, + "minItems": 1, + "type": "array" +} - changed
Input schema / properties / startLine / descriptionPrevious value: -"Start line (1-based, inclusive). Defaults to 1 when endLine is set."New value: +"Start line (1-indexed)" - changed
Input schema / properties / startLine / maximumPrevious value: -9007199254740991New value: +100000 - changed
Input schema / properties / tail / descriptionPrevious value: -"Read last N lines"New value: +"Return last N lines" - removed
Input schema / requiredRemoved value: -[ - "path" -] - removed
Output schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - removed
Output schema / properties / contentRemoved value: -{ - "description": "Content", - "type": "string" -} - removed
Output schema / properties / contentHashRemoved value: -{ - "description": "SHA-256 of full file content", - "pattern": "^[a-f0-9]{64}$", - "type": "string" -} - removed
Output schema / properties / endLineRemoved value: -{ - "description": "End line", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" -} - removed
Output schema / properties / hasMoreLinesRemoved value: -{ - "description": "More lines?", - "type": "boolean" -} - removed
Output schema / properties / headRemoved value: -{ - "description": "Head lines", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" -} - removed
Output schema / properties / linesReadRemoved value: -{ - "description": "Lines read", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" -} - removed
Output schema / properties / okRemoved value: -{ - "const": true, - "type": "boolean" -} - removed
Output schema / properties / pathRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / resourceUriRemoved value: -{ - "description": "Full content URI", - "type": "string" -} - added
Output schema / properties / resultsAdded value: +{ + "description": "Per-path results ordered to match the input paths", + "items": { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "description": "Error details; present on failure", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "path": { + "type": "string" + }, + "suggestion": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" + }, + "path": { + "description": "Requested file path", + "type": "string" + }, + "value": { + "additionalProperties": false, + "description": "Read result; present on success", + "properties": { + "contentHash": { + "description": "SHA-256 hex digest of the returned content (present when includeHash=true)", + "pattern": "^[0-9a-f]{64}$", + "type": "string" + }, + "continuation": { + "additionalProperties": false, + "description": "Next-read arguments; present when content was truncated due to size limits", + "properties": { + "args": { + "additionalProperties": false, + "description": "Ready-to-use arguments for the next call; pass verbatim", + "properties": { + "endLine": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "path": { + "type": "string" + }, + "startLine": { + "exclusiveMinimum": 0, + "type": "integer" + } + }, + "required": [ + "path", + "startLine", + "endLine" + ], + "type": "object" + }, + "hint": { + "description": "One-sentence description of the data still remaining to be read", + "type": "string" + }, + "tool": { + "description": "Tool name to call for the next chunk", + "type": "string" + } + }, + "required": [ + "tool", + "args", + "hint" + ], + "type": "object" + }, + "endLine": { + "description": "End line", + "exclusiveMinimum": 0, + "type": "integer" + }, + "hasMoreLines": { + "description": "True when additional lines remain beyond what was returned", + "type": "boolean" + }, + "head": { + "description": "Head lines requested", + "exclusiveMinimum": 0, + "type": "integer" + }, + "kind": { + "description": "Broad file kind: text, binary, image, audio, or pdf", + "enum": [ + "text", + "binary", + "image", + "audio", + "pdf" + ], + "type": "string" + }, + "linesRead": { + "description": "Number of lines returned in this response", + "minimum": 0, + "type": "integer" + }, + "mimeType": { + "description": "Detected MIME type (e.g. text/typescript)", + "type": "string" + }, + "resourceUri": { + "description": "Resource URI for externalized content (present when file is stored in resource store)", + "type": "string" + }, + "startLine": { + "description": "Start line", + "exclusiveMinimum": 0, + "type": "integer" + }, + "tail": { + "description": "Tail lines requested", + "exclusiveMinimum": 0, + "type": "integer" + }, + "totalLines": { + "description": "Total line count in the full file", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / startLineRemoved value: -{ - "description": "Start line", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" -} - added
Output schema / properties / summaryAdded value: +{ + "additionalProperties": false, + "properties": { + "failed": { + "minimum": 0, + "type": "integer" + }, + "succeeded": { + "minimum": 0, + "type": "integer" + }, + "total": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "total", + "succeeded", + "failed" + ], + "type": "object" +} - removed
Output schema / properties / tailRemoved value: -{ - "description": "Tail lines", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" -} - removed
Output schema / properties / totalLinesRemoved value: -{ - "description": "Total lines", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" -} - removed
Output schema / properties / truncatedRemoved value: -{ - "description": "Truncated?", - "type": "boolean" -} - changed
Output schema / requiredPrevious value: -[ - "ok" -]New value: +[ + "results", + "summary" +]
- Removed
read_many - Added
replace_text - Removed
rm - Removed
roots - Removed
search_and_replace - Added
search_text - Changed
stat6 fields changed- removed
Input schema / $schemaRemoved value: -"https://json-schema.org/draft/2020-12/schema" - added
Input schema / oneOfAdded value: +[ + { + "required": [ + "path" + ] + }, + { + "required": [ + "paths" + ] + } +] - changed
Input schema / properties / path / descriptionPrevious value: -"Absolute path to file or directory."New value: +"Single file path; mutually exclusive with paths" - added
Input schema / properties / pathsAdded value: +{ + "description": "Array of file paths for batch mode (max 1000); mutually exclusive with path", + "items": { + "description": "File or directory path inside an allowed workspace root.", + "maxLength": 4096, + "minLength": 1, + "type": "string" + }, + "maxItems": 1000, + "minItems": 1, + "type": "array" +} - removed
Input schema / requiredRemoved value: -[ - "path" -] - changed
Output schema / (root)Previous value: -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "additionalProperties": false, - "properties": { - "info": { - "additionalProperties": false, - "properties": { - "accessed": { - "description": "Accessed", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", - "type": "string" - }, - "created": { - "description": "Created", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", - "type": "string" - }, - "isHidden": { - "description": "Hidden?", - "type": "boolean" - }, - "mimeType": { - "description": "MIME type", - "type": "string" - }, - "modified": { - "description": "Modified", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", - "type": "string" - }, - "name": { - "description": "Name", - "type": "string" - }, - "path": { - "description": "Absolute path", - "type": "string" - }, - "permissions": { - "description": "Permissions", - "type": "string" - }, - "size": { - "description": "Size (bytes)", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "symlinkTarget": { - "description": "Target (symlink)", - "type": "string" - }, - "tokenEstimate": { - "description": "Est. tokens (size/4)", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "type": { - "description": "Type", - "enum": [ - "file", - "directory", - "symlink", - "other" - ], - "type": "string" - } - }, - "required": [ - "name", - "path", - "type", - "size", - "created", - "modified", - "accessed", - "permissions", - "isHidden" - ], - "type": "object" - }, - "ok": { - "const": true, - "type": "boolean" - } - }, - "required": [ - "ok" - ], - "type": "object" -}New value: +null
- Removed
stat_many - Removed
tree - Removed
write
TDQS
Scored across 13 tools
Each tool targets a distinct file system operation — listing, reading, writing, editing, moving, diffing, searching, etc. Even overlapping operations like edit vs replace_text are clearly differentiated by scope (literal sequential vs bulk regex) and search_text vs find_files by content vs filename. No ambiguity remains.
Most tools follow a clear verb or verb_noun pattern (create, delete, read, list_roots, search_text). The only deviation is the mix of single-word verbs (list, diff, stat) and compound verbs (replace_text, find_files), but all use snake_case consistently, making the set predictable.
13 tools is well-scoped for a filesystem server. Each tool has a distinct purpose and none feel redundant. The count covers the breadth of file operations without excessive granularity or missing essentials.
The tool surface covers the full file lifecycle: create, read, edit, patch, move (with copy), delete, plus metadata (stat), listing (list/find), search, and root discovery. Operations are logically paired (e.g., diff and patch enable safe application of changes), and bulk variants exist for efficiency. No obvious gaps that would break agent workflows.
Maintenance
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
Related MCP Servers
- AlicenseBqualityCmaintenanceAn MCP server that provides LLMs access to other LLMs424 npm78MIT
- -licenseNot gradedqualityNot gradedmaintenanceA Filesystem MCP server that allows an LLM to read and list files from a specified directory on your local machine through the Model Context Protocol.2-
- FlicenseNot gradedqualityDmaintenanceMCP server providing filesystem operations, shell execution, and web search capabilities.-
- AlicenseNot gradedqualityDmaintenanceExperimental MCP server for local LLM orchestration with filesystem tools (read, write, list, delete files) and a CLI agent that communicates via Ollama.5 npmISC