
Local Workspace Mcp
io.github.peter4leadsonv0.1.0更新于 Oct 10, 2026
Bounded, read-oriented filesystem, git and named-task tools over authorized workspace roots.
概览
让助手在已授权的工作区根目录内进行有边界的只读操作:列目录、读文件、搜索内容,以及只读查看 git 状态。
- 功能
- 这是一个本地 stdio MCP 服务器,提供 14 个工具:workspace_roots、有边界的文件读取与搜索(fs_list、fs_stat、fs_read、fs_read_many、fs_search_files、fs_search_content)、只读 git 工具(git_status、git_diff、git_log、git_show、git_branches),以及 task_list 和用于运行运维者预先声明命令的 task_run。所有路径都被限制在配置的工作区根目录内,并按默认拒绝清单检查密钥、.env 文件、私钥和 .git 内部文件。没有写入、删除或任意 shell 工具。
- 适用场景
- 当助手需要读取和搜索本地代码仓库或项目目录,或查看未提交的 git 状态,而又不想授予通用文件系统或 shell 访问权限时使用。适合希望把访问边界放在运维者配置中、而不是依赖客户端权限提示的团队。
- 运行要求
- 需要 Node.js 20 或更高版本;git_* 工具需要 git,内容搜索需要 ripgrep(rg)。通过 stdio 在本地运行,由 MCP 宿主启动。需要在 ~/.config/local-workspace-mcp/config.json 提供运维者配置文件,定义工作区根目录和允许的任务。未声明需要账号、API 密钥或网络访问。
安装
在 SourceWeft 中
- 打开 控制台中的 Local Workspace Mcp,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
local-workspace-mcp
Your AI assistant needs context. It does not need unrestricted access.
A read-oriented MCP server for local workspaces. Write, delete, and
arbitrary-shell tools do not exist in it. Not gated or disabled: absent. Every
filesystem path is checked against operator-authorized roots and a
default-deny list for secrets, .env files, private keys, and .git
internals. Git read tools and operator-declared named tasks provide
bounded capability without giving the model a shell.
- 14 tools over stdio JSON-RPC: bounded filesystem reads/searches,
read-only git (status/diff/log/show/branches), and
task_runfor explicitly allowlisted commands. - One transport: each MCP host spawns
workspace-mcp serve --stdio. No inbound listener, no daemon; the server itself makes no network calls (allowlisted tasks run as your user and are not network-restricted). - Honest scope:
task_runexecutes real commands as your user. The allowlist bounds what can be invoked, not what invoked code can do. It is not a sandbox.
Evaluating this for team use? Start at docs/THREAT-MODEL.md and SECURITY.md.
The problem
Hosts' built-in file tools are convenient but live in the client's
permission loop, the same loop users bypass out of fatigue
(--dangerously-skip-permissions, "yes to everything"). The official
@modelcontextprotocol/server-filesystem ships read and write tools
with no default secrets denylist, and has already overwritten a user's
.env (upstream issue #1869). Generic filesystem MCPs give a model file
access; they do not give it bounded access.
This server moves the boundary server-side: roots, deny rules, task allowlists, and audit live in an operator config that repository content cannot widen. A compromised or bypassed prompt loop cannot invoke a tool that does not exist.
Quick start
Requires Node ≥ 20, plus git for git_* tools and ripgrep (rg) for
content search. Building from a clone needs pnpm (corepack enable).
The generated config ships a placeholder workspace named example —
replace it with a real root or doctor will report root.example FAIL.
A healthy run ends:
Minimal working config (strict JSON — no comments or trailing commas):
Then connect a host (below), confirm it registered (claude mcp list,
/mcp, or your host's equivalent), and ask: "use workspace_roots, then
fs_list on myproj". First useful call sequence: fs_list → git_status
→ git_diff on uncommitted work, where a chat client otherwise
has no eyes.
Usage examples
Denied behavior is explicit, never silent:
Denial codes are the policy working as intended, not errors to report;
widening access happens only in the operator config. Task/search timeouts
and output limits return timedOut:true/truncated:true in a normal
result rather than an error. INTERNAL_ERROR covers spawn/tool failures and should not
appear in healthy use; git_* on a non-git root returns {repo:false}
gracefully.
Tool surface
All 14 tools, annotated readOnlyHint where true (hosts can auto-approve
pure reads). task_run is the only tool with side effects.
Supported hosts
GUI hosts (Claude Desktop especially) spawn servers with a minimal PATH,
not your shell's. If a host reports ENOENT or stays disconnected while
doctor is green, the binary is not on the host's PATH. Use the absolute
path (which workspace-mcp) as the command and check the host's MCP log.
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json
on macOS):
Codex CLI (~/.codex/config.toml, or codex mcp add):
For ChatGPT, tunnel-client polls an outbound HTTPS path and spawns the
stdio command locally; no inbound network listener opens. (tunnel-client
can optionally bind a loopback-only health endpoint — see OPERATIONS.)
Operator runbook: docs/OPERATIONS.md.
Permission model
- Roots are explicit. Nothing outside
workspaces[].pathis reachable;~, control characters, Windows drive paths, and..traversal are rejected before any syscall, and again afterrealpath. Point roots at the projects you actually work on; never authorize~/or a parent directory that contains things the assistant should not read. - Denied by default, including:
.env*/*.envspellings (except.env[.*].{example,sample,template}),.envrc, private-key filenames,.pem/.key/.p12/.pfx/.jks/.kdbx,.ssh,.aws,.azure,.gnupg,.kube/kubeconfig,.docker,.npmrc,.netrc,.pgpass,.pypirc,.git-credentials/.gitconfig, shell histories,*.tfvars,secrets.{json,yaml,yml,toml}/.secrets/secrets.d,credentials/service-account*.json/gha-creds-*.json, and all.gitinternals. Representative list — the authoritative rules areDEFAULT_DENYinsrc/policy.ts(repository); denies are fail-closed. - Name is not the only boundary: a file containing
BEGIN … PRIVATE KEYarmor under a benign name is refused in reads,git_showblobs, and search previews. The scan is heuristic defense-in-depth (encoding and size-budget limits documented in the threat model); name/policy denies are the primary boundary. - Git is read-only and hardened: argv-only spawns,
--end-of-options, strict ref validation, literal pathspecs,GIT_TERMINAL_PROMPT=0. Inside allowlisted tasks the headline repo-config exec keys are pinned viaGIT_CONFIG_*overrides (core.fsmonitor,core.sshCommand,diff.external,core.hooksPath; per-driverdiff.<name>/filter.<name>hooks are a documented residual). Rename diffs and merge-conflictdiff --ccblocks are policy-checked on every path they name. - Tasks are declared, not typed: argv arrays in config,
shell:false, sanitized environment, per-task timeout and output caps. Operator env cannot injectGIT_*, loader, or interpreter hooks. - Errors are bounded: every failure is
CODE: message; absolute host paths are scrubbed from tool errors, except that a task-spawn failure reports the operator-configured argv itself.task_runreturns subprocess stdout/stderr verbatim — that output belongs to commands the operator allowlisted.
Errors: ACCESS_DENIED, OUTSIDE_ROOT, NOT_FOUND, BINARY_FILE,
RESOURCE_LIMIT, TASK_DENIED, UNKNOWN_WORKSPACE,
WORKSPACE_UNAVAILABLE, INVALID_ARGUMENT, INTERNAL_ERROR, plus
CONFIG_ERROR at config load/startup. task_run and search timeouts
return timedOut:true in the result. Git-command failures surface as
INVALID_ARGUMENT (git_diff/git_log/git_show map non-zero exits,
including timeout kills) or INTERNAL_ERROR (git_status); a timeout on
repo detection reads as repo:false, and git_branches tolerates a
non-zero exit with an empty list.
What it will not do
No writes, edits, deletes, renames, or file creation. No shell. No git
mutations (commit, push, clean …). No network listener. No
repo-controlled configuration. Tasks execute only what the operator
pre-declared, and even those run as your user with real side effects,
so keep the allowlist tight and prefer read-only commands.
Security architecture
Ports-and-adapters, one implementation, one transport:
The full boundary analysis, the 30-finding adversarial ledger, the 33-case regression suite, and documented residual risks live in docs/THREAT-MODEL.md. Disclosure policy in SECURITY.md.
Configuration reference
~/.config/local-workspace-mcp/config.json; refused at load if the file or
its directory is group/world-writable, or the path is a symlink.
Common limits keys (key: default): maxReadFileBytes: 5 MB file-size
ceiling for reads, defaultReadBytes: 64 KiB per read,
maxSearchResults: 200, searchDeadlineMs: 20 s, gitTimeoutMs: 15 s,
maxTaskOutputBytes: 64 KiB, walkEntryCap/walkDepthCap: 50k entries /
20 deep. Full schema with bounds: src/config.ts in the repository. Callers can pass
timeoutMs to task_run to shorten a task's timeout; it can never
exceed the configured value or the 300 s hard cap.
Overrides: WORKSPACE_MCP_CONFIG (config file), WORKSPACE_MCP_CONFIG_DIR
(config dir + audit log location), workspace-mcp serve --config <path>
(CLI flag). init-config refuses to overwrite an existing config; delete
the file first if you intend a reset.
Troubleshooting
workspace-mcp doctor --json gives machine-readable output for CI or
wrapper scripts. Full reset: delete the config and
~/.config/local-workspace-mcp/audit.jsonl, re-run init-config. To
uninstall entirely, also remove the host entry (claude mcp remove, the
mcpServers/mcp_servers block, or the tunnel profile) and npm uninstall -g local-workspace-mcp (or npm unlink if linked from a clone).
Compatibility
- Node ≥ 20 (uses
node:utilparseArgs; tested on Node 22). - macOS/Linux. Windows is untested; path validation is POSIX-shaped.
- MCP SDK
@modelcontextprotocol/sdkv1.x line (spec ≤ 2025-11-25); v2 API surface is deliberately not adopted yet (see docs/RESEARCH.md). - ripgrep required for
fs_search_content; git required forgit_*.
Verifying the build
Contributing, security, license
- CONTRIBUTING.md: RED-first tests, fail-closed rules, no new capability surface without design review.
- SECURITY.md: vulnerability reporting and scope.
- CHANGELOG.md: release notes by finding/version.
- License: MIT.
Release status
v0.1.0 is published on npm as local-workspace-mcp and listed on the
official MCP Registry as io.github.peter4leadson/local-workspace-mcp.
The evaluated release surface (this README,
docs/THREAT-MODEL.md, the test suite, and the packaged tarball) is
described in docs/RELEASE-PACKET.md.
Maintained by Peter C. Bennett.
来源:README.md,提交 8ac23da
工具
0版本历史
1- v0.1.0最新Oct 10, 2026


