Bridge Server¶
Local Node.js + Express server providing REST API and WebSocket for real-time communication between the extension and MCP plugin.
Overview¶
The bridge server is optional. The extension works fully standalone. The bridge exists to:
- Provide REST API access for the MCP plugin
- Store lifecycle state in JSON files for persistence outside Chrome
- Enable WebSocket commands (e.g., wake tabs from Claude Code)
Module Structure¶
packages/bridge/
src/
server.ts # Entry point, PID/port management, signal handling
app.ts # Express app factory, middleware
storage.ts # JSON file storage with path traversal protection
websocket.ts # WebSocket manager for real-time broadcasting
types.ts # Shared type definitions
routes/
health.ts # GET /health
tabs.ts # GET/POST /tabs
lifecycle.ts # CRUD for lifecycle tabs
meeting.ts # Meeting mode start/end
stats.ts # Tab statistics
Startup¶
- Checks for existing PID file, cleans up stale instances
- Tries port 19876, increments up to 10 times if taken
- Writes PID and port files to
~/.tab-manager/ - Starts Express + WebSocket server on
127.0.0.1
Middleware Stack¶
- CORS: rejects requests with
Originheader that isn'tchrome-extension:// - Logging: logs
METHOD /pathfor every request - Body parsing:
express.json({ limit: "1mb" }) - Routes: health, tabs, lifecycle, meeting, stats
- Error handler: catches unhandled errors, returns 500
Storage¶
JSON files in ~/.tab-manager/:
| File | Contents |
|---|---|
config.json |
Extension configuration |
active-tabs.json |
Last synced active tabs from extension |
lifecycle-tabs.json |
Snoozed, queued, and watching tabs |
Storage operations are synchronous (single-user, single-process). All JSON.parse calls have try/catch with safe defaults on corruption.
Path traversal protection prevents ../../ in file names.
Input Validation¶
| Endpoint | Validation |
|---|---|
POST /lifecycle/snooze |
URL must be http/https, wakeAt must be positive number |
POST /lifecycle/queue |
URL must be http/https |
POST /lifecycle/watch |
URL must be http/https, cssSelector required and max 500 chars |
POST /tabs/sync |
Body must be array, each tab must have url (string), title (string), id (number) |
POST /meeting/end |
meetingId required |
WebSocket¶
The WebSocket server runs on the same port at /ws. It supports:
- Broadcast: server pushes
state-changeevents to all connected clients when lifecycle state changes - Commands: clients can send
{ type: "command", payload: { command: "wake", lifecycleIds: [...] } }to trigger tab wakes in the extension
Tests¶
37 tests covering routes, storage, WebSocket, and integration: