code-tools-mcp
v1.1.0
Published
Local-only MCP server exposing fast code navigation, reading, and editing tools over STDIO.
Maintainers
Readme
Code Tools MCP Server
- Local-only MCP server exposing core coding tools for LLMs via STDIO.
- Tools:
list_directory,read_file,write_file,search_file_content,glob,replace,read_many_files.
Codex CLI on Windows has limitations because it relies on writing PowerShell/Python scripts for basic read, write, grep operations. This MCP server exposes those standard tools making Codex CLI faster on Windows. You can use it on Linux or Mac, it will work but may not be necessary.
This is without warranty, any issues or bugs should be reported to the repository but be aware of the risks and use it at your own risk.
Release notes are tracked in CHANGELOG.md.
Install
- Global:
npm i -g code-tools-mcp - One-off:
npx code-tools-mcp
Run
code-tools-mcp --root C:/path/to/workspace
CODEX CLI Config Example
[mcp_servers.code-tools]
command = "{path to npm.cmd}"
args = [ "-y", "code-tools-mcp"]
env = { APPDATA = "C:\\Users\\{username}\\AppData\\Roaming", LOCALAPPDATA = "C:\\Users\\{username}\\AppData\\Local", HOME = "C:\\Users\\{username}", SystemRoot = "C:\\Windows", ComSpec = "C:\\Windows\\System32\\cmd.exe" }
startup_timeout_ms = 20_000Workspace root is auto-detected:
- If
CODE_TOOLS_MCP_ROOTis set, it wins. - Else if a CLI flag is passed, it’s used:
--root C:/path/to/workspace(or-r). - Else, the server looks upward from the current working directory for a
.gitfolder and uses that directory as root. - Else, it defaults to the current working directory.
- After MCP initialization, if the client supports Roots, the server calls
roots/listand uses thosefile://roots as the active permission roots. - If the client sends
notifications/roots/list_changed, the server refreshes roots automatically. - If no valid
file://roots are returned, the existing env/CLI/git-derived roots remain in effect.
Additional workspace roots:
- Set
CODE_TOOLS_MCP_ROOTSor pass--roots(path.delimiter-separated) to add extra workspace directories.
Optional unrestricted path mode:
- Set
CODE_TOOLS_MCP_ALLOW_ANY_PATHS=trueto allow access outside configured workspace roots. - In this mode, tools still apply sensitive-path checks; ignore filtering defaults to off for out-of-workspace paths unless explicitly enabled.
Claude config example without env var (pass a root flag):
{
"mcpServers": {
"code-tools": {
"command": "node",
"args": [
"C:/Users/adity/Projects/code-tools-mcp/dist/index.js",
"--root",
"C:/Users/adity/Projects/code-tools-mcp"
]
}
}
}Claude Desktop Config Example
Add to your Claude config JSON:
{
"mcpServers": {
"code-tools": {
"command": "node",
"args": ["/ABSOLUTE/PATH/code-tools-mcp/dist/index.js"],
"env": { "CODE_TOOLS_MCP_ROOT": "/ABSOLUTE/PATH/TO/YOUR/WORKSPACE" }
}
}
}Notes
- Uses STDIO transport; avoid
console.log(stdout). Any diagnostics are written to stderr. - File operations are restricted to workspace roots.
- You can opt into unrestricted filesystem access with
CODE_TOOLS_MCP_ALLOW_ANY_PATHS=true. - By default, all tools enforce the same path policy: sensitive and git-ignored paths are blocked unless explicitly overridden.
ripgrepis a deprecated alias forsearch_file_content; usesearch_file_contentgoing forward..geminiignoreparameters are parsed for Gemini parity but not yet applied to ignore logic (kept as a no-op while we align behavior with.gitignorehandling). Planned for the next minor release; see CHANGELOG for updates.
TODO:
- Repomap using AST
Tools
list_directory
Lists directory contents with directories first, respects .gitignore.
Parameters:
dir_path(string, required): Absolute path, or workspace-relative path to directoryno_ignore(boolean, optional): Skip gitignore filteringrespect_git_ignore(boolean, optional): Explicitly enable/disable gitignore filteringignore(string[], optional): Glob patterns to ignore (name matching)file_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }max_entries(number, optional): Maximum entries included in response
Example:
await client.callTool('list_directory', {
dir_path: '/path/to/workspace/src'
});read_file
Reads a file with optional pagination. Binary-aware (images, audio, PDF).
Parameters:
file_path(string, required): Absolute path, or workspace-relative path to fileno_ignore(boolean, optional): Skip gitignore filteringrespect_git_ignore(boolean, optional): Explicitly enable/disable gitignore filteringfile_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }offset(number, optional): Starting line (0-based)limit(number, optional): Number of lines to return
Example:
await client.callTool('read_file', {
file_path: '/path/to/workspace/src/index.ts',
offset: 0,
limit: 100
});write_file
Creates or overwrites a file.
Parameters:
file_path(string, required): Absolute path, or workspace-relative path of file to writeno_ignore(boolean, optional): Skip gitignore filteringrespect_git_ignore(boolean, optional): Explicitly enable/disable gitignore filteringfile_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }content(string, required): Full file contentmodified_by_user(boolean, optional)ai_proposed_content(string, optional)
Example:
await client.callTool('write_file', {
file_path: '/path/to/workspace/src/new-file.ts',
content: 'export const foo = "bar";'
});search_file_content
Fast regex search using ripgrep (falls back to JS search if unavailable).
Parameters:
pattern(string, required): Search pattern (regex by default)dir_path(string, optional): Directory or file to search (absolute or workspace-relative)include(string, optional): Glob filter (e.g.,**/*.ts)case_sensitive(boolean, optional): If true, search is case-sensitive (default false)fixed_strings(boolean, optional): If true, treat pattern as literalcontext(number, optional): Context lines around each match (-C)after(number, optional): Lines after each match (-A)before(number, optional): Lines before each match (-B)no_ignore(boolean, optional): If true, do not respect ignore files/default excludesrespect_git_ignore(boolean, optional): Explicitly enable/disable ignore filteringfile_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }max_matches(number, optional): Maximum matches to returnmax_output_bytes(number, optional): Maximum output size in bytes
Example:
await client.callTool('search_file_content', {
pattern: 'function.*async',
include: '**/*.ts'
});glob
Finds files matching a glob pattern.
Parameters:
pattern(string, required): Glob pattern (e.g.,src/**/*.ts)dir_path(string, optional): Absolute directory to search withincase_sensitive(boolean, optional): Case-sensitive matching (default false)no_ignore(boolean, optional): Skip gitignore filteringfile_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }respect_git_ignore(boolean, optional): Respect .gitignore (default true)respect_gemini_ignore(boolean, optional): Reserved for Gemini compatibilitymax_results(number, optional): Maximum paths included in response
Example:
await client.callTool('glob', {
pattern: 'src/**/*.ts'
});replace
Replaces text within a file using exact literal matching.
Parameters:
file_path(string, required)no_ignore(boolean, optional): Skip gitignore filteringrespect_git_ignore(boolean, optional): Explicitly enable/disable gitignore filteringfile_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }instruction(string, optional)old_string(string, required)new_string(string, required)expected_replacements(number, optional)modified_by_user(boolean, optional)ai_proposed_content(string, optional)
Example:
await client.callTool('replace', {
file_path: '/path/to/workspace/src/index.ts',
old_string: 'const foo = 1;',
new_string: 'const foo = 2;'
});read_many_files
Reads and concatenates content from multiple files.
Parameters:
include(string[], required): Glob patterns or pathsexclude(string[], optional)no_ignore(boolean, optional): Skip gitignore filteringrespect_git_ignore(boolean, optional): Explicitly enable/disable gitignore filteringrecursive(boolean, optional): Defaults to trueuseDefaultExcludes(boolean, optional): Defaults to truefile_filtering_options(object, optional):{ respect_git_ignore?: boolean, respect_gemini_ignore?: boolean }max_files(number, optional): Maximum files included in responsemax_output_bytes(number, optional): Maximum output size in bytes
Example:
await client.callTool('read_many_files', {
include: ['src/**/*.ts']
});Using These Tools from an MCP Client
Tool discovery: clients call
tools/list(supports cursor pagination) and may receivetools/list_changedwhen the set changes.
TypeScript Example
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
// Connect to the server
const transport = new StdioClientTransport({
command: 'node',
args: ['/path/to/code-tools-mcp/dist/index.js'],
env: { CODE_TOOLS_MCP_ROOT: '/path/to/workspace' }
});
const client = new Client({
name: 'example-client',
version: '1.0.0'
}, {
capabilities: {}
});
await client.connect(transport);
// List available tools
const tools = await client.listTools();
console.log('Available tools:', tools.tools.map(t => t.name));
// Call a tool
const result = await client.callTool({
name: 'read_file',
arguments: {
file_path: '/path/to/workspace/src/index.ts'
}
});
console.log('Result:', result);Tool Stability
- Tool names are stable and will not change in minor versions
- Parameter names are stable - new optional parameters may be added
- Descriptions are concise - detailed parameter docs are in JSON Schema (via Zod
.describe())
TypeScript Types
All tool input and output types are available in src/types/tools.ts:
import type {
ReadFileInput,
ReadFileOutput,
SearchFileContentInput,
SearchFileContentOutput,
// ... other types
} from 'code-tools-mcp/types/tools';