CodeTunnel — Secure VS Code MCP Server
Connect any MCP client to the live project open in VS Code: files, search, editor state, language intelligence, terminal jobs, Git, workspace settings, metrics, and audit history.
CodeTunnel is safe by default: it does not start automatically, does not open a public tunnel, and starts in read-only mode. Permissions are separate and explicit.
Quick start
- Install the extension and open a folder in a trusted VS Code workspace.
- Run CodeTunnel: Open Setup Guide from the Command Palette.
- Start the local server. Enable the public link only if a remote/web client needs it.
- Copy either:
- a temporary MCP link for browser clients (15-minute scoped ticket; no master key in the URL), or
- the MCP config (JSON) for a durable desktop-client configuration using a bearer header.
- Keep read-only mode on for inspection. Enable only the write capabilities the task requires.
The status bar shows Off, Local only, Creating link…, or Ready, plus (RO) when read-only mode is active. Its tooltip and Setup Guide show the effective permission profile and active session count.
For reliable public access, CodeTunnel checks for cloudflared. If it is missing, the Setup Guide offers an explicit Install Cloudflare Tunnel button on Windows and macOS, runs the system package manager in the background after confirmation, and shows the public URL only after the remote MCP endpoint passes a health check. A verified localtunnel fallback is attempted only when an installed cloudflared cannot establish a Quick Tunnel.
Stable Cloudflare Named Tunnel
Run CodeTunnel: Set Up Stable Cloudflare Named Tunnel for a permanent hostname. The guided flow starts the local server, installs cloudflared if necessary, accepts either the tunnel token or Cloudflare's full cloudflared service install … command, asks for the public hostname, stores only the extracted token in VS Code Secret Storage, and verifies the public MCP endpoint before showing Ready.
The remotely managed tunnel must already have a Cloudflare public hostname whose service targets the loopback URL shown by the wizard (normally http://127.0.0.1:3845). If verification fails, CodeTunnel keeps the token securely stored, turns the failed public link off, and offers to open Cloudflare, retry, or switch to a temporary Quick Tunnel.
MCP and HTTP endpoints
| Endpoint |
Authentication |
Purpose |
GET / |
No |
Health/discovery; exposes no credentials |
GET /.well-known/mcp.json |
No |
Cached MCP discovery manifest |
GET, POST, DELETE /mcp |
Yes |
Primary official Streamable HTTP MCP transport |
GET /sse or /s |
Yes |
Deprecated legacy SSE compatibility transport |
POST /messages?sessionId=… |
Yes |
Legacy SSE client-to-server messages |
POST /rpc |
Yes |
Explicit stateless JSON-RPC 2.0 and { "tool", "args" } calls |
GET /tools |
Yes |
Cached enabled-tool manifest and JSON Schemas |
GET /openapi.json |
Yes |
Cached OpenAPI description for stateless calls |
Durable clients authenticate with Authorization: Bearer <master-key> or X-API-Key: <master-key>. Master credentials are deliberately rejected in query strings.
Temporary tickets are random, short-lived, can carry reduced scopes, are accepted only on MCP connection endpoints, and bind to the first session that uses them. Rotating the master key revokes all live sessions and outstanding tickets.
Streamable HTTP example
{
"mcpServers": {
"codetunnel": {
"url": "http://127.0.0.1:3845/mcp",
"headers": {
"Authorization": "Bearer YOUR_MASTER_KEY"
}
}
}
}
The configured port may move forward by up to nine ports if 3845 is occupied. Use Copy MCP config (JSON) to get the actual active URL.
Set codetunnel.enabledToolCategories to advertise only the packs a client needs, then restart CodeTunnel.
- Filesystem (15):
read_file, read_multiple_files, read_files, write_file, edit_file, edit_files, insert_lines, replace_lines, append_file, list_directory, get_file_info, create_directory, delete_path, move_path, copy_path
- Search (3):
find_files, search_file_content, replace_in_files
- Editor (8):
get_active_editor, get_open_editors, get_diagnostics, open_file, set_selection, save_file, close_file, show_message
- Language (14):
get_document_symbols, search_workspace_symbols, go_to_definition, go_to_type_definition, find_implementations, find_references, get_hover, get_signature_help, get_completions, rename_symbol, format_document, organize_imports, get_code_actions, apply_code_action
- Terminal (9):
run_command, start_command, get_command, cancel_command, list_command_jobs, run_in_terminal, list_terminals, list_tasks, run_task
- Git (14):
git_status, git_diff, git_log, git_show, git_blame, git_branches, git_add, git_unstage, git_commit, git_checkout, git_stash, git_pull, git_push, git_command
- Workspace (7):
get_workspace_info, execute_vscode_command, list_vscode_commands, get_setting, update_setting, get_codetunnel_metrics, get_codetunnel_audit_log
Every tool is declared once in src/tools/registry.ts. MCP registration, input schemas, output schemas, annotations, REST discovery, OpenAPI, and the generated agent prompt derive from that registry.
Fast and reliable workspace operations
- Live unsaved editor content is preferred when a document is dirty.
read_file supports line ranges; read_files uses bounded parallelism and one shared response budget.
- Reads return file size, truncation state, line-count certainty, and a SHA-256 content hash.
- Write/edit tools accept
expected_hash for optimistic concurrency.
- Multi-file edits are prepared first and applied as one
WorkspaceEdit.
- Exact edit matching is the default; whitespace-relaxed matching must be requested explicitly.
- Search returns every match on a line, supports cursors/deadlines/file caps, skips binaries and large files, and rejects risky regular expressions.
- Long commands can run as jobs and be polled or cancelled. Output preserves bounded head/tail data and process trees are terminated on cancel/timeout.
- MCP results place the complete object in
structuredContent; compact text avoids transmitting the same payload twice.
- SDK cancellation is propagated into cancellable search and synchronous command work.
Multi-root paths
In a multi-root workspace, prefix a path with the folder name or zero-based folder index:
frontend/src/app.ts
1/services/api.ts
Results include unambiguous root-prefixed paths.
Security model
Capability boundaries
| Capability |
Setting |
Default |
| Read files/search/editor/language/Git metadata |
Always available when authenticated |
On |
| Any mutation |
codetunnel.readOnly=false |
Off |
| Shell commands, jobs, terminals, tasks |
codetunnel.allowCommandExecution |
Off |
| Commands without per-call approval |
codetunnel.allowUnattendedCommands |
Off |
| Git mutations |
codetunnel.allowGitWrite |
Off |
| Non-sensitive settings writes |
codetunnel.allowSettingsWrite |
Off |
| Allowlisted VS Code commands |
codetunnel.allowVsCodeCommands |
Off |
| Restricted raw Git escape hatch |
codetunnel.allowArbitraryGit |
Off |
| Local confirmation for sensitive actions |
codetunnel.confirmSensitiveActions |
On |
VS Code Workspace Trust is enforced independently; untrusted workspaces cannot mutate state even if settings allow it. Request tickets may reduce these permissions further with scopes such as files.read.
codetunnel.allowUnattendedCommands is an explicit high-risk opt-in for trusted automation. It bypasses prompts only for terminal execution tools; authentication, read-only mode, Workspace Trust, command permission and ticket scopes remain enforced. Git, settings and other sensitive actions retain their normal confirmation behavior.
Filesystem and process hardening
- Lexical containment plus native real-path checks block
.., absolute-path escapes, and symlink escapes, including destinations that do not exist yet.
- Workspace roots cannot be moved or deleted.
- Oversized files can be prefix-read but cannot be edited from truncated content; append remains large-file safe.
- File writes are serialized per path, destination paths are revalidated, and CRLF is preserved.
- Git uses argument-safe process execution, disables hooks and external diff helpers, validates refs/remotes, and bounds output/time.
- VS Code commands use an exact allowlist; CodeTunnel security settings cannot be changed through the MCP settings tool.
Transport hardening
- Local bind defaults to
127.0.0.1; public tunneling is opt-in.
- Request bodies, batches, sessions, concurrency, rates, output, and idle time are bounded.
- JSON-RPC structure and methods are validated before execution.
- CORS defaults to disabled and uses an explicit origin allowlist when enabled.
- Large JSON responses can be gzip-compressed; static manifests use ETags.
- Each MCP session receives an isolated SDK server instance.
- New Cloudflare and LocalTunnel links are verified against a random server-instance ID before the UI displays Ready, and reconnect with bounded backoff.
- Installing cloudflared is always user-initiated. CodeTunnel uses Windows Package Manager or Homebrew in the background and verifies the executable afterward.
Observability
get_codetunnel_metrics returns per-tool call counts, errors, and timing. get_codetunnel_audit_log returns a bounded metadata-only history of mutations; file contents, command output, and credentials are not stored in the audit ring.
Settings
Access and startup
| Setting |
Default |
codetunnel.autoStart |
false |
codetunnel.publicTunnel |
false |
codetunnel.readOnly |
true |
codetunnel.allowCommandExecution |
false |
codetunnel.allowUnattendedCommands |
false |
codetunnel.allowGitWrite |
false |
codetunnel.allowSettingsWrite |
false |
codetunnel.allowVsCodeCommands |
false |
codetunnel.allowArbitraryGit |
false |
codetunnel.confirmSensitiveActions |
true |
codetunnel.requireAuth |
true |
Network and limits
| Setting |
Default |
codetunnel.port |
3845 |
codetunnel.bindHost |
127.0.0.1 |
codetunnel.corsOrigin |
empty (browser CORS disabled) |
codetunnel.commandTimeoutMs |
60000 |
codetunnel.maxFileSizeBytes |
2097152 |
codetunnel.maxResponseBytes |
1048576 |
codetunnel.maxConcurrentRequests |
8 |
codetunnel.maxBatchSize |
20 |
codetunnel.maxSessions |
8 |
codetunnel.sessionIdleTimeoutMinutes |
30 |
codetunnel.requestTimeoutMs |
30000 |
codetunnel.rateLimitPerMinute |
120 |
codetunnel.compactResponses |
true |
codetunnel.allowedVsCodeCommands is an exact command allowlist. Legacy fsvk.* commands/settings are still recognized for compatibility. Plaintext token settings are migrated to VS Code SecretStorage.
Filesystem requests use hardware-aware bounded I/O concurrency. Large line-range reads stop after the requested range instead of loading and hashing the entire file, simultaneous identical reads share one disk operation, and local search reads use the native filesystem while preserving workspace and symlink containment. Directory traversal also bounds metadata I/O so a large tree does not freeze the extension host.
Run the repeatable local benchmark with:
npm run benchmark:fs
Results vary by storage, operating system, and repository shape, so compare runs on the same machine and workspace conditions.
Development and release checks
npm install
npm run compile
npm run lint
npm test
npm audit --omit=dev
npm run package
Press F5 to run an Extension Development Host. Do not test by exposing a real workspace publicly; use a disposable project and the narrowest permissions possible.
License
MIT
| |