Workspace Bridge
Workspace Bridge
Workspace Bridge is a single-user, Linux-first MCP server for giving ChatGPT controlled access to local development projects.
It runs locally and exposes only registered projects. It supports both Streamable HTTP and stdio MCP transports. An OpenAI Secure MCP Tunnel client can start the stdio command directly, without a separate HTTP service process.
Quick start
npm install
npm run build
npm run startnpm run start listens on 127.0.0.1:3001 and exposes:
GET /healthMCP Streamable HTTP at
/mcp
When the server listens on loopback (the default), the MCP endpoint does not require a Bearer token. This is the intended setup for a local OpenAI Secure MCP Tunnel client.
For a stdio MCP client, build the project and start the stdio entrypoint:
npm run build
npm run stdioThe stdio entrypoint writes only MCP JSON-RPC messages to stdout. Keep logs and diagnostics on stderr so the transport remains valid.
If you intentionally bind the server to a non-loopback address, configure a Bearer token before starting it:
node dist/cli.js auth generateRetrieve the configured token later with:
export WORKSPACE_BRIDGE_TOKEN="$(node dist/cli.js auth show)"Register a project from another terminal:
node dist/cli.js project add my-project /absolute/path/to/project
node dist/cli.js project list
node dist/cli.js project show my-project
# Add another folder to the same project under a named root.
node dist/cli.js project root add my-project frontend /absolute/path/to/frontend
node dist/cli.js project root list my-projectThe configuration is stored at ~/.config/workspace-bridge/config.json unless --config is supplied.
A project starts with a main root. The legacy single-folder configuration is
stored as { "path": "..." }; after adding another folder it is represented as
named roots, for example:
{
"projects": {
"my-project": {
"roots": {
"main": { "path": "/absolute/path/to/project" },
"frontend": { "path": "/absolute/path/to/frontend" }
}
}
}
}Use root on file, command, process, and Git tools to select a named root.
It defaults to main, so existing calls remain compatible. Paths and command
cwd values are relative to the selected root. File moves are restricted to a
single root; Git operations run from the selected root.
Permission modes
The default mode is guarded. Change it locally with:
node dist/cli.js policy set-mode guarded
node dist/cli.js policy set-mode permissiveguarded marks writes and process execution as consequential so ChatGPT can request confirmation. permissive removes that confirmation hint. Both modes still enforce project selection, path validation, output limits, and timeouts.
Commands run with a root-relative cwd (the main root by default), optional per-process environment overrides, and optional one-shot UTF-8 stdin. Cwd is resolved and checked inside the selected project root; this still does not provide a complete filesystem sandbox for arbitrary commands.
Coding-focused MCP tools
The bridge includes batch tools so a coding task does not need one MCP round trip per file or command:
workspace_context: returns project overview, a bounded tree, and optionally Git diff in one read-only call.file_read_many: reads multiple files concurrently while deduplicating paths and bounding the response size.file_write_many: writes multiple UTF-8 files after validating every path and expected hash, with one confirmation.file_patch_many: applies multiple exact unified diffs after validating every path, patch, and expected hash, with one confirmation.apply_patch: applies a Codex-style*** Begin Patchenvelope with Add/Delete/Update/Move operations after validating all hunks.file_replace: replaces an exact text fragment when its occurrence count matchesexpectedOccurrences(default 1), failing safely when it is missing or ambiguous.file_stat: returns lstat-style metadata for a known path and can stream a SHA-256 when requested.file_mkdir: creates a project directory with explicit recursive behavior.bridge_info: reports bridge capabilities, limits, policy, and project-root restrictions without secrets.project_root_add/project_root_remove: manage named folders inside an existing project;project_infoandproject_listreturn all configured roots.git_show: shows a bounded local revision, optionally for one project-relative historical path.command_run_batch: runs bounded commands with configurable concurrency, per-command cwd/env/stdin, and independent results.
Use the batch tools for independent files or checks. Keep dependent commands and edits in separate calls so their ordering remains explicit. The existing single-file and single-command tools remain available for focused work.
file_patch and file_patch_many require standard unified diffs with ---, +++, and @@ headers. Use apply_patch for Codex-style *** Begin Patch input. Unsupported or malformed input is rejected instead of being treated as a successful no-op.
command_run accepts optional cwd, env, stdin, and yieldTimeMs. If the command is still running after that wait, the result includes a sessionId; use process_read with that ID and stop it with process_stop when finished. Command summaries retain the beginning and end of noisy output and report omitted bytes.
File writes and patches are UTF-8 only. Images under the configured byte and pixel limits are returned as MCP image content; other binary files return metadata. Symlinks are allowed only when their resolved target stays inside the selected project root. Directory listings and trees do not follow symlinks.
The audit log is stored beside the config as audit.log and contains request metadata only: tool, project, duration, command name, exit code, and errors. It does not record tokens, file contents, environment variables, or full command output.
OpenAI Secure MCP Tunnel
The tunnel client can launch the stdio entrypoint itself, so no HTTP bridge process is needed:
tunnel-client init \
--sample sample_mcp_stdio_local \
--profile workspace-bridge-stdio \
--tunnel-id tunnel_0123456789abcdef0123456789abcdef \
--mcp-command "node /absolute/path/to/workspace-bridge/dist/cli.js stdio"
tunnel-client doctor --profile workspace-bridge-stdio
tunnel-client run --profile workspace-bridge-stdioReplace the tunnel ID and absolute project path with the local values. CONTROL_PLANE_API_KEY must be present in the tunnel client's environment.
For the existing HTTP transport, run the bridge and the OpenAI Secure MCP Tunnel client in separate terminals:
npm run start
./tunnel-client run --profile workspace-bridge --health.listen-addr 127.0.0.1:18081The tunnel client forwards to the local MCP endpoint configured by the workspace-bridge profile. No extra Authorization header is needed for the default loopback listener. CONTROL_PLANE_API_KEY is unrelated to Workspace Bridge MCP authentication. The systemd setup below loads it from an environment file.
The tunnel client's health endpoint is available at http://127.0.0.1:18081.
Run both with systemd
systemd/workspace-bridge.service is a user-service unit for the current checkout. It starts both npm run start and ./tunnel-client run ..., forwards both processes' logs to the journal, and restarts the pair if either process exits.
Create the tunnel client's environment file without committing it:
mkdir -p ~/.config/workspace-bridge
editor ~/.config/workspace-bridge/tunnel-client.env
chmod 600 ~/.config/workspace-bridge/tunnel-client.envPut the following in that file:
CONTROL_PLANE_API_KEY=your_runtime_api_keyInstall and start the user unit:
mkdir -p ~/.config/systemd/user
cp systemd/workspace-bridge.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now workspace-bridge.service
systemctl --user status workspace-bridge.serviceFollow logs with:
journalctl --user -u workspace-bridge.service -fIf the repository is moved, update WorkingDirectory, ExecStart, and RequiresMountsFor in the unit. To run it without an active login session, enable user lingering once with loginctl enable-linger "$USER".
Cloudflare Tunnel
For a later HTTPS deployment, point a Cloudflare Tunnel published application at http://127.0.0.1:3001 and protect it with Cloudflare Access. The bridge's loopback exception is intentional: it does not replace an Access policy for a publicly published hostname. This is a transport option, not a replacement for Workspace Bridge project authorization.
The optional tunnel child process can be configured and managed locally:
node dist/cli.js tunnel set cloudflared -- tunnel --url http://127.0.0.1:3001
node dist/cli.js tunnel enable
node dist/cli.js serveFor a named Cloudflare Tunnel or another client, replace the command and arguments. serve stops the managed tunnel and all bridge-owned project processes during shutdown.
Development
npm run typecheck
npm test
npm run buildLatest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Asutorufa/workspace-bridge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server