mcp-local-files
by YoshiaKefasu
README.md
# Local Files MCP
English | [日本語](README.ja.md)
A small local MCP server that lets ChatGPT/Codex read, search, and edit a single allowed folder on this Windows machine through OpenAI Secure MCP Tunnel.
This project is intentionally simple and local-first. The server runs over stdio, and `tunnel-client.exe` forwards ChatGPT MCP requests to it. The exposed folder is controlled by `root-dir.txt`, not hardcoded in `server.js`.
## Current folder
This repository lives at:
```text
%USERPROFILE%\mcp-local-files
```
The folder currently exposed to ChatGPT/Codex is stored in:
```text
%USERPROFILE%\mcp-local-files\root-dir.txt
```
To see or change it manually:
```powershell
type %USERPROFILE%\mcp-local-files\root-dir.txt
notepad %USERPROFILE%\mcp-local-files\root-dir.txt
```
## Files
```text
server.js MCP stdio server implementation
cli.js npm CLI wrapper and tunnel-client downloader
package.json Node.js dependencies and npm scripts
run-mcp.cmd Starts the MCP server using ROOT_DIR from root-dir.txt
mcp-here.cmd Writes the current terminal directory into root-dir.txt
root-dir.txt Current allowed folder for Local Files MCP
AGENTS.md Instructions for Codex/agents working on this folder
```
## Install
From PowerShell:
```powershell
cd %USERPROFILE%\mcp-local-files
npm install
```
When installed as an npm package, the CLI command is:
```powershell
mcp-local-files --help
```
## First-time tunnel setup
The CLI can guide the first tunnel profile setup:
```powershell
mcp-local-files setup
```
It will:
```text
download OpenAI tunnel-client if needed
open the OpenAI Tunnels page
open the OpenAI Runtime API keys page
ask for a tunnel_id and Runtime API key
create the default tunnel-client profile
save the Runtime API key outside this project folder
```
OpenAI setup pages:
```text
https://platform.openai.com/settings/organization/tunnels
https://platform.openai.com/settings/organization/api-keys
```
The default profile name shown by setup is:
```text
local-files
```
The Runtime API key is stored in the user's private config file:
```text
%USERPROFILE%\.mcp-local-files\secrets.json
```
Do not commit this file. It is intentionally outside the npm package folder.
After setup, the profile you just created is saved as the CLI default. That means `mcp-local-files --tunnel-here` will use that profile automatically unless you pass `--profile`. If you already ran setup with an older CLI, the command also falls back to the only saved profile when exactly one profile exists.
Non-interactive values can be passed like this:
```powershell
mcp-local-files setup --profile local-files --tunnel-id tunnel_xxx --no-open
```
The Runtime API key is still requested interactively so it does not appear in shell history.
On Windows, the default MCP command saved by setup is:
```text
cmd /d /s /c "%USERPROFILE%/mcp-local-files/run-mcp.cmd"
```
This keeps the existing `run-mcp.cmd` / `root-dir.txt` flow intact.
`run-mcp.cmd` reads `ROOT_DIR` from `root-dir.txt`, then exports it to the child `node` process using the `endlocal & set` trick so the env var survives across cmd's local scope. `server.js` resolves `ROOT_DIR` from, in order:
1. `process.env.ROOT_DIR`
2. `process.argv[2]`
3. `%USERPROFILE%\mcp-local-files\root-dir.txt`
This lets the same `run-mcp.cmd` work both as the tunnel target and as a local smoke test without changing directory first.
## Change the exposed folder quickly
Open Windows Terminal, `cd` into the folder you want ChatGPT/Codex to access, then run:
```powershell
%USERPROFILE%\mcp-local-files\mcp-here.cmd
```
Example:
```powershell
cd C:\path\to\your-project
%USERPROFILE%\mcp-local-files\mcp-here.cmd
```
After changing the folder, restart the tunnel client so the new `ROOT_DIR` is applied.
With the npm CLI, this can be done in one step:
```powershell
cd C:\path\to\your-project
mcp-local-files --tunnel-here
```
`--tunnel-here` writes the current folder to the root file, downloads OpenAI `tunnel-client` on first use if needed, verifies its SHA256 checksum, and starts the last profile created by `mcp-local-files setup`.
Use a different profile when needed:
```powershell
mcp-local-files --tunnel-here --profile local-files
```
## Start the local MCP server directly
For a local smoke test:
```powershell
cmd /d /s /c "%USERPROFILE%/mcp-local-files/run-mcp.cmd"
```
The process should keep running. Stop it with `Ctrl+C`.
The npm CLI can also start the stdio server directly:
```powershell
mcp-local-files --root C:\path\to\folder
```
## Run through OpenAI Tunnel
The profile already created is:
```text
local-files
```
Normal run command:
```powershell
cd C:\path\to\tunnel-client-folder
.\tunnel-client.exe run --profile local-files
```
The npm CLI can download and run OpenAI `tunnel-client` automatically:
```powershell
mcp-local-files tunnel run --profile local-files
```
If `mcp-local-files setup` saved a Runtime API key for that profile, the CLI passes it to `tunnel-client` as `CONTROL_PLANE_API_KEY` only for the child process.
On first use, it downloads the matching platform zip from the official `openai/tunnel-client` GitHub release, downloads `SHA256SUMS.txt`, verifies the archive, extracts the binary into the user cache, and runs it from there.
Default cache locations:
```text
Windows: %USERPROFILE%\.cache\mcp-local-files\tunnel-client
macOS/Linux: ~/.cache/mcp-local-files/tunnel-client
```
To use an existing binary instead of auto-download:
```powershell
mcp-local-files --tunnel-client C:\path\to\tunnel-client.exe tunnel run --profile local-files
```
Useful diagnostic command:
```powershell
cd C:\path\to\tunnel-client-folder
.\tunnel-client.exe doctor --profile local-files --explain
```
If the profile must be recreated, prefer forward slashes in the MCP command to avoid Windows escaping problems:
```powershell
.\tunnel-client.exe init --sample sample_mcp_stdio_local --profile local-files --tunnel-id tunnel_xxx --mcp-command 'cmd /d /s /c "%USERPROFILE%/mcp-local-files/run-mcp.cmd"'
```
Do not commit or paste Runtime API keys into this project. Set them only in the terminal environment when needed.
The tunnel-client binary is not bundled in this package. It is fetched from OpenAI's public release artifacts when the CLI needs it. Keep the Apache-2.0 `LICENSE` and OpenAI `NOTICE` terms in mind if redistributing downloaded artifacts yourself.
## Exposed MCP tools
The server registers these tools:
```text
list_files List files and folders inside ROOT_DIR
read_file Read UTF-8 text files inside ROOT_DIR
search_files Search text files inside ROOT_DIR
stat_file Return metadata for a file or folder inside ROOT_DIR
copy_file Copy one file inside ROOT_DIR without deleting the source
copy_files Copy multiple files inside ROOT_DIR without deleting sources
write_file Create or overwrite UTF-8 text files inside ROOT_DIR
replace_in_file Replace exact text inside a UTF-8 text file inside ROOT_DIR
read_binary_file_base64 Read a binary file inside ROOT_DIR and return base64
write_base64_file Decode base64 and write a binary file inside ROOT_DIR
```
### Copy / transfer tools
Use these when you want TransferFiles-style local movement inside the exposed folder. They only copy files inside `ROOT_DIR`; they do not delete source files and they still block absolute paths or `..` escapes.
`stat_file` input:
```json
{
"path": "dist/app.zip"
}
```
`copy_file` input:
```json
{
"sourcePath": "dist/app.zip",
"destinationPath": "incoming/app.zip",
"overwrite": false
}
```
`copy_files` input:
```json
{
"files": [
{
"sourcePath": "dist/app.zip",
"destinationPath": "incoming/app.zip"
}
],
"overwrite": false
}
```
Batch copies are checked before copying starts. Existing destination files are protected unless `overwrite` is explicitly set to `true`. Single copied files are capped at 100 MB, and one batch can contain up to 100 files.
### Binary file tools
Use these only for files that must be transferred as bytes, such as PNG, JPEG, WebP, GIF, ZIP, or PDF files.
`read_binary_file_base64` input:
```json
{
"path": "images/example.png",
"maxBytes": 10000000
}
```
It returns `path`, `size`, `base64`, `encoding: "base64"`, and a simple `mimeType` guess. If the file is larger than `maxBytes`, the server returns an error instead of truncating the data.
`write_base64_file` input:
```json
{
"path": "images/example.png",
"base64": "iVBORw0KGgo...",
"overwrite": false
}
```
It creates parent folders when needed. Existing files are protected unless `overwrite` is explicitly set to `true`. Decoded binary writes are capped at 25 MB.
## Safety model
The server is scoped to one folder only. All paths passed to tools must be relative to `ROOT_DIR`.
The server blocks:
```text
absolute paths
.. path traversal outside ROOT_DIR
text reads for non-allowlisted file types
large text search over files bigger than 2 MB
binary reads above the requested maxBytes limit
binary writes above the server write-size limit
copying files larger than 100 MB
batch copy requests above 100 files
```
The directory walker skips:
```text
node_modules
.git
.venv
```
This is intentional. Do not expose `C:\`, the full user profile, or folders containing secrets unless the user explicitly requests it and understands the risk.
## Troubleshooting
If ChatGPT says connector creation failed and the tunnel log shows `file already closed`, usually the local MCP process exited immediately. Run this first:
```powershell
cmd /d /s /c "%USERPROFILE%/mcp-local-files/run-mcp.cmd"
```
If the log says the command is not recognized, check the profile command path. Prefer this form:
```text
cmd /d /s /c "%USERPROFILE%/mcp-local-files/run-mcp.cmd"
```
If `ROOT_DIR is required` appears, check `run-mcp.cmd` and `root-dir.txt`.
If Node cannot find packages, run:
```powershell
cd %USERPROFILE%\mcp-local-files
npm install
```
## Notes
Keep the MCP server stdio-clean. Do not print normal logs to stdout from `server.js`, because stdout is used by the MCP protocol. Use stderr only for critical startup errors.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues