跳到正文
DEV Community· Remzi Binar·· 3 小時前AI 評分60

blink artifacts 如何透過 Durable Objects 和 Yjs,讓智能體與用戶同時編輯同一頁面

How agents and people edit the same page at once: a remote MCP server on Durable Objects and Yjs

AI 導讀

Remzi Binar 建立 blink artifacts,讓任何 MCP client 透過遠端 MCP 發佈頁面,並讓用戶在瀏覽器與智能體同時編輯同一頁面。

正文

Coding agents are good at making things you want to share: a one-page project summary, a dashboard, a slide deck. Sharing is where it gets awkward. Claude's artifacts live inside a claude.ai account, other agents have nothing comparable, and once a teammate wants to fix a sentence, you are back to copying HTML around.

I built blink artifacts to fix that: any MCP client publishes a page over remote MCP, you get one link, and people edit it in the browser while the agent keeps editing it over MCP. This post is about the parts that were harder than they looked.

One Durable Object per artifact

Every artifact is a Cloudflare Durable Object holding a Yjs document. The browser editors (CodeMirror for HTML, ProseMirror for docs, a canvas for slides) sync to it over a WebSocket. The Durable Object uses WebSocket hibernation, so an idle artifact costs nothing while sockets stay open.

The agent never sees Yjs. Over MCP it sees plain files: index.html, doc.md, one HTML file per slide. Reads project the Yjs state into text; writes apply a text diff back into the shared document, inside the same Durable Object, so a human typing and an agent writing are just two clients of one CRDT.

D1 keeps the index (accounts, artifacts, members, tokens), R2 keeps uploaded files, and version history lives in the artifact's own Durable Object storage.

Edits that fail loudly instead of clobbering

An agent's view of a file is always a little stale: a person may have changed it since the agent last read it. So the write tools are built to refuse rather than guess:

  • edit_file takes exact find/replace pairs. If the text matches nowhere it returns no_match; if it matches more than once, multi_match. Either way nothing is written, and the agent re-reads.
  • write_files takes an optional baseRev. If any file it touches changed after that revision, it returns conflict with the paths, and the agent merges instead of overwriting.
  • changes_since(rev) returns only what changed, so an agent coming back to a page after your team edited it reads a diff, not the whole thing.

This turned out to matter more than the CRDT itself. Yjs makes concurrent edits converge; it does not make an agent's overwrite of a paragraph you just rewrote a good idea.

Untrusted HTML on its own origin

Published pages are arbitrary HTML and JavaScript, so they never run on the app's origin. Each artifact is served from its own subdomain on a separate domain, keyed by a secret that is rotated when access changes, inside a sandboxed iframe. The page gets no cookies and no credentials. When it wants shared state (a small database, comments, the viewer's identity) it asks the parent window over postMessage, and the shell makes the API call with the viewer's own role.

Asking the agent, without a server-side LLM

People can ask questions on a page: select an element, type @ and pick an agent. There is no model on the server. The question waits in the artifact's Durable Object until the owner's own agent picks it up (with a background listener, or by long-polling asks_wait), answers it in a comment or writes the answer into the doc block, and resolves it. The agent that answers is the one you already pay for and already trust with your code.

OAuth so claude.ai can connect without a token

CLI agents connect with a bearer token. claude.ai and Claude Desktop custom connectors expect OAuth, so the server is also its own OAuth 2.1 authorization server: protected-resource and authorization-server metadata, dynamic client registration, PKCE (S256), short-lived access tokens and rotating refresh tokens, all on D1. In claude.ai you add https://artifact.blinkbrosai.com/mcp under Settings → Connectors, sign in with an email code, approve, and that is it.

Try it

It is free for 3 active artifacts, no card. Setup for Claude Code, Codex, Copilot CLI, Antigravity and claude.ai is in the docs.

I built most of it with Claude Code: I wrote the acceptance tests first, Claude wrote most of the implementation, I read every diff. Happy to answer questions about any of the pieces above, and I would especially like to hear what breaks.

來源:DEV Community · dev.to