feat: add create_vault_directory and move_document MCP tools

- create_vault_directory: create directories in a vault (with parents=True).
  Idempotent — returns 'exists' if directory already present; errors
  if the path exists but isn't a directory.
- move_document: move or rename a document within a vault. Creates parent
  directories at destination. Errors if src missing, src isn't a file,
  or dest already exists.
This commit is contained in:
Helmsman Quill 2026-07-07 20:45:44 -04:00
parent 5c0ee23350
commit 39fae1501b

View file

@ -530,6 +530,75 @@ def edit_document(vault_name: str, doc_path: str, content: str) -> dict[str, Any
} }
@mcp.tool()
def create_vault_directory(vault_name: str, dir_path: str) -> dict[str, Any]:
"""FILESYSTEM TOOL (not memory) — Create a new directory in a vault on disk.
Use before create_document when building out folder structures, or for
organizing notes into subdirectories.
Args:
vault_name: Name of the vault.
dir_path: Relative path for the new directory (e.g. 'Session-12' or 'NPCs/Dragon-Cult').
Returns the created directory path.
"""
# Normalize: strip leading slash, handle backslashes
clean = dir_path.lstrip("/\\").replace("\\", "/")
target = _resolve_doc(vault_name, clean)
if target.exists():
if not target.is_dir():
return {"error": f"Path exists but is not a directory: {dir_path}", "vault": vault_name}
return {"vault": vault_name, "path": str(target.relative_to(_vault_path(vault_name))).replace("\\", "/"),
"action": "exists", "note": "Directory already exists"}
target.mkdir(parents=True, exist_ok=True)
return {
"vault": vault_name,
"path": str(target.relative_to(_vault_path(vault_name))).replace("\\", "/"),
"action": "created",
}
@mcp.tool()
def move_document(vault_name: str, src_path: str, dest_path: str) -> dict[str, Any]:
"""FILESYSTEM TOOL (not memory) — Move or rename a document within a vault.
Moves a document from src_path to dest_path. Both paths are relative to the
vault root. Automatically creates parent directories at the destination if
they don't exist.
Args:
vault_name: Name of the vault.
src_path: Current relative path of the document (e.g. 'Notes/draft.md').
dest_path: Target relative path (e.g. 'Archive/draft.md' or 'Notes/final.md').
Returns the move result with both old and new paths.
"""
src = _resolve_doc(vault_name, src_path)
if not src.exists():
return {"error": f"Source document does not exist: {src_path}", "vault": vault_name}
if not src.is_file():
return {"error": f"Source is not a file: {src_path}", "vault": vault_name}
dest = _resolve_doc(vault_name, dest_path)
if dest.exists():
return {"error": f"Destination already exists: {dest_path}", "vault": vault_name}
dest.parent.mkdir(parents=True, exist_ok=True)
src.rename(dest)
vault = _vault_path(vault_name)
return {
"vault": vault_name,
"old_path": src_path.lstrip("/\\").replace("\\", "/"),
"new_path": str(dest.relative_to(vault)).replace("\\", "/"),
"action": "moved",
}
# ── Entrypoint ──────────────────────────────────────────────────────────── # ── Entrypoint ────────────────────────────────────────────────────────────
def main(): def main():