lite-locator-mcp
Fewer tokens for Playwright test agents, without replacing the LLM. Two ideas:
- Shortlist instead of the whole page. For each test step ("Add the Backpack to the cart"), small local models rank the page's elements and the LLM sees only the best few, with ready Playwright locators or MCP refs. If none fits, the LLM can still ask for the full accessibility tree.
- What changed instead of a new snapshot. After a click or fill, the LLM gets the outcome in a few lines (navigated, the elements that appeared / disappeared / changed, or no change), not a full snapshot that then sits in the history and is re-read on every LLM call.
Measured on a 13-step SauceDemo checkout, tokens the agent's LLM re-reads across all its calls: plain Playwright MCP 220,771 → 29,119 with both (−87%). On the hard held-out test set (38 sites never seen in training) the right element is first 72% of the time and in the shortlist 82–90% of the time, at 150–500 tokens instead of ~12,000 for the page.
Runs on CPU, no GPU and no PyTorch (int8 ONNX models are inside the package, nothing is downloaded at runtime).
Install: pick your setup
| You use | Install | Notes |
|---|---|---|
| Your own Python code with a Playwright page | pip install lite-locator-mcp |
|
LangChain / LangGraph with langchain-mcp-adapters |
pip install lite-locator-mcp |
Works with any mcp 1.x (tested with 1.3). Don't add [mcp]: langchain-mcp-adapters needs mcp 1.x. |
| An MCP client (Claude Code, Cursor, Copilot) | pip install "lite-locator-mcp[mcp]" |
The proxy needs mcp ≥ 2.3; put it in its own venv if something else needs mcp 1.x. |
Needs Python 3.10+, and Node 18+ when you use Playwright MCP. A browser once: python -m playwright install chromium.
On Windows, onnxruntime needs the Microsoft Visual C++ 2015–2022 runtime (usually already installed).
1. LangGraph agent with Playwright MCP (three lines change)
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from lite_locator.langchain import with_lite_locator, LOCATOR_HINT, snapshot_hook
tools = with_lite_locator(await load_mcp_tools(session)) # ★1 adds browser_find_element; actions return changes
agent = create_react_agent(llm, tools,
prompt=LOCATOR_HINT + your_prompt, # ★2 find first; full snapshot only when needed
pre_model_hook=snapshot_hook) # ★3 old full snapshots are not re-read
What the LLM then sees:
browser_find_element(step="Add the Sauce Labs Backpack to the cart")
- button "Add to cart" [ref=e54] (card: Sauce Labs Backpack; in: main)
- button "Add to cart" [ref=e102] (card: Sauce Labs Onesie; in: main)
(2 of 47 elements, ~44 tokens instead of ~1039 for the full page.)
browser_click(ref="e54") -> ### Result
Same page. Changes:
+ button "Cart, 1 items"
- button "Cart, empty"
+ button "Remove" (card: Sauce Labs Backpack)
- button "Add to cart" (card: Sauce Labs Backpack)
Your Playwright MCP server, its version and flags stay as they are. with_lite_locator(tools, diffs=False) keeps the
action tools unchanged. Full example: examples/quickstart_langgraph.py
(Azure OpenAI or a local Ollama model).
2. Your own Python code with a Playwright page
from lite_locator import Locator, act
loc = Locator() # loads the models once (~2 s)
hits = loc.find(page, "Enter standard_user in the username field")
hits.prompt # the shortlist as text for your LLM prompt (it picks one)
hits[0]["py"] # 'page.locator("[data-test=\"username\"]")' : for generated test code
hits.tokens, hits.full_tokens # ~55 vs ~307
print(act(page, hits[0], "fill", "standard_user")) # Same page. Changes: ~ textbox "Username" -> ...: standard_user
print(act(page, loc.find(page, "login button")[0], "click"))
# Navigated to a new page. Page: Swag Labs .../inventory.html Has: 20 buttons, 4 options, 3 links, 1 combobox
act(page, hit, action, value) actions: click, dblclick, hover, fill, type, select, check, uncheck,
press. Async pages: await loc.afind(...), await aact(...). to_locator(page, hit) gives a live Playwright
Locator. Full example: examples/quickstart_page.py. Exploring a site and generating
pytest tests with an LLM: examples/explore_generate.py.
3. MCP client (proxy in front of Playwright MCP)
{"mcpServers": {"playwright": {"command": "lite-locator-mcp", "args": ["--", "--headless", "--isolated"]}}}
All Playwright MCP tools stay available; browser_find_element(step) is added and actions return what changed.
Arguments after -- go to Playwright MCP. Options: --lam 0.005|0.01|0.02 (more / default / fewer candidates),
--full-snapshots (actions return Playwright's own full snapshot), --lean (start Playwright MCP with
--snapshot-mode none). PLAYWRIGHT_MCP overrides the child command (default npx -y @playwright/mcp@0.0.83).
How "what changed" behaves
| After the action | The LLM gets |
|---|---|
| A new page | Navigated to a new page. + title, URL, main headings, what can be done there |
| Part of the page changed | Same page. Changes: + + added, - removed, ~ changed elements, with the card (product) for repeated buttons |
| Nothing changed, even after waiting up to 3 s for slow responses | No change detected within 3 s. If this action submits, pays or deletes something, do not repeat it: check the page with browser_snapshot first. |
| A new tab or a browser dialog | A new tab opened: … / A browser dialog appeared: '…' (it was dismissed); act(..., dialog="accept") to accept |
Changes that happen by themselves (tickers, carousels, timers) are filtered out: the page is compared with itself just before the action.
Limits (when to use the full snapshot)
- Checks over a whole page or list (counts, "all items", totals): the shortlist shows only a few elements. The hint
tells the LLM to use
browser_snapshotfor these. - The right element is outside the shortlist for roughly 10–18% of hard steps; the LLM then sees nothing fits and takes the full snapshot (more tokens, still correct).
- English pages only (the models are English).
- Visual-only changes (colours, canvas charts) are invisible in accessibility snapshots, full or diff.
Development and release checks
Every change is recorded in CHANGELOG.md (what, why, how it was checked). ci/check.sh runs lint
(ruff), a security scan (bandit), a secrets scan, a dependency vulnerability audit (pip-audit), the package build and
metadata checks, the test suite on local test pages in three environments (proxy with mcp 2.x, LangChain with the
latest adapters, and next to mcp 1.3), and a ranking quality gate against a recorded baseline. The report goes to
ci/reports/<version>.md; ci/publish.sh uploads only an all-green report of the current commit.
The training code (data collection, model training and evaluation) is not part of this repository; the package ships the trained models.
Credits and licences
- Code: MIT (see LICENSE).
- Retriever fine-tuned from BAAI/bge-small-en-v1.5 (MIT).
- Reranker fine-tuned from cross-encoder/ms-marco-MiniLM-L6-v2 (Apache-2.0).
- Training data includes Mind2Web (Deng et al., 2023, CC BY 4.0) and pages from public websites.
Metadata
Release files for lite-locator-mcp 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| lite_locator_mcp-0.1.4.tar.gz | 38.2 MB | Without provenance | Details for lite_locator_mcp-0.1.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lite_locator_mcp-0.1.4-py3-none-any.whl | Python 3 | none | any | Details for lite_locator_mcp-0.1.4-py3-none-any.whl |
Total release size: 76.4 MB
Release files / lite_locator_mcp-0.1.4.tar.gz
| Download URL | lite_locator_mcp-0.1.4.tar.gz |
|---|---|
| Size | 38.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
acba07ba44c033783705930196ca9547ab25f391ef8093acb65280d957422a04
|
|
BLAKE2b-256 checksum How to use checksums |
5368b85c496f4e645e063f36dbff6f26f325daf36f89731ef283cb97eb7213c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / lite_locator_mcp-0.1.4-py3-none-any.whl
| Download URL | lite_locator_mcp-0.1.4-py3-none-any.whl |
|---|---|
| Size | 38.3 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d9c4b2007f37da5edcbf74b53932e6c3d0009425bcb02381ce7662527c3aa603
|
|
BLAKE2b-256 checksum How to use checksums |
f60fe5a5739ba13e993a8c615e7d1d54ace52f77d2d5a9890b0ccd8aad9b3f31
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for lite-locator-mcp 0.1.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.