Selvage for agents
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Selvage for agentshost my ~/notes folder and give me the invite link"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Selvage for agents
A Model Context Protocol server that lets an agent take part in a Selvage session: host a folder, or join an invite link, and read, edit and write the files the room shares.
It is a client of the session protocol, like the editor clients: it drives the same sync engine and speaks the same wire, so an agent and a person edit the same documents and see each other's changes.
Status: v1 in progress. Hosting, joining by invite, and editing in both directions work today.
Get it working
You need:
Node 22.18 or newer on
PATH. The sync engine runs at every end of a session, so an agent that edits a room runs the same code an editor does.A
selvagedto connect to. Start one and note the address it prints.
It is a stdio server — one JSON-RPC message per line — and its diagnostics go to stderr. There are two ways to start it, and they start the same program. An agent should have it configured in its client, not drive it by hand.
From a checkout. npm ci installs the dependencies and builds the runnable artifact, because
prepare runs npm run build: a bundle of the sources and the vendored engine, written to
dist/. Point a client at it — this is the mcp.json entry (mcpServers is the key every
client reads):
{
"mcpServers": {
"selvage": {
"command": "node",
"args": ["/path/to/mcp/dist/main.js"],
"cwd": "/path/to/mcp",
"env": {
"SELVAGE_SERVER_URL": "ws://127.0.0.1:8080",
"SELVAGE_DISPLAY_NAME": "agent",
"SELVAGE_NOTIFY_CHANGES": "true"
}
}
}
}pi's own command line writes the same entry, which is the one-liner to hand an agent that runs
under pi:
pi mcp add selvage \
--env SELVAGE_SERVER_URL=ws://127.0.0.1:8080 \
--env SELVAGE_DISPLAY_NAME=agent \
--cwd /path/to/mcp \
-- node /path/to/mcp/dist/main.js-l writes .pi/mcp.json in the project rather than the global ~/.pi/agent/mcp.json. Either
way the server is a tool the client starts and the agent just calls.
dist/main.js carries a shebang and the executable bit, so it is also the command bin names:
selvage-mcp and node <path>/dist/main.js are the same process. npm run build alone rebuilds
it. src/main.ts is the same server run straight from the sources — Node 22.18 strips the types
as it loads it — so node <path>/src/main.ts works too, with no build at all.
From git, with no checkout. npx fetches the repository, installs it, which runs the same
build, and starts the command the package declares:
{
"mcpServers": {
"selvage": {
"command": "npx",
"args": ["-y", "github:selvage-protocol/mcp"],
"env": {
"SELVAGE_SERVER_URL": "ws://127.0.0.1:8080",
"SELVAGE_DISPLAY_NAME": "agent"
}
}
}
}There is no cwd to set: the server reads its own package.json from where it was installed, and
everything else it needs arrives through the environment or through a tool call.
The package is not on npm, and npx selvage-mcp does not exist. It stays private: true:
publishing needs an account and a token for the scope, and that is not a decision this repository
takes on its own. The git form above is the same package a publish would carry.
SELVAGE_SERVER_URL is the Selvage server's address, and it matters because it is the only way
the address arrives without being asked for: a client starts this server as a subprocess and has
nothing to answer a question with, so session_host falls back to this variable when its
server_url argument is left out, and refuses, naming both, when neither is there. A bare
ws://host:port is enough — the engine adds the /session path — and what you hand it is the
address selvaged prints. SELVAGE_DISPLAY_NAME is the name the other seats see; without it
this server seats itself as agent.
SELVAGE_PUBLISH_CURSOR is the fallback for a publish_cursor argument on either session tool,
and it is off unless it is set to one of 1, true, yes or on. With it on, the server
publishes a cursor for the path each read_file, edit_file and write_file touches — a
position in the room's document, renewed on the room's awareness clock and cleared when the
session ends — so a person sharing the file can see which file the agent has open and roughly
where in it. It is off by default because a caret in someone's editor says the agent is there,
and an agent that never asked for that should not have one invented for it. It is a position and
nothing more: it says the last tool call landed there, not that the agent is looking.
A first session:
Start
selvagedand note the address it prints.Ask the agent to call
session_hostwith the folder to share. It answers with two links, each carrying the key fragment:page:is the link a person opens in a browser, andinvite:is the link a client joins with.Give a person the
page:link (:SelvageJoin <link>in the Neovim client, or the VS Code one, or a browser). Give a peer theinvite:link, or paste either intosession_join— both forms name the same room, token and fragment. The fragment is the room's key, so hand out a link only to whoever should join, and hand it over whole.Both read and edit the same files. A guest's edit arrives in the room and on the host's disk; a host's write arrives in every guest's
read_file.To work live rather than poll, have the agent call
wait_for_change: it blocks until the other seat edits something and answers with the changed region, not the file. See "An agent as a live collaborator" below.
Related MCP server: pairdown
Tools
| Share a folder and host the room. The folder is walked once, here. Answers with the room id, the path count, and two links that carry the same key fragment: a |
| Join a room from either link |
| Where this server stands: room, role, mode, granted paths, open documents, participants, and any problem the session could not fix. Answers with no session too. |
| Leave the room and free the process to host or join again. A host leaving closes the room for everyone. |
| The room's shared paths, sorted, filtered by |
| The room's text for one shared path. |
| Replace |
| Replace a shared file's whole text. |
| One line per seat: name, role, whether it is this connection, the cursor a seat publishes — which this server does only when asked to — and where that peer is: the document its cursor names and the line and column the engine resolved. A peer that publishes nothing says so. |
| Block until the room changes a watched path, then answer with a bounded partial report — the changed region as a small diff hunk, not whole files. Returns at the first change or at its deadline, and reports what changed since the previous wait. |
An agent as a live collaborator
An agent that works with a person has a different loop from one that answers a question: it has
to be told when the room moves, and it must not be handed the whole document every time. That
loop is session_host or session_join, then wait_for_change, then edit_file on what the
wait reported, then wait_for_change again.
wait_for_change blocks until a watched path actually changes, or until its deadline. It is
always bounded: it never polls and never spins, a deadline that passes says no change within N ms rather than looking like a change, and timeout_ms is capped at five minutes. Watching is
the whole grant and the open documents unless paths names a subset.
What it returns is a partial update, never a file. For each path that changed it gives the
changed region as a small diff hunk — - lines for the old text, + for the new, a few lines of
context, line numbers — and the path's new length in characters. A path that appeared or
disappeared is said in words instead of a hunk. The report is capped in size and says how many
changes it left out; those stay pending and the next wait carries them. It reports what changed
since the previous wait, not since the call, so a change that lands while the agent is thinking
between two waits is reported by the next one rather than swallowed.
edit_file is the writing half: the agent applies the smallest change to the region the wait
showed it, and the room and the other seats see it. The agent's own edit is not reported back to
it; only what the room did after is.
Being told without asking
With notify_changes: true on session_host or session_join — or SELVAGE_NOTIFY_CHANGES set
in the server's environment, the way SELVAGE_SERVER_URL works — the server also tells its client
about room activity as it happens, as MCP notifications/message messages: document changes with
the same bounded hunk, and peers joining and leaving. It is off by default. The messages are rate
limited (at most one per path per half second, and at most twenty in a second across the room),
never carry the invite, and are a second channel rather than a replacement for wait_for_change:
a notification is best-effort, and the wait still owes the change it announced.
A client that wants none of them can set a higher level with logging/setLevel; the messages are
all info, so a warning floor silences them. That method exists only because the logging
capability is declared, and the capability is declared only when this server has a channel to
send through.
Where the other people are
list_participants reads the awareness peers publish — which is anchored to relative positions —
and reports each peer's document and selection as a line and column, so an agent can see where a
person is typing and answer them there:
grace (guest, publishes a cursor) in src/main.rs selecting line 12, column 3 to line 12, column 9
ada (host, this connection, no cursor seen)A peer that publishes nothing is reported as such, and one whose anchors this replica cannot place is reported with its document and no position, rather than at a position this server invented.
What it cannot do
A host cannot create a file its folder does not already hold. The write side of the path clamp is a read: a write happens only for a path the folder already resolves to a plain file of it, so a path the room has and the folder does not is a recorded refusal, not a new file.
Nothing republishes the listing after the initial walk. The paths a host's walk finds are the room's listing for the life of the session; a file added to the folder later is not listed until a new session, and no folder watcher runs on this side.
A guest writes nothing to disk. The room's text lives in the agent's replica; a guest's edits go to the room and nowhere else. Hosting is the only direction that touches a disk.
A published cursor is a position, not attention. Off by default, and when on it sits where the last
read_file,edit_fileorwrite_filetouched the path — the end of the range an edit changed, the start of the file for a read — and stays there between calls. It is renewed on the room's awareness clock and cleared when the session ends, and it says nothing about what the agent is reading.A change names a path, never its author. The engine reports that a document changed but not which peer changed it, so a wait or a notification says a path moved and never names the peer who moved it.
The invite fragment is a credential. Both links
session_hostanswers with carry the same room key in their fragment, so either one is as sensitive as the room itself: anything that holds a link can join and read what the host shares. A failed join reports the URL as[invite]rather than echoing the key.
Which link goes to whom, in one place: the page: link is what a person opens — a browser
fetches the client at the server's page root — and the invite: link is what a client dials,
and what session_join takes. A person given the invite: form, or a client given the page:
form, may still get in — the two are one invitation — but the page link is the one that opens as
a page.
Tests
npm run typecheck and npm test are what CI runs, and they need nothing but Node: the suite
includes interop against a real third-party client, the official @modelcontextprotocol/sdk,
driving the shipped process.
test/packed.test.ts is the packaging proof rather than the exercise: it runs npm pack here,
installs the tarball into a scratch project outside this checkout, and drives the installed command
— not the sources — through initialize and tools/list. It fails, rather than skipping, when the
artifact is not in the tarball.
npm run test:e2e is the proof rather than the exercise: two MCP servers over their real stdio,
editing one room on a real selvaged, converging in both directions. It fails rather than
skipping when there is no server binary, naming the command that builds one, and finds the binary
through SELVAGE_SELVAGED or as a sibling reference_server's build. The runner has no such
checkout, so CI and scripts/ci-local.sh e2e both take the published binary from its image
instead — scripts/fetch-selvaged.sh, a second of network — and name it in SELVAGE_SELVAGED.
nix develop . --max-jobs 2 -c ./scripts/ci-local.sh all runs this checkout's whole gate: lint,
checks, e2e, links.
Licence
MIT OR Apache-2.0: LICENSE-MIT and LICENSE-APACHE.
Available Tools
10 toolsedit_fileA
Replace "old_string" with "new_string" in one granted path's shared text. Refuses, changing nothing, when "old_string" does not appear, when it appears more than once unless "replace_all" is true, and when "new_string" writes two lines the document already holds again: an ambiguous edit is never guessed at, and a piece of the document written a second time through a line-level tool is what "write_file" is for. Only the changed range is published to the room, and a viewer's edit is refused. A host's own folder follows the room, but this cannot create a file the folder does not already hold.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | A granted path, as list_paths spells it. | |
| new_string | Yes | The text to put in its place. | |
| old_string | Yes | The exact text to replace, including its whitespace. | |
| replace_all | No | Replace every occurrence instead of refusing when "old_string" is ambiguous. Defaults to false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: refusals are atomic ('changing nothing'), only the changed range is published to the room, viewer edits are refused, host folders follow the room, and new files cannot be created. This is well beyond what the schema conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, and each sentence adds new information rather than restating the name. The phrasing is dense and occasionally tangled (e.g. 'writes two lines the document already holds again'), which costs some parseability but not much length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no annotations and no output schema, the description covers refusal behavior, atomicity, publication scope, and permissions. It stops short of describing what the caller observes on success (return payload/confirmation), leaving a small gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning by tying old_string to uniqueness/ambiguity semantics and replace_all to the refusal behavior rather than just 'replace every occurrence'. Path semantics are left to the schema, keeping it short of a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (replace old_string with new_string in one granted path's shared text) and explicitly routes a competing case to a named sibling, write_file. An agent can distinguish it from read_file/write_file/list_paths without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-not conditions (old_string absent, old_string ambiguous without replace_all, duplicated-line insertion) and names write_file as the alternative for one of them. It does not fully spell out general routing against write_file, but the exclusions and alternative are explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_participantsA
List the seats in the room: each display name, its role, whether it is this connection, whether a cursor is published, and where the peer is — the document its cursor names and the line and column the engine resolved from the awareness it publishes, so an agent can see where a person is typing and answer them there. This server publishes a cursor only when a session was opened with "publish_cursor", so its own seat has one only then; a peer that publishes nothing says "no cursor seen" rather than being shown at an invented position. The roster is as current as the last event from the room, so a seat that has just arrived or left may still be reported as it was.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses that this server publishes a cursor only when a session was opened with 'publish_cursor', that a non-publishing peer is reported as 'no cursor seen' rather than at an invented position, and that the roster is only as current as the last room event so arrivals/departures may lag. The only gap is that read-only/side-effect-free status is implied by 'List' rather than stated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, and each of the three sentences earns its place: what is returned, the cursor-publishing conditional, and the staleness caveat. The first sentence is nonetheless long and clause-heavy, cramming the field list and the resolved-position mechanics together, which slightly hurts scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description correctly substitutes by describing the roster fields, the conditional cursor behavior, and the freshness limit. For a zero-parameter read tool this is close to complete; only an explicit statement that the call has no side effects is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline of 4 applies and there is no parameter syntax for the description to compensate for. The description instead spends its words on what the returned fields mean, which is appropriate given the empty input schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('List the seats in the room') and then enumerates exactly what each seat entry contains: display name, role, whether it is this connection, cursor publication state, and resolved peer location. That enumeration makes the tool's output unusually unambiguous. However, it never explicitly routes the agent away from a sibling such as session_status, so it stops short of the 5 bar of explicit sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: 'so an agent can see where a person is typing and answer them there' gives a clear motivating scenario. There is no explicit when-to-use/when-not, no prerequisites, and no named alternative (e.g. session_status) for the adjacent 'who is here / what state' question, so an agent must infer the boundary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_pathsA
List the paths the room's grant names, sorted, optionally filtered by a prefix and capped at 100 unless a higher limit is asked for (at most 1000). The listing is what the host walked when the room opened and is not live: a file the folder gained since is not in it, and one the folder lost still is, until a new session. A truncated answer says so and names the limit. Takes the optional prefix and limit.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many paths to answer with, at most 1000. Defaults to 100. | |
| prefix | No | Answer only with paths starting with this text. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does unusually well: it discloses that the listing is a non-live snapshot taken at session open (added files absent, removed files still present until a new session) and that truncation is signaled with the limit named. It omits permission/auth requirements, but for a read-style listing this is strong behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core behavior—sorted listing, cap semantics, staleness, truncation signaling—is front-loaded and dense. The final sentence ('Takes the optional prefix and limit') is largely redundant with the schema and could be cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no annotations and no output schema, the description still covers the key operational facts an agent needs: the 100/1000 cap behavior, snapshot staleness, and truncation signaling. Return shape is not detailed, but truncation behavior and cap semantics cover the riskiest unknowns.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both 'limit' (max 1000, default 100) and 'prefix'. The description reinforces the default cap and maximum but adds no syntax or format detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'List the paths the room's grant names,' with scope details (sorted, prefix-filtered, capped). It's clearly a directory-listing operation distinguishable from read_file/edit_file siblings, though it never names an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied (enumerate paths for the session) but there is no explicit when-to-use/when-not, nor any routing to alternatives like read_file or session_host. The closing sentence restates the parameters rather than offering selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_fileA
Read one granted path's shared text as the room holds it. Only a path the room's grant names can be read; a path that is not text, or is over the size a session carries, is refused rather than answered. The text is the room's — the host folder's copy where the host is keeping that true — and this tool never reads the disk itself. A guest waits for the room to send the document and is refused if it does not arrive in time.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | A granted path, as list_paths spells it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose real behavioral traits: refusal on non-text, oversized, or ungranted paths, that the returned text is the room's/host's copy rather than a disk read, and that a guest blocks on the room and is refused on timeout. These error and timing semantics are valuable. It omits concrete size limits and return format details, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the four sentences each cover a distinct concern (scope, refusals, provenance, guest behavior). However, metaphorical phrasing like 'where the host is keeping that true' adds length without precision, and no sentence is trimmed to essentials.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema and no annotations, yet the description covers the key things an agent needs: grant requirement, refusal conditions, data provenance, and guest timeout behavior. The main gap is the shape of the returned content (plain text vs. structured), which is implied but not stated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
One parameter with 100% schema coverage; the schema already documents that 'path' is a granted path spelled as list_paths spells it, and the description adds no syntax or format beyond that. Baseline 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a clear verb (read) and resource (one granted path's shared text), and the 'never reads the disk itself' clause distinguishes it from a naive filesystem read. It does not explicitly name sibling alternatives, but the read/write split against write_file and edit_file is self-evident. The oblique framing ('as the room holds it') slightly blurs the resource.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied through constraints rather than stated: only granted paths can be read, non-text or oversized paths are refused. It never says when to prefer this over siblings or what to do instead (e.g., use list_paths to discover grantable paths). Adequate but no explicit when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
session_hostA
Share a folder with a room and host it, and answer with the two links that open it. The folder is walked once, here: the paths the walk finds are the room's listing, and only a file the folder holds at that moment can be listed, so a file that appears in the folder later is not listed until a new session, because nothing republishes the listing — and a host cannot create a file its folder does not already hold. A person opens the page link in a browser; a client — another session_join included — joins with the client invite, and either form works. Both links carry the same room, token and key fragment, and the fragment is the credential: hand a link, whole, only to whoever should join. A host leaving closes the room for everyone. Takes the folder, the server address, a display name, and the two opt-ins: whether to publish a cursor, and whether to tell the client about room activity as it happens.
| Name | Required | Description | Default |
|---|---|---|---|
| folder | Yes | The folder to share, as the path this server should read it by. | |
| server_url | No | The Selvage server's address, such as ws://127.0.0.1:8080. Falls back to the SELVAGE_SERVER_URL variable. | |
| display_name | No | The name this seat shows in the room. Falls back to the SELVAGE_DISPLAY_NAME variable, then to "agent". | |
| notify_changes | No | Whether this server tells its client about room activity without being asked — document changes as bounded partial reports, and peers joining and leaving — as MCP notifications. Off by default; falls back to the SELVAGE_NOTIFY_CHANGES variable. | |
| publish_cursor | No | Whether this seat publishes a cursor for the path each read, edit and write touches, so another seat can see which file this agent is working in and where. Off by default; the SELVAGE_PUBLISH_CURSOR variable is the fallback. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations to lean on, the description carries the full burden and delivers: it discloses the snapshot walk semantics (folder is walked once, later files are not listed), the host's inability to create files the folder lacks, the secret-handling stance on the link fragment ('the fragment is the credential'), and the lifecycle consequence that a host leaving closes the room for everyone. These are exactly the non-obvious traits an agent needs before invoking a stateful, credential-bearing operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is correctly front-loaded, but the remainder is a dense, em-dash-heavy paragraph that revisits the snapshot rule at length ('nothing republishes the listing') and repeats phrasing about the two link forms. The information earns its place, but the packing makes it slower to parse than it needs to be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, unannotated tool with no output schema, the description covers the return value (two links), the lifecycle, the credential model, and the effects of both flags, which is most of what an agent needs. It does not address failure behavior (bad folder path, unreachable server) or whether the room survives a reconnect, leaving a modest gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all five parameters, including fallback environment variables and default values. The description adds only a light framing of the two booleans as 'the two opt-ins' and a restatement of the other three inputs, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb and resource ('Share a folder with a room and host it') plus the result ('the two links that open it'). It explicitly contrasts itself with the sibling session_join ('a client — another session_join included — joins with the client invite'), so an agent can route between host and join without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes the context clearly (use this to make a folder available to a room; other participants join via session_join) and explains which link a person uses versus which a client uses. It stops short of explicit when-not-to-use or prerequisite criteria, which keeps it below a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
session_joinA
Join a room from an invite URL in either form session_host answered with — the page link a person would open, or the client invite — which has to carry the key in its fragment. Answers with the room id, this seat's role, the participants and how many paths the host granted. The listing is what the host shared when the room opened, so a path shared afterwards is not in it until a new session. A guest edits the room's shared text and writes nothing to its own disk. Takes the invite, a display name, and the two opt-ins: whether to publish a cursor, and whether to tell the client about room activity as it happens.
| Name | Required | Description | Default |
|---|---|---|---|
| invite | Yes | Either invite form a host handed out, fragment and all: the page link a person opens (`http(s)://…/`) or the client invite (`ws(s)://…/session`). That fragment is a credential. | |
| display_name | No | The name this seat shows in the room. Falls back to the SELVAGE_DISPLAY_NAME variable, then to "agent". | |
| notify_changes | No | Whether this server tells its client about room activity without being asked — document changes as bounded partial reports, and peers joining and leaving — as MCP notifications. Off by default; falls back to the SELVAGE_NOTIFY_CHANGES variable. | |
| publish_cursor | No | Whether this seat publishes a cursor for the path each read, edit and write touches, so another seat can see which file this agent is working in and where. Off by default; the SELVAGE_PUBLISH_CURSOR variable is the fallback. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does substantial work: it discloses what the call returns (room id, seat role, participants, granted path count), the snapshot nature of the path listing ('a path shared afterwards is not in it until a new session'), and the guest's write behavior ('edits the room's shared text and writes nothing to its own disk'). It omits auth/permission requirements and rate-limit behavior, so it is not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action in the first clause and keeps everything in one dense but coherent paragraph. Some sentences duplicate schema content (the invite fragment as credential, the two opt-ins), costing a little efficiency but nothing is truly wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema and no annotations, so the description must describe both behavior and returns — and it does, covering the result fields, snapshot semantics, and write behavior. The main residual gap is the absence of any auth or permission prerequisite for joining.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter including the fragment-as-credential detail and the fallback variables. The description largely restates the invite forms and the two opt-ins rather than adding meaning beyond the schema, matching the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (join a room from an invite URL) and explicitly ties the invite to the output of the sibling session_host, so the agent can distinguish it from session_host and session_status without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly establishes the entry condition — you need an invite URL of either form from session_host — and implies the join use case. It does not spell out when not to use it or name an alternative join path, so it falls short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
session_leaveA
Leave the room this server is in, closing the connection and freeing the session so it can host or join again. A host leaving closes the room for everyone: the other seats are disconnected and the room ends, and the only way back is to host again or to join another invite. With no session it is a caller error, because there is nothing to leave.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present the description carries the full burden, and it does: it discloses that the connection closes, that the session is freed for reuse, that a host leaving disconnects other seats and ends the room irreversibly ('the only way back is to host again or to join another invite'), and that calling with no session is a caller error. That is unusually complete side-effect and error disclosure for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the core action, then the host-specific consequence, then the error condition. No filler and no repetition of the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema and no annotations exist, so the description must cover behavior itself, and it covers action, side effects on self, side effects on others, reversibility, and the error mode. Nothing an agent needs before invoking it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate; the baseline for a no-param tool is 4. The description correctly implies no arguments are needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Leave the room this server is in') and immediately clarifies the effect: closing the connection and freeing the session. It is clearly distinguishable from siblings like session_host, session_join, and session_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear precondition ('With no session it is a caller error') and explains the host-leaving case, which tells the agent when this call is meaningful. It does not, however, explicitly route the agent to an alternative sibling (e.g. session_status to check state first), so it stops short of full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
session_statusA
Report the live session: role, room, mode, participants, whether the connection is carrying traffic, the granted paths this replica holds, the room's open documents, and any problem the session could not fix (a room change a host could not put on disk, a granted path the folder would not read). With no session open it still answers, saying there is none, rather than failing: asking where this server stands is not a mistake.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose meaningful behavior: it is safe to call with no session and will not fail, and it reports problems the session could not fix (a host's failed room change, an unreadable granted path). It never explicitly states read-only/no side effects or auth requirements, but the reporting framing makes non-mutation clear.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the two substantive sentences (what it reports; no-session behavior) both earn their place. The trailing reassurance 'asking where this server stands is not a mistake' is mildly rhetorical padding in an otherwise dense, information-rich description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by enumerating the returned fields and the no-session/error-reporting cases. It does not describe the response's shape or format, but for a zero-parameter status read this is close to complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so per the rubric the baseline is 4. The description correctly adds no parameter detail because there is none to add; the schema is trivially complete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Report') and resource ('the live session') and then enumerates exactly what is reported: role, room, mode, participants, traffic, granted paths, open documents, and unresolved problems. This clearly separates it from the sibling mutators session_host/session_join/session_leave and listers like list_participants.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear context ('asking where this server stands') plus an important edge case: it still answers with 'there is none' when no session is open rather than failing. It does not name or route to any sibling alternative (e.g. list_participants for participant detail), so routing is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
wait_for_changeA
Block until the room changes a watched path, then answer with a bounded partial report rather than whole files: for each path that changed, the changed region as a small diff hunk with line numbers and a few lines of context, and the path's new length; a path that appeared or disappeared is said in words instead of a hunk. Watching is the whole grant and the open documents unless "paths" names a subset. It returns at the first change or at "timeout_ms" (default 30000, at most 300000), never spinning, and a deadline that passes says so plainly rather than looking like a change. It reports what changed since the previous wait, not since the call: a change that lands while the agent is thinking is reported by the next wait, so nothing is lost between two waits. The report is capped in size and says how many changes it left out.
| Name | Required | Description | Default |
|---|---|---|---|
| paths | No | The granted paths to watch, as list_paths spells them. Omitted watches the whole grant and the open documents. | |
| timeout_ms | No | How long to block before answering that nothing changed, in milliseconds. Defaults to 30000, at most 300000. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so richly: no spinning, first-change-or-timeout semantics, timeout default and ceiling, the critical detail that changes are reported since the previous wait rather than since the call, and that the report is size-capped with an omitted-count. This is exactly the behavioral context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense paragraph that is front-loaded with the core purpose and return shape. Every sentence carries real information, though the run-on style and length keep it from being maximally tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex blocking tool with no output schema, the description fully specifies the return payload (diff hunks with line numbers and context, new lengths, verbal appearance/disappearance, omitted-change count) and the timeout behavior. Nothing needed to invoke or interpret it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are already documented in the schema (paths semantics, timeout default/max). The description largely repeats this rather than adding syntax or format detail, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource — block until a watched path changes, then return a bounded partial diff report. An agent can distinguish it from read_file, edit_file, or session_status without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clarifies the default watch scope (whole grant plus open documents unless paths names a subset) and when it returns, which implies the use case. However, it never says when to prefer this over alternatives such as read_file or session_status, nor any when-not condition, so usage is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
write_fileA
Replace a granted path's whole shared text with "content". This is how a host changes a file its folder already holds: the folder follows the room, but a file the folder does not hold cannot be created, and a path the room did not grant cannot be written at all. A viewer is refused, and text over the size a session carries is refused. Prefer edit_file where a small change is meant.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | A granted path, as list_paths spells it. | |
| content | Yes | The whole text the path should hold. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does well: it discloses whole-content replacement, that creation is impossible (file must already be held), authorization requirements (granted path, viewer refused), and a size limit. It stops short of describing the return value, idempotency, or concurrency behavior, which keeps it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action, then constraints, then the alternative. Dense and mostly waste-free, though the metaphorical phrasing ("the folder follows the room") requires a re-read.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-param mutation tool with no output schema, it covers preconditions, permissions, and limits adequately. The only real gap is what a successful write yields or signals, which an agent would otherwise infer.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both params are documented in the schema (path as list_paths spells it, content as the whole text). The description reinforces but adds no syntax or format detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Replace a granted path's whole shared text with content") and immediately differentiates itself from the sibling edit_file, so an agent can tell the two apart without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use this to replace a whole file, but "Prefer edit_file where a small change is meant." It also lays out the preconditions that select against this tool (file not held by the folder, path not granted, viewer role).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.1.0- First observed
edit_file - First observed
list_participants - First observed
list_paths - First observed
read_file - First observed
session_host - First observed
session_join - First observed
session_leave - First observed
session_status - First observed
wait_for_change - First observed
write_file
TDQS
Scored across 10 tools
Each tool targets a distinct action or resource: session lifecycle tools are separate from file operations, listing, participant inspection, and change waiting. The only mild overlap is session_status vs list_participants, but the descriptions clearly distinguish an overall session report from a detailed seat roster.
Most names follow predictable snake_case patterns: session_* for lifecycle, list_* for collections, and verb_noun for file operations. wait_for_change is a minor deviation, but the set remains highly readable and consistent overall.
Ten tools are well-scoped for a collaborative room/session server, covering session lifecycle, shared file reading/writing/editing, path listing, participant listing, and change watching without obvious redundancy.
The surface covers core room and file collaboration workflows, including read, write, edit, list, join, host, leave, and wait for changes. Minor gaps exist around creating, deleting, or renaming shared files and modifying grants after hosting, but these appear partly by design since the host folder supplies the file set.
Maintenance
Related MCP Connectors
- flockfsOAuthcom.flockfs
A real-time, multiplayer filesystem for agents and humans: shared files, live edits, history.
Create and improve live, shared interactive documents with your agent.
Collaborative word processor you can use with your agent.
Persistent docs and memory for AI agents — read, write, organize & search a shared workspace.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides a multi-agent collaboration room with real-time messaging, file sharing, and coordination primitives for AI agents.2MIT
- FlicenseNot gradedqualityBmaintenanceEnables agents to join multiplayer markdown rooms, collaborate on documents live with humans, and respond to mentions via comments.-
- FlicenseNot gradedqualityBmaintenanceEnables AI agents to join ephemeral collaborative chatrooms, read room context, poll messages, publish findings, and update their status alongside human participants.-
- AlicenseAqualityAmaintenanceEnables an AI agent to join a live Excalidraw collaboration room as a headless participant, read the drawing in real time, and add or modify elements on the shared canvas.1820 npm3MIT