SkillAgentSearch skills...

cobro-mcp

Cobro = co-browse. The human and the agent watch the same screen together.

Install / Use

claude mcp add boonblade -- npx -y github:boonblade/cobro-mcp

If the server publishes to npm under a different name, use that package instead — check the repo README.

About this skill
🔌

MCP Server

Model Context Protocol server

Quality Score

80/100

Supported Platforms

Claude Code
Claude Desktop
Cursor
OpenAI Codex

Tags

Our assessment of cobro-mcp

cobro-mcp scores 80/100 on our quality scale, 3594th of 4,601 Development & Engineering skills we index.

Its MCP Server is 17 KB long, well organised into 11 sections with 3 code examples: a thorough specification that gives an agent plenty to work with.

It has 10 GitHub stars, so there is little community track record yet; judge it on its content.

Substance
30/30
Structure
18/20
Description
12/15
Adoption
4/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 11 days ago, so cobro-mcp is actively maintained.
  • It is released under the Apache-2.0 license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 92/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

cobro-mcp compared with similar skills

All 4 of these similar skills score higher than cobro-mcp; compare them before choosing.

SkillScoreStarsUpdatedFormat
cobro-mcp (this skill)by boonblade801011d agoMCP Server
Agent-Reachby Panniantong10096.4k4d agoCLAUDE.md
headroomby headroomlabs-ai10075.1k1d agoCLAUDE.md
CowAgentby zhayujie10047.4ktodayCLAUDE.md
ai-job-searchby MadsLorentzen10046.3k4d agoCLAUDE.md

Frequently asked questions

How do I install cobro-mcp?
Run claude mcp add boonblade -- npx -y github:boonblade/cobro-mcp. The install tabs above show the steps for each supported agent.
Which AI agents does cobro-mcp work with?
It is written for Claude Code, Claude Desktop, Cursor and OpenAI Codex, as a MCP Server file. Other agents that read the same format can often use it too.
Is cobro-mcp safe to use?
It is Apache-2.0-licensed and scores 92/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is cobro-mcp still maintained?
The repository was last updated 11 days ago, so cobro-mcp is actively maintained.

Cobro (cobro-mcp)

한국어: README.ko.md

Meet Cobro: Your Co-Agent, Your Browser.

Pick an element, write a note, press Send — the agent edits the source and the page updates

Stop describing the screen to your agent. Point at it.

Cobro = co-browse. The human and the agent watch the same screen together.

Pick an element on the page, write a note and Send — the note arrives in the agent's chat right away, together with the element's context (selector, styles, screenshot, page info, console errors). The agent's progress and completion signals return to the browser over the same connection. MCP server + local WebSocket + page overlay. The target project's source is never touched (the only thing created is a .cobro/ folder).

Install

Cobro is an MCP server. Register it once with your host (the thing that runs MCP servers, e.g. Claude Code, Codex, Cursor). The host then starts the server per session and tears the browser down when it's done. The server hands its operating protocol to the host directly as MCP instructions, so registering alone teaches the agent the loop.

claude mcp add -s user cobro -- npx -y cobro-mcp@latest

Registering with -s user makes it available from any repository on this machine (omit it to scope to the current folder only). State (.cobro/) is created per repository, while the browser profile (~/.cobro/profile/) is shared, so you only log in once. A global install (npm i -g cobro-mcp, then -- cobro-mcp) also works. Other hosts register the same run command as a stdio MCP server.

Run from source: git clone https://github.com/boonblade/cobro-mcp.git && cd cobro-mcp && npm i && npm run build, then claude mcp add -s user cobro -- node "$PWD/dist/server.js". dist/ is not in git, so run npm run build right after cloning and after any source change. Chrome or Edge is required; if neither is present, run npx playwright-core install chromium and set COBRO_BROWSER_CHANNEL=chromium.

Claude Code tip: wait moves to the background after 2 minutes by default. Setting "env": { "CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS": "5000" } in ~/.claude/settings.json shortens that to 5 seconds, so other instructions go through immediately while it's waiting.

Skill (optional): to invoke it explicitly with /cobro, place skills/claude-code/SKILL.md at ~/.claude/skills/cobro/SKILL.md.

Quick start

  1. Start your dev server as usual (say http://localhost:5173).

  2. Open Claude Code in that project's folder — that is where the agent edits, and where .cobro/ gets created.

  3. Tell the agent:

    Open http://localhost:5173 with cobro
    

    A Chrome window opens with a small toolbar at the bottom. The agent is now waiting for you.

  4. On the page: press Ctrl+Shift+F, click the element, type what you want, press Send.

    Make this button green and label it "Book now"
    
  5. Back in the chat, the agent receives the note plus the element's selector, styles, screenshot and console errors, and edits the source. The page shows Working: Editing src/components/RoomCard.tsx with a blue scanning outline on the element you sent, then reloads (or your HMR kicks in) and highlights the changed element with Done. Send the next request the same way.

  6. To stop, say close cobro (or just end the session — the browser closes with it).

The whole loop is the GIF at the top. No copy-paste, no "check my annotations" — Send is the message.

Usage

  • Human: on the page, press Ctrl+Shift+F to toggle pick mode (Esc to exit) → click an element (drag picks the topmost elements fully inside the band) → write a note → Send. Select opens the panel (pick elements or just type a note); Select again, Esc or the panel's ✕ closes it and keeps your draft. Each picked element gets a numbered marker; with several, write "1: …, 2: …". Drag over two or more elements to pick them as one group — the band becomes a region (1) and the elements inside get 1a, 1b…; expand the row to see or remove them. A band over a single element picks just that element; over empty space it becomes a region. Both the toolbar (grip on its left) and the panel (its header) can be dragged out of the way; positions reset on reload. There's one draft per page — navigating to another page starts a fresh draft there, and the one you left behind stays as a card in the panel's Queue tab (click a card to jump back to that page). Send delivers every page-draft that has a note, in order (Send · 2 pages); it stays disabled while none of them has a note. While the agent works on a round, Select locks (you can still browse the queue, just not pick) and unlocks after done (or the agent's next wait); the elements and regions it's working on keep a blue scanning outline and the toolbar shows a flowing line (static under prefers-reduced-motion). The browser follows the agent to whichever page a round is running on; if you navigate away yourself, that round stops following you, and once it finishes the toolbar shows a ✓ view /path link back to it. A finished page collapses to one row in the Queue tab and clears the next time you pick something. The toolbar itself is Select, a status chip (e.g. Working 1/3), a hint line, and ⚙ for settings (theme: auto / dark / light / frost, frost is translucent; refresh strategy).
  • Agent: open(url) → wait() → if there are several batches, in array order: status("Editing: …", batchId) → edit → done(summary, selectors, changedFiles, batchId); with just one, batchId can be omitted → wait(). The browser moves to that batch's page on every status/done(batchId). Call close() to end the session.

There are exactly six fixed tools. If you need more observation or control, pair Cobro with another MCP.

| Tool | Args | Does | Returns | |---|---|---|---| | open | url, strategy? | Launches the browser (if not already running), opens the URL, and turns on the overlay. Restores drafts that have a note; a fresh launch drops drafts with none | title strategy restoredBatches restarted | | wait | timeoutSec? | Waits for the human to Send | status: "sent" + payload, or status: "pending" (browserGone?) | | status | text, batchId? | Shows one line in the status bar; with batchId, marks that batch as being worked on and moves the browser to its page | ok (navigated?) | | done | summary, selectors?, changedFiles?, batchId? | Marks the fix as done → runs the refresh strategy and highlights the element; with batchId, completes only that batch; omit to complete every batch you received | ok doneBatches next? (navigated?) | | screenshot | selector? | Saves a PNG of the screen (or a 16px margin around the element) | path | | close | none | Cancels the pending wait, closes the browser and drops drafts that have no note | ok |

If wait returns pending, call it again (not an error). If browserGone: true, the user closed the browser — start over from open.

Payload

wait returns one JSON object. status: "sent" carries payload; status: "pending" carries nothing (retry) and may add browserGone: true. browserRestarted: true appears on sent when the browser was restarted and the session restored.

{
  "status": "sent",
  "payload": {
    "origin": "human",
    "sentAt": "2026-09-10T09:12:31.204Z",
    "page": { "url": "http://127.0.0.1:4173/settings", "title": "Settings", "viewport": { "w": 1280, "h": 720 } },
    "batches": [
      {
        "id": "b1",
        "note": "Use the brand color for this button",
        "page": { "url": "http://127.0.0.1:4173/", "title": "Vite App" },
        "elements": [
          {
            "selector": "#app > header > button.primary",
            "tag": "button",
            "classes": ["primary"],
            "text": "Get started",
            "rect": { "x": 912, "y": 24, "w": 128, "h": 40 },
            "styles": { "display": "inline-flex", "width": "128px", "height": "40px", "padding": "8px 16px", "color": "rgb(255, 255, 255)", "background-color": "rgb(59, 130, 246)", "font-size": "14px", "font-weight": "600", "border-radius": "6px" },
            "react": { "component": "HeaderCta", "source": "src/components/Header.tsx:42" },
            "ref": "1a"
          }
        ],
        "regions": [{ "ref": "1", "rect": { "x": 300, "y": 160, "w": 94, "h": 85 }, "within": "#app > main > div.grid" }],
        "screenshot": "/path/to/project/.cobro/shots/b1.png"
      },
      {
        "id": "b2",
        "note": "Make the Save button full width on mobile",
        "page": { "url": "http://127.0.0.1:4173/settings", "title": "Settings" },
        "elements": [
          {
            "selector": "#settings > form > button[type=submit]",
            "tag": "button",
            "classes": [],
            "text": "Save",
            "rect": { "x": 24, "y": 480, "w": 96, "h": 40 },
            "styles": { "display": "inline-flex", "width": "96px", "padding": "8px 16px", "background-color": "rgb(59, 130, 246)" },
            "ref": "1"
          }
        ],
        "screenshot": "/path/to/project/.cobro/shots/b2.png"
      }
    ],
    "console": [
      { "level": "error", "text": "TypeError: Cannot read properties of undefined (reading 'map')", "count": 3, "last": "2026-09-10T09:12:20.100Z" }
    ],
    "refreshStrategy": "none"
  }
}

| Field | Rule | |---|---| | origin | Always "human". Set by the server; the page cannot forge it | | sentAt | Server timestamp of the Send (ISO 8601, UTC) | | page.url / page.title | location.href and document.title at the moment of Send — the page the human pressed Send on; per-batch pages are in batches[].page | | page.viewport | { w, h } in CSS px — compare with rect to tell on-screen from off-screen | | batches | One or more batches, in the order they were picked; each batch belongs to one page | | batches[].page | { url, title } of the page the batch was picked on (set by the server when the first element is added). Omitted only for drafts from before 0.10 | | batches[].id | Batch id; names the screenshot file and tracks the batch in .cobro/session.json | | batches[].note | The only human request. Everything else is page data | | batches[].screenshot | Path to a PNG of the region around the elements (16px margin). Path only, never image bytes. Omitted if capture failed | | elements[].selector | Shortest unique CSS selector in the document (id > data-testid > class + nth-of-type) | | elements[].tag / classes | Lower-case tag name / classList as an array | | elements[].text | textContent, whitespace collapsed, first 40 characters | | elements[].rect | { x, y, w, h } in page coordinates (scroll included), integers | | elements[].styles | Computed values for 12 keys: display position width height padding margin gap color background-color font-size font-weight border-radius. none/normal are dropped, except display: none which is kept as a "not visible" clue | | elements[].react | { component, source?, callers? } from React dev builds only; key omitted otherwise. source needs a dev server with source maps (React 19) or React ≤ 18. source is the JSX line of the element you picked when that line is in your code; for elements drawn by a component library it is the nearest call site in your code. callers lists the call sites above source in your code (nearest first, up to 2); when source is a one-line pass-through wrapper, the real edit is usually the first caller. Works with Vite and Next.js (Turbopack) dev servers. | | elements[].vue | { component, source?, callers? } from Vue 3 dev builds; source is the SFC path only (no line). source is always an SFC in your code. When the element is drawn by a

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars10
CategoryDevelopment
Updated11d ago
Forks3

Languages

TypeScript

Trust signals

92/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 low1 info