{"description":"Read or write a Cobalt Capture review. Read: reviewer notes plus each annotated screenshot as an image. Write: an agent compiles a browser-testing/QA run (screenshots + notes) into a shareable review.","endpoint":"https://cobaltcapture.com/mcp","instructions":"Cobalt Capture stores visual product-review sessions \u2014 screenshots with notes and annotations \u2014 at shareable URLs a coding agent can read or write.\n\nREAD a review: call get_review with a review URL (/r/<slug> or /s/<slug>) or a bare slug. It returns the reviewer's notes as text plus each annotated screenshot as an image.\n\nREADY-MADE PLAYBOOKS. Before improvising a site review, check whether one of these covers it \u2014 they are maintained server-side and are more thorough than anything worth reconstructing:\n- usability_pass \u2014 first-time-user usability audit. Signs up with a disposable inbox, works the real jobs cold in a browser, screenshots every point of friction, ranks findings by severity. Has one human approval stop (personas/jobs).\n- positioning_pass \u2014 positioning, messaging, and 'who is this for' audit of the public pages, desktop and phone. Read-only, so it needs no accounts and runs start to finish unattended.\n- flow_pass \u2014 the user names a flow (signup, checkout, inviting a teammate) and you walk it step by step, reporting what broke and what merely confused. Any email the flow sends is audited too. Reach for this when they point at a specific journey rather than the whole site.\nFetch either with get_playbook(name, url) and follow it verbatim; clients that surface MCP prompts also expose them as slash commands. If the user asks for a UX review, a QA pass, a first-time-user test, or a messaging/positioning critique, reach for these rather than inventing a method.\n\nWRITE a review (e.g. to compile a browser-testing or QA run into one shareable report):\n1. Call create_review(title, summary?) ONCE. It returns a `review` slug and a `claim_token` \u2014 keep both; the claim_token authorizes every write. At this point you don't yet know what you'll find, so keep the initial summary to the task/scope.\n2. For each screen, call add_screenshot(review, claim_token, image_url, comment?) as you capture it (see GETTING THE IMAGE IN below for where image_url comes from). Do this per-screen rather than batching at the end, so nothing is lost. Use add_note(review, claim_token, text) for standalone notes.\n3. When the run is DONE, call update_review(review, claim_token, summary) with the headline findings and outcome. This puts the verdict at the TOP of the review, above the step-by-step screenshots \u2014 do this instead of leaving conclusions in a trailing note, which forces every reader to scroll past the intermediate steps first.\n4. Share the returned review_url. No login is required: reviews are anonymous and auto-expire in 30 days unless the human opens the save_url to keep them.\n\nGETTING THE IMAGE IN. add_screenshot needs a real image, not a description. It takes an `image_url` on Cobalt's own storage, which you get by POSTing the image to https://cobaltcapture.com/api/upload (multipart field `file`, no auth, returns {url, key}). Three paths, in order of preference:\n1. PREFERRED \u2014 upload from INSIDE the script that took the screenshot. If you are driving a browser (Playwright, Puppeteer, Selenium), you are already running a script with permission to run; do the POST in that same script, right after the capture. One process, no extra shell command, no second permission prompt, and the bytes never pass through you. In Python:\n     page.screenshot(path='shot.png')\n     url = requests.post('https://cobaltcapture.com/api/upload',\n                         files={'file': ('shot.png', open('shot.png','rb'), 'image/png')}\n                        ).json()['url']\n   The 3-tuple matters: requests sends application/octet-stream without it and the\n   upload is rejected with 'not an image'.\n   Then pass `url` to add_screenshot as `image_url`. Print the urls (or collect them) so you have them for the tool calls. Node/Playwright equivalent: the built-in request context, `request.post(..., { multipart: { file: ... } })`. Do NOT shell out to curl as a separate step when a script is already running \u2014 that is a second permission surface for no gain.\n2. Live-browser screen share, when the shot must come from the HUMAN's own browser session, or when you can run page JavaScript but cannot execute scripts or shell commands: open a Cobalt editor page (/new) in that browser, have the human click Capture once and pick the window (a one-time gesture; browsers require it and no automation can drive that picker). Then call window.cobaltGrab() on that page via your javascript_tool for each screen \u2014 it grabs the current shared frame, uploads it, and returns {ok, url}. Pass that url as `image_url`.\n3. `curl -sS -F file=@shot.png https://cobaltcapture.com/api/upload` \u2014 the last resort, for when you have a file but no script running (and a shell you are allowed to use). It works, but in a sandboxed client it costs a permission prompt per call, which is why it is third.\nNOT A PATH: base64 via `image`. The server accepts it (PNG/JPEG/WebP up to 8 MB decoded), but tool-call arguments are text YOU generate, so a screenshot means emitting tens of thousands of base64 tokens perfectly \u2014 slow, expensive, and unreliable. Worse, if the screenshot reached you as an image rather than a file, you cannot transcribe it at all. Reserve `image` for genuinely small images you already hold as base64 text.\n\nTEST ACCOUNTS / EMAIL. If the product needs a signup to review, and create_inbox is in your tool list, use it \u2014 do NOT ask the human for an email account, an inbox API key, or any third-party service. There is nothing for them to set up. Steps:\n1. create_inbox() -> returns `address` and `inbox_token`. Use a FRESH inbox per test persona.\n2. Type `address` into the product's signup form and submit.\n3. check_inbox(address, inbox_token) -> reads what arrived. Mail normally lands within seconds; if it's empty, wait ~5s and call again (there is no blocking wait). Give it a few tries before concluding the product never sent anything \u2014 'no verification email arrived' is a real finding, but only after you've actually waited.\n4. Pull the code or confirmation link out of the message and continue in the browser. Note that senders often rewrite links for click tracking, so the confirm URL may point at a tracking domain rather than the product's own \u2014 follow it anyway, it redirects correctly.\nInboxes expire, so don't reuse one across a long session. If create_inbox is NOT available, don't block the run: drive up to the verification wall, capture it, record 'signup requires email verification' as a finding, and continue with whatever is reachable without an account.\n\nThe claim_token from create_review is the handle for the whole session \u2014 pass it to every add_screenshot / add_note / update_review call for that review.","name":"cobalt-capture","prompts":[{"arguments":[{"description":"The site to test, e.g. https://example.com","name":"url","required":true},{"description":"Optional: 'owned' if you have the codebase here, 'external' if not. Left blank, it works this out.","name":"mode","required":false}],"description":"First-time-user usability audit of a website, delivered as a shareable Cobalt review. Finds the friction a real person would hit signing up and getting to first value. Works on a site you own (uses the repo for root causes) or one you don't.","name":"usability_pass"},{"arguments":[{"description":"The site to walk, e.g. https://example.com","name":"url","required":true},{"description":"Optional: 'owned' if you have the codebase here, 'external' if not. Left blank, it works this out.","name":"mode","required":false}],"description":"Walk a specific flow YOU define on a site \u2014 signup, checkout, inviting a teammate \u2014 and get back a review of what broke, what confused, and what could be better, step by step. Describe the flow in a sentence; no script needed. Audits any email the flow sends as part of the experience.","name":"flow_pass"},{"arguments":[{"description":"The site to audit, e.g. https://example.com","name":"url","required":true},{"description":"Optional: 'owned' if you have the codebase here, 'external' if not. Left blank, it works this out.","name":"mode","required":false}],"description":"Positioning, messaging, and 'who is this for' audit of a website's public pages, delivered as a shareable Cobalt review. Judges what the site communicates to a cold visitor \u2014 audience, category, proof, jargon, consistency \u2014 on desktop and phone. Fully autonomous: reads public pages only, no accounts, no approval stop.","name":"positioning_pass"}],"protocolVersion":"2025-06-18","tools":[{"description":"Fetch a Cobalt Capture review by its share link or slug. Returns the reviewer's notes as text AND each annotated screenshot as an image, so you can see exactly what was marked and what to change.","inputSchema":{"properties":{"review":{"description":"A Cobalt review URL (https://cobaltcapture.com/r/<slug> or /s/<slug>) or just the slug.","type":"string"}},"required":["review"],"type":"object"},"name":"get_review"},{"description":"Start a new Cobalt Capture review and get a handle for filling it in. Use this to compile findings from a browser-testing or QA run into one shareable, durable report. Returns a `review` slug and a `claim_token` \u2014 pass BOTH back to add_screenshot / add_note to append content. Also returns a `review_url` to share and a `save_url` the human opens (while signed in) to save the review to their account permanently. The review is anonymous and auto-deletes after 30 days unless saved.","inputSchema":{"properties":{"product_url":{"description":"Optional URL of the product/site under review.","type":"string"},"summary":{"description":"Optional overview shown at the top of the review (plain text or markdown). Good place for the overall task, outcome, and headline findings.","type":"string"},"title":{"description":"Title of the review, e.g. 'Onboarding walkthrough \u2014 Jupiter Invoice'.","type":"string"}},"required":["title"],"type":"object"},"name":"create_review"},{"description":"Append a screenshot (with an optional note) to a review created by create_review. The image is stored durably in Cobalt \u2014 this is the way to persist screenshots that would otherwise be lost when they only pass through a browser tool. Call once per screen as you go. Input is `image_url`: POST the image to https://cobaltcapture.com/api/upload (multipart field `file`, no auth, returns {url, key}) and pass back the url. If a browser-automation script took the screenshot, do that POST inside the same script (Python `requests.post(...)`, or Playwright's request context) rather than shelling out to curl afterwards \u2014 same result, one less permission prompt. The `image` base64 parameter exists for small images only: tool-call arguments are model-generated text, so a real screenshot means emitting tens of thousands of base64 tokens perfectly, and an image you received as an image cannot be transcribed at all.","inputSchema":{"properties":{"claim_token":{"description":"The claim_token from create_review (authorizes the write).","type":"string"},"comment":{"description":"Optional note describing what this screen shows or what's wrong with it.","type":"string"},"image":{"description":"For SMALL images only (< ~100 KB) that you already hold as base64 text: a base64-encoded PNG/JPEG/WebP (data: URL prefix accepted). Not the path for screenshots \u2014 emitting one as tool-call text is slow and error-prone, and an image you received as an image cannot be transcribed. Upload it and pass image_url instead.","type":"string"},"image_url":{"description":"The image URL returned by Cobalt's own upload: POST the file as multipart field `file` to https://cobaltcapture.com/api/upload (returns {url, key}) \u2014 ideally from inside the script that captured it \u2014 or use the browser hook window.cobaltGrab(). Must be on Cobalt's own image host; external URLs are refused.","type":"string"},"review":{"description":"The review slug or URL from create_review.","type":"string"},"source_url":{"description":"Optional URL of the page the screenshot was taken on.","type":"string"},"title":{"description":"Optional short caption for the screenshot.","type":"string"}},"required":["review","claim_token"],"type":"object"},"name":"add_screenshot"},{"description":"Append a free-standing text note (not tied to a screenshot) to a review created by create_review. Use for context, steps taken, or a summary of findings between screenshots.","inputSchema":{"properties":{"claim_token":{"description":"The claim_token from create_review (authorizes the write).","type":"string"},"review":{"description":"The review slug or URL from create_review.","type":"string"},"text":{"description":"The note text (plain text or markdown).","type":"string"}},"required":["review","claim_token","text"],"type":"object"},"name":"add_note"},{"description":"Update a review's summary and/or title after the fact. Use this at the END of a run to land the verdict at the TOP of the review: the headline findings don't exist until the work is done, but readers (and agents ingesting the review) want them before scrolling past every intermediate screenshot. Prefer this over burying conclusions in a closing note.","inputSchema":{"properties":{"claim_token":{"description":"The claim_token from create_review (authorizes the write).","type":"string"},"review":{"description":"The review slug or URL from create_review.","type":"string"},"summary":{"description":"The overview shown at the top of the review \u2014 put the headline findings and outcome here. Replaces any existing summary.","type":"string"},"title":{"description":"Optional replacement title.","type":"string"}},"required":["review","claim_token"],"type":"object"},"name":"update_review"},{"description":"Fetch a Cobalt test playbook by name and follow it exactly. Use this when the user asks for a usability pass, QA run, first-time-user review, or a positioning/messaging audit and you don't already have the playbook text. Available: 'usability_pass' \u2014 a first-time-user usability audit (signs up, drives the product, finds friction); 'positioning_pass' \u2014 a positioning and messaging audit of the public site (read-only, fully autonomous); 'flow_pass' \u2014 walks a specific flow the user names (signup, checkout, invite) and reports what broke and what confused, including any email it sends. All deliver a shareable Cobalt review. Prefer this over fetching the playbook from a URL: it comes through the protocol complete, whereas web fetchers summarize it and a summarized playbook silently drops the instructions that make the run work.","inputSchema":{"properties":{"name":{"description":"Playbook name, e.g. 'usability_pass'.","type":"string"},"url":{"description":"The site to run it against, if you already know it.","type":"string"}},"required":["name"],"type":"object"},"name":"get_playbook"},{"description":"Create a disposable email inbox you can monitor, for signing up to the product you're testing. Returns an `address` to type into the signup form and an `inbox_token` to read it with. Use this instead of asking the human for an email account or a third-party inbox API key \u2014 no setup needed. Inboxes are temporary; create a fresh one per test persona.","inputSchema":{"properties":{"review":{"description":"Optional review slug this inbox belongs to \u2014 a label for your own bookkeeping.","type":"string"}},"type":"object"},"name":"create_inbox"},{"description":"Read mail delivered to an inbox from create_inbox \u2014 e.g. to pull a verification code or confirmation link out of a signup email. Returns newest-first. Mail usually lands within seconds; if the response is empty, wait a few seconds and call again (there is no blocking wait). Extract the code or link yourself and continue the flow in the browser. Pass format=\"html\" when you are AUDITING the email rather than reading a code out of it: the default text view flattens away the layout, typography and branding, which is exactly what an email audit is judging.","inputSchema":{"properties":{"address":{"description":"The inbox address from create_inbox.","type":"string"},"format":{"description":"\"text\" (default) flattens each message to readable text \u2014 right for pulling a code or link. \"html\" returns the raw HTML part instead, for auditing the email as a rendered artifact: write it to a file, open it in the browser, and screenshot it.","enum":["text","html"],"type":"string"},"inbox_token":{"description":"The inbox_token from create_inbox (authorizes reading).","type":"string"}},"required":["address","inbox_token"],"type":"object"},"name":"check_inbox"}],"transport":"streamable-http","version":"1.8.1"}
