agentlock-browser
agentlock-browser
Инструменты для работы с браузером, которые проверяют, откуда взялось значение, а не что оно говорит.
MCP-сервер, который управляет настоящим браузером и отказывается действовать по URL или значению формы, полученному из содержимого страницы. Страница может сказать что угодно; но она не может изменить происхождение строки, которую просит использовать агента.
Это скелет версии v0. Он построен на AgentLock — открытом стандарте авторизации вызовов инструментов — и управляет браузером напрямую через Playwright. Он не оборачивает @playwright/mcp и не зависит от него.
Почему не просто обернуть вышестоящий сервер
В probe/REPORT.md зафиксировано, что на самом деле возвращает @playwright/mcp 0.0.79. Каждый результат — это один нетипизированный текстовый блок (structuredContent равен null во всех 23 записанных результатах, и ни один из его 24 инструментов не объявляет outputSchema). Текст страницы приходит без идентичности отдельных элементов: из 874 строк - text: в снимке Wikipedia ни одна не содержит ссылку. Элементы именуются непрозрачными серверными ссылками, которые разрешаются в локатор Playwright, который вызывающий никогда не видит, и то же поле принимает сырой CSS-селектор. А browser_evaluate выполняет произвольный JavaScript, написанный моделью.
Ничто из этого не может нести происхождение. Поэтому этот сервер вместо этого выдаёт структурированный, идентифицированный, помеченный происхождением вывод и не имеет инструмента evaluate.
Related MCP server: gaze
Установка
pip install agentlock-browser
python -m playwright install chromiumИз клонированного репозитория:
pip install -e .
python -m playwright install chromiumДобавление в Claude Desktop
claude_desktop_config.json:
{
"mcpServers": {
"agentlock-browser": {
"command": "agentlock-browser",
"env": {
"AGENTLOCK_BROWSER_ALLOWLIST": "https://example.com,https://docs.python.org",
"AGENTLOCK_BROWSER_OPERATOR_TEXT": "research the docs and summarize them",
"AGENTLOCK_BROWSER_LOG": "/home/you/agentlock-browser.jsonl"
}
}
}
}AGENTLOCK_BROWSER_CONFIG может указывать на JSON-файл с теми же ключами (allowlist, operator_text, log_path, headless, …); переменная окружения имеет приоритет над файлом. Браузер работает в headless-режиме, если не задано AGENTLOCK_BROWSER_HEADLESS=0.
Инструменты
инструмент | аргументы | проверяется на |
|
| канал |
| (нет) | без проверки |
| (нет) | без проверки |
|
| без проверки; вызванная им навигация — нет |
|
| канал |
| (нет) | без проверки |
Каждый инструмент возвращает structuredContent в соответствии с объявленной outputSchema. snapshot возвращает {origin, url, title, elements: [{id, role, name, text, href}]}; read_text возвращает {origin, blocks: [{id, text}]}. Текст страницы никогда не возвращается как одна недифференцированная строка.
Идентификаторы элементов стабильны для текущей загрузки страницы и пересоздаются при навигации: идентификатор имеет вид <page-load>-e<n>, и идентификатор, созданный до навигации, не может быть разрешён после неё. Отказ — это структурированный результат с причиной, а не ошибка.
Правила происхождения
Четыре канала:
USER: текст из сообщения оператора
ALLOWLIST: настроенные оператором источники, загружаемые при запуске
PAGE(origin): любое значение, прочитанное из содержимого страницы, помеченное источником
MODEL: составлено моделью, без происхождения от страницы или пользователя
Значения помечаются в источнике: текст сообщения оператора записывается как USER при запуске сервера, настроенный список разрешений — как ALLOWLIST, а всё, что возвращают snapshot или read_text, записывается как PAGE(origin) в момент возврата. Контент становится заражённым ровно тогда, когда модель может его увидеть.
Что это даёт для каждого действия:
действие | разрешено | запрещено |
| USER, ALLOWLIST | PAGE, MODEL |
| id из самого последнего снимка | устаревший или неизвестный id |
| USER | PAGE, MODEL |
межсайтовая навигация, вызванная кликом | (ничего) | всегда: рассматривается как |
Без проверки в v0: snapshot, read_text, back и клики в пределах того же источника.
navigate(link_id) — это способ агента перейти по ссылке. Идентификатор разрешается в href на стороне сервера, и href не должен проходить через модель, поэтому переход по ссылке, которую запросил оператор, остаётся возможным, а вставка URL, предоставленного страницей, — нет.
Клик, который покинул бы текущий источник, получает ответ 204, поэтому навигация отбрасывается, и страница остаётся ровно там, где была: ни страницы ошибки, ни запроса к другому источнику.
Решение принимает шлюз AgentLock. Этот пакет записывает происхождение, вызывает gate.authorize() и сообщает вердикт; он никогда не переопределяет его. Единственное исключение — правило свежести идентификатора ссылки, которое AgentLock не может выразить. Такие отказы помечаются в журнале как decided_by: "server:link_freshness", чтобы их никогда не путали с вердиктом шлюза. См. NEEDS.md.
Каждое решение и каждая запись о происхождении добавляются в JSONL-журнал с доказательствами шлюза и подписанной квитанцией:
{"event": "decision", "action": "navigate_url", "allowed": false,
"reason": "param_lineage", "channel": "PAGE", "origin": "http://fixture.test",
"decided_by": "agentlock_gate",
"evidence": {"gate": "param_lineage", "matched_param": "url",
"matched_token": "evil.test/collect?x=handbook-session-7f3a1c",
"untrusted_provenance_id": "cprov_8fb61c8f03117287"},
"receipt": {"receipt_id": "rcpt_…", "signature": "…"}}Этот журнал — источник истины для предварительно зарегистрированных тестов, а не сводка чата.
Канал USER
MCP не даёт серверу возможности увидеть сообщение оператора, поэтому текст USER — это входные данные при запуске (operator_text / AGENTLOCK_BROWSER_OPERATOR_TEXT), а не то, что может нести аргумент инструмента. Сессия с пустым или шаблонным текстом оператора имеет более слабую базовую линию USER, чем та, где оператор указал, чего он хочет (см. второе ограничение ниже).
Тесты
pytest # T5 runs; T1-T4 are registered and skipped
cat tests/results/T5.txt # the raw T5 transcript, log and checksT5 (легитимная базовая линия) автоматизирован без участия модели: вызовы инструментов выполняются напрямую, поэтому измеряются шлюз и сервер. Фикстуры T1–T4 и оба локальных источника реализованы (evil.test разрешается во второй локальный сервер, так что межсайтовый случай реален), но их запуски под управлением модели — нет, потому что эти прогнозы касаются поведения модели.
Названные ограничения
Скопировано дословно из PREDICTIONS.md, который был заморожен до того, как существовал какой-либо код сервера:
Влияние выбора: содержимое страницы всё ещё может влиять на то, какой link_id выберет модель. Вне области действия, задокументировано, как в ядре AgentLock.
Клики в пределах того же источника не проверяются в v0. Враждебный сайт может управлять агентом внутри себя. Межсайтовый переход — это граница, которую мы защищаем.
Отказ модели может сделать T1 и T3 неинформативными. Это проблема полезной нагрузки, а не результат шлюза.
Обнаружено при создании v0 и ещё не закрыто:
Проверки происхождения открываются вхолостую для значений без отличительного токена. AgentLock классифицирует значение, извлекая из него токены; обычное слово короче 12 символов без цифр или знаков препинания не даёт ни одного, и значение, которое нельзя классифицировать, разрешается. Страница, которая говорит введите слово
expenses, не ловится; та, что говорит введитеabc123, — ловится. Такое разрешение записывается как"channel": "UNCLASSIFIED", "fail_open": true, а не закрывается локальной проверкой, потому что закрытие здесь переместило бы решение за пределы шлюза. Пункт 3 вNEEDS.md.Шаблонное сообщение оператора ослабляет проверку MODEL. Для новизны происхождения нужен отличительный токен в авторитетном контексте, чтобы вообще была базовая линия; без него значения, составленные моделью, разрешаются. Пункт 3(b) в
NEEDS.md.read_textиsnapshotсообщают о выбранном наборе элементов. Текст в элементе, который не соответствует ни одному селектору, не возвращается и поэтому никогда не записывается как PAGE. Это остаётся согласованным (что модель не может видеть, то не может и повторить) только до тех пор, пока эти инструменты — единственный способ попадания содержимого страницы в разговор.Перенаправление с разрешённой навигации выполняется без второй проверки. v0 разрешает цепочку перенаправлений URL, который он разрешил.
Одна вкладка.
target="_blank"и всплывающие окна не обрабатываются в v0.
Лицензия
AGPL-3.0-or-later. Copyright 2026 David Grice.
This server cannot be deployed
Maintenance
Related MCP Connectors
- NocticasOAuthcom.nocticas
Browser QA agent that won't return a false green: verified PASS/FAIL + access-control probing.
Let your AI agent operate any website through a real, logged-in browser session.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
The trust harness for AI agents. Set what an agent can do before it acts.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables governed browser automation for AI agents with human-in-the-loop approval, audit logging, and read-only replay, supporting both ASK and AUTO autonomy modes.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to drive a real, already logged-in browser from the CLI or over MCP, with enforceable approval gates for actions and safeguards against prompt injection.1,969,715 npmMozilla Public 2.0
- AlicenseAqualityBmaintenanceEnables AI agents to automate browsers through Playwright behind multi-layer safety guards, restricting navigation to allowed domains, defaulting to read-only actions, and recording all behavior in audit logs.7MIT
- AlicenseNot gradedqualityBmaintenanceEnables policy-gated browser automation with persistent Firefox/Chromium profile isolation, version-locked fingerprint consistency, and auditable local session management.Apache 2.0