bowmark-mcp 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -86
- package/bin/bowmark-mcp.mjs +29 -0
- package/package.json +7 -19
- package/dist/bridge.js +0 -73
- package/dist/cli.js +0 -6
package/README.md
CHANGED
|
@@ -1,97 +1,41 @@
|
|
|
1
|
-
# bowmark-mcp —
|
|
1
|
+
# bowmark-mcp — compatibility forwarder
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
against it, and sends it to `run` — Bowmark executes it on the live sites in a
|
|
6
|
-
sandbox and hands back the result. The canonical server is hosted, streamable
|
|
7
|
-
HTTP, no auth required: `https://api.bowmark.ai/mcp`.
|
|
3
|
+
**The bridge moved to [`@bowmark/mcp`](https://www.npmjs.com/package/@bowmark/mcp).**
|
|
4
|
+
This package is the old name, kept working.
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
descriptions, and results pass through verbatim — the hosted server stays the
|
|
12
|
-
single source of truth; nothing is reimplemented here. (Python-flavored
|
|
13
|
-
environments: the same bridge exists on PyPI as `bowmark-mcp` — `uvx
|
|
14
|
-
bowmark-mcp`.)
|
|
15
|
-
|
|
16
|
-
`mcp-name: ai.bowmark/bowmark`
|
|
17
|
-
|
|
18
|
-
## Use
|
|
19
|
-
|
|
20
|
-
```sh
|
|
21
|
-
npx bowmark-mcp
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Any MCP host config:
|
|
25
|
-
|
|
26
|
-
```json
|
|
6
|
+
```jsonc
|
|
7
|
+
// what you have: still works, prints one notice on stderr
|
|
27
8
|
{ "mcpServers": { "bowmark": { "command": "npx", "args": ["bowmark-mcp"] } } }
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
If your host speaks streamable HTTP, skip this bridge and connect directly to
|
|
31
|
-
`https://api.bowmark.ai/mcp`.
|
|
32
9
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|---|---|
|
|
37
|
-
| `BOWMARK_MCP_URL` | Target MCP URL. Default `https://api.bowmark.ai/mcp?s=n` (`?s=n` attributes the install to the npm bridge). Point at `http://localhost:3001/mcp` for a local Bowmark API. |
|
|
38
|
-
| `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. |
|
|
10
|
+
// what to move to
|
|
11
|
+
{ "mcpServers": { "bowmark": { "command": "npx", "args": ["@bowmark/mcp"] } } }
|
|
12
|
+
```
|
|
39
13
|
|
|
40
|
-
|
|
14
|
+
Nothing else changes: same bridge, same tools, same endpoint, same version
|
|
15
|
+
number. `bin/bowmark-mcp.mjs` imports `@bowmark/mcp/cli` in-process and that is
|
|
16
|
+
the whole package. In-process rather than spawned, because a child would need
|
|
17
|
+
stdio, signal and exit-code plumbing to be transparent, and every one of those
|
|
18
|
+
is a way for a stdio transport to break.
|
|
41
19
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Mirrors `packages/bowmark-mcp/python` (the PyPI bridge) exactly.
|
|
47
|
-
- **Pass-through only.** No tool logic lives here; the agent-surfaces sync
|
|
48
|
-
rule in the root CLAUDE.md is unaffected because descriptions/schemas ride
|
|
49
|
-
through from `apps/api/src/routes/mcp.ts`. `callTool` results are returned
|
|
50
|
-
verbatim (content, structuredContent, isError).
|
|
51
|
-
- **`?s=n` source code** is registered in `apps/api/src/mcp-sources.ts` +
|
|
52
|
-
`mcp-registry/sources.json` (npm stdio bridge channel; the PyPI bridge is
|
|
53
|
-
`?s=p`).
|
|
54
|
-
- **npm ownership verification needs BOTH markers — the registry checks the
|
|
55
|
-
published package.json, not just the README.** The `mcp-name:
|
|
56
|
-
ai.bowmark/bowmark` line above and the `"mcpName": "ai.bowmark/bowmark"`
|
|
57
|
-
field in `package.json` are both load-bearing; don't remove either. Learned
|
|
58
|
-
the hard way 2026-07-04: `release-mcp.yml`'s registry republish failed for
|
|
59
|
-
five straight api releases (`server returned status 400: ... "NPM package
|
|
60
|
-
'bowmark-mcp' is missing required 'mcpName' field"`) because only the
|
|
61
|
-
README marker was present — the package.json field is what `mcp-publisher
|
|
62
|
-
publish` actually validates against the live npm package.
|
|
63
|
-
- **Versioning is manual** (thin bridge, not the api): bump `package.json`
|
|
64
|
-
when it changes. Not wired into release-please; `private` is deliberately
|
|
65
|
-
absent so npm publish works — release-please doesn't manage this package.
|
|
20
|
+
**PyPI did not move.** `uvx bowmark-mcp` is unchanged, and it is now the
|
|
21
|
+
*blessed* name there: PEP 752 standardises hyphen-prefixed namespaces rather
|
|
22
|
+
than `@scope`, and PyPI has no rename, alias or redirect mechanism at all. Only
|
|
23
|
+
npm is scoped.
|
|
66
24
|
|
|
67
|
-
##
|
|
25
|
+
## Why a forwarder rather than `npm deprecate`
|
|
68
26
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
27
|
+
`npm deprecate` provides a warning to anyone who installs the package. It warns,
|
|
28
|
+
it never blocks, and npm has no "moved" mechanism at all. The name has real
|
|
29
|
+
traffic (3,032 npm installs in the month to 2026-07-28) and every published
|
|
30
|
+
config, registry listing and benchmark harness names it, so the old name has to
|
|
31
|
+
keep *working*, not merely warn. `npm deprecate bowmark-mcp` is run as well: it
|
|
32
|
+
is the notice, this package is the compatibility.
|
|
72
33
|
|
|
73
|
-
|
|
34
|
+
It is never deleted and never unpublished.
|
|
74
35
|
|
|
75
|
-
|
|
76
|
-
`npx -y bowmark-mcp` against prod). To ship a version: **bump `version` in
|
|
77
|
-
`package.json` (and the two `version` literals in `src/bridge.ts`) and
|
|
78
|
-
merge.** The actual publish does NOT run in this monorepo's CI — this repo
|
|
79
|
-
is private, and npm Trusted Publishing (OIDC) validates against
|
|
80
|
-
`package.json`'s `repository.url`, which (correctly) points at the public
|
|
81
|
-
mirror `github.com/bowmark-ai/mcp`, not `Metroxe/bowmark`. So merging here
|
|
82
|
-
only lands the version bump; `release-bowmark-mcp.yml` mirror-syncs it to
|
|
83
|
-
`bowmark-ai/mcp`, and THAT repo's own `.github/workflows/publish.yml`
|
|
84
|
-
(source-controlled at
|
|
85
|
-
[`packages/bowmark-mcp/.github/workflows/publish.yml`](../.github/workflows/publish.yml)
|
|
86
|
-
in this monorepo, mirrored in like any other file) does the real `npm
|
|
87
|
-
publish`. Auth is npm **Trusted Publishing** (OIDC) — no token, configured
|
|
88
|
-
on npmjs.com (package Settings → Trusted Publisher → GitHub Actions, repo
|
|
89
|
-
`bowmark-ai/mcp`, workflow `publish.yml`). Publishing from the public
|
|
90
|
-
mirror also auto-generates provenance attestations, which npm only produces
|
|
91
|
-
for public-repo publishes — impossible from this private monorepo
|
|
92
|
-
regardless of `repository.url`. Not release-please; the bump IS the release
|
|
93
|
-
action.
|
|
36
|
+
## Versioning
|
|
94
37
|
|
|
95
|
-
`
|
|
96
|
-
|
|
97
|
-
|
|
38
|
+
The forwarder's `version` and its `@bowmark/mcp` dependency are pinned to the
|
|
39
|
+
canonical package's version, exactly. A forwarder on an older version would
|
|
40
|
+
silently serve an older bridge to everyone who never migrated, which is the one
|
|
41
|
+
thing it exists to prevent. Both pins are checked in CI upstream.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The `bowmark-mcp` npm name, kept alive forever.
|
|
3
|
+
//
|
|
4
|
+
// The bridge itself moved to `@bowmark/mcp` on 2026-08-05. This package is the
|
|
5
|
+
// old name continuing to work: npm cannot be unpublished past 72h, `npm
|
|
6
|
+
// deprecate` only WARNS and never blocks an install, and there is no "moved"
|
|
7
|
+
// mechanism on npm at all — so a forwarder is the only thing that actually
|
|
8
|
+
// preserves compatibility for the configs already in the wild.
|
|
9
|
+
// `.claude/rules/agent-surfaces.md` § Published packages cannot be unpublished.
|
|
10
|
+
//
|
|
11
|
+
// Two properties this file exists to hold:
|
|
12
|
+
//
|
|
13
|
+
// - **stdout belongs to the protocol.** The notice goes to stderr. A single
|
|
14
|
+
// stray byte on stdout desynchronises the JSON-RPC framing and the host
|
|
15
|
+
// drops the server with no useful error.
|
|
16
|
+
// - **The real CLI is imported IN-PROCESS, never spawned.** A child process
|
|
17
|
+
// would need stdio, signal and exit-code plumbing to be transparent, and
|
|
18
|
+
// every one of those is a way for a stdio transport to break. An import
|
|
19
|
+
// shares this process's own stdio by construction.
|
|
20
|
+
//
|
|
21
|
+
// `@bowmark/mcp` declares `./cli` in its `exports` map precisely so this line
|
|
22
|
+
// is a supported entry point rather than a deep import into someone's dist/.
|
|
23
|
+
// No em dash in this string: it is outbound copy, read by everyone who installs
|
|
24
|
+
// the old name. Root CLAUDE.md, "no em dashes in outbound-to-others content".
|
|
25
|
+
console.error(
|
|
26
|
+
"bowmark-mcp: this package now forwards to @bowmark/mcp. Point your config at `npx @bowmark/mcp` (same bridge, same behaviour).",
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
await import("@bowmark/mcp/cli");
|
package/package.json
CHANGED
|
@@ -1,44 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bowmark-mcp",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"
|
|
5
|
-
"description": "Bowmark MCP over stdio \u2014 a thin bridge to the hosted Bowmark MCP at https://api.bowmark.ai/mcp. The web as callable functions: read a typed library, write a script, Bowmark runs it on the live sites.",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Compatibility forwarder. The Bowmark stdio MCP bridge now ships as @bowmark/mcp; this package re-execs it so every config already in the wild keeps working.",
|
|
6
5
|
"license": "MIT",
|
|
7
6
|
"author": "Bowmark AI <christopher@bowmark.ai>",
|
|
8
7
|
"homepage": "https://bowmark.ai",
|
|
9
8
|
"repository": {
|
|
10
9
|
"type": "git",
|
|
11
10
|
"url": "git+https://github.com/bowmark-ai/mcp.git",
|
|
12
|
-
"directory": "node"
|
|
11
|
+
"directory": "node-compat"
|
|
13
12
|
},
|
|
14
13
|
"keywords": [
|
|
15
14
|
"mcp",
|
|
16
15
|
"model-context-protocol",
|
|
17
|
-
"bowmark"
|
|
18
|
-
"browser-automation",
|
|
19
|
-
"ai-agents"
|
|
16
|
+
"bowmark"
|
|
20
17
|
],
|
|
21
18
|
"type": "module",
|
|
22
19
|
"bin": {
|
|
23
|
-
"bowmark-mcp": "
|
|
20
|
+
"bowmark-mcp": "bin/bowmark-mcp.mjs"
|
|
24
21
|
},
|
|
25
22
|
"files": [
|
|
26
|
-
"
|
|
23
|
+
"bin",
|
|
27
24
|
"README.md"
|
|
28
25
|
],
|
|
29
26
|
"engines": {
|
|
30
27
|
"node": ">=20"
|
|
31
28
|
},
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "tsc -p tsconfig.json",
|
|
34
|
-
"prepublishOnly": "rm -rf dist && tsc -p tsconfig.json",
|
|
35
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
36
|
-
},
|
|
37
29
|
"dependencies": {
|
|
38
|
-
"@
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@types/node": "^22.10.7",
|
|
42
|
-
"typescript": "~5.9.2"
|
|
30
|
+
"@bowmark/mcp": "2.1.0"
|
|
43
31
|
}
|
|
44
32
|
}
|
package/dist/bridge.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// Bowmark MCP stdio bridge (Node) — the npm twin of packages/bowmark-mcp/python (PyPI).
|
|
2
|
-
//
|
|
3
|
-
// A thin stdio MCP server that proxies every request to the hosted Bowmark
|
|
4
|
-
// MCP (streamable HTTP, https://api.bowmark.ai/mcp). Exists for stdio-only
|
|
5
|
-
// MCP hosts in Node-flavored environments: they run `npx bowmark-mcp` and get
|
|
6
|
-
// the exact hosted tools — schemas, descriptions, and envelopes pass through
|
|
7
|
-
// verbatim, so the hosted server stays the single source of truth and this
|
|
8
|
-
// package never needs a re-publish when the api's tools change.
|
|
9
|
-
//
|
|
10
|
-
// Env:
|
|
11
|
-
// BOWMARK_MCP_URL Target MCP URL. Default https://api.bowmark.ai/mcp?s=n
|
|
12
|
-
// (`?s=n` tags the install source as the npm bridge;
|
|
13
|
-
// point at http://localhost:3001/mcp for a local API).
|
|
14
|
-
// BOWMARK_API_KEY Optional key, forwarded as X-Bowmark-Key. Omit for the
|
|
15
|
-
// anonymous tier.
|
|
16
|
-
//
|
|
17
|
-
// Design note — one remote session PER REQUEST, retried once. The hosted MCP
|
|
18
|
-
// is stateless and auth-free, so a fresh connection is semantically identical
|
|
19
|
-
// to a held one, costs one initialize round-trip (noise next to an `ask`
|
|
20
|
-
// synthesis), and sidesteps every long-lived-connection failure mode (idle
|
|
21
|
-
// HTTP timeouts, half-open sockets) without reconnect bookkeeping. Mirrors
|
|
22
|
-
// the Python bridge's contract exactly.
|
|
23
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
24
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
25
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
26
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
27
|
-
export const DEFAULT_URL = "https://api.bowmark.ai/mcp?s=n";
|
|
28
|
-
export function targetUrl(env = process.env) {
|
|
29
|
-
return env.BOWMARK_MCP_URL?.trim() || DEFAULT_URL;
|
|
30
|
-
}
|
|
31
|
-
/** Optional API key -> X-Bowmark-Key header (the hosted MCP also accepts
|
|
32
|
-
* Authorization: Bearer; one header is enough). */
|
|
33
|
-
export function authHeaders(env = process.env) {
|
|
34
|
-
const key = env.BOWMARK_API_KEY?.trim();
|
|
35
|
-
return key ? { "X-Bowmark-Key": key } : undefined;
|
|
36
|
-
}
|
|
37
|
-
async function withRemote(fn) {
|
|
38
|
-
const client = new Client({ name: "bowmark-mcp-bridge", version: "2.0.0" });
|
|
39
|
-
const headers = authHeaders();
|
|
40
|
-
const transport = new StreamableHTTPClientTransport(new URL(targetUrl()), {
|
|
41
|
-
requestInit: headers ? { headers } : undefined,
|
|
42
|
-
});
|
|
43
|
-
await client.connect(transport);
|
|
44
|
-
try {
|
|
45
|
-
return await fn(client);
|
|
46
|
-
}
|
|
47
|
-
finally {
|
|
48
|
-
await client.close().catch(() => { });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/** One retry on any failure: the remote is stateless, so a fresh session is
|
|
52
|
-
* equivalent, and a transient network blip shouldn't fail the host's call. */
|
|
53
|
-
export async function callRemote(fn, connect = withRemote) {
|
|
54
|
-
try {
|
|
55
|
-
return await connect(fn);
|
|
56
|
-
}
|
|
57
|
-
catch (first) {
|
|
58
|
-
console.error(`bowmark-mcp: retrying after: ${String(first)}`);
|
|
59
|
-
return await connect(fn);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
export function buildServer(remote = callRemote) {
|
|
63
|
-
const server = new Server({ name: "bowmark", version: "2.0.0" }, { capabilities: { tools: {} } });
|
|
64
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
65
|
-
return await remote((c) => c.listTools());
|
|
66
|
-
});
|
|
67
|
-
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
68
|
-
// Returned verbatim — content, structuredContent, and isError all ride
|
|
69
|
-
// through, so upstream errors reach the host exactly as the api sent them.
|
|
70
|
-
return await remote((c) => c.callTool({ name: req.params.name, arguments: req.params.arguments ?? {} }));
|
|
71
|
-
});
|
|
72
|
-
return server;
|
|
73
|
-
}
|
package/dist/cli.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// stdout belongs to the protocol; anything human goes to stderr.
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { buildServer } from "./bridge.js";
|
|
5
|
-
const server = buildServer();
|
|
6
|
-
await server.connect(new StdioServerTransport());
|