Skip to content

Navigation Menu

Sign in
Sign up

Latest commit

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

projectfs

License: MIT Release Python Dependencies: none

Per-project folder scoping for Claude Desktop.

Claude Desktop's local MCP servers are global. Every server you add in claude_desktop_config.json is visible to every chat and every Project, and a filesystem server is locked to whatever directories you pass it at startup. There is no built-in way to say "this folder belongs to that Project and nothing else."

projectfs is one small local MCP server that adds that missing layer:

  • You define named projects, each mapped to a set of local folders.
  • Each project gets a random token.
  • File operations (read_file, write_file, list_dir, ...) only work when the caller passes a valid project token, and only ever touch that project's folders.
  • You put a project's token in that Claude Project's instructions. Now that Project can read and write its own folder, and other chats can't.

One running server handles all your projects. Adding or moving a folder is a CLI command or a tool call — no editing claude_desktop_config.json, no restart.

It is a single standard-library Python file. No dependencies.

Not affiliated with Anthropic. "Claude" is a trademark of Anthropic.


Screenshot

projectfs CLI: init, link a folder to a project, and list the project with its paste-ready instruction block


Install

Option A — Desktop Extension (.mcpb)

  1. Download projectfs.mcpb from the latest release.
  2. Open it with Claude Desktop (or Settings → Extensions → Install from file).
  3. Enable it.

The bundle keeps its config at ~/.projectfs/config.json. Manage projects with the CLI (Option B step 3 onwards) — there is no chat tool for attaching folders, by design.

Option B — manual

  1. Copy server.py somewhere stable, e.g. ~/projectfs/server.py.

  2. Add it to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) / %APPDATA%\Claude\claude_desktop_config.json (Windows):

    {
     "mcpServers": {
     "projectfs": {
     "command": "python3",
     "args": ["/absolute/path/to/server.py"]
     }
     }
    }

    On macOS, /usr/bin/python3 is a safe absolute value. On Windows use python.

  3. Create a project and attach a folder:

    python3 server.py add my-notes
    python3 server.py link my-notes ~/Documents/Notes
    
  4. Fully quit Claude Desktop and reopen it.


Quick start

# attach a folder to a project (creates the project)
python3 server.py link my-notes ~/Documents/Notes
# see the project, its token, and a ready-to-paste instruction block
python3 server.py list

list prints something like:

== my-notes ==
 project_token: pf_xxxxxxxxxxxxxxxxxxxxxxxx
 folder: /Users/you/Documents/Notes
 --- paste into this project's Claude instructions: ---
 For all file operations use the projectfs tools with
 project_token "pf_xxxxxxxxxxxxxxxxxxxxxxxx". Save every document you produce as a
 file in this project's folder via write_file.

Paste that block into the Claude Project's custom instructions. Done.


Usage example

Say you keep a research project's files in ~/Documents/field-notes and you want one Claude Desktop Project — and only that Project — to read and write them.

1. Attach the folder and get the token

$ python3 server.py link field-notes ~/Documents/field-notes
created project field-notes
attached field-notes -> /Users/you/Documents/field-notes
project_token: pf_EXAMPLEprojectTOKENxxxxxx
$ python3 server.py list
== field-notes ==
 project_token: pf_EXAMPLEprojectTOKENxxxxxx
 folder: /Users/you/Documents/field-notes
 --- paste into this project's Claude instructions: ---
 For all file operations use the projectfs tools with
 project_token "pf_EXAMPLEprojectTOKENxxxxxx". Save every document you produce as a
 file in this project's folder via write_file.

2. Put the token in the Claude Project's instructions

For all file operations use the projectfs tools with project_token pf_EXAMPLEprojectTOKENxxxxxx. Save every document you produce as a file in this project's folder via write_file, and read existing files with read_file / list_dir before answering.

3. Use it in that Project's chats

You: What's already in the folder? Then draft outline.md for the paper.

Claude: (calls list_dir → sees sources.md, interview-01.md) The folder has sources.md and interview-01.md. Here's a draft outline — (calls write_file outline.md) saved to outline.md.

Any other chat that doesn't carry that token can't touch ~/Documents/field-notes. A second project (taxes, client-acme, ...) gets its own link call, its own token, and its own isolated folder set, all served by the same running projectfs.


CLI

Run as python3 server.py <cmd> (or ./projectfs <cmd> with the bundled wrapper).

Command What it does
add <project> create a project, print its token
link <project> <folder> attach an existing folder to a project (creates it if new)
unlink <project> <folder> detach a folder
rm <project> delete a project entry
list every project: folders, token, paste-ready instruction block

init and admin-token still exist for older setups but are no longer needed — the admin token gated a chat-side remap tool that has since been removed.

Config location, in order of precedence:

  1. $PROJECTFS_CONFIG
  2. config.json next to server.py (if it exists)
  3. ~/.projectfs/config.json

MCP tools

Tool Needs Notes
list_projects names + folders only, never tokens
list_dir project_token path optional, defaults to the first folder
read_file project_token UTF-8 text; 5 MiB cap
write_file project_token creates parent dirs inside the project's folders
append_file project_token good for running logs
delete_file project_token files only
move_file project_token source and dest both inside the project's folders

Paths may be absolute (must resolve inside an attached folder) or relative (resolved against the project's first folder). Symlink and .. escapes are rejected, and each operation re-opens the resolved path component-by-component with O_NOFOLLOW so a symlink swapped in after the check can't redirect it outside the folder. Writes cap at 10 MiB. Directory delete/move is refused.


The token model

The project token is the access boundary. Any caller that has a project's token and the projectfs tools can read and write that project's folders — so:

  • Put a token only in the instructions of the Project it belongs to.
  • Don't paste tokens into ad-hoc chats.
  • Rotate a token by editing config.json (or rm + re-add the project).

Attaching and detaching folders is CLI-only. There is no MCP tool for it, so a chat can't quietly attach ~/ to a project and read everything — even one that has somehow obtained a token can only reach folders already mapped to that project.


Security notes

  • This is scoping and blast-radius control, not a sandbox. The server runs as your user account with your permissions. Don't attach a folder you would not want a misbehaving or prompt-injected chat to write in.
  • A caller with no token can still call list_projects (names and folder paths, no file contents).
  • macOS: folders under ~/Desktop, ~/Documents, ~/Downloads, and iCloud Drive are protected by the OS privacy layer (TCC). Claude Desktop may be able to create a file there but not list or read the directory. Keep projectfs folders outside those, or grant Claude Desktop Full Disk Access.
  • The server logs startup and error lines to stderr; Claude Desktop captures these in ~/Library/Logs/Claude/mcp*.log.
  • Anthropic may add native per-project MCP scoping to Claude Desktop, which would make this unnecessary.

License

MIT — see LICENSE.

About

Per-project folder scoping for Claude Desktop — attach folders to named projects, gate every file operation with a per-project token. One MCP server, no dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /