bowmark-mcp — Bowmark MCP over stdio
Bowmark turns the interaction-gated web into a callable function library.
An agent reads the library (get_library), writes a short JavaScript script
against it, and sends it to run — Bowmark executes it on the live sites in a
sandbox and hands back the result. The canonical server is hosted, streamable
HTTP, no auth required: https://api.bowmark.ai/mcp.
This package is a thin stdio bridge to that hosted server, for MCP hosts
whose client only speaks stdio (for example browser-use's MCPClient). Tool
schemas, descriptions, and results pass through verbatim — the hosted server
stays the single source of truth; nothing is reimplemented here.
(Node-flavored environments: the same bridge exists on npm as bowmark-mcp —
npx @bowmark/mcp, packages/bowmark-mcp/node/ in the monorepo. The npm name is
scoped and this one is not, deliberately: PyPI cannot be scoped and has no rename
mechanism, so bowmark-mcp here is already what PEP 752 blesses.)
mcp-name: ai.bowmark/bowmark
Use
uvx bowmark-mcp # or: pipx run bowmark-mcp / python -m bowmark_mcp
Example — browser-use:
from browser_use.mcp.client import MCPClient
bowmark = MCPClient(server_name="bowmark", command="uvx", args=["bowmark-mcp"])
Example — any MCP host config:
{ "mcpServers": { "bowmark": { "command": "uvx", "args": ["bowmark-mcp"] } } }
If your host speaks streamable HTTP, skip this bridge and connect directly to
https://api.bowmark.ai/mcp.
Environment
| Var | Meaning |
|---|---|
BOWMARK_MCP_URL |
Target MCP URL. Default https://api.bowmark.ai/mcp/pypi (the /pypi destination attributes the install to this bridge). Point at http://localhost:3001/mcp for a local Bowmark API. |
BOWMARK_API_KEY |
Optional. Forwarded as X-Bowmark-Key; a free key (bowmark.ai dashboard) lifts the anonymous per-IP daily cap to your plan budget. |
Design notes (repo-internal)
- One remote session per request, retried once.
streamablehttp_clientis an anyio-scoped context manager; holding one session across handler tasks trips "exit cancel scope in a different task". The hosted MCP is stateless, so a fresh session per request is semantically identical and costs one initialize round-trip — noise next to arunthat drives real browsers. - The real host's name is relayed upstream. The api tailors its operating
guidance per host and detects the host from
clientInfoon the handshake — but through a bridge that names the BRIDGE, so every install here would read the platform-neutral text. So the host's ownclientInfo.name(from OUR stdio handshake) is forwarded asX-Bowmark-Clienton every proxied request, and the api ranks that above its own handshake. Best-effort: an unknown or missing name simply falls back to the neutral text, never an error. - Pass-through only. No tool logic lives here; the agent-surfaces sync rule
in the root CLAUDE.md is unaffected because descriptions/schemas ride through
from
apps/api/src/routes/mcp.ts. - The
/pypidestination is registered inapps/api/src/mcp-destinations.ts+mcp-registry/sources.json(PyPI stdio bridge channel). It carries the channel and deliberately pins NO platform, because the relayedX-Bowmark-Clientabove is a better answer than any pin. The old?s=pquery form still resolves for installs already in the wild. Adding a destination:.claude/rules/mcp-destinations.md. - The
mcp-name: ai.bowmark/bowmarkline above is load-bearing: the official MCP Registry validates PyPI package ownership by finding that marker in the package README. Don't remove it. - Versioning is manual (this is a thin bridge, not the api): bump
pyproject.tomlwhen it changes. Not wired into release-please.
Tests
cd packages/bowmark-mcp/python
uv run --with pytest --with-editable . pytest -q
Network-free (the remote hop is monkeypatched). A live smoke against prod:
uv run --with-editable . python - <<'EOF'
import asyncio, bowmark_mcp
async def main():
tools = await bowmark_mcp.list_tools_impl()
print([t.name for t in tools])
asyncio.run(main())
EOF
Publishing to PyPI
Live since 2026-07-03, published + cold-verified via uvx bowmark-mcp
against prod. To ship a new version: bump version in pyproject.toml
and merge. The actual publish does NOT run in this monorepo's CI — this
repo is private, and PyPI Trusted Publishing (OIDC) validates against
pyproject.toml's Repository URL, which (correctly) points at the public
mirror github.com/bowmark-ai/mcp, not Metroxe/bowmark. So merging here
only lands the version bump; release-bowmark-mcp.yml mirror-syncs it to
bowmark-ai/mcp, and THAT repo's own .github/workflows/publish.yml
(source-controlled at
packages/bowmark-mcp/.github/workflows/publish.yml
in this monorepo, mirrored in like any other file) does the real uv publish. Auth is PyPI Trusted Publishing (OIDC) — no token, configured
on pypi.org (project → Publishing → GitHub publisher for bowmark-ai/mcp /
publish.yml). Publishing from the public mirror also auto-generates
provenance attestations. Not release-please; the bump IS the release
action. If the registry surface changed, also bump
packages[0].version in mcp-registry/server.json (rides the next
api-release republish — the registry validates ownership via the mcp-name
marker above).
Manual fallback (creds: 1Password item "PyPI", vault
Christopher-Macbook-CLI, token in the Christopher_Bowmark_API_Key field;
service-account op read requires the vault in the path):
cd packages/bowmark-mcp/python && rm -rf dist && uv build && UV_PUBLISH_TOKEN="$(op read 'op://Christopher-Macbook-CLI/PyPI/Christopher_Bowmark_API_Key')" uv publish
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bowmark_mcp-2.1.0.tar.gz.
File metadata
- Download URL: bowmark_mcp-2.1.0.tar.gz
- Upload date:
- Size: 63.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce5fc1575d28b8ba2c4789796a2bca67fdadae27bb184d910fee68f05d7f87c5
|
|
| MD5 |
fd2fe418798da9444bae0a024dc8ad6f
|
|
| BLAKE2b-256 |
bcbec3baa6e511d48f4e7da0f78d29c51ba4ddc95c6e964d25ad22319e1a5d55
|
File details
Details for the file bowmark_mcp-2.1.0-py3-none-any.whl.
File metadata
- Download URL: bowmark_mcp-2.1.0-py3-none-any.whl
- Upload date:
- Size: 7.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.2 {"installer":{"name":"uv","version":"0.12.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
297fa0e5876d6e8efa000d85c14a1ff93df5ae464e53f8f528aed57c329b9cb0
|
|
| MD5 |
ec48021985404a364e6ac56f7790bbed
|
|
| BLAKE2b-256 |
836d7151ad82fed345f812502dae398ae3a1abeb2a7885353f10605b83422228
|