delete_note
Delete a note from your Obsidian vault. Choose soft-delete to move to .trash or permanent removal with no recovery path.
Instructions
Delete a note from the vault. Requires a readwrite API key.
By default this is a soft-delete: the file is moved to
.trash/<YYYYMMDD-HHMMSS>-<basename> inside the vault root. The indexer
skips dot-prefixed directories, so search and embeddings drop the note
automatically on the next reindex pass (≤ 5 minutes). Soft-deleted files
accumulate in .trash/ — emptying that directory is the user's
responsibility.
With permanent=True, the file is os.unlink-ed directly with no
recovery path inside this server. Existing backups are the rollback story.
Dangling backlinks left behind by a delete are surfaced via
get_backlinks and find_orphans. See get_vault_guide for context.
Args: path: Vault-relative path to the note. permanent: If True, unlink instead of soft-deleting.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | ||
| permanent | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/mcp_server/tools.py:960-1001 (handler)Core implementation of delete_note. Soft-deletes by moving to .trash/ with a timestamp prefix, or permanently deletes via os.unlink when permanent=True.
@_tracked("delete_note", ["path", "permanent"]) async def delete_note_impl(path: str, permanent: bool = False) -> str: """Soft-delete a note to `.trash/`, or `os.unlink` it when `permanent=True`.""" if err := _require_write(): return err from src.services.vault import _vault_root, validate_path uid = current_user_id.get() try: full_path = validate_path(path, user_id=uid) except ValueError as e: return str(e) if not full_path.is_file(): return f"Note not found: {path}" if permanent: try: os.unlink(full_path) except OSError as e: return f"Permanent delete failed: {e}" return f"Permanently deleted: {path}" vault = _vault_root(uid) trash = vault / ".trash" trash.mkdir(parents=True, exist_ok=True) timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") base = f"{timestamp}-{full_path.name}" dest = trash / base counter = 1 while dest.exists(): dest = trash / f"{timestamp}-{counter}-{full_path.name}" counter += 1 try: os.replace(full_path, dest) except OSError as e: if getattr(e, "errno", None) == 18: shutil.move(str(full_path), str(dest)) else: return f"Soft-delete failed: {e}" rel = dest.relative_to(vault).as_posix() return f"Soft-deleted: {path} → {rel}" - src/mcp_server/server.py:349-370 (registration)MCP tool registration/decorator: @mcp.tool() on async def delete_note(path, permanent) – delegates to delete_note_impl.
@mcp.tool() async def delete_note(path: str, permanent: bool = False) -> str: """Delete a note from the vault. Requires a readwrite API key. By default this is a soft-delete: the file is moved to `.trash/<YYYYMMDD-HHMMSS>-<basename>` inside the vault root. The indexer skips dot-prefixed directories, so search and embeddings drop the note automatically on the next reindex pass (≤ 5 minutes). Soft-deleted files accumulate in `.trash/` — emptying that directory is the user's responsibility. With `permanent=True`, the file is `os.unlink`-ed directly with no recovery path inside this server. Existing backups are the rollback story. Dangling backlinks left behind by a delete are surfaced via `get_backlinks` and `find_orphans`. See `get_vault_guide` for context. Args: path: Vault-relative path to the note. permanent: If True, unlink instead of soft-deleting. """ return await delete_note_impl(path, permanent=permanent) - src/mcp_server/server.py:5-7 (registration)Import of delete_note_impl from src.mcp_server.tools into the server registration file.
from src.mcp_server.tools import ( create_note_impl, delete_note_impl,