DEV Community

I built a local-first tab manager and gave it an MCP server so Claude can organize my tabs

My browser is a war crime I'm a designer and developer, and a hopeless tab hoarder. On a normal day I've got 70+ tabs open across a couple of windows, every favicon crushed into an unreadable sliver, and my "system" for finding anything is to just… open another tab. Bookmarks? A write-only graveyard I never revisit. I bounced between tab managers for a while. Toby was the one that stuck - until it capped the free plan at 60 saved tabs and started asking for an account. For a tab hoarder, a 60-tab cap is like a notes app that locks after 60 words. That was the nudge. So I did the reasonable thing and spent way too long building my own. It's called Mos Tab. This is the build story - the parts I'm proud of, the part that nearly broke me (multiple windows, I'm looking at you), and the weirdly fun part at the end where I let Claude reorganize my tabs. What I actually wanted Three non-negotiables: - Local-first. No account required. Data lives in the browser, works offline. I was tired of "sign in to see your own bookmarks." - No limits on the free stuff. Saving a tab locally costs me nothing to run - so there's no reason to cap it. - A calm dashboard, not a list. I stare at my new tab all day; it should feel like a desk, not a void. The shape of it Mos Tab overrides chrome://newtab (MV3). The data model is a boring normalized tree - Space β†’ Collection β†’ Section β†’ Tab - records plus ID arrays for ordering. It's a single Zustand store with a persist middleware writing to chrome.storage.local . Your currently-open tabs show in a live side panel (chrome.tabs ); one click saves them into a collection, drag to organize, and a cmdk palette searches everything you've saved. Stack, for the curious: React 18 + TypeScript + Vite + @crxjs/vite-plugin , Tailwind, dnd-kit, zod, i18next (5 languages). Since the new tab is prime real estate, I threw in the stuff I open a tab for anyway - calendar, pomodoro, to-dos, sticky notes. All free. So far, so normal. Then I wanted sync. The hard part: sync (or, how multiple windows tried to kill me) Local-first is lovely until you have two machines. I wanted optional cloud sync (the paid tier - more on money later) without turning this into a database migration. The backend is deliberately dumb: a Cloudflare Worker + R2, one JSON blob per user (state.json ). No per-row DB. To save, you PUT the blob back with an ETag conditional request (If-Match ). On a 412 (someone wrote first), you pull, 3-way merge on the client, and push again. The 3-way merge (base + local + remote) handles the real cases: both sides added a tab β†’ keep both; something in base vanished on one side β†’ it was deleted, respect it; field conflicts β†’ local wins. Sticky-note text conflicts resolve to a symmetric content-addressed copy, so two machines converge instead of endlessly duplicating. The part that actually broke me wasn't cross-device - it was multiple windows on the same machine. Every window wanted to push, each invalidating the others' ETag β†’ 412 β†’ merge β†’ push β†’ 412 β†’ forever. A beautiful infinite loop. The fix: only the focused window pushes. Every window can pull (reads are idempotent), but writes are gated to the focused one, with a flush-on-blur so the last edit isn't lost when you switch away. Add adaptive polling backoff (15s active β†’ 2min idle) and an "away mode" that stops polling after 20 min of no activity, and it stops hammering the Worker. The fun part: teaching Claude to use my tabs (MCP) Here's the part I'm actually excited about. I gave Mos Tab a remote MCP server so you can connect Claude or ChatGPT to it. Once connected, the assistant can search and summarize everything you've saved (tabs, notes, to-dos) and - the fun bit - save new things it finds into a brand-new space, add notes, or make a public share link. All from the chat. Concrete example. I told Claude: "Find the top-rated budget strollers on Amazon, save them to my tabs, and make a share link." A few seconds later there was a new "Stroller shortlist" space on my dashboard, organized into categories, and a public link I sent straight to my wife - she opened it in her browser, nothing to install. Two design decisions I'm happy with: - It's add-only. The assistant can create spaces, notes, and shares - but it can't touch or reorganize your existing spaces. No "the AI nuked my setup" risk. That made me trust it way more. - The implementation is boring on purpose. Streamable HTTP JSON-RPC on the same plain- fetch Worker - no Durable Objects, no McpAgent. Auth is a long-lived scoped token you mint in settings (JWTs expire, which is bad for a persistent connection). Reads filter the same per-user blob in memory; writes are read-modify-write withIf-Match / 412 retry - so an AI-created space merges through the exact same 3-way sync as a manual edit. That last bit is the thing I like most: AI writes go through the same door as everything else. No special path, no separate store. Money, honestly Because someone always asks: the whole local app is free - unlimited tabs, spaces, all the widgets, import/export. Local storage costs me nothing to run, so there's no reason to cap it. The paid tier is only the stuff that actually hits a server: multi-device sync, public sharing, and the AI/MCP integration. I can't give the server stuff away free - kind of wish I could. That's the whole pricing philosophy, and it keeps me honest. What I learned - "Local-first" is a great default and a real constraint. Deciding exactly what syncs vs. what stays on-device (your data and notes sync; theme and zoom are per-device) took more thought than the merge algorithm. - The boring backend was the right call. One blob + conditional PUT scales further than I expected - the 5MB blob cap only bites around 15-30k saved tabs, and by then I'd happily move to per-entity storage (D1). - MCP is more fun than it has any right to be. Giving an assistant a small, safe, add-only surface into your own data feels like the actual point of these protocols. Try it Live on the Chrome Web Store (Chrome + Chromium - Edge, Brave, Arc, etc.): - Chrome Web Store: https://chromewebstore.google.com/detail/jmahojbjdibdhfniieiljgildbpcefoc - Site: https://mos-tab.com - A live shared space (the actual "stroller shortlist" Claude built): https://mos-tab.com/s/10yyW9pbA_R69a2quKLM392V I'm a solo dev, so I read everything - feedback on the sync merge model or the MCP design especially welcome. Top comments (0)

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.