agents' s3 living documents
One notebook, many agents — and their humans. Two operations:
The concurrency guarantees given by slivingdoc allows for agents to seamlessly share context regardless of where, and when, they are run. Only restriction being access to a S3-compatible bucket.
Leave a note for your agent, or listen in to your agents' notes.
pull
Pull the notebook into a directory
pull
mcp:
notes_pull()
→ OK
cli:
slivingdoc pull
→ OK
reads the accepted state from the bucket — one small manifest
called current, plus the immutable packs it
references — and writes the notebook into your directory as
ordinary UTF-8 text files.
No path needed: every server owns one directory and names it in each result. That directory is a checkout, not a place inside the notebook — it mirrors the whole notebook and remembers its own baseline, what it last saw. Unpublished local edits are merged with the accepted state, never overwritten.
The caller never sees Git object IDs, pack names, or S3 keys. The directory is the whole interface.
edit
Edit with ordinary file tools
Between calls there is no protocol at all. Agents — as many as you
like — read and write the files with the tools they already have:
a text editor, sed, an LLM's file tools.
The notebook is directories and UTF-8 text files — binary files are rejected, never mangled. Bytes and line endings are preserved. slivingdoc does nothing until the next call.
As such, humans can work in the same directory: open the notebook in your editor and write alongside your agents. Its perfectly possible to use slivingdoc as a agent-free distributed notebook as well.
commit
Commit publishes your changes
commit
mcp:
notes_commit(message)
→ OK
cli:
slivingdoc commit -m "note"
→ OK
merges your files with the latest accepted state, packs the
result, and uploads the pack. Packs are immutable: uploading one
publishes nothing.
Your change is the delta since the checkout's baseline — so a
directory that was never pulled is refused with
INVALID_REQUEST before any network work. Pull once
first: commit publishes intent, not a guess.
Publication is a single conditional replace of the small
current manifest — If-Match on the ETag
the writer observed. The bucket is the durability boundary;
everything local is a rebuildable cache.
race
Concurrent writers cannot clobber each other
Two agents commit at once. Both observed current at
ETag e1; S3 accepts exactly one replacement for it.
The loser gets a precondition failure — expected contention, not
an error.
The losing writer downloads what it missed, merges against the new head, and retries with a fresh proposal. No lock object, no lease, no clock. Accepted state is never silently overwritten.
conflict
Conflicts are just text
When your change and an accepted remote change touch the same
lines, the call returns CONTENT_CONFLICT and writes
standard conflict markers into the affected files. The structured
error names every file and marker line range.
Resolve with the same file tools you edit with: keep the text you
want, delete the marker lines, call
notes_commit again. A complete marker block is never
accepted into the notebook.
checkpoint
Checkpoints keep cold starts fast
Every commit uploads one small incremental pack. Left alone, that chain would make a fresh server download thousands of objects. So after a configurable count (1,024 by default), slivingdoc compacts the stable prefix into one complete-state checkpoint.
A new reader needs one checkpoint plus the short tail after it. Checkpoints never block writers, and a failed checkpoint never touches accepted state.
Get started
Node 22+ and an S3-compatible bucket. Works in any MCP host — or with none at all: the same binary is a CLI.
1
Point it at a bucket
That is the whole configuration. AWS S3, Tigris, MinIO, or anything S3-compatible that supports conditional writes. Credentials come from the normal AWS chain.
2
Add it to your MCP host
The npm launcher downloads the right native binary for your platform, verifies its SHA-256, and runs it. No Git, no toolchain.
3
Let agents pull and commit
Two tools, no arguments to coordinate, plain files, OK
on success. Conflicts come back as editable text, never as a stuck
state.
Raw MCP configuration for any compatible host
{
"mcpServers": {
"slivingdoc": {
"command": "npx",
"args": [
"-y", "slivingdoc", "serve",
"--bucket", "my-notes"
],
"env": {
"AWS_ACCESS_KEY_ID": "<your-access-key-id>",
"AWS_SECRET_ACCESS_KEY": "<your-secret-access-key>",
"AWS_REGION": "us-east-1"
}
}
}
}
Each server takes its own scratch directory and discards it on exit —
the notebook lives in the bucket. Pass --workspace-root
to pin a fixed directory that humans and agents share instead. Prefer
AWS_PROFILE over pasted keys when ~/.aws is
configured, and for a non-AWS store add
AWS_ENDPOINT_URL_S3.
claude mcp add slivingdoc \ --env AWS_ACCESS_KEY_ID=\ <your-access-key-id> \ --env AWS_SECRET_ACCESS_KEY=\ <your-secret-access-key> \ -- npx -y slivingdoc serve \ --bucket my-notes
Add --scope project to write a shared
.mcp.json instead of your user config.
codex mcp add slivingdoc \ --env AWS_ACCESS_KEY_ID=\ <your-access-key-id> \ --env AWS_SECRET_ACCESS_KEY=\ <your-secret-access-key> \ -- npx -y slivingdoc serve \ --bucket my-notes
Stored in ~/.codex/config.toml under
[mcp_servers.slivingdoc]. Verify with
/mcp.
gemini mcp add -s user \ -e AWS_ACCESS_KEY_ID=\ <your-access-key-id> \ -e AWS_SECRET_ACCESS_KEY=\ <your-secret-access-key> \ slivingdoc -- npx -y slivingdoc serve \ --bucket my-notes
Written to ~/.gemini/settings.json; drop
-s user for a per-project entry.
{
"mcp": {
"slivingdoc": {
"type": "local",
"command": [
"npx", "-y", "slivingdoc", "serve",
"--bucket", "my-notes"
],
"enabled": true,
"environment": {
"AWS_ACCESS_KEY_ID": "<your-access-key-id>",
"AWS_SECRET_ACCESS_KEY": "<your-secret-access-key>"
}
}
}
}
Goes in opencode.json at the project root, or
globally in ~/.config/opencode/.
pi install npm:pi-mcp-adapter
pi has no built-in MCP. The adapter reads the standard
.mcp.json — paste the spec above into your project's
.mcp.json and restart pi.
npx -y slivingdoc pull notes --bucket my-notes # edit UTF-8 text files under notes/ npx -y slivingdoc commit notes --bucket my-notes -m "meeting summary"
The same two operations, driven by hand — humans and cron jobs
share the notebook without an MCP host. Same flags and environment
as serve. The path is optional and defaults to the
working directory; a relative one resolves against it.
No bucket yet?
Tigris is plug-and-play: a
free tier, one global endpoint, and the conditional writes
slivingdoc's startup probe requires. Set
AWS_ENDPOINT_URL_S3 to
https://t3.storage.dev and AWS_REGION to
auto. For offline evaluation, the
SeaweedFS example
runs a local bucket in one docker compose up.