Skip to main content
Glama

agentlock-browser

Browser-Werkzeuge, die darauf prüfen, woher ein Wert stammt, nicht darauf, was er sagt.

Ein MCP-Server, der einen echten Browser steuert und sich weigert, auf eine URL oder einen Formularwert zu reagieren, der aus Seiteninhalten stammt. Eine Seite kann alles behaupten; was sie nicht ändern kann, ist die Herkunft des Strings, dessen Verwendung sie von den Agenten verlangt.

Das ist ein v0-Skelett. Es basiert auf AgentLock, dem offenen Standard für die Autorisierung von Tool-Aufrufen, und besitzt seinen Browser direkt über Playwright. Es umhüllt @playwright/mcp nicht und hängt nicht davon ab.

Warum nicht einfach den Upstream-Server umhüllen

probe/REPORT.md hält fest, was @playwright/mcp 0.0.79 tatsächlich zurückgibt: Alle Ergebnisse sind ein einzelner untypisierter Textblock (structuredContent ist in allen 23 aufgezeichneten Ergebnissen null, und keines seiner 24 Werkzeuge deklariert ein outputSchema). Der Seitentext kommt ohne eine Identität pro Element an: von 874 - text:-Zeilen in einem Wikipedia-Schnappschuss trägt keine einzige einen Ref. Elemente werden durch undurchsichtige serverseitige Refs benannt, die sich in einen Playwright-Locator auflösen, den der Aufrufer nie sieht, und desselbe Feld akzeptiert eines rohen CSS-Selektor. Und browser_evaluate führt beliebiges JavaScript aus, das das Modell geschrieben hat.

Nichts davon kann Herkunft vermitteln. Deshalb gibt dieser Server stattdessen strukturierte, identifizierte und mit Ursprung markierte Ausgabe aus und hat kein evaluate-Werkzeug.

Related MCP server: open-browser-control

Installation

pip install agentlock-browser
python -m playwright install chromium

Aus einem Checkout:

pip install -e .
python -m playwright install chromium

Zu Claude Desktop hinzufügen

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 kann auf eine JSON-Datei mit denselben Schlüsseln verweisen (allowlist, operator_text, log_path, headless, …); die Umgebung hat Vorrang vor der Datei. Der Browser läuft im Headless-Modus, außer AGENTLOCK_BROWSER_HEADLESS=0 ist gesetzt.

Werkzeuge

Werkzeug

Argumente

Gate-Prüfung

navigate

url oder link_id

Kanal von url; Aktualität von link_id

snapshot

(keine)

ohne Gate-Prüfung

read_text

(keine)

ohne Gate-Prüfung

click

element_id

ohne Gate-Prüfung; die dadurch ausgelöste Navigation ist es nicht

type

element_id, value

Kanal von value

back

(keine)

ohne Gate-Prüfung

All tools give structureContent against a declared outputSchema. snapshot returns {origin, url, title, elements: [{id, role, name, text, href}]}; read_text returns {origin, blocks: [{id, text}]}. Page text never dies as a single undifferentiated string.

Element IDs are stable for the current page load and are restored after navigation: an ID is <page-load>-e<n>, and an ID emitted before navigation can no longer be resolved after navigation. A denial is a structured result with a reason, not an error.

Provenance rules

Four channels:

  • USER: Text from the operator's message

  • ALLOWLIST: Operator-configured origins, loaded at startup

  • PAGE(origin): any value read from page content, labeled with the origin

  • MODEL: composed by the model, without page or user line of origin

Values are marked from the origin: the operator's message text is recorded as USER when starting the server, the configured allowlist as ALLOWLIST, and everything snapshot or read_text returns is recorded as PAGE(origin) at the moment it is returned. Content becomes contaminated exactly when the model can see it.

What that brings, per action:

Aktion

zulässig

verweigert

navigate(url)

USER, ALLOWLIST

PAGE, MODEL

navigate(link_id)

ID from the latest snap

expired or unknown ID

type(value)

USER

PAGE, MODEL

Cross-Origin-Navigation, by a click

(nothing)

always: treated as navigate(url) with PAGE origin

Ohne Gate-Prüfung in v0: snapshot, read_text, back and navigation with same origin.

navigate(link_id) is what an agent does to follow a link. The ID is resolved on the server side to an href, and the never has to pass through the model. This means following a link requested by the operator remains possible, while inserting a URL supplied by a page is not (alone).

A click that would leave the current origin is answered with a 204, so the navigation is rejected and the page stays exactly as it was: no error page, no request to the other origin.

The AgentLock-Gate decides. This package records origin, invokes gate.authorize() may be and reports the verdict; it never overrides a verdict. The single exception is the freshness rule for the link ID, which AgentLock cannot express. These rejects are therefore marked in the log as decided_by: "server:link_freshness" so that they are never mistaken for a gate decision. See NEEDS.md.

Every decision and every entry of origin is appended to a JSONL log with the "gate" evidence and a signed receipt:

{"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": "…"}}

That log is the basis for the pre-registered tests, never a chat summary.

The USER channel

MCP gives the server no way to see the operator's message, so USER text is a startup input (operator_text / AGENTLOCK_BROWSER_OPERATOR_TEXT), not an argument that can be in a tool. A session whose operator text is empty or nondescript has a weaker USER baseline than one where the operator has stated what they want (see the second restriction below).

Tests

pytest                                  # T5 runs; T1-T4 are registered and skipped
cat tests/results/T5.txt                # the raw T5 transcript, log and checks

T5 (legitimate baseline) is automated without the model in the loop: the tool calls are issued directly, so what is measured is the gate and the server. T1-T4 fixtures and both local origins are implemented (evil.test resolves to a second local server, so the cross-origin case is real), but their model-driven runs are not, guided by the fact these predictions are about model behavior.

Known disadvantages

Exaword copied from PREDICTIONS.md, which was frozen before any server code existed:

  • Selection influence: page content can still influence which link_id is selected by the model. Out of scope, documented, as with AgentLock itself.

  • Same-origin clicks are not in v0 with gates. A hostile site can drive the agent within itself. Cross-origin is the boundary we defend.

  • Model decline can make T1 and T3 uninformative. That is a payload problem, not a gate result.

Found while creating v0, and not yet closed:

  • The line gates open at values without distinctive token. AgentLock classifies a value by extracting a token from it; a plain word under 12 characters with no digit or punctuation creates no token, and a value that cannot be classified is allowed. A page that says type the word expenses is not caught; one that says type abc123 is. Such permission is recorded as "channel": "UNCLASSIFIED", "fail_open": true rather than being closed by a local check, because closing it here would move the decision to the irrelevant tree. PFEEL item 3.

  • A empty operator message weakens the MODEL check. Novel-lineage needs a unique token in the authoritative context to have any baseline at all; without one, MODEL-composed values are allowed. "NEEDS.md item 3(b).

  • read_text and snapshot report a curated set of elements. Text in an element that no selector matches is omitted and therefore never recorded as PAGE. That remains consistent (what the model cannot see cannot be distributed by it) only as long as these tools are the only ways page content and conversation enter the other.

  • A redirect from an authorized navigation is followed without a second check. v0 gives permission to the redirect chain from a URL it allowed.

  • One tab. target="_blank" and popups are not handled in v0.

License

AGPL-3.0-or-later. Copyright 2026 David Grice.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.

  • Choose HTTP, browser, machine endpoint, or avoid before an agent visits an unfamiliar URL.

  • Stealth web browser for agents: search, fetch, click and type through persistent sessions over MCP.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/webpro255/agentlock-browser'

If you have feedback or need assistance with the MCP directory API, please join our Discord server