AI-native collaborative document editor
Humans and AI agents co-edit documents in real time through the same CRDT protocol.
AI agents are first-class editors. They connect via MCP or REST API, appear in the presence bar with live cursors, and their keystrokes show up in real time — just like another human collaborator.
Suggestion mode keeps humans in control. Agent edits are proposed, not committed. Review additions (green underline) and deletions (strikethrough), then accept or reject — individually or in bulk.
CRDT-first. Every document is a Yjs CRDT, synced via PartyKit WebSocket rooms. True conflict-free editing for any number of humans and agents, with zero coordination overhead. Local persistence via IndexedDB means documents load instantly.
One schema, everywhere. The ProseMirror schema in packages/editor-extensions is the single source of truth — used identically by the browser editor, the MCP server, and the sync server.
Headings, paragraphs, bullet / ordered / task lists, blockquotes, code blocks (syntax-highlighted), tables (resizable), callouts (info / warning / error / success), toggles, images, horizontal rules. Bold, italic, underline, strikethrough, inline code, links, highlights, text color.
31 REST API endpoints for document editing, page management, comments, version history, suggestions, and export. Any LLM can integrate — not locked to MCP. Agents authenticate with workspace tokens or invite links.
Live cursors and selection awareness for every participant. Yjs CRDT sync with zero perceptible latency. Agents appear with distinct purple cursors and thinking / writing / idle status.
Highlight text to start a thread. Threaded replies with @mentions. Resolve and reopen discussions. Full comment API for agents.
Auto-snapshots every 30 minutes and on session end. Manual save with custom labels. Restore any version with one click.
Page tree with nested hierarchy. Folders. Drag-and-drop reordering. Favorites. Soft-delete with trash recovery. Invite links and email invitations. Role-based access (owner / admin / member / guest).
Markdown and HTML export from the editor or via API.
Type / for a Notion-style command menu — insert any block type with type-ahead search.
Browser (TipTap + Yjs) AI Agent (MCP / REST)
│ │
└───── WebSocket ─────┬───── HTTP ──┘
│
PartyKit Server
(Yjs rooms)
│
Supabase Postgres
(metadata, auth, comments,
versions, permissions)
Documents live entirely in Yjs — Supabase stores workspace metadata, not document content.
apps/
web/ React 19 + Vite frontend (Cloudflare Pages)
mcp-server/ MCP + HTTP API server for AI agents (Fly.io)
packages/
shared/ Types, constants, branding config, agent guide
editor-extensions/ TipTap extensions + ProseMirror ↔ Yjs bridge
party/ PartyKit server for real-time Yjs sync
supabase/
migrations/ PostgreSQL schema (14 migrations)
skills/
motion-agent/ Claude Code skill for agent integration
- Node.js >= 20
- pnpm 10+
- Supabase project (auth, metadata, comments, versions)
- PartyKit account (real-time sync)
git clone https://github.com/AudiTT/motion.git
cd motion
pnpm installCopy .env.example to .env and fill in your values:
# Supabase
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# PartyKit
VITE_PARTYKIT_HOST=localhost:1999Create party/.dev.vars for the PartyKit server:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-keysupabase db pushOr apply migrations 001–014 manually via the Supabase SQL editor.
pnpm dev # Web app + PartyKit + MCP serverOr run individually:
pnpm dev:web # http://localhost:5173
pnpm dev:party # ws://localhost:1999
pnpm dev:mcp # MCP server (stdio mode)pnpm build # shared → editor-extensions → apps
pnpm typecheck # Type-check all packagesCreate a session with a workspace agent token:
curl -X POST https://your-mcp-server.fly.dev/sessions \
-H "Content-Type: application/json" \
-d '{"agent_token": "your-token", "agent_name": "Claude"}'The response includes a session_id. Use it in all subsequent calls:
# Read the document
curl https://your-mcp-server.fly.dev/sessions/$SESSION_ID/document
# Insert a block
curl -X POST https://your-mcp-server.fly.dev/sessions/$SESSION_ID/blocks \
-H "Content-Type: application/json" \
-d '{"index": -1, "block": {"type": "paragraph", "content": [{"type": "text", "text": "Hello from an agent!"}]}}'
# Disconnect
curl -X DELETE https://your-mcp-server.fly.dev/sessions/$SESSION_IDSee skills/motion-agent/references/api-reference.md for all 31 endpoints.
DOCUMENT_ID=<page-id> \
WORKSPACE_ID=<workspace-id> \
PARTYKIT_HOST=your-partykit-host \
SUPABASE_URL=https://your-project.supabase.co \
SUPABASE_SERVICE_ROLE_KEY=your-key \
AGENT_NAME=Claude \
pnpm dev:mcpA ready-made skill is included at skills/motion-agent/. Copy it to your Claude Code project:
cp -r skills/motion-agent/ .claude/skills/motion-agent/By default, agent edits are wrapped in suggestion marks:
- Agent inserts or modifies content → appears as green underlines (additions) or strikethroughs (deletions)
- Human reviews in the editor → accepts or rejects each suggestion
- Agents can also use
"mode": "direct"to bypass suggestions for trusted operations
| Service | Platform | Command |
|---|---|---|
| Web App | Cloudflare Pages | cd apps/web && pnpm build && npx wrangler@latest deploy |
| PartyKit | PartyKit | cd party && npx partykit deploy --var SUPABASE_URL=... --var SUPABASE_SERVICE_ROLE_KEY=... |
| MCP Server | Fly.io | fly deploy (from project root) |
| Database | Supabase | supabase db push |
fly apps create your-mcp-server
fly secrets set \
PARTYKIT_HOST="your-partykit-host" \
SUPABASE_URL="https://your-project.supabase.co" \
SUPABASE_SERVICE_ROLE_KEY="your-key"
fly deployVerify: curl https://your-mcp-server.fly.dev/health
Motion is designed to be forked and rebranded. The app name is centralized in a single config file:
packages/shared/src/config.ts
export const APP_NAME = "Motion"; // Display name in UI
export const APP_DESCRIPTION = "..."; // Tagline
export const APP_SLUG = "motion"; // CLI and MCP server name
export const DEFAULT_MCP_HOST = "..."; // Default API endpointAfter changing the config, also update:
| File | What to change |
|---|---|
apps/web/index.html |
<title> tag |
package.json files |
@motion/ package prefix (global find-replace) |
fly.toml |
app name |
party/partykit.json |
name field |
skills/motion-agent/ |
Skill name, descriptions, example URLs |
Then rebuild: pnpm build
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-thing) - Make your changes
- Run
pnpm build && pnpm typecheckto verify - Open a pull request
MIT — Viet Anh Trinh