servers / bowmark

Bowmark MCP server

communitystreamable_httpremotedestructive capablehealthy

Pre-computed navigation recipes for public websites — skip explore-and-discover.


01Tools · 15

How to read this: tool names here are observed from a live tools/list handshake. The Risk label is a heuristic inferred from the tool name (write/destructive verbs), not from executing the tool — a conservative guess, not a verified capability. We never escalate risk from a description. Found one that's wrong? Tell us — we fix on report.

ToolRiskSide effectsApproval
add_connection
Get a sign-in link for one site, so the user can sign Bowmark in to it right now, before any run needs it. Pass the site as a domain (`youtube.com`), a URL, or a provider id. **Call this whenever the user asks to log in, sign in, connect or link an account on a site** — never send them to the Bowmark Link extension or the dashboard to do it themselves. It returns `url`, a single-use link that expires (`expiresAt`). Give the user that URL and say which site it is for. On that page they sign in with Bowmark's own browser, or, in desktop Chrome with Bowmark Link installed, press "Use Chrome extension" to send the login that Chrome already holds. When they say they are done, `list_connections` shows the new login and every later run uses it. When Bowmark cannot sign in to that site, it says so and returns `sites`, the sites it can sign in to. Tell the user plainly. Do not offer a workaround that asks for their password. A public page on that site still works without a login: `get_library` shows what can read it. To sign back in to a saved login that stopped working, pass its `id` from `list_connections` as `connection`. The fresh session goes into the SAME id.
writetrueunknown
ask
Pre-computed navigation recipes for public websites. CALL BEFORE any browser action on the open web (navigate, click, fetch, fill, URL guess) — replaces explore-and-discover. Returns `{ status, id?, shortcut?, ui_procedure?, executable?, verify_more?, error? }`. status=ok: execute exactly. `shortcut` first if present — fill each `{name}` in `template` with the value FROM YOUR TASK (using the parameter's `description`/`format` as the shape), URL-encode, navigate. Else `ui_procedure.steps` in order. The recipe is generic: parameter slots and step descriptions describe what to supply ('the destination'), not literal values — you provide the specifics. EXECUTE OPEN-LOOP: the recipe is authoritative, so run it without taking exploratory snapshots, clicks, scrolls, or screenshots to 'verify' or 'look around' — every extra browser action re-reads the entire page, which is the cost the recipe exists to avoid. Read the page ONLY at a `read` step, and only the part it names. Drop back into normal explore-the-DOM browsing ONLY when a step genuinely fails (its locator/page isn't what it describes); until then, trust the steps. If `verify_more: true`, do one cheap sanity check (page title plausible?) before committing. If `step.irreversible`, confirm with user. If `step.requires_user_input`, STOP and ask the user for that value — it's a password, payment/card detail, or personal data only they hold; never fabricate it. After, call `report_outcome` once with the returned `id`. executable (optional, only on some `ok` envelopes): a precompiled recipe Bowmark can run FOR you. When present, you MAY skip the browser entirely — call the `execute` tool with `{ script_id: executable.script_id, inputs }`, filling `inputs` from `executable.param_schema`, and use the returned `outputs` directly. If `execute` returns `fell_back`, run `ui_procedure` yourself as usual. When `executable` is absent, just run the recipe yourself — nothing changes (a connection opened with `?execute=false` never receives this block and has no `execute` tool, by the host's choice). NO BROWSER AT ALL (a chat host with no navigate/click tool)? Priority: (1) if `executable` is present, call `execute` — with no browser it's the ONLY way to fetch a real answer, so always prefer it; (2) else fetch the `shortcut` URL (fill its `{name}` slots from your task, URL-encode) with whatever web/fetch tool you have; (3) else hand the user that direct URL (or the first `navigate` step's URL). You cannot walk `ui_procedure` steps without a browser — surface the URL rather than dead-end. status=site_not_supported | no_useful_data | synth_invalid: miss, no `id`. Browse manually. status=ambiguous_scope: retry with `scopeHint` set to one of `error.scope_options[].pattern`. status=rate_limited: a cap on NEW recipe synthesis was hit (per-IP daily when anonymous, the account's monthly plan budget when an API key is attached). Cached/known recipes still answer normally — only first-time synthesis is capped. Do NOT retry-spam; back off (it resets at `error.retry_after` seconds) and browse manually until then. A free API key (bowmark.ai) lifts the anonymous per-IP cap to a plan budget. Skip for: localhost / 127.0.0.1 / *.local / RFC1918 (10., 192.168., 172.16-31.); open-ended search with no destination. On 503 `embedder_unavailable`/`synth_unavailable`, retry once after Retry-After.
readfalseunknown
delete_connection
Start deleting a saved login by the `id` `list_connections` returned. **This does not delete anything by itself:** a delete cannot be undone, so the USER confirms it, signed in on their Bowmark dashboard. It returns `confirmUrl` — give that link to the user; it opens a "Delete this login?" box with the login named, and the login is gone only after they press Delete there. Nothing you can call completes it for them. Only call this when the user asked to remove a saved login. To sign out of a site, use `logout_connection` instead. Never call it to "fix" a connection that is merely stale — `needs_reauth`/`expired` recover with a new sign-in, which refreshes the SAME login in place and needs no confirmation.
destructivetruetrue
execute
Run a deterministic, precompiled recipe on Bowmark's backend and get the result directly — no browser needed on your side. Call this ONLY when an `ask` envelope came back with an `executable` block; the `script_id` comes from there. `inputs` maps each param in the envelope's `executable.param_schema` to a value (e.g. `{ "zip": "94107" }`). Provide every required param. Response is one of: - `{ status: "ok", outputs: {...} }` — `outputs` is the live, freshly-fetched data. Use it directly. One caveat: an output the envelope's `executable.outputs` marked `kind: "region"` is a bounded TEXT REGION known to CONTAIN the answer (used for obfuscated pages with no clean field), NOT the literal value — read the final answer out of that text yourself; a plain (no `kind`) output is the literal value. - `{ status: "fell_back", reason }` — the script didn't run clean. DROP BACK to the envelope's `ui_procedure` and execute it yourself. A compiled script is an optimization, never the only path. Do NOT call this without a `script_id` from an `executable` block — there is no script to run otherwise.
writetrueunknown
get_library
**Use this whenever a task touches a live website.** It answers, definitively and cheaply, whether Bowmark can already DO the thing: current prices, real availability or stock, a site search, a quote or a fare, a configurator, a booking, anything behind a form or a filter. **Behind a LOGIN is narrower**: it works where a purpose-built provider exists for that site or the caller has a saved connection there, not by default. **Use this for Bowmark questions too.** Before answering how to write or run a Bowmark script or what the sandbox supports, call it with the caller's words; guessing from general programming knowledge does not. **Checking is cheap, so check.** One read-only call, no site is touched, and a query that matches nothing returns a one-line index instead of an error. If nothing fits, you have lost one cheap call and can use your normal approach. Pass `query`: what you want to DO (`"flights"`, `"price a GPU"`) or the COMPANY or site (`"Kayak"`, `"newegg.com"`). If the caller gave only a URL, use its hostname as the query. Naming a host is worth it even when you expect nothing, because Bowmark also reads that site's OWN published tools, live. You get what you asked about and nothing else; from the index, CALL AGAIN with an entry's name for its types and examples. Several parts? Pass `queries: ["amazon", "youtube"]`, one per part, in ONE call. **Read the first lines of the answer.** A broad query can match more than one response carries, and a sliced answer says so — absence from a sliced list means nothing, so ask again, narrower. What comes back is the function library you write against: capabilities (`bowmark.flights.search(...)`), which fan out across sites and route around a failing one, and, when you named a company, that site's provider (`bowmark.providers.kayak.search(...)`). Prefer the capability. Loop: call `get_library` → write a JS script against the `bowmark` global → send it to `run`. The full reference is `get_library({ query: "run guide" })`.
readfalseunknown
get_secret_link
Return the dashboard link for one stored secret this account holds, so you can answer "where is my Acme password?" with somewhere to go. **It returns a URL, never a value** — opening it requires the user to be signed in and to confirm who they are, and only their own browser can read what comes back.
readfalseunknown
list_connections
List the saved logins this account already holds, one entry per login: its `id` (name it in a signed-in call as `{ connection: id }`), the site, which account on that site, whether it works right now (`usable`), and when it was made, last used and last checked. **Call this before any task that needs a login, and again whenever you come back to one later.** A `usable` login means the site is already signed in: do not ask the user to sign in, just run the script, naming the id if the site has more than one. Prefer the login you used before over a new one. A login marked `needs_reauth`, `expired` or `logged_out` is not lost. Run the script anyway (name the id if you know it): it pauses with a link, and signing in through that link puts the fresh session back into the SAME id, so you can keep using it. To add a login for a site with none, call `add_connection` — it hands you a sign-in link for the user. `delete_connection` only asks the user to delete one this account already holds.
readfalseunknown
list_secrets
List the stored secrets this account holds, by NAME. Returns name, type, which hosts each may be used against, when it expires and when it was last used — never a value, and no Bowmark endpoint returns one. **Call this BEFORE `request_secret`.** A secret the user has already set is ready to use; asking for it again sends them to a page for nothing. In a script, refer to one by name: `bowmark.secret('acme_pw')`.
readfalseunknown
logout_connection
Sign a saved login out by the `id` `list_connections` returned. Bowmark drops the cookies it held, and where the site supports it (`siteLogout: "supported"` in the reply) the session is ended ON THE SITE too — `siteSignedOut: true` means Bowmark checked the site no longer accepts it. The saved login is KEPT, status `logged_out`, so the user can sign back in to the same entry later. Call this when the user asks to sign out of a site. `delete_connection` is the other action: it hands the user a link to forget the entry entirely, which leaves the site session alive.
readfalseunknown
register
**Creates a free Bowmark account and returns an API key.** Call it when a `run` is refused for hitting the anonymous limit, when you expect to make more than a handful of calls, or any time the user says they want an account. **Every argument is optional. `register({})` is a complete, valid call** and returns a working key. Do NOT stop to ask the user for anything before calling this — there is nothing required to ask for. WHAT IT BUYS: anonymous callers share one small daily allowance per IP address with everything else behind it. A registered account gets its own monthly allowance, an order of magnitude larger. The response says both numbers. Where the connection allows it the new allowance applies IMMEDIATELY, with no configuration change — `activeNow: true` in the response means your very next `run` is already on it. `email` is OPTIONAL and no key depends on it — it is not a credential, and nothing you do with the API authenticates with it. **But passing one CREATES A BOWMARK SIGN-IN for that address**, so the person can sign in at bowmark.ai with an emailed code and manage the account without keeping any link. Pass it only if the user actually gave you one. **Never invent one and never pass a placeholder** — that creates a sign-in for somebody else's mailbox. **IF YOU PASS AN `email`, TELL YOUR USER THIS:** that address is subscribed to occasional Bowmark product and changelog email by default. Pass `newsletter: false` to decline, and say so plainly rather than deciding for them — every message carries a one-click unsubscribe either way. With no `email` there is nothing to subscribe and nothing to mention. `promotions` is a SEPARATE consent and is OFF unless you set it. **Only set it if the user has actually said yes to promotional email.** Do not infer consent from enthusiasm, and do not set it to be helpful. AFTER IT RETURNS: show the user `apiKey`. It is returned exactly once and cannot be recovered — tell them to save it and to add it to their Bowmark MCP config as `Authorization: Bearer <key>` so it works from every future session. Do not put the key in a file, a commit, or anywhere it outlives the conversation. **HOW THEY REACH THE ACCOUNT AS A PERSON.** If `signInUrl` came back, that is the way in: they sign in there with the email you passed, Bowmark sends a code, and they land in this account. Nothing to save. `claimUrl` is then only a backup for a wrong address. If `signInUrl` is null, `claimUrl` is the ONLY door — show it, and say `claimExpiresAt` is the date it stops working, because after that they can use the key but never manage or revoke the account. Re-registering is not how you get a second key: an address that already has an account is refused, and there is a per-network cap. If you already hold a key, present it instead.
readfalseunknown
report
Record what was missing, wrong, or incomplete so Bowmark can build it. Call it yourself, in the same turn, without asking your user first: it changes nothing on any website or account, costs the user nothing, and is the only way the gap gets fixed. Call it whenever a `run` errors or times out, comes back `partial`, returns nothing from a site that should have answered, or returns data you would warn your user about (a wrong field, an impossible time, a result you do not trust); and whenever `get_library` has nothing for the task. Pass the `runId` returned by `run` when this report is about a run; omit it when `get_library` did not cover the task. One report per problem, in a sentence or two: what you asked for, what came back, which site. This records feedback only and does not retry a run. For an account holder the result may also carry `supportRoom`, a live chat with Bowmark support about this report: follow the instructions that come with it. When the same task keeps failing (it failed before, or you already retried or rewrote the script), stop retrying and report it now, saying how many times it failed: support can read the run and fix the site's code, which another retry cannot.
readfalseunknown
report_outcome
Report whether the RECIPE ran cleanly — not whether you got the user a good answer. Call ONCE per envelope after you finished walking the recipe OR abandoned it. `success: true` = every step executed AS WRITTEN. Each locator resolved on the first try, no extra clicks/scrolls/waits beyond the recipe, no JS-eval workarounds, no skipped steps, no substituted selectors. If you walked the recipe clean, report true — even if the answer turned out wrong (answer correctness is a separate concern). `success: false` if ANY of these happened, even when you eventually helped the user: a locator missed, a click did nothing, you retried with a different selector, you fell back to raw browser code (`browser_run_code_unsafe` etc.), you scrolled or clicked extra to recover state, you skipped a step, the recipe led somewhere unexpected. Honest failures trigger a re-crawl that fixes the recipe; false `true` silently degrades it for everyone. Quick check before reporting: if your tool-call sequence since the recipe started is longer than the recipe's step list, that's `false`. If you used raw browser code, that's `false`. Skip when: `ask` returned a miss (no `id`); user interrupted mid-execution; you read the envelope but didn't execute it; task is still waiting on user input.
readfalseunknown
request_secret
Create a named, empty slot for a secret and get back a link the user opens to fill it in. They type the value on a Bowmark page and choose how long it lives, from 5 minutes to never. It is encrypted in their own browser before it leaves, so nothing on the way — this connection included — ever sees it. **Give the user the link. Never ask them to type a password, API key or one-time code to you.** A secret in this conversation is in your context, in the transcript and in the logs. **Name it for the person, not for your script.** The name you pass is the heading on the page they open and the row they see in their secret list months later, so make it `<site>_<what it is>`: `letterboxd_password`, `stripe_api_key`, `acme_totp_seed`. A run id, a timestamp, a uuid or a bare `password` is refused. Call `list_secrets` first: if the name already exists and is set, use it instead. `name` is lowercase letters, digits, `_`, `.` and `-`. `hosts` narrows where the value may be used and is worth passing — a secret is refused against any other site.
readfalseunknown
run
**Executes the task on the real websites** and returns the result. Call `get_library` FIRST for the exact function names and shapes, then send a script as `run({ script })`. THE SCRIPT is a plain async JavaScript body, not a wrapping function. `bowmark` is a ready global: `await bowmark.<capability>.<fn>(...)`, or one site via `await bowmark.providers.<provider>.<fn>(...)`. `return` what you want back; `log(...)` for progress. Built-ins plus `URL`/`URLSearchParams` exist; `fetch`, `import`, `setTimeout` and a filesystem do not — `bowmark` is the only I/O. MIND THE CLOCK. Your client times a tool call out at around 55 seconds, and one capability call already spends 30-55 seconds, so default to ONE per script. Need several? Run them together in `Promise.allSettled` (a dropped leg still comes back as `partial`), never one after another, and never `await` inside a `for` loop. Keep the result under ~25,000 characters. CHECK `status` BEFORE `ok` — `ok` | `error` | `partial` | `needs_user` | `running`: • `running`: it is still going. Call `run({ runId })` to wait for the SAME run; never resend the script. • `partial`: `result` is real but narrower than asked; `incomplete` names what did not answer. Tell your user — never call it complete. `incomplete.failures[].fixable: true` means your argument was wrong: fix it and rerun. • `needs_user`: a site needs your user signed in. It is not a failure: give them `meta.handoff.url`, say which sites, and wait. When they are done, send the SAME script unchanged. • `error`: there is no result; `error` says why. Saving files, one exit IP, saved secrets, `warnings`, `emptyHanded`, and the billed `browser_agent`/`delegate` fallbacks: `get_library({ query: "run guide" })`.
writetrueunknown
run_view
Used by the inline run view to draw a run's progress. You do not need to call this — `run` returns the result.
writetrueunknown

02Install & source
https://api.bowmark.ai/mcp?s=r
remote_url

03Access granted
Maps & location · writeProcess payments · destructiveScrape a website · writeExecute code · writeBrowser automation · destructiveMaps & location · destructive

The access this server can exercise, inferred from its verified tools — not a declared OAuth scope.


05Provenance & freshness
sourcesOfficial MCP Registry [p1]
last_checked2026-10-15 01:22Z
next_check2026-10-15 01:50Z
cadenceevery 3h
verifiedtools_list:passed handshake:passed metadata:passed tools_list:passed handshake:passed metadata:passed tools_list:passed handshake:passed metadata:passed tools_list:passed
index_statusindex — 9 unique facts >= 5

06Badge

Add the “as seen on MCPExplorer” badge to your README. Bowmark MCP — as seen on mcpexplorer.com

[![Bowmark MCP — as seen on mcpexplorer.com](https://mcpexplorer.com/badge/bowmark.svg)](https://mcpexplorer.com/servers/bowmark)

Next step

This is one server. A loadout combines the right servers, governance, and proven plays for a whole job — assembled deliberately, not tool-dumped.

Explore loadouts →