bookmark-bridge 0.1.0 → 0.1.2

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 CHANGED
@@ -1,22 +1,22 @@
1
1
  # Bookmark Bridge
2
2
 
3
- Lets an MCP client read and reorganize Chrome bookmarks, with every change
4
- recorded in git so it can be rolled back.
3
+ An MCP client can read and reorganize your Chrome bookmarks through this.
4
+ Every change lands in git first, so anything can be rolled back.
5
5
 
6
6
  ```
7
7
  MCP client --stdio--> bookmark-bridge --ws(127.0.0.1)-- Chrome extension --chrome.bookmarks--> Chrome
8
8
  ```
9
9
 
10
- The extension is the WebSocket client; it dials out because an extension cannot
11
- listen on a port. A second MCP process that cannot bind the port joins the first
12
- as a peer and relays through it, so several sessions share one browser.
10
+ The extension dials out rather than listening, because an extension cannot open
11
+ a port. A second MCP process that finds the port taken joins the first as a peer
12
+ and relays through it, so several sessions share one browser.
13
13
 
14
14
  ## Install
15
15
 
16
- Two pieces: this server, and a Chrome extension that dials into it.
16
+ Two pieces: this server, and the Chrome extension that dials into it.
17
17
 
18
- **1. Register the server with your MCP client.** It is fetched on first run; no
19
- Rust needed. macOS and Linux only — the server uses `flock` and POSIX file modes.
18
+ **1. Register the server with your MCP client.** It downloads itself on first
19
+ run, so you do not need Rust. macOS, Linux and Windows.
20
20
 
21
21
  <details open>
22
22
  <summary><b>Claude Code</b></summary>
@@ -72,19 +72,25 @@ Install the bookmark-bridge MCP server for me.
72
72
  extension from https://github.com/semanticist21/bookmark-bridge and then
73
73
  check again.
74
74
 
75
- It is macOS and Linux only. Do not build from source; the npx package fetches
76
- a prebuilt binary.
75
+ Do not build from source; the npx package fetches a prebuilt binary.
77
76
  ```
78
77
 
79
78
  </details>
80
79
 
81
- Your client starts and stops the server for you; there is no daemon to run.
80
+ Your client starts and stops it for you. Nothing to leave running. `git` must be
81
+ on PATH — the snapshots are a real git repository.
82
82
 
83
83
  **2. Install the extension.** From the Chrome Web Store, or load `extension/`
84
84
  at `chrome://extensions` with developer mode on.
85
85
 
86
- That is the whole setup. The extension shows a green light in its popup once the
87
- two find each other.
86
+ That is all of it. The extension's popup goes green once the two find each other.
87
+
88
+ The extension sleeps when you are not using it. Chrome ends an idle service
89
+ worker, and this one is not woken on a timer — tidying bookmarks is occasional
90
+ work, and polling a socket nobody is using is what Manifest V3 set out to stop.
91
+ It wakes on its own when the browser starts, when a setting changes, when you
92
+ touch a bookmark, and when you open its popup. If an agent reports that the
93
+ extension is not connected, clicking the toolbar icon reconnects it.
88
94
 
89
95
  <details>
90
96
  <summary>From source instead</summary>
@@ -108,26 +114,26 @@ extension's storage. `BOOKMARK_BRIDGE_PORT` changes the port.
108
114
 
109
115
  ## Safety model
110
116
 
111
- Three independent mechanisms, in the order they engage:
117
+ Three mechanisms, in the order they engage.
112
118
 
113
119
  **Snapshots.** Every mutating call commits the whole tree to a git repository at
114
- `~/.local/state/bookmark-bridge/history` before and after the change. If the
115
- before-snapshot fails, the change is refused — a mutation that cannot be undone
116
- is not worth making. Pass `force: true` to override.
120
+ `~/.local/state/bookmark-bridge/history`, before and after. A failed
121
+ before-snapshot refuses the change: a mutation nobody can undo is not worth
122
+ making. `force: true` overrides that.
117
123
 
118
124
  **Compare-and-swap.** Any operation may carry `expect: {title?, url?, parentId?}`.
119
- If the current value differs, that operation alone is skipped and reported with
120
- the actual value. This is what protects a plan from a user edit that landed
121
- between planning and applying.
125
+ If the live value differs, that one operation is skipped and reported with what
126
+ was actually there. This is what survives a user edit landing between planning
127
+ and applying.
122
128
 
123
- **Drift detection.** `history_drift` compares the live tree against `agent-head`,
124
- the commit marking the agent's last completed operation. The difference is what
125
- someone else changed. The extension additionally records bookmark events tagged
126
- `agent` or `user`; that ledger is evidence, and the diff is the authority.
129
+ **Drift detection.** `history_drift` diffs the live tree against `agent-head`,
130
+ the commit marking the agent's last finished operation. Whatever differs is
131
+ someone else's work. The extension also logs bookmark events tagged `agent` or
132
+ `user`; that ledger is evidence, the diff is the authority.
127
133
 
128
134
  ## Known limits
129
135
 
130
- These are real. Read them before trusting the output.
136
+ Read these before trusting the output.
131
137
 
132
138
  - **Reading list is invisible.** `chrome.readingList` is a separate API whose
133
139
  items never appear in the bookmark tree. Neither layer sees them.
@@ -150,12 +156,14 @@ These are real. Read them before trusting the output.
150
156
 
151
157
  ## Tests
152
158
 
153
- `cd mcp && cargo test` spawns the real binary, plays the extension with a
154
- WebSocket client holding an in-memory tree, and drives it over MCP. It covers the
155
- protocol handshake, compare-and-swap skipping, idempotent create, the batch size
156
- cap, refusing to mutate without a snapshot, restoring a deleted subtree under new
157
- ids, drift reporting only the user's changes, and two sessions sharing one
158
- extension through the peer relay.
159
+ `cd mcp && cargo test` runs the real binary against a fake extension: a
160
+ WebSocket client holding an in-memory tree. The destructive paths are covered
161
+ without a browser, including restoring a deleted subtree under new ids, refusing
162
+ to mutate when no snapshot can be taken, and two sessions sharing one extension
163
+ through the peer relay.
164
+
165
+ Two release-blocking defects came out of writing these, both invisible on a
166
+ machine that had already been set up once.
159
167
 
160
168
  ## Tools
161
169
 
@@ -169,11 +177,11 @@ extension through the peer relay.
169
177
 
170
178
  ## Privacy
171
179
 
172
- The extension holds `bookmarks`, `storage` and `alarms`. It connects only to
173
- `127.0.0.1` and rejects WebSocket handshakes carrying an `http(s)` origin, so a
174
- web page cannot reach it. Nothing is sent anywhere else. The token stops a page
175
- from connecting; it does not stop another process on the same machine, which can
176
- read the token file just as easily.
180
+ The extension holds `bookmarks` and `storage`. It connects only to
181
+ `127.0.0.1` and turns away handshakes carrying an `http(s)` origin, so a web page
182
+ cannot reach it. Nothing goes anywhere else. The token stops a page; it does not
183
+ stop another process on this machine, which can read the token file just as
184
+ easily.
177
185
 
178
186
  ## License
179
187
 
@@ -20,8 +20,11 @@ const TARGETS = {
20
20
  'darwin-x64': 'x86_64-apple-darwin',
21
21
  'linux-x64': 'x86_64-unknown-linux-gnu',
22
22
  'linux-arm64': 'aarch64-unknown-linux-gnu',
23
+ 'win32-x64': 'x86_64-pc-windows-msvc',
23
24
  };
24
25
 
26
+ const EXE = process.platform === 'win32' ? '.exe' : '';
27
+
25
28
  function die(message) {
26
29
  process.stderr.write(`bookmark-bridge: ${message}\n`);
27
30
  process.exit(1);
@@ -30,15 +33,13 @@ function die(message) {
30
33
  const key = `${process.platform}-${process.arch}`;
31
34
  const target = TARGETS[key];
32
35
  if (!target) {
33
- die(`no prebuilt server for ${key}. The server uses flock and POSIX file modes, `
34
- + `so Windows is not supported. Build from source: `
35
- + `https://github.com/${REPO}`);
36
+ die(`no prebuilt server for ${key}. Build from source: https://github.com/${REPO}`);
36
37
  }
37
38
 
38
39
  // Cached per version, so upgrading the package fetches a matching server rather
39
40
  // than silently reusing an old one.
40
41
  const cacheDir = join(here, '..', '.bin', pkg.version);
41
- const binary = join(cacheDir, 'bookmark-bridge');
42
+ const binary = join(cacheDir, `bookmark-bridge${EXE}`);
42
43
 
43
44
  async function exists(path) {
44
45
  try { await stat(path); return true; } catch { return false; }
@@ -86,7 +87,8 @@ async function install() {
86
87
  // directory.
87
88
  await rm(cacheDir, {recursive: true, force: true});
88
89
  await rename(join(staging, `bookmark-bridge-${target}`), cacheDir);
89
- await chmod(binary, 0o755);
90
+ // Windows has no execute bit; the archive already carries it elsewhere.
91
+ if (process.platform !== 'win32') await chmod(binary, 0o755);
90
92
  } finally {
91
93
  await rm(staging, {recursive: true, force: true});
92
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bookmark-bridge",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Chrome bookmarks as MCP tools, with every change recorded in git so it can be rolled back",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,10 +11,22 @@
11
11
  "bin": {
12
12
  "bookmark-bridge": "bin/bookmark-bridge.mjs"
13
13
  },
14
- "files": ["bin/", "README.md"],
14
+ "files": [
15
+ "bin/",
16
+ "README.md"
17
+ ],
15
18
  "engines": {
16
19
  "node": ">=18"
17
20
  },
18
- "os": ["darwin", "linux"],
19
- "keywords": ["mcp", "model-context-protocol", "bookmarks", "chrome"]
21
+ "os": [
22
+ "darwin",
23
+ "linux",
24
+ "win32"
25
+ ],
26
+ "keywords": [
27
+ "mcp",
28
+ "model-context-protocol",
29
+ "bookmarks",
30
+ "chrome"
31
+ ]
20
32
  }