-
-
1. One shared Markdown document with Owner, Editor, and Reviewer sessions connected.
-
2. Pending participants cannot see document content until the Owner assigns a Role.
-
3. The Owner can assign, update, or revoke Roles from one access panel.
-
4. The Roles view shows which capabilities are allowed for each Role.
-
7. After one same-location Proposal is applied, the two remaining alternatives become explicit Conflicts.
-
9. Owner-only Activity shows the actor, Role, action, time, source, outcome, and target.
Inspiration
I once built an AI TTS editor at a company where I worked. That experience raised a question: Can people and AI work together inside the same editor? If so, what matters, and what needs to change?
WebMCP lets a web page expose tools that an AI agent can use. That makes it possible for the agent to work inside an editor. But tools alone are not enough. The system also needs rules for who can read, propose changes, edit, and manage access. Without those boundaries, people and AI can simply write whatever they want, and the result is worthless slop.
The real challenge is not putting AI inside an editor. It is designing a structure in which people and agents can work on the same document with different roles and responsibilities. That's why I built Ground.
What it does
Without structured tools like WebMCP, a browser agent has to infer meaning from buttons and input fields. This approach is especially likely to produce incorrect actions in complex web applications such as editors. Ground instead exposes explicit WebMCP tools for reading a document, replacing exact text, and proposing changes. For each page session, Ground registers only the tools allowed by the current participant's Role.
Ground is a real-time collaborative editor for Markdown documents. The session that creates a document becomes its Owner. Anyone joining through the shared link remains Pending until the Owner assigns a Role.
- Create a document at
/ - The session that creates the document becomes the Owner and receives an Owner recovery link
- Share the document using the
Sharebutton - A new participant enters a display name and remains Pending
- The Owner assigns the Editor or Reviewer Role
- An Editor can edit directly, while a Reviewer creates Proposals
- The Owner reviews Proposals and resolves Conflicts
- After access is revoked, the participant can no longer access the document or WebMCP tools
Owners and Editors can read documents, propose changes, and apply them through WebMCP. Reviewers can read documents and propose changes, but cannot edit directly. Pending and Revoked participants see only a status screen, and no document tools are exposed to them.
Changes proposed by Reviewers enter the review list as Proposals based on the exact original text. The Owner can keep the current wording or apply the proposed wording. If a Proposal's target text has changed or can no longer be located at review time, it is marked as a Conflict. This keeps AI-assisted editing explicit and reviewable.
Each Activity entry shows who acted, their Role at execution time, what they did, when it happened, the source, the target, and the outcome.
How Ground was built
Ground builds on CollabMD, an MIT-licensed open-source project. The following table shows the parts reused from the existing CollabMD and the parts extended by Ground for this challenge.
Existing CollabMD and Ground additions
| Reused from CollabMD | Added in Ground |
|---|---|
| CodeMirror editor, Yjs synchronization, presence and cursors, Undo/Redo | Owner/Editor/Reviewer Roles, Pending/Revoked Access states, Proposals/Conflicts, and Activity |
| Text-based revision guard, reading the active document via WebMCP, and replacing text | Role-based Tool discovery and execution-time server authorization |
| - | Document-specific URLs, Share, Owner recovery, Supabase persistence, Vercel deployment |
Features implemented in Ground
Ground registers three WebMCP tools through document.modelContext.registerTool():
collabmd_read_active_documentcollabmd_apply_text_editscollabmd_propose_text_edit
Another interesting Ground feature is that Roles can be defined declaratively in a file to suit different needs.
{
"roles": {
"owner": [
"document.read",
"document.suggest",
"document.edit",
"conflict.resolve",
"grant.manage"
],
"editor": [
"document.read",
"document.suggest",
"document.edit"
],
"reviewer": [
"document.read",
"document.suggest"
]
}
}
When the Ground server starts, it reads and validates the project's collabmd.governance.json file, then passes the resulting Roles to the client. Non-Owner Roles can be added or changed by combining the five permission units (Capabilities), while the Owner must have all five. Adding a new Capability requires corresponding code changes; the MVP uses only these five.
The client uses these definitions to render the Roles and Manage Access screens and to expose the WebMCP tools currently allowed for the participant. Immediately before each WebMCP tool runs, the server checks the current Access state, Role, and Capability based on the user ID verified through the Supabase anonymous session.
Markdown text, Proposal records, and Activity are stored as separate shared data within a single Y.Doc, while Conflict is represented as a Proposal state. The UndoManager tracks content-edit history. Proposal approval or rejection, Conflict resolution, Role changes, and Activity records are governance decisions and therefore remain outside document Undo/Redo.
Challenges
The first challenge was permission revocation. An agent may retain a reference to a WebMCP tool discovered before its Role changes. Removing the tool from later discovery results does not invalidate that existing reference, so authorization is checked again immediately before every execution. If the current Role no longer permits the tool, the call is rejected.
The second challenge was stale Proposals. Just because Yjs can make concurrent document edits converge does not mean that an earlier Proposal can still be applied safely. If the exact target of a Proposal has changed, Ground treats it as a Conflict and asks the Owner to make a decision.
Undo/Redo required another boundary. Both are forms of document edits. Role assignment, Proposal approval or rejection, and Conflict resolution are not text editing commands but governance decisions. Therefore, I kept them outside the editor's edit history.
What I'm Most Proud Of
I didn't just create a simple permissions panel. Within a single editor, Ground provides Role configuration, participant Access management, WebMCP tools based on Role scope, server-side authorization, a Proposal workflow, deterministic Conflict handling, and visible collaboration history.
What I'm most proud of is that an Access change actually affects how the product behaves. When an Editor's access is revoked, that participant returns to a status-only screen. The editor and document tools disappear, and even if the participant attempts to use an editing tool discovered before revocation, the server denies the request.
I am also proud of how I used the Role manifest to declaratively define Access control and the Roles shown in the interface. There is no longer a need to hardcode separate Roles in the client to add or modify Non-Owner Roles, and management has become easier as well.
Lessons Learned
Tool discovery and authorization have different lifecycles. Discovery determines what an agent should see at a given moment. Authorization determines whether a specific call is still permitted at the moment it is executed. For permission revocation to work as expected, this second determination must occur when the tool is executed.
Convergence and consensus are also different problems. Even if Yjs synchronizes all participants to the same document state, the Proposal target may change and become invalid. Conflict preserves the intent of the change and allows it to be handled later.
What's Next for Ground
Ground's next step is to separate the Role and Capability configuration and the execution-time authorization layer into a reusable SDK so that it can be easily integrated into other WebMCP-based editors.
I also aim to preserve the account-less sharing flow while moving toward a structure that can selectively support invitations, team-level document management, and application-specific Capabilities when needed.
Built With
- codemirror
- codex
- collabmd
- docker
- javascript
- node.js
- playwright
- vite
- vitest
- webmcp
- websocket
- y-websocket
- yjs
Log in or sign up for Devpost to join the conversation.