Layven
About
Shared drive for your agents and teammates.
Details
- Author
- layvenio
- Categories
- File Management
Jump to
Setup
Install Layven in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/layvenio/layven-mcp
Follow the installation instructions in the repository README, then restart your MCP client.
Layven is a shared drive your agents mount over MCP. Every write is versioned, every action is attributed to the agent that made it, and anything can be rolled back.
One config block, no agent code changes. Hosted athttps://api.layven.io/mcp.
claude mcp add --transport http layven https://api.layven.io/mcp --header "Authorization: Bearer agd_your_agent_token"
The point of the product shows up on the second connection. Create a second token, connect a different tool with it, and have that tool read the file the first one just wrote. Both agents see the same drive, and the activity feed says which one did what. Walkthrough:examples/two-agent-handoff.md.
claude mcp add --transport http layven https://api.layven.io/mcp --header "Authorization: Bearer agd_your_agent_token"
Pastehttps://api.layven.io/mcpand authenticate with OAuth. That UI has no custom-header field, which is why OAuth exists.
Same as Claude.ai: pastehttps://api.layven.io/mcpand authenticate with OAuth.
{ "mcpServers": { "layven": { "url": "https://api.layven.io/mcp", "headers": { "Authorization": "Bearer agd_your_agent_token" } } } }
[mcp_servers.layven] url = "https://api.layven.io/mcp" http_headers = { "Authorization" = "Bearer agd_your_agent_token" }
Same JSON as Cursor, but the key ishttpUrlinstead ofurl, because Gemini readsurlas an SSE endpoint.
{ "mcpServers": { "layven": { "httpUrl": "https://api.layven.io/mcp", "headers": { "Authorization": "Bearer agd_your_agent_token" } } } }
{ "mcpServers": { "layven": { "url": "https://api.layven.io/mcp", "headers": { "Authorization": "Bearer agd_your_agent_token" } } } }
There are two ways in, and both resolve to the same token record, so permissions, revocation and the audit trail are identical either way.
Static bearer token.SendAuthorization: Bearer agd_...on every request. Create it in the console; the secret is shown once.
OAuth 2.1, for clients whose UI has no custom-header field:
Consent mints an ordinary token, which you can see and revoke in the console like any other.
- POST https://api.layven.io/mcp, Streamable HTTP, stateless.
- Every POST is fully independent. No session id, no session resumption, no separate SSE endpoint, no stdio.
- GET and DELETE return HTTP 405 with exactly this body:
{"jsonrpc":"2.0","error":{"code":-32000,"message":"Method not allowed (stateless)"},"id":null}
Every tool takes an optionalworkspace, the workspace slug. A token granted exactly one workspace may omit it.listwith noworkspacefans out over every workspace the token can reach.
Onlydeleteis annotated destructive, and it writes a tombstone version thatrestorebrings back.write,delete,lockandunlockare annotated idempotent;edit,move,restore,request_uploadandfinalize_uploadare not. No tool reaches outside Layven.
NOT_FOUND(unresolvable workspace),PERMISSION_DENIED,RATE_LIMITEDandINTERNALcan come back from any tool, so theErrorsline under each tool below names what is specific to that tool.
List a folder, or search every granted workspace for paths matching a substring.
workspace? string workspace slug; omit to fan out over every granted workspace path string default "" (workspace root) recursive boolean default false include_deleted boolean default false limit integer 1..1000, default 500, applied per workspace cursor? string opaque; requires an explicit workspace q? string 1..1024 chars, case-insensitive substring of the full path; implies recursive
{ "results": [ { "workspace": { "id": "...", "slug": "research" }, "entries": [ { "path": "notes/todo.md", "type": "file", "size": 1284, "version": 7, "modified_at": "2026-08-05T09:12:44Z", "modified_by_label": "research-agent", "by_actor": "agent", "deleted": true } ], "next_cursor": "..." } ] }
typeis"file"or"folder".size,version,modified_at,modified_by_label,by_actoranddeletedare optional per entry.
- resultsis always an array of per-workspace blocks, even when the token is granted exactly one workspace. Do not write a client that expects a bareentriesarray.
- Passingcursorwhile fanning out returnsINVALID_PATHwithreason: "cursor_requires_workspace". Pin the workspace before you paginate.
- Folders are synthesized from path prefixes. There are no folder records to create or delete.
Read a file, either the current version or an older one.
workspace? string path string version? integer defaults to the current version
Returns, inline, when the raw size is 262144 bytes or less and the mime type is text:
{ "path": "notes/todo.md", "version": 7, "size": 1284, "mime_type": "text/markdown", "encoding": "utf8", "content": "..." }
Returns, for anything larger or binary:
{ "path": "datasets/corpus.tar.gz", "version": 3, "size": 431912448, "mime_type": "application/gzip", "download_url": "https://...", "url_expires_at": "2026-08-05T09:27:00Z" }
Errors:NOT_FOUND(withdeleted: trueandlast_versionwhen the path is tombstoned),INVALID_PATH.
- The response shape switches on size and mime type, not on a flag you pass. Above 256 KiB, or for any non-text file, you get adownload_urlvalid for 15 minutes instead of inline content. Handle both shapes.
Search file contents with a regular expression.
workspace? string path? string search a single file prefix? string search one folder; omit both path and prefix to search the whole workspace pattern string min 1 char, RE2 syntax case_sensitive boolean default false context_lines integer 0..10, default 2 max_matches integer 1..200, default 50
{ "matches": [ { "path": "notes/todo.md", "line": 42, "text": "TODO: rotate the staging token", "before": ["..."], "after": ["..."] } ], "files_scanned": 128, "files_skipped_binary": 3, "files_skipped_too_large": 0, "truncated": false }
Errors:INVALID_PATTERN,INVALID_PATH.
- Patterns are RE2, so there are no backreferences and no lookaround. An invalid pattern returnsINVALID_PATTERN.
- Matches are found within single lines only. A pattern spanning a newline will never match.
- One entry per matching line, likegrep -n, no matter how many occurrences that line holds.
- truncated: truemeans the scan hit its budget and the results are partial. Narrow theprefixor tighten the pattern.
- Line numbers are display-only. Never paste one intoedit'sold_str.
List the versions of one file, newest first.
workspace? string path string limit integer 1..1000, default 50 cursor? string digits only
{ "versions": [ { "version": 7, "op": "write", "size": 1284, "content_hash": "...", "created_at": "2026-08-05T09:12:44Z", "by_label": "research-agent", "by_actor": "agent", "moved_from": "drafts/todo.md", "purged": false, "restorable_until": "2027-08-05T09:12:44Z" } ], "next_cursor": "182734" }
opis one ofwrite,edit,move,delete,restore.moved_from,by_actorandrestorable_untilare optional.
- Newest first, solimit: 1is the cheapest way to confirm a write landed.
- next_cursoris an opaque string, never a number. It is absent on the last page, which is how you know to stop.
- purged: truemeans retention released the content of that version. The row stays for the audit trail, butrestoreto it returnsNOT_FOUNDwithreason: "version_purged".
List what changed in a workspace and which agent changed it.
workspace? string since? string RFC 3339 timestamp path_prefix? string limit integer 1..1000, default 100 cursor? string
{ "events": [ { "at": "2026-08-05T09:12:44Z", "actor": "agent", "by_label": "research-agent", "by_actor": "agent", "op": "write", "path": "notes/todo.md", "old_path": "drafts/todo.md", "version": 7 } ], "next_cursor": "9014522" }
actoris one ofagent,user,system. Newest first.
opiswrite,edit,move,delete,restore,purge,lock,unlockorgrep. System sweeps also recordpurge_retentionandpurge_workspace_delete, both with no path. Onlywrite,edit,move,deleteand a single-filerestorecarry aversion; on the rest it is null. Note thatpurgecarries a real path or folder prefix, so it matches apath_prefixfilter: if you are auditing a folder from this feed, a purge shows up alongside the writes.
- next_cursoris an opaque string, never a number. It is absent on the last page, which is how you know to stop.
- This is a poll. There are no webhooks, no triggers and no push. Agents coordinate by callingactivityon an interval, which fits comfortably inside the rate limit at seconds-to-minutes cadence.
- sinceis a timestamp, not a cursor, and two events can share one. Expect occasional re-delivery and make the work idempotent. Seeexamples/two-agent-handoff.md.
Create or replace a file with inline content.
workspace? string path string content string encoding "utf8" | "base64" default "utf8" mime_type? string expected_version? integer fail instead of overwriting a concurrent change override_lock boolean default false
Inline cap: 1 MiB decoded. Anything larger goes throughrequest_upload.
{ "path": "notes/todo.md", "version": 8, "size": 1301, "content_hash": "...", "deduplicated": false, "unchanged": true }
unchangedis present only when it applies.
Errors:INVALID_PATH,VERSION_CONFLICT,LOCKED,QUOTA_EXCEEDED,TOO_LARGE.
- Writing content byte-identical to thecurrentversion is a no-op: it returnsunchanged: trueand creates no new version. For a scheduled job that is worth logging loudly, because it usually means the generator did not run.
- Content identical to anolderversion does create a new version.
- Writing to a tombstoned path resurrects it.
Apply string replacements to an existing text file without resending the whole body.
workspace? string path string edits array of 1..20 objects: old_str string min 1 char new_str string replace_all boolean default false expected_version? integer override_lock boolean default false
{ "path": "notes/todo.md", "version": 9, "edits_applied": 2, "replacements": [1, 3], "size_before": 1301, "size_after": 1288, "context": "..." }
unchangedis present only when it applies.
Errors:STRING_NOT_FOUND,STRING_NOT_UNIQUE,NOT_EDITABLE,TOO_LARGE_FOR_EDIT,VERSION_CONFLICT,LOCKED,QUOTA_EXCEEDED,NOT_FOUND.
- Edits apply in order and commit atomically asonenew version, so a 20-edit call is one entry inhistory, not twenty. If any edit fails, none of them land.
- Eachold_strmust match exactly once, otherwise you getSTRING_NOT_FOUNDorSTRING_NOT_UNIQUE. Setreplace_allwhen you mean every occurrence.
- Because edits apply in order, a laterold_strmust match the text as the earlier edits left it.
workspace? string from string to string expected_version? integer overwrite boolean default false override_lock boolean default false
{ "from": "drafts/todo.md", "to": "notes/todo.md", "version": 1 }
versionis the new version at the destination.
Errors:NOT_FOUND,INVALID_PATH,VERSION_CONFLICT(withreason: "destination_exists"),LOCKED.
- The token needs write access tobothpaths. A prefix rule that covers the source but not the destination fails withPERMISSION_DENIED.
- A live destination withoverwrite: falsereturnsVERSION_CONFLICTwithreason: "destination_exists". That failure is useful: two workers racing to claim the same item canmoveit, and the loser gets a clean error instead of duplicated work.
workspace? string path string expected_version? integer override_lock boolean default false
{ "path": "notes/todo.md", "version": 10, "unchanged": true }
unchangedis present only when it applies.
Errors:NOT_FOUND,INVALID_PATH,VERSION_CONFLICT,LOCKED.
- This writes a tombstone version, it does not erase content. The file stays recoverable withrestorefor as long as your plan's version history window allows.
- Writing to the same path later resurrects it as a new version on the same history.
- listhides tombstoned paths unless you passinclude_deleted: true.
Restore one file to an earlier version, or a whole folder to a point in time.
workspace? string path string to_version integer override_lock boolean default false
workspace? string prefix string at string RFC 3339 timestamp in UTC, e.g. 2026-08-05T14:04:55Z override_lock boolean default false
{ "path": "notes/todo.md", "version": 11 }
{ "restored": 42, "deleted": 3, "affected_paths": ["content/pricing.md", "content/index.md"], "total": 45 }
affected_pathsis a capped sample;totalis the true count.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.

