MarkLayer

io.github.thevrusPublished by GitHub account thevrusv0.6.1Updated Oct 31, 2026

Work visual annotations on a live webpage: watch, acknowledge, resolve, reply, or leave your own.

Overview

AI-generated overview

Bridges MarkLayer webpage annotations to a coding agent, letting it read, acknowledge, resolve, reply to and create review comments on a live page.

What you can do
  • Connect to and inspect a review room: join by share URL or id, read room metadata, and read the annotated page's headings, text, links and controls with selectors.
  • Read and follow feedback: list, fetch and watch annotations, including element targets, reply threads and attached images.
  • Respond to annotations: acknowledge, resolve with a summary, dismiss with a reason, or reply without changing status.
  • Leave your own feedback: create comment annotations and propose exact text edits as accept-or-reject diffs.
  • Run the review: publish a next-steps checklist, tick steps off, ask multiple-choice polls, and read a combined feedback summary.
  • Open a new review room on a preview or staging URL and…
When to use it
Use it when a human reviews a webpage with MarkLayer and wants an agent to work through the comments as a queue, or when you want an agent to audit a page and leave its own findings and copy fixes for a human to triage.
Requirements
Runs locally via npx (Node.js) over stdio, or as a remote endpoint per share link. Needs a MarkLayer room share URL or room id, passed with --room or MARKLAYER_ROOM or set through marklayer_connect_room. Optional MARKLAYER_AGENT names the agent for humans; MARKLAYER_API_BASE overrides the worker URL. Network access to marklayer.app.
Before you install
Annotations, replies, checklists, polls and suggested edits are written to a shared room and are visible to the people in it; resolving or dismissing changes the status reviewers see. Reviewer attachments are fetched from URLs supplied in comments. Room ids act as access tokens, so treat share links as secrets. No authentication is declared.

Installation

In SourceWeft

  1. Open MarkLayer in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

marklayer-mcp

MCP (Model Context Protocol) server that bridges MarkLayer annotations to AI coding agents.

When users annotate any webpage with MarkLayer, your agent receives the comments as a structured work queue: it can acknowledge, resolve, dismiss, and reply to each one — and the human sees the status updates live. It also works the other way: point an agent at a room and ask it to review the page, and it can leave its own comments and copy-edit suggestions for the human to triage.

Install

You may not need to. Every MarkLayer share link is already a remote MCP server at the same address plus /mcp, so a client that supports remote (Streamable HTTP) servers connects with nothing installed:

bash
claude mcp add --transport http marklayer https://marklayer.app/s/abc123/mcp

Append ?agent=cursor, or any name, so the people in the room see who is working; it defaults to agent. That endpoint is bound to its room, so it has every tool below except marklayer_connect_room.

This package is the stdio version of the same tools, with no Claude-specific wiring. Use it when a client cannot reach remote servers, or when you want one install that covers every room (Claude Code, Cursor, Windsurf, VS Code, Codex CLI, etc.):

bash
# Claude Code:claude mcp add marklayer -- npx -y marklayer-mcp
# Or pre-connect to a specific room:claude mcp add marklayer -- npx -y marklayer-mcp --room https://marklayer.app/s/abc123

For any other MCP client, add a stdio server entry pointing at the same command (adjust the config file/key to your client — Cursor's ~/.cursor/mcp.json, Windsurf's mcp_config.json, VS Code's .vscode/mcp.json, etc.):

json
{  "mcpServers": {    "marklayer": {      "command": "npx",      "args": ["-y", "marklayer-mcp"],      "env": { "MARKLAYER_AGENT": "cursor" }    }  }}

Set MARKLAYER_AGENT (or --agent) to identify which agent this is — it's what humans see as the comment author and the "assigned to" badge, so a non-Claude client should not leave it at the default.

Usage

In your agent prompt:

Watch my MarkLayer annotations. For each one, acknowledge it, make the fix, then resolve it with a summary.

The agent will call marklayer_watch_annotations in a loop and process incoming feedback as it arrives.

Tools

ToolDescription
marklayer_connect_roomConnect to a room by share URL or bare id.
marklayer_room_infoPage URL, viewport width, timestamps.
marklayer_read_pageThe page's headings, text, links and controls, with selectors. Lets the agent review a page nobody has annotated yet.
marklayer_list_annotationsList annotations, optionally filtered by status.
marklayer_get_annotationFull detail + reply thread for one annotation.
marklayer_watch_annotationsBlock until new annotations arrive, return a batch.
marklayer_acknowledgeMark in-progress and tag with this agent.
marklayer_resolveMark resolved, optionally posting a reply with the summary.
marklayer_dismissMark dismissed with a reason the human will see.
marklayer_replyPost a reply without changing status (e.g. clarifying questions).
marklayer_create_annotationLeave a new comment yourself — proactive feedback rather than a response.
marklayer_suggest_editPropose an exact copy/grammar fix for a piece of text, as a diff the human can accept.
marklayer_share_previewOpen a new review room on a URL you built and connect to it. Same repo + branch as the room you are in (or previous) makes it the next version.
marklayer_set_next_stepsPublish an ordered checklist, each step linked to annotation ids. Replaces the previous list.
marklayer_complete_next_stepTick a step off; reviewers see it live.
marklayer_askPut a 2-6 option poll to the room. One vote per reviewer, revotable.
marklayer_get_feedbackOne read: annotation counts by status, open annotations, checklist progress, poll tallies.

Options

--room <url-or-id>   Connect at startup; otherwise call marklayer_connect_room.--api-base <url>     Override worker URL (default https://marklayer.app).--agent <name>       Identifier shown to humans (default "claude-code").

Equivalent env vars: MARKLAYER_ROOM, MARKLAYER_API_BASE, MARKLAYER_AGENT.

Troubleshooting

"Connection closed" on first start. npx -y downloads the package on the first run, and a cold download can outlast the MCP client's startup timeout. Prime the cache once (npx -y marklayer-mcp --help) and reconnect, or install it up front with npm i -g marklayer-mcp and point the client at the binary.

Tools return "room is not connected". The WebSocket dropped — rooms are held open by a Durable Object and a long-idle agent can be disconnected. Call marklayer_connect_room with the same URL to reattach.

Creating rooms programmatically

This server consumes an existing share link. To mint one (or many) from code — e.g. seed a room per page in a batch of URLs before pointing the agent at it — POST directly to the public HTTP API:

bash
curl -X POST https://marklayer.app/api/$ID \  -H 'Content-Type: application/json' \  -d '{"ops":[],"url":"https://example.com/page-1","width":1440,"expires_in":2592000}'# Share link: https://marklayer.app/s/$ID

For a review room on a preview build, POST /api/$ID/preview takes { url, title, repo?, branch?, sha?, note?, questions?, previous? } and never overwrites an existing id. Versions chain only through previous, a room you already hold: two rooms are never linked by repo and branch alone, because the version switcher shows each room's neighbours. marklayer_share_preview calls this route over stdio; over HTTP (/s/:id/mcp) it creates the room in the Worker, chains from the room in the URL, and returns an mcpUrl to reconnect to, since that endpoint stays bound to its own room.

$ID is caller-supplied (use nanoid / crypto.randomUUID() — it's the access token). No auth, no SDK. Full details: https://marklayer.app/llms-full.txt

Source: apps/mcp/README.md at commit 259986f

Tools

17

marklayer_read_page

Read the page this room annotates — its headings, paragraphs, links, buttons and labels, each with the CSS selector and tag that locate it. Call this FIRST when asked to review, audit or improve a page and no annotations exist yet: without it you can only answer feedback someone else left, never find anything yourself. Every entry is already in the shape marklayer_suggest_edit and marklayer_create_annotation take, so pass `selector`, `tag` and `markdown` straight through to anchor a mark to the element you are talking about. If the answer says the page renders client-side, the copy is not in the HTML and you should say so rather than review an empty shell.

Takes no parameters.

marklayer_connect_room

Connect to a MarkLayer room (annotation session) by URL or bare room id. Disconnects any prior room.

Parametersroomrequiredpassword

marklayer_room_info

Get metadata about the connected room: page URL, viewport width, timestamps.

Takes no parameters.

marklayer_list_annotations

List every annotation in the connected room across all tools (comment, area, selection, inspect). Each entry carries a `kind` discriminator and, where the user marked an element, a `target` block with selector + markdown so you can address the change without reopening the page. Filter by status: open, in_progress, resolved, approved, dismissed, or all (default). `approved` means the person who asked for the change confirmed the fix, so those need nothing from you.

Parametersstatus

marklayer_get_annotation

Get full detail for a single annotation of any kind (comment, area, selection, inspect), including its element `target` context and — for comments — the reply thread.

Parametersidrequired

marklayer_watch_annotations

Block until there is work, then return a batch of whatever arrives within a short window. CALL THIS AGAIN IMMEDIATELY EVERY TIME IT RETURNS, including when it returns nothing — an empty result means the window expired, not that the reviewer is done. Nothing pushes work to you: between your calls, a comment someone leaves reaches no one. Keep looping until the person tells you to stop or ends the session. Each event has a `kind`: "new" is an annotation someone just left; "handoff" is one given to you — assigned to you, a reply that @mentions you, or a reply on a thread you already own. A handoff also carries `request`, the reply that asked: read it, it is the instruction, and act on it rather than only acknowledging. Every event carries the full annotation with its `target` element context.

ParametersbatchMstimeoutSeconds

marklayer_acknowledge

Mark an annotation as in_progress and tag it with this agent so the human sees you are working on it.

Parametersidrequired

marklayer_resolve

Mark an annotation as resolved. Pass a summary describing what was changed — it will be posted as a reply to the thread.

Parametersidrequiredsummary

marklayer_dismiss

Mark an annotation as dismissed when it cannot be acted on. The reason will be visible to the human.

Parametersidrequiredreasonrequired

marklayer_reply

Post a reply to an annotation thread without changing its status. Use to ask clarifying questions.

Parametersidrequiredtextrequired

marklayer_create_annotation

Leave a new comment annotation yourself — proactive feedback (a bug, a UX issue, an accessibility gap, an idea) rather than only responding to a human’s. For an exact text replacement (a copy or grammar fix), use marklayer_suggest_edit instead — it renders as a diff the human can accept, rather than prose describing one. Use this after you have looked at the page with your own tools (a screenshot, a DOM read) and decided something is worth flagging; call it once per finding so each becomes its own pin the human can triage. x/y are document pixels (not viewport pixels — scroll offset already added in). Pass selector + tag + markdown (straight from marklayer_read_page) to pin to that element: the pin sits on the element and follows it when the page reflows, and x/y become optional (a fallback only). Omit all three for a fixed-point pin at x/y.

Parametersxytagtextrequiredmarkdownpriorityselector

marklayer_suggest_edit

Propose an exact replacement for a piece of text on the page — a copy or grammar fix — as a diff the human can accept, rather than a comment describing the change in prose. `rects` are the on-page bounding boxes of `text`, in document pixels: one per line it spans, so a wrapped sentence highlights as several boxes rather than one box swallowing the whole paragraph between its first and last line. Pass selector + tag + markdown together to anchor it to the containing element so it re-resolves if the page reflows.

Parameterstagtextrequiredrectsrequiredcommentmarkdownpriorityselectorsuggestionrequired

marklayer_share_preview

Open a new review room on a page you built — a preview deploy, a staging URL — and get back the link to hand to a human. Call this when you have something for someone to look at, rather than asking them to open a URL and describe what they see. The page is framed live and reviewers annotate it directly; their feedback then reaches you through the other tools. Pass `repo` + `branch` (and `sha`) when the page comes from a branch: a later preview of the same repo and branch becomes the next version of this one, and reviewers can step between versions. Over stdio this also connects you to the new room. Over HTTP the endpoint stays bound to the room in its URL, so reconnect to the returned `mcpUrl` to work the new room.

Parametersshaurlrequirednoterepotitlerequiredbranchpreviousquestions

marklayer_set_next_steps

Publish your plan as a short ordered checklist the reviewers see live in the room, distilled from their feedback — one step per change you will make, each naming the annotation ids it answers. Replaces any list you set before. Tick steps off with marklayer_complete_next_step as you finish them, so the humans can watch the work land instead of asking where it is.

Parametersstepsrequired

marklayer_complete_next_step

Tick a step of the checklist off (or back on with done: false). Reviewers see it change live.

Parametersidrequireddone

marklayer_ask

Put a multiple-choice question to the room — "Which hero variant?", "Ship with the banner?" — when you need a decision rather than open-ended feedback. Each reviewer gets one vote and may change it. Read the tally with marklayer_get_feedback.

Parameterspromptrequiredoptionsrequired

marklayer_get_feedback

Everything the room has told you, in one read: annotation counts by status with the open ones in full, the next-steps checklist and how far along it is, and every poll with its current tally. Use it to decide what to do next; use marklayer_watch_annotations to wait for more.

Takes no parameters.

Version history

3
  1. v0.6.1LatestOct 31, 2026
  2. v0.6.0Oct 17, 2026
  3. v0.5.0Sep 16, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts