Files
bookmarks-api/AGENTS.md
T

4.5 KiB

Agent handshake — Firefox Agent Bridge

Use this when a script or coding agent needs to create, update, move, or delete Firefox bookmarks while Firefox is open. Do not edit places.sqlite if this bridge is reachable.

Preconditions

  1. Native host installed: powershell -NoProfile -File tools/install-native-host.ps1
  2. Extension loaded in the target Firefox profile (about:debugging temporary add-on, or a signed install)
  3. Firefox running (the host process is started by the extension)

If http://127.0.0.1:17634/health fails, the host is not up — open Firefox and confirm the extension is loaded. If /health works but /v1/ready fails, the stdio pipe is down; reload the extension.

Auth

Token file (created by the installer):

%LOCALAPPDATA%\firefox-agent-bridge\token

Send it on every request except /health:

Authorization: Bearer <token>

Override with env FAB_TOKEN or FAB_URL (default http://127.0.0.1:17634).

Preferred caller

From this repo:

python tools/client.py METHOD [ARGS_JSON]

ARGS_JSON is a JSON array matching the WebExtension function arguments.

Examples:

python tools/client.py meta.methods
python tools/client.py bookmarks.search "[{\"title\":\"10.132.x.x\"}]"
python tools/client.py bookmarks.getChildren "[\"FOLDER_ID\"]"
python tools/client.py bookmarks.create "[{\"parentId\":\"FOLDER_ID\",\"title\":\"NPM\",\"url\":\"http://10.132.99.80:81/\"}]"
python tools/client.py bookmarks.remove "[\"BOOKMARK_ID\"]"
python tools/client.py bookmarks.removeTree "[\"FOLDER_ID\"]"

HTTP equivalent:

POST /v1/call
Content-Type: application/json

{"method": "bookmarks.search", "args": [{"title": "10.132.x.x"}]}

Allowlisted methods (v0.1)

Method Args Notes
meta.ping (none) Bridge alive
meta.methods (none) This table
bookmarks.getTree (none) Full tree
bookmarks.getSubTree id
bookmarks.getChildren id
bookmarks.get id or [id, …]
bookmarks.search {title? url? query?} or string
bookmarks.create {parentId?, title, url?} Omit url to create a folder
bookmarks.update id, {title? url?}
bookmarks.move id, {parentId? index?}
bookmarks.remove id Bookmark or empty folder
bookmarks.removeTree id Folder and descendants
bookmarks.getRecent numberOfItems

Do not invent other browser.* names. Adding an API means editing ALLOWED in extension/background.js and reloading the extension.

REST aliases (same auth):

HTTP Maps to
GET /health host process only (no token)
GET /v1/ready meta.ping
GET /v1/methods meta.methods
GET /v1/bookmarks/tree bookmarks.getTree
GET /v1/bookmarks/{id} bookmarks.get
POST /v1/bookmarks/search body is the query object
POST /v1/bookmarks/create body is CreateDetails
POST /v1/bookmarks/update {id, changes}
POST /v1/bookmarks/move {id, destination}
POST /v1/bookmarks/remove {id}
POST /v1/bookmarks/remove-tree {id}
POST /v1/call {method, args}

Typical folder replace

  1. bookmarks.search {title: "…"} — take the hit without a url (that is the folder). Fail if zero or many.
  2. bookmarks.getChildren on that id.
  3. bookmarks.remove each bookmark; bookmarks.removeTree each child folder.
  4. bookmarks.create each new item with parentId set.

examples/replace_named_folder.py does exactly that. It is an example, not a sync service.

You cannot modify Firefox's bookmark root (The bookmark root cannot be modified). Operate on a named subfolder (toolbar / menu / a folder the user already created).

Failure modes

Symptom Cause What to do
Connection refused on :17634 Firefox closed or extension not loaded Open Firefox; load/reload the add-on
401 Missing/wrong bearer Read the token file
502 method not allowed Typo or API not in ALLOWED Use meta.methods
502 timeout Extension died mid-call Reload the add-on
Temporary add-on gone after restart Unsigned on Firefox Release Load again, or sign via AMO unlisted
Native host not found Installer not run tools/install-native-host.ps1

What this project is not

  • Not a bidirectional sync implementation
  • Not a general Firefox remote-control surface (no tabs, history, cookies, native file access)
  • Not a reason to keep editing places.sqlite on a live profile