Skip to main content
Glama

agentlock-browser

値が「何を言っているか」ではなく「どこから来たか」でゲートするブラウジングツール。

実際のブラウザを操作し、ページコンテンツに由来するURLやフォーム値を拒否するMCPサーバーです。ページは何でも言えますが、エージェントに使わせようとしている文字列の来歴(provenance)を変えることはできません。

これはv0スケルトンです。AgentLock(ツール呼び出し認可のオープン標準)上に構築され、Playwrightを直接操作してブラウザを所有します。@playwright/mcpをラップせず、依存もしません。

なぜ上流サーバーをラップしないのか

probe/REPORT.mdには、@playwright/mcp 0.0.79が実際に返す内容が記録されています。すべての結果は単一の型なしテキストブロックであり(記録された23件すべてでstructuredContentはnull、24個のツールのいずれもoutputSchemaを宣言していません)。ページテキストは要素ごとの識別子なしで届きます。Wikipediaスナップショットの874行の- text:行のうち、refを持つものはゼロです。要素は、呼び出し側には見えないPlaywrightロケーターに解決される不透明なサーバー側refで名前付けされ、同じフィールドが生の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は、同じキー(allowlist、operator_text、log_path、headlessなど)を持つJSONファイルを指すことができます。環境変数がファイルより優先されます。AGENTLOCK_BROWSER_HEADLESS=0でない限り、ブラウザはヘッドレスで実行されます。

ツール

ツール

引数

ゲート条件

navigate

url または link_id

urlのチャネル; link_idの鮮度

snapshot

(なし)

ゲートなし

read_text

(なし)

ゲートなし

click

element_id

ゲートなし; それが引き起こすナビゲーションはゲート対象

type

element_id, value

valueのチャネル

back

(なし)

ゲートなし

すべてのツールは、宣言されたoutputSchemaに対してstructuredContentを返します。snapshotは{origin, url, title, elements: [{id, role, name, text, href}]}を返し、read_textは{origin, blocks: [{id, text}]}を返します。ページテキストが単一の未分化な文字列として返されることはありません。

要素IDは現在のページ読み込み中は安定しており、ナビゲーション時に再生成されます。IDは<page-load>-e<n>の形式で、ナビゲーション前に発行されたIDはナビゲーション後に解決できません。拒否はエラーではなく、理由を含む構造化された結果です。

来歴ルール

4つのチャネル:

  • USER: オペレーターのメッセージからのテキスト

  • ALLOWLIST: オペレーターが設定した発信元。起動時に読み込まれる

  • PAGE(origin): ページコンテンツから読み取られた任意の値。発信元でタグ付けされる

  • MODEL: モデルが構成した値。ページやユーザーの系統を持たない

値はソースでタグ付けされます。オペレーターのメッセージテキストはサーバー起動時にUSERとして記録され、設定された許可リストはALLOWLISTとして、snapshotまたはread_textが返すすべては、返された瞬間にPAGE(origin)として記録されます。コンテンツは、モデルがそれを見ることができるまさにその時に汚染されます。

それがアクションごとに買うもの:

アクション

許可

拒否

navigate(url)

USER, ALLOWLIST

PAGE, MODEL

navigate(link_id)

最新のスナップショットからのID

古いまたは不明なID

type(value)

USER

PAGE, MODEL

クリックによるクロスオリジンナビゲーション

(なし)

常に: PAGE来歴を持つnavigate(url)として扱われる

v0でゲートなし: snapshot、read_text、back、および同一オリジンのクリック。

navigate(link_id)は、エージェントがリンクをたどる方法です。IDはサーバー側でhrefに解決され、hrefはモデルを通過する必要がありません。したがって、オペレーターが要求したリンクをたどることは可能ですが、ページが提供したURLを貼り付けることはできません。

現在のオリジンを離れるクリックは204で応答されるため、ナビゲーションは破棄され、ページはそのままの場所に留まります。エラーページも、他のオリジンへのリクエストもありません。

AgentLockゲートが決定します。 このパッケージは来歴を記録し、gate.authorize()を呼び出し、判定を報告します。決して上書きしません。唯一の例外はリンクIDの鮮度ルールで、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ベースラインを持ちます(下記の2番目の制限を参照)。

テスト

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

T5(正当なベースライン)はモデルなしで自動化されています。ツール呼び出しが直接発行されるため、測定されるのはゲートとサーバーです。T1〜T4のフィクスチャと両方のローカルオリジンは実装されています(evil.testは2番目のローカルサーバーに解決されるため、クロスオリジンケースは実際のものです)が、モデル駆動の実行はされていません。それらの予測はモデルの動作に関するものだからです。

明示された制限

PREDICTIONS.mdから逐語的にコピーされました。これはサーバーコードが存在する前に凍結されました:

  • 選択への影響: ページコンテンツは、モデルがどのlink_idを選択するかを依然として誘導できます。範囲外、文書化済み、コアAgentLockと同じ。

  • 同一オリジンのクリックはv0ではゲートされません。 敵対的なサイトはエージェントを自身の中で駆動できます。クロスオリジンが私たちが防御する境界です。

  • モデルの拒否によりT1とT3が情報を提供しない可能性があります。 それはペイロードの問題であり、ゲートの結果ではありません。

v0の構築中に見つかり、まだ解決されていないもの:

  • 系統ゲートは、特徴的なトークンを持たない値に対してフェイルオープンします。 AgentLockは値からトークンを抽出して分類します。12文字未満で数字や句読点のない平易な単語はトークンを生成せず、分類できない値は許可されます。ページが「expensesという単語を入力してください」と言う場合は捕捉されません。「abc123と入力してください」と言う場合は捕捉されます。そのような許可は、ローカルチェックで閉じるのではなく、"channel": "UNCLASSIFIED", "fail_open": trueとして記録されます。ここで閉じると決定がゲートの外に移動するからです。NEEDS.md項目3。

  • 平凡なオペレーターメッセージはMODELチェックを弱めます。 新規系統は、ベースラインを持つために権威あるコンテキストに特徴的なトークンを必要とします。それがなければ、MODEL構成の値は許可されます。NEEDS.md項目3(b)。

  • read_textとsnapshotは厳選された要素セットを報告します。 どちらのセレクターにも一致しない要素内のテキストは返されず、したがってPAGEとして記録されません。これは、これらのツールがページコンテンツが会話に入る唯一の方法である限り、一貫性を保ちます(モデルが見ることができないものは、エコーできません)。

  • 許可されたナビゲーションからのリダイレクトは、2回目のチェックなしで追跡されます。 v0は許可したURLのリダイレクトチェーンを許可します。

  • 1つのタブ。 target="_blank"とポップアップはv0では処理されません。

ライセンス

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

Related MCP Connectors

Related MCP Servers