No description
Find a file
2026-10-02 19:11:17 +07:00
docs/superpowers feat: add GemPhoneFarm controls to Jev bridge 2026-10-02 18:41:06 +07:00
jev_gemlogin_bridge docs: embed Google search guidance in phone tools 2026-10-02 19:11:17 +07:00
tests feat: add GemPhoneFarm controls to Jev bridge 2026-10-02 18:41:06 +07:00
.env.example feat: add GemPhoneFarm controls to Jev bridge 2026-10-02 18:41:06 +07:00
.gitignore chore: ignore local worktrees 2026-09-28 15:52:08 +07:00
pyproject.toml feat: add evidence collection and profile quarantine 2026-10-02 17:03:34 +07:00
README.md docs: embed Google search guidance in phone tools 2026-10-02 19:11:17 +07:00

Jev + GemLogin Browser Bridge

Current version: 0.2.0

Bounded browser-control MCP server for using Jev to make typed decisions from the current GemLogin browser state. It starts a GemLogin profile, attaches Playwright over CDP, exposes the current page as bounded text and accessible elements, and sends that state to Jev for structured decisions.

Jev does not execute arbitrary code or click by itself. The available browser actions are intentionally limited to starting a profile, inspecting, navigating to HTTP(S), clicking by accessible role/name, and filling an accessible textbox or combobox.

Requirements

Install or prepare these before starting the MCP server:

  1. Python 3.11 or newer and pip.
  2. GemLogin Desktop / Local API, running on the same machine. The bridge expects the default API at http://localhost:1010.
  3. At least one usable GemLogin profile ID, or an Android device managed by GemPhoneFarm.
  4. A valid Jev API key.
  5. An MCP-compatible host such as Codex, Claude Desktop, or another MCP client.
  6. Network access to the configured Jev endpoint.

For GemPhoneFarm phone control, GemPhoneFarm must be running at http://localhost:1256, the device must be authorized in ADB, and the device should be opened in the GemPhoneFarm scrcpy view once so the atx-agent port forward is available for screenshots and UI hierarchy.

The optional GEMLOGIN_SCRIPT_LIBRARY environment variable points to a local directory of .gemlogin/.ptcbrowser workflows. It defaults to /Users/boombayah/Documents/Gemlogin/Script and is used only to inspect block capabilities; workflow files are not uploaded or sent to Jev.

Playwright is installed as a Python dependency. No separate browser download is required because the bridge connects to the browser launched by GemLogin over CDP. If your environment uses Playwright independently, you may additionally run python -m playwright install chromium.

Install

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"

Dependencies used in the latest workflow

The latest GemLogin video-search workflow was verified on 2026-09-22 with the following packages installed in the local .venv:

Package Version used Role
httpx 0.28.1 Call the GemLogin Local API and Jev API
mcp 1.30.0 Expose the MCP server and tools
playwright 1.63.0 Attach to the GemLogin browser over CDP
pytest 9.1.1 Run the test suite
pytest-asyncio 1.4.0 Test async browser/API code
respx 0.23.1 Mock HTTP requests in tests

The installation also brings transitive dependencies required by those packages, including anyio, httpcore, pydantic, uvicorn, jsonschema, and related support libraries. They are resolved automatically by pip from pyproject.toml.

No new production dependency was added specifically for the video search: the workflow used the existing MCP tools jev_gemlogin_start, jev_gemlogin_navigate, and jev_gemlogin_inspect. No separate Playwright browser binary was installed because Playwright attaches to the browser already launched by GemLogin through CDP.

Configure the Jev API key

The key is read from the TYPESAFE_API_KEY environment variable. Put it in the MCP client’s environment, not in the source code and not in Git.

This bridge now uses TypeSafe’s official Jev System One API and Python SDK. The previous custom Jev endpoint is no longer used. Jev remains the model name, but requests are sent through typesafe-sdk to https://api.typesafe.ai/v1/systemone.

Option A: temporary shell configuration

export TYPESAFE_API_KEY="your-real-typesafe-api-key"
export GEMLOGIN_BASE="http://localhost:1010"
export GEMPHONEFARM_BASE="http://localhost:1256"
export TYPESAFE_ENDPOINT="https://api.typesafe.ai/v1/systemone"
export TYPESAFE_MODEL="jev-latest"

Option B: local .env file

cp .env.example .env

Edit .env and replace replace-with-your-jev-api-key. The .gitignore already excludes .env. This project does not load .env automatically; use your MCP host's environment-file support or export the variables before starting the server.

Never commit the real key. The jev_gemlogin_status tool only reports whether a key is configured; it never returns the key.

Register with Codex

From the project directory:

codex mcp add jev-gemlogin-browser \
  --env TYPESAFE_API_KEY="your-real-typesafe-api-key" \
  --env GEMLOGIN_BASE="http://localhost:1010" \
  --env TYPESAFE_ENDPOINT="https://api.typesafe.ai/v1/systemone" \
  -- \
  "$PWD/.venv/bin/python" -m jev_gemlogin_bridge.server

If the environment is already exported, the shorter form works:

codex mcp add jev-gemlogin-browser \
  -- "$PWD/.venv/bin/python" -m jev_gemlogin_bridge.server

For other MCP clients, configure the equivalent stdio server command:

Command: /absolute/path/to/jev-gemlogin-bridge/.venv/bin/python
Arguments: -m jev_gemlogin_bridge.server
Environment: TYPESAFE_API_KEY=your-real-typesafe-api-key

Typical MCP flow

  1. Start GemLogin Desktop and confirm its Local API is available.
  2. Call jev_gemlogin_status to verify configuration.
  3. Call jev_gemlogin_start(profile_id) for one profile, or jev_gemlogin_start_many([profile_a, profile_b]) for multiple profiles.
  4. Call jev_gemlogin_navigate(url, profile_id) or inspect the existing page.
  5. Call jev_gemlogin_inspect(profile_id) to read bounded page state and accessible elements.
  6. Call jev_decide(instruction, questions) with a bounded choice, score, or noul schema.
  7. Inspect Jev's decision before invoking the selected safe action.
  8. Call jev_gemlogin_click or jev_gemlogin_type with the target profile_id and inspect again after a page transition.

Before jev_gemlogin_task runs a profile, the bridge performs quarantine preflight checks. A failed configured proxy or a detected Facebook checkpoint blocks that profile and returns a proposed issue group. The profile is not run again until jev_gemlogin_quarantine_release is called. Use jev_gemlogin_quarantine_status to inspect blocked profiles, then explicitly approve a move into an existing GemLogin group with jev_gemlogin_quarantine_apply. The public GemLogin API does not expose group creation, so the target group must already exist in GemLogin.

For independent work across profiles, call jev_gemlogin_task with one objective and a profile-specific context map. Every task goes through the supervisor: it creates one short-lived async worker for each profile, including when only one profile is requested. Workers own their profile's queued browser operations, run profiles concurrently, and return a result per profile. The workers are stopped after the task; the underlying GemLogin profiles are not deleted. It only supports HTTP(S) navigation and accessible click/fill actions; posting, liking, commenting, sharing, messaging, and uploading are intentionally unavailable.

The supervisor sends Jev a bounded DOM/accessibility snapshot kept in memory; it does not create screenshot files or browser-state files. The snapshot is trimmed to visible elements and bounded text before it is sent to TypeSafe. Common search actions use deterministic browser controls: the worker finds a visible search field, submits the query, and opens an external result link. Jev chooses the intent from state-aware options instead of choosing arbitrary Google settings or stale selectors.

Task results include bounded evidence collected locally by the bridge: visited page URLs, page titles, bounded page text, and deduplicated links with anchor text. Evidence is returned to the caller for verification and is kept out of the Jev decision payload. This applies to any browser task that produces links; the caller can use the evidence to produce a detailed summary with traceable sources.

To run a stored workflow directly, call jev_workflow_run with its workflow ID, profile IDs, and parameter values. Use jev_workflow_status to poll the run and jev_workflow_stop to stop it. The run tool passes through only the workflow parameters plus explicit execution options; it does not read or expose cookies, passwords, or API keys.

The server does not bypass login, extract cookies, expose passwords, or return the Jev API key.

GemPhoneFarm phone flow

The same MCP server exposes jev_phone_* tools for individual Android devices. Use jev_phone_list_devices to get numeric device IDs, then use jev_phone_dump_ui to read the foreground accessibility tree. Each node includes text, content description, resource ID, bounds, a center coordinate, and a selector. This structured data lets a non-vision Jev model locate and tap controls. Custom Canvas, game, or remote-desktop surfaces may not expose Android accessibility nodes.

jev_phone_list_devices()
jev_phone_dump_ui(device_id=7)
jev_phone_tap(device_id=7, x=540, y=700)
jev_phone_screenshot(device_id=7)

jev_phone_task performs a bounded inspect/decide/action loop for one device. Direct actions use ADB; screenshots and UI hierarchy use GemPhoneFarm's capture endpoint or atx-agent over the ADB forward. The standalone gemphonefarm-mcp project remains available and is not modified by this bridge.

Google search procedure on a phone

When a phone task must search Google and open a result:

  1. Open a Google search URL with the encoded query, for example https://www.google.com/search?q=gemlogin.io.
  2. Dump the Android UI hierarchy after the results load.
  3. Do not click the editable search field just because its text matches the query.
  4. Select a visible clickable result card whose content-desc contains both the target domain and title, such as gemlogin.io https://gemlogin.io GemLogin.
  5. Tap the center of that node's bounds.
  6. Dump the hierarchy again and verify Chrome's URL bar contains the target domain and no longer contains google.com/search.
  7. If the result moved, refresh the hierarchy and retry once. Do not submit forms, sign in, or click account/registration actions unless explicitly asked.

This distinction is important because Google exposes both the search input and the result card as clickable Android nodes. The query text alone is not a safe selector for the result.

Multi-profile example

jev_gemlogin_start_many(["facebook-profile", "news-profile"])
jev_gemlogin_task(
  profile_ids=["facebook-profile", "news-profile"],
  objective="ค้นหาข่าวน้ำท่วมล่าสุด",
  profile_contexts={
    "facebook-profile": "ใช้ Facebook",
    "news-profile": "ใช้เว็บไซต์ข่าวที่เปิดอยู่"
  }
)

The task returns one status and summary per profile. Profiles run concurrently, but operations within the same profile are serialized by that profile's worker. A disconnected profile is reported as failed or disconnected; the bridge does not reconnect it automatically. Publishing and communication actions remain unavailable.

Evidence and quarantine usage

When a task needs verifiable results, use jev_gemlogin_task. Each profile result includes an evidence object containing visited pages and deduplicated links. Review these URLs as the source trail for the final summary. The evidence is collected by the bridge and is not passed to Jev for decision making.

If preflight detects a proxy failure or a Facebook account issue, the result is returned with status: "quarantined"; no browser actions are run for that profile. Inspect the blocked profiles:

jev_gemlogin_quarantine_status()

After creating the matching group in GemLogin, explicitly approve the move:

jev_gemlogin_quarantine_apply(
  profile_ids=["profile-id"],
  group_name="Issue-Proxy"
)

The bridge does not create or delete GemLogin groups through the public API. It only moves quarantined profiles into an existing group. To allow a profile to run again, explicitly release it:

jev_gemlogin_quarantine_release(profile_id="profile-id")

Supported issue group names are Issue-Proxy, Issue-Facebook-Checkpoint, Issue-Facebook-Login, and Issue-Unknown.

Tools exposed

Tool Purpose
jev_gemlogin_status Show non-secret configuration and readiness
jev_gemlogin_start Start a GemLogin profile and attach over CDP
jev_gemlogin_start_many Start multiple profiles with per-profile results
jev_gemlogin_sessions List non-secret metadata for started sessions
jev_gemlogin_task Distribute a bounded non-publishing objective across profiles
jev_gemlogin_quarantine_status List profiles blocked by proxy/account issues
jev_gemlogin_quarantine_apply Explicitly move blocked profiles to an existing group
jev_gemlogin_quarantine_release Explicitly unblock a profile
jev_gemlogin_workflow_blocks Inspect observed GemLogin blocks, fields, and usage guidance
jev_gemlogin_inspect Read URL, title, bounded visible text, and interactive elements
jev_gemlogin_navigate Navigate to an HTTP(S) URL
jev_gemlogin_click Click an accessible element by role and name
jev_gemlogin_type Fill an accessible textbox or combobox
jev_decide Ask Jev to choose among caller-defined typed options
jev_workflow_run Start a stored workflow on selected profiles
jev_workflow_status Check whether a workflow is running
jev_workflow_stop Stop a workflow on selected profiles

Test

Phone tools exposed by this bridge are jev_phone_status, jev_phone_list_devices, jev_phone_device_info, jev_phone_screenshot, jev_phone_dump_ui, jev_phone_tap, jev_phone_swipe, jev_phone_press_key, jev_phone_type_text, jev_phone_start_app, jev_phone_stop_app, and jev_phone_task.

python -m pytest tests -q

Security notes

  • Keep TYPESAFE_API_KEY in the MCP host environment or an untracked .env file.
  • Do not commit cookies, browser profiles, passwords, or session exports.
  • Use only profiles and websites you are authorized to access.
  • Navigation is restricted to HTTP(S), and page text/elements are bounded before they are sent to Jev.