fsguard-mcp
Provides tools for interacting with Git repositories, including initializing repositories, checking status, staging files, creating commits, generating diffs, and viewing commit history.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fsguard-mcpshow me the git status and recent commits"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
fsguard-mcp
A filesystem + git MCP server that confines every operation to an allowed directory tree using symlink-resolved path containment, not string prefix matching.
Why this exists
Anthropic's own official filesystem and git MCP servers (@modelcontextprotocol/server-filesystem, part of modelcontextprotocol/servers, 89.7k★) have carried five separate path-confinement CVEs across two servers in ten months, and the pattern is still active:
CVE-2025-53109 / CVE-2025-53110 (filesystem, CVSS 8.4/7.3) — the "allowed directory" check used naive
startsWith()prefix matching, defeated by symlinks and by sibling directories that merely share a string prefix (e.g. an allowed/home/user-safealso matches/home/user-safe-evil), giving filesystem-wide read/write and a documented RCE path.CVE-2025-68143 / CVE-2025-68144 / CVE-2025-68145 (git) —
git_initaccepted arbitrary unvalidated paths,git_diff/git_checkoutpassed user-controlled arguments straight to thegitCLI (argument injection), and--repository-confined mode didn't actually verifyrepo_pathstayed inside the confined directory.CVE-2026-27735 (git, disclosed ~2 months before this project started) —
git_add, implemented via GitPython'srepo.index.add(), doesn't enforce working-tree boundaries for../-style paths, allowing staging and exfiltrating files outside the repo.A documented RCE chain:
git_initin a writable directory → a malicious.git/configwith a "clean" filter → a.gitattributesthat applies it →git_addtriggers the filter → arbitrary shell command runs.
Every one of these was patched with another string/prefix check bolted onto that one function. Nobody moved the boundary enforcement to a place a new tool can't simply forget to include — which is exactly how the fourth CVE landed four months after the first three were "fixed."
Related MCP server: Local Files MCP Server
How fsguard-mcp is different
One safety primitive, used everywhere. Every tool — filesystem or git — resolves its target path through the same
ConfinedRoot(seeconfined_path.py) before doing anything else. There's no per-tool path check to forget.Symlink-resolved, component-based containment — not string matching. A path is only inside the root if its fully resolved real path (every symlink followed) is a real ancestor-relative subpath of the root's own resolved real path, checked with
Path.is_relative_to()on resolved paths — neverstartswith()on a string. This alone closes CVE-2025-53109/53110's exact failure mode:/allowed-evilcannot pass a containment check against a resolved root of/allowed, because path-component comparison isn't string-prefix comparison.No shelling out to
gitfor content, ever. Git operations run throughdulwich— a pure-Python git implementation with no subprocess and no argv built from user input for anything content-related, and (critically) no clean/smudge filter execution, which is what the documented RCE chain depends on. There is no argument-injection surface here because there's no argument list being handed to an external process for reading/writing file content. (dulwich does still runpre-commit/commit-msg/post-commithooks viasubprocess.call()if they exist — real process execution, unrelated to content filtering.git_commitalways passesno_verify=Trueto skip them categorically, rather than relying on them happening not to be runnable.)Write operations validate the parent directory too, not just an existing target — closing the class of bug where a target doesn't exist yet (so "does this path resolve inside the root" was checked against a path that doesn't exist, and therefore couldn't be symlink-resolved) but its parent directory is itself a symlink pointing outside. Non-existent path segments are lexically normalized (
./..collapsed as pure path algebra) before any of this, independent of what happens to exist on disk — an earlier version of this project checked containment before normalizing, which happened to pass all its tests on Windows (whose path APIs normalize..for you) while being bypassable on Linux/macOS. It's fixed now, and there are tests for the exact case, but it's the reason this project treats "the test suite is green on my machine" with real suspicion..git/configcan't redirect operations outside the root. dulwich honors a repo's owncore.worktreeconfig entry, and every git operation re-opens aRepofrom a path string internally — so a caller could write a.git/configwithcore.worktreepointing anywhere, and every subsequent git tool would silently operate outside the confined root, invisible to the per-path check (which only ever sees the confined repo directory, never wherever dulwich actually redirected itself to). This was found in this project's own second-round security review — a real read/exfiltration primitive using nothing but this server's own exposed tools, more severe than any CVE it was built to fix. Every git tool now refuses to open a repo whose config setscore.worktreeat all, and independently re-verifies that theRepoobject it actually opened reports its working path as the exact directory that was validated.UNC paths and cross-drive paths are rejected before touching the network or disk at all. Resolving a
\\host\share\...path makes Windows actually attempt an SMB connection — and Windows will try to authenticate that connection as the server process, which is the "forced NTLM auth via UNC path" credential-theft technique, on top of blocking the server for a full connection timeout against an unreachable host. A candidate anchored on a different drive or host than the confined root is now rejected by a cheap string comparison, before any filesystem or network call. NTFS Alternate Data Streams (file.txt:hidden) are also rejected outright — they're invisible to directory listings but fully readable/writable through the same path string, and can forge the absence of Windows' download-warning "Mark of the Web."
Tools
Tool | Does |
| Read a text file |
| Create or overwrite a text file |
| List a directory's entries |
| Find files matching a glob pattern, recursively |
| Move/rename a file |
| Initialize a git repository |
| Staged/unstaged/untracked files |
| Stage files |
| Commit staged changes |
| Show a diff |
| Show commit history |
Setup
pip install fsguard-mcp
export FSGUARD_ROOT="/path/to/the/one/directory/tree/this/server/may/touch"
fsguard-mcpFSGUARD_ROOT is required — there is no default, and the server refuses to guess one. Point your MCP client at the fsguard-mcp command with FSGUARD_ROOT set in its env config.
Testing
pip install -e ".[dev]"
pytest tests/ -vAll 68 tests are self-contained (real temp directories, real symlinks, real git repos) — no external services needed.
Known limitation
Containment is checked, then a filesystem operation runs — there is an inherent TOCTOU (time-of-check-to-time-of-use) gap between the two. A concurrent process with write access to the confined root's own tree could in principle swap a symlink in that window (verified with a working proof-of-concept during review). Closing this fully needs an OS-level primitive (e.g. Linux openat2(RESOLVE_BENEATH), a real mount namespace) rather than anything achievable in portable Python; this project's guarantee is "correct containment logic, checked immediately before use," not "immune to a concurrent attacker who can already write inside the root."
Status
v0.1.0, live on PyPI. 68 passing tests (unit-level, with real symlinks and real git repos created on disk — not just string-logic assertions). Went through two rounds of adversarial security review before its first commit; both found real, working bypasses (a ..-traversal escape through not-yet-existing paths on POSIX, and the core.worktree redirection above, among smaller findings) that are now fixed, covered by tests written directly against the reported exploit, and re-verified against a fresh pip install of the published package.
License
MIT
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables file system operations such as listing, reading, and creating files within a scoped local project directory. It provides a secure way to manage local files through standardized MCP tools built with FastMCP.
- FlicenseAqualityDmaintenanceProvides safe local file operations through MCP, including reading, writing, searching, organizing, and protected deletion with configurable path restrictions.122
- FlicenseNot gradedqualityCmaintenanceExposes a secure, path-confined bridge to a local workspace and git remotes, enabling MCP clients to search, read, write, reset files, and perform git operations.
- AlicenseNot gradedqualityAmaintenanceEnables AI clients to securely operate isolated coding workspaces with file, command, Git, and deployment tools via authenticated remote MCP.7MIT
Related MCP Connectors
A MCP server built for developers enabling Git based project management with project and personal…
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
MCP-native collaborative markdown editor with real-time AI document editing
Latest 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/BerkantACUN/fsguard-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server