A review widget doorman injects into any gated site, and an MCP server that hands the page the visitor is looking at to Claude or ChatGPT — live edits, then a ticket.
Doorman already stands in front of the site, so it is the one place that can add something to it without touching the project's code. Agent-01 is what it adds: a corner bubble the reviewer can talk through, and an MCP server that exposes the page they are looking at as tools.
Put the browser and Claude side by side. Say "the hero heading is too big". Watch it change in the tab. Then say "file that", and the change request lands with the before and after of every edit attached.
No browser extension, no code in the customer project, nothing to install.
Agent-01 is off until a site asks for it. PUT /sites/{slug} replaces the whole
record, so read the current one, add the block, and put it back:
BASE=https://griller.kunde.example/_doorman/api/v1
AUTH="Authorization: Bearer $DOORMAN_ADMIN_TOKEN"
curl -s "$BASE/sites/griller" -H "$AUTH" \
| jq '.widget = {enabled: true, mcp: {enabled: true}, tickets: {enabled: true}}' \
| curl -X PUT "$BASE/sites/griller" -H "$AUTH" -H 'Content-Type: application/json' -d @-
Changes apply immediately — no restart. Reload the site. A small bubble appears in the corner, showing who the gate let
in — anna@kunde.dk, and how she got there.
[on | off]. The toggle below.Switch MCP on in the widget. It shows a URL:
https://griller.kunde.example/_doorman/mcp/<token>
Add it as a custom connector in Claude or ChatGPT, or as a remote MCP server in Claude Code:
claude mcp add --transport http griller https://griller.kunde.example/_doorman/mcp/<token>
The assistant can now see and change that tab. Ask it to snapshot the page
first; everything else works from the selectors that come back.
The toggle is also how you choose which tab. Only one tab is attached at a time — switch MCP on somewhere else and the first one is told it lost the session, so there is never any doubt about which page an assistant is talking to.
The URL is the credential: anyone holding it can drive that page. It expires (14 days by default), and switching MCP off and on again mints a new one.
| Read | snapshot, query, get_html, get_selection |
| Change | set_text, set_style, set_attribute, replace_html |
| Interact | click, fill, navigate (same-origin only) |
| Undo | list_edits, revert_edits |
| Hand back | create_ticket |
Every change is highlighted in the page as it happens — the reviewer watching is the point, not an afterthought — and every change is journalled, so nothing an assistant does is invisible or irreversible.
create_ticket asks the page for its own account of the URL, the selection and
the journal rather than taking the assistant's word for it.
run_jsOff by default. Add "allowScript": true to the mcp block and the
assistant also gets an arbitrary JavaScript expression in the page.
It is genuinely useful — a one-liner is often the honest answer to "why is this element three pixels off" — and it turns the connector URL into an execution credential in the reviewer's session. Enable it on your own staging, think twice on anything a customer is looking at.
Doorman stores them and hands them back; the platform that owns the customer relationship owns the UI.
GET /_doorman/api/v1/sites/{slug}/tickets?status=open
GET /_doorman/api/v1/sites/{slug}/tickets/{id}
PATCH /_doorman/api/v1/sites/{slug}/tickets/{id} {"status": "closed"}
A ticket carries the title and body, who raised it and how they got in, the page
URL, the selected element, and the full edit journal — selector, what kind of
change, and the value it replaced. Set tickets.webhookUrl and each new one is
also POSTed straight to your issue tracker, chat channel or automation.
Content-Security-Policy, doorman widens the declared
script-src/connect-src/default-src by 'self' and nothing else. It never
removes a directive and never adds one that was not there.