write_file
Save generated files, including PDFs or images, into the Obsidian vault with atomic, non-clobbering writes. Supports base64 or text content and optional overwrite with hash verification.
Instructions
Write a file into the vault — including non-markdown (e.g. save a
generated PDF or image). Requires write permission — a readwrite API key, or an OAuth token
carrying the readwrite scope.
Peer to create_note/edit_note, which stay markdown-only.
content carries the bytes: with encoding="base64" (default) it is
base64-decoded to raw bytes; with encoding="text" it is written verbatim
as UTF-8. The write is atomic — the bytes are staged and flushed before
anything is published — missing parent folders are created, and content over
MAX_FILE_WRITE_BYTES (default 25 MB, decoded length) is refused.
No-clobber by default: writing over an existing file requires
overwrite=True. The default publishes by linking a staged, never-named
inode into place in one kernel-atomic step, so an existing file cannot be
replaced; overwrite=True publishes with a single same-directory rename
instead. A vault filesystem that cannot stage an unnamed file refuses the
no-clobber write with an error naming
VAULT_ALLOW_NAMED_STAGING_FALLBACK, rather than staging under a visible
name. Any path with a component starting with . (dot-directories and
dot-files alike) and path traversal are rejected; invalid
base64 errors without writing anything. A path whose final component is a
symlink is refused, naming its target, so overwrite=True cannot clobber a
file through an alias; symlinked folders inside the vault work normally.
The MCP transport also bounds the whole request body (sized so a base64
write at the cap always gets through). Base64 is therefore the always-safe
encoding: encoding="text" content whose JSON escaping inflates past that
bound is rejected by the transport with a bare HTTP 413 before this tool
runs — send such content as base64 instead.
Optional expected_hash binds an existing whole file on overwrite=True.
Obtain its canonical sha256:<64 lowercase hex> value from read_file's
base64 header, read_file(hash_only=True), or a prior write's success.
Syntax is checked before path work; a hash with no-clobber or a missing
destination is no_incumbent. An over-cap incumbent cannot be guarded.
A stale hash refuses before publication; matching also enables the in-call
comparison, which refuses an edit arriving during this call as
concurrent_write. Without a hash, overwrite remains unconditional unless
the deployment requires preconditions. Creation is exempt.
Success reports the hash of the bytes this call published, not necessarily what remains when the response arrives. Over-cap incumbents or results omit the hash without failing an otherwise permitted unguarded write.
Args: path: Vault-relative destination path (e.g. "Outputs/report.pdf"). content: File contents — base64 string (default) or UTF-8 text. encoding: "base64" (default) or "text". overwrite: If True, replace an existing file. Off by default. expected_hash: Optional whole-file raw-byte digest of the incumbent.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | ||
| content | Yes | ||
| encoding | No | base64 | |
| overwrite | No | ||
| expected_hash | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |