About mountable.io
mountable.io provides a shared POSIX filesystem and CLI for agents and teams to manage persistent files. It also runs as an MCP server for creating mount tickets and managing filesystems and sessions.
Key features
- 01Mount shared filesystems
- 02Create and inspect filesystems
- 03Issue one-time sandbox tickets
- 04List and revoke mount sessions
- 05Serve MCP over stdio
Use cases
- 01Provide persistent workspaces to agent sandboxes
- 02Share files across agents or teams
- 03Grant sandbox access with read-only tickets
MCP support
`mountable mcp` exposes organization, filesystem, usage, and mount-session tools over stdio. MCP clients can create mount tickets; mounting itself runs as a command in the sandbox.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
npx -y mountable-cliMOUNTABLE_API_KEYSecretOrganisation API key. Stays on the MCP host, never in the sandbox.Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.
Tools
7Saved tool list · updated 1d ago
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | the filesystem's name; names need not be unique |
| org_id | string | No | the organisation ID; optional |
Details
From the project READMEMountable CLI
mountable mounts a Mountable filesystem, a shared
POSIX filesystem for agents and teams, on any Linux machine or agent sandbox
and on a Mac (macOS 26, Apple Silicon), and manages filesystems, mount
tickets and sessions. It is also an MCP server.
npx -y mountable-cli login # npm
uvx mountable login # PyPI
curl -fsSL https://mountable.io/install.sh | sh # standalone binary
The CLI is one self-contained binary: it downloads nothing at mount time and
keeps the session's credentials in memory. Mounting needs FUSE (/dev/fuse)
on Linux; on macOS it needs nothing else installed and no sudo (see
macOS).
Authentication
- People:
mountable loginsigns this machine in with a device flow. - Backends and agents: set
MOUNTABLE_API_KEY=mtbl_…. An API key belongs to one organisation, and the commands use it. - Sandboxes: need no credentials, only a one-time ticket on stdin.
Commands that work inside an organisation take --org ORG_ID, then
MOUNTABLE_ORG, then the only organisation the caller has; with several,
they fail with org_required.
Commands
| Command | Does |
|---|---|
mountable orgs list | the caller's organisations |
mountable fs list | filesystems |
mountable fs create NAME | create a filesystem; prints its ID |
mountable fs usage FS_ID | stored bytes and files against quotas, 30-day traffic |
mountable ticket create FS_ID [--ro] [--idempotency-key KEY] [--max-duration D] | a one-time mount ticket for a sandbox; the session lasts D (such as 12h or 720h; default 12 hours, at most the API key's or login's expiry) |
mountable sessions list FS_ID | live mount sessions |
mountable sessions revoke SESSION_ID | revoke a session; its mount stops |
mountable mount [--ro] FS_ID DIR | mount with your login |
mountable mount --ticket-stdin DIR | mount with a ticket read from stdin |
mountable unmount DIR | unmount; on Linux, pending writes are committed |
mountable mcp | MCP server over stdio |
mountable login, logout, version, licenses |
Deleting a filesystem is done in the console.
Output, errors and exit codes
--jsonon every command writes one JSON document to stdout, with the API's field names (help --jsonwrites{"usage": …}). Diagnostics go to stderr. Nothing ever prompts. The exception ismountable mcp, whose stdout is the MCP protocol.mountable mount --jsonwrites JSON lines as the mount progresses:{"event":"mounted","path":…,"filesystem_id":…,"mode":…}once the mount is usable, and{"event":"unmounted","reason":"signal|unmount|revoked|expired|error","detail":…}when it ends. Wait formountedbefore using the directory.- Exit codes:
0success;1an error from the API, network, mount or credentials;2wrong usage. - Errors carry a stable code and a next step: on stderr, or with
--jsonas{"error":{"code":…,"message":…,"hint":…}}on stdout. For example,unauthenticated(runmountable loginor setMOUNTABLE_API_KEY),org_required(pass--org),not_found(check the ID withmountable fs list),payment_required(the hint is the console's billing link),rate_limited(waitretry_afterseconds). - The one-time ticket appears only in the result of
ticket create. API keys, login tokens, certificates and tickets never appear in errors or logs.
Retries
- Reads (
list,usage) are safe to retry. ticket createsends an idempotency key: the one given with--idempotency-key, or a random one it prints to stderr before sending. Retry with the same key after a timeout. When the outcome is unknown (network_errororoutcome_unknown), the error'sidempotency_keyis the key that request was sent with: retry with exactly that key. A replay returns the existing session without a ticket:- still
requested: its ticket was lost. The CLI revokes that session, creates a new one with a fresh key and reports both session IDs (replaced_session_id). activeor later: the ticket was used. The CLI revokes nothing and fails withticket_already_used; use a new key for another mount.
- still
fs createhas no idempotency key, and names need not be unique. After a timeout, runfs listand look for the name before creating again.sessions revokeis idempotent.- On HTTP 429 (
rate_limited), waitretry_afterseconds.
Agent workflow
The backend holds the API key; the sandbox gets only a ticket.
# Backend
export MOUNTABLE_API_KEY=mtbl_…
FS_ID=$(mountable fs create agent-workspace)
mountable ticket create "$FS_ID" --idempotency-key job-42 --json # → {"ticket": "mtbltk_…", …}
# Sandbox, with the ticket in MOUNTABLE_TICKET
mkdir -p /mnt/work
printf '%s\n' "$MOUNTABLE_TICKET" | mountable mount --ticket-stdin --json /mnt/work &
# wait for {"event":"mounted",…}, use /mnt/work, then:
mountable unmount /mnt/work
Any existing, writable directory works as the mount point; mount checks it
before it uses the ticket. The ticket is read from stdin, so it never appears
in an argument list.
macOS
On macOS 26 (Apple Silicon) there is nothing else to install: no macFUSE,
no kernel extension and no sudo. The CLI serves your filesystem to macOS's
own NFS client, so it works like any other folder:
FS_ID=$(mountable fs list --json | jq -r '.[0].id') # or the ID from `mountable fs list`
mkdir -p ~/mnt/work
mountable mount "$FS_ID" ~/mnt/work # add --ro for read-only
mountable mount stays running while the folder is mounted; use the folder
from another terminal or any app, and stop with Ctrl-C or
mountable unmount ~/mnt/work.
Your mount is private to your account: the CLI serves it over a socket only you can open and checks your user ID on every request. Revoking the session, or letting it expire, takes the mount away. Every write the Mac sends is saved by the service before macOS hears OK, so a crash of the CLI never loses a write macOS was told succeeded. Changes from other machines show up within moments.
Limits
- Writes are write-through, at about 7 MiB/s; each write macOS sends, up to 1 MiB, uses one unit of the filesystem's metadata rate.
- Files reach about 9.76 GiB written sequentially, less when written in many
small append-and-
fsynccycles. - A file takes about 9,999 writes that change existing bytes or append small pieces; after that, writes to it fail with "file too large". Keep databases (SQLite and the like) and other files rewritten in place on local disk, or copy them in and out. Editors save by writing a fresh file (or truncating first), so they are unaffected.
- A failed write is reported once per file, to the first
write,fsyncorcloseof it by any program. - Writes through
mmapcan fail without an error. - Other mounts are coordinated per write, not for the whole time a file is open.
- Finish writing (close or
fsyncyour files) before you unmount: a forced unmount of a busy mount can drop writes still in flight. - macOS creates
._files beside most files; to keep them out of git, runmkdir -p ~/.config/git && echo '._*' >> ~/.config/git/ignore(or append._*to the filegit config --global core.excludesFilenames, if set). - No Spotlight indexing, and locks (
flock,fcntl) are local to the mount.
Skill
A skill teaches coding agents this workflow, the error codes and the retry rules:
npx skills add writeitai/mountable
It lives in skills/mountable/SKILL.md.
MCP server
mountable mcp serves the tools list_organizations, list_filesystems,
create_filesystem, filesystem_usage, create_mount_ticket,
list_mount_sessions and revoke_mount_session over stdio
(create_mount_ticket takes an optional max_duration_seconds). It
authenticates like the CLI. Results are the API's JSON; errors return the
code and hint. Mounting stays a process in the sandbox:
create_mount_ticket returns the ticket and the command that mounts with it.
Give the server MOUNTABLE_API_KEY through its environment: inherited from
the environment the MCP client starts in, or set in the client's MCP
configuration under env. Never pass the key as a command-line argument,
where it would end up in process listings and shell history.
Claude Code (.mcp.json in the project; ${MOUNTABLE_API_KEY} is taken
from the environment Claude Code runs in, so the key stays out of the file):
{
"mcpServers": {
"mountable": {
"command": "npx",
"args": ["-y", "mountable-cli", "mcp"],
"env": { "MOUNTABLE_API_KEY": "${MOUNTABLE_API_KEY}" }
}
}
}
Claude Desktop (claude_desktop_config.json) and Cursor
(~/.cursor/mcp.json); keep these files private, since they hold the key:
{
"mcpServers": {
"mountable": {
"command": "npx",
"args": ["-y", "mountable-cli", "mcp"],
"env": { "MOUNTABLE_API_KEY": "mtbl_…" }
}
}
}
SDKs
Backends that read and write files without mounting use the files API through the SDKs, which create and refresh their own short token sessions from an API key.
Both are published as mountable-sdk, Python 3.10+ on PyPI and TypeScript on
npm (Node 18+, Bun, or Deno):
pip install mountable-sdk # import mountable_sdk
npm install mountable-sdk # import { Mountable } from "mountable-sdk"
from mountable_sdk import Mountable
fs = Mountable().filesystem("fs_…", mode="rw") # MOUNTABLE_API_KEY
info = fs.write("notes/a.md", "hello")
fs.write("notes/a.md", "hello again", if_match=info.etag)
print(fs.read("notes/a.md").data)
They live in sdk/python and sdk/typescript,
behave the same way (the shared cases are in sdk/contract.md),
and will be released together on tags sdk-vX.Y.Z.
LLM provider starters
examples/llm-providers has stdlib-only tools for
the OpenAI Agents SDK and Responses API, Anthropic Messages API and Claude
Code, Gemini ADK, and xAI Grok. The orchestrator holds MOUNTABLE_API_KEY
and the sandbox receives the one-time ticket on stdin. The guide is
https://mountable.io/docs/integrations.
Showcase demos
examples/showcase runs two demos: a planner sandbox
and a coder sandbox sharing one directory, then a read-only laptop mount;
and a revocation kill switch. python3 examples/showcase/relay.py --dry-run
prints the sequence without calling the API. The same illustrations play at
https://mountable.io/showcase and do not create a filesystem.
Notebook starters
examples/notebooks has a Jupyter notebook for Colab,
Kaggle, Deepnote and Hugging Face Spaces and a Marimo notebook. They keep
datasets, feature caches and checkpoints in a Mountable folder with
mountable-sdk, so the files stay when the kernel restarts.
Multi-agent coding harness
examples/research-showcases/multi-agent-harness
runs a planner, coder and verifier on one shared folder. A signed file-change
webhook triggers a read-only verifier mount. Run harness.py offline by
default, or use --live to create a filesystem with a monthly spend cap and
rate limit.
Licenses
mountable licenses prints the licenses and notices of the third-party code
compiled into the CLI, which also ship as
THIRD_PARTY_NOTICES with every package and release
archive.
Documentation: https://mountable.io/docs.
Development
go test ./...
go run ./tools/notices > THIRD_PARTY_NOTICES # after changing dependencies
packaging/build.sh 0.0.0 # binaries, release archives, npm tarball and wheel in dist/
# SDKs
(cd sdk/python && uv run ruff format --check . && uv run ruff check . && uv run pyright && uv run pytest)
(cd sdk/typescript && npm ci && npm run typecheck && npm test && npm run build)
Releases are cut by pushing a tag vX.Y.Z (CLI) or sdk-vX.Y.Z (SDKs); see
packaging/README.md.
The Mountable service itself is operated by WriteIt.ai s.r.o.; this repository
contains the open-source client only.
License
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.