feat(tools): add vault_read/list/write/delete_file MCP tools#39
Merged
Conversation
added 2 commits
May 11, 2026 12:35
Four new tools for raw markdown CRUD against the brain vault, designed for MCP clients (Microsoft Copilot Studio) that need write access without ssh. - vault_read_file(path) — read .md file content + size - vault_list_files(directory, pattern, recursive) — list .md files, hidden segments always excluded - vault_write_file(path, content, commit_message) — create or overwrite, hard-rejects writes without required frontmatter (date, tags, type), commits + pushes origin/main under flock with rebase-on-conflict + rollback on push failure - vault_delete_file(path, commit_message) — delete + commit + push Path safety: relative-only, .md-only, no '..', no dot-prefixed segments (.git/.obsidian/.trash/.claude/.neurostack), symlink-escape rejected. Concurrency serialised via fcntl.flock on <vault_root>/.neurostack-write.lock. Tool count on the MCP server: 20 → 24. Tests: tests/test_file_tools.py covers path safety, read/list/write/delete happy paths and rejections, custom commit messages, idempotent no-op writes, rollback on simulated push failure for both create and overwrite, and two-thread concurrency. Tests use a tmp git repo with a tmp bare remote; no traffic to raphasouthall/brain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Microsoft Copilot Studio agent needs write access to the brain vault over
MCP (
copilot.solidit.uk→ mcp-shim → NeuroStack:8001). The existing 20tools are all read/search/memory; nothing can author or edit a markdown file.
This PR adds the four tools needed to close that gap.
Tool surface
vault_read_fileread_only(path) -> {path, exists, size_bytes, content}vault_list_filesread_only(directory="", pattern="*.md", recursive=True) -> {directory, files:[{path,size_bytes,modified_iso}]}vault_write_fileidempotentwrite(path, content, commit_message=None) -> {path, written, created, bytes_written, commit_sha, pushed, rolled_back, no_changes, git_error, index_update_needed, index_hint}vault_delete_filedestructivewrite(path, commit_message=None) -> {path, deleted, commit_sha, pushed, rolled_back, no_changes, git_error, index_update_needed, index_hint}All paths are relative to
config.vault_root(defaults to~/brain).MCP server tool count: 20 → 24.
Security model
Path safety
_safe_pathrejects:., or...(blocks.git,.obsidian,.trash,.claude,.neurostack,.gitignore).mdvault_rootFrontmatter validation (writes only)
Hard-rejects if:
---\n...\n---\nYAML blockdate,tags,typeis missing orNone(empty list/empty string is accepted — agents can iterate)
Concurrency
Writes and deletes serialise under
fcntl.flockon<vault_root>/.neurostack-write.lock. The lock file is dot-prefixed so itis filtered out by the tools' own listing rules; it never enters git.
Note
Deploy step: add
.neurostack-write.lockto brain's.gitignoreif notalready present.
Git flow / conflict handling
On every write or delete:
If the push is rejected (non-fast-forward etc.):
If the rebase or the second push fails, the local commit is rolled back
with
git reset --hard HEAD~1(only if our commit is still HEAD), theworking tree returns to the pre-write state via git's own restoration,
and the response carries
pushed=false,rolled_back=true,git_error="...".Tests
tests/test_file_tools.py(31 cases, all pass alongside the existing 517):non-md extension, empty input, symlink-pointing-outside, valid path,
_safe_dirhappy + traversal.pattern, rejected dir.
missing required-field rejection, custom commit message, idempotent
no-op (same content twice), rollback on push failure for both create
and overwrite.
flock serialises commits, no git collision.
Tests run against a
tmp_pathgit repo with atmp_pathbare remote. Notraffic to
raphasouthall/brainfrom CI or dev machines.End-to-end verified via
uv run neurostack serve --transport http --port 8003against a throwaway tmp repo —
initialize → tools/listreports 24 tools;tools/callround-trip onvault_write_file→vault_list_files→vault_read_file→vault_delete_fileworks (writes commit and push, readsreturn content, deletes commit and push).
Deploy plan (post-merge — manual)
feat/vault-file-tools→mainon GitHub..neurostack-write.lockto~/brain/.gitignore(one-off).Out of scope
vault_append_file(deferred to v2 — write+overwrite covers the agentuse case)
index.mdupdates (the tools returnindex_update_needed+index_hint; the Copilot agent's system prompt will handle thefollow-up edit)
on
copilot.solidit.ukis untouched)