@mcp-fe/mcp-worker 0.1.2 → 0.1.4
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/docs/architecture.md +100 -2
- package/docs/guide.md +122 -0
- package/docs/index.md +8 -0
- package/docs/multi-tab.md +637 -0
- package/docs/tab-manager.md +150 -0
- package/index.js +416 -5
- package/mcp-service-worker.js +458 -67
- package/mcp-shared-worker.js +457 -62
- package/package.json +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/lib/built-in-tools.d.ts +9 -1
- package/src/lib/built-in-tools.d.ts.map +1 -1
- package/src/lib/mcp-controller.d.ts +13 -2
- package/src/lib/mcp-controller.d.ts.map +1 -1
- package/src/lib/tab-manager.d.ts +108 -0
- package/src/lib/tab-manager.d.ts.map +1 -0
- package/src/lib/worker-client.d.ts +47 -0
- package/src/lib/worker-client.d.ts.map +1 -1
package/docs/architecture.md
CHANGED
|
@@ -160,12 +160,110 @@ public async handleRegisterTool(toolData: Record<string, unknown>) {
|
|
|
160
160
|
|
|
161
161
|
1. **MCP Client** calls tool via MCP protocol
|
|
162
162
|
2. **Worker** receives MCP tool call
|
|
163
|
-
3. **Worker** sends `CALL_TOOL` message to main thread
|
|
164
|
-
4. **Main Thread** executes handler function
|
|
163
|
+
3. **Worker** sends `CALL_TOOL` message to main thread (with targetTabId if specified)
|
|
164
|
+
4. **Main Thread** (specific tab) executes handler function
|
|
165
165
|
5. **Main Thread** sends `TOOL_CALL_RESULT` back
|
|
166
166
|
6. **Worker** resolves promise and returns to MCP
|
|
167
167
|
7. **MCP Client** receives result
|
|
168
168
|
|
|
169
|
+
## Multi-Tab Support
|
|
170
|
+
|
|
171
|
+
### Architecture
|
|
172
|
+
|
|
173
|
+
The library supports multiple browser tabs running the same application, with intelligent routing of tool calls:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
┌─────────────────────┐
|
|
177
|
+
│ MCP Client │
|
|
178
|
+
│ (Claude, etc.) │
|
|
179
|
+
└──────────┬──────────┘
|
|
180
|
+
│ MCP Protocol (with optional tabId param)
|
|
181
|
+
▼
|
|
182
|
+
┌─────────────────────┐
|
|
183
|
+
│ Shared Worker │
|
|
184
|
+
│ │
|
|
185
|
+
│ Tab Registry │
|
|
186
|
+
│ ├─ Tab 1 (active) │
|
|
187
|
+
│ ├─ Tab 2 │
|
|
188
|
+
│ └─ Tab 3 │
|
|
189
|
+
│ │
|
|
190
|
+
│ Tool Registry │
|
|
191
|
+
│ └─ get_page_info │
|
|
192
|
+
│ ├─ Tab 1 ✓ │ ← Hybrid routing logic
|
|
193
|
+
│ └─ Tab 2 ✓ │
|
|
194
|
+
└──────────┬──────────┘
|
|
195
|
+
│ Route to specific tab or active tab
|
|
196
|
+
▼
|
|
197
|
+
┌─────────────────────┐
|
|
198
|
+
│ Main Threads │
|
|
199
|
+
│ ├─ Tab 1 (active) │ ← Focused/visible tab
|
|
200
|
+
│ └─ Tab 2 │
|
|
201
|
+
└─────────────────────┘
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Tab Management
|
|
205
|
+
|
|
206
|
+
**Automatic Tab Registration:**
|
|
207
|
+
- Each tab gets unique ID via `crypto.randomUUID()`
|
|
208
|
+
- Stored in `sessionStorage` (persists across refreshes)
|
|
209
|
+
- Registered with worker on init
|
|
210
|
+
|
|
211
|
+
**Focus Tracking:**
|
|
212
|
+
- Active tab tracked via `window.focus` and `document.visibilitychange`
|
|
213
|
+
- Worker's `TabManager` is the single source of truth for `activeTabId`
|
|
214
|
+
- Use `list_browser_tabs` tool to query which tab is currently active
|
|
215
|
+
|
|
216
|
+
### Hybrid Routing Strategy
|
|
217
|
+
|
|
218
|
+
When a tool is called, the worker uses this intelligent logic:
|
|
219
|
+
|
|
220
|
+
1. **Explicit `tabId` parameter**: Route to specified tab (always respected)
|
|
221
|
+
2. **Only one tab has tool**: Route to that tab automatically (even if not active) ⭐
|
|
222
|
+
3. **Active tab has tool**: Route to focused tab (user is likely working there)
|
|
223
|
+
4. **Active tab lacks tool**: Route to first available tab with the tool
|
|
224
|
+
5. **No active tab**: Route to first available tab (fallback)
|
|
225
|
+
|
|
226
|
+
**Example:**
|
|
227
|
+
```typescript
|
|
228
|
+
// Scenario: Tab A (inactive) has toolX, Tab B (active) doesn't
|
|
229
|
+
|
|
230
|
+
// Agent calls without tabId
|
|
231
|
+
get_page_info()
|
|
232
|
+
// → Routes to Tab A automatically (only tab with the tool)
|
|
233
|
+
// User doesn't get an error, tool "just works"
|
|
234
|
+
|
|
235
|
+
// Agent discovers tabs first
|
|
236
|
+
list_browser_tabs()
|
|
237
|
+
// → [{ tabId: "abc-123", title: "Dashboard", isActive: false }, ...]
|
|
238
|
+
|
|
239
|
+
// Agent targets specific tab
|
|
240
|
+
get_page_info({ tabId: "abc-123" })
|
|
241
|
+
// → Routes to Dashboard tab precisely
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Tool Schema Enhancement
|
|
245
|
+
|
|
246
|
+
All tools automatically get optional `tabId` parameter:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"type": "object",
|
|
251
|
+
"properties": {
|
|
252
|
+
// ... your properties ...
|
|
253
|
+
"tabId": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"description": "Optional: Target specific tab by ID. If not provided, uses the currently focused tab."
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Built-in Meta Tools
|
|
262
|
+
|
|
263
|
+
**`list_browser_tabs`**: Discover available tabs
|
|
264
|
+
- Returns tab IDs, URLs, titles, active status
|
|
265
|
+
- Use before calling tools with specific tabIds
|
|
266
|
+
|
|
169
267
|
## Why This Works
|
|
170
268
|
|
|
171
269
|
- **Worker**: Runs 24/7, maintains MCP connection
|
package/docs/guide.md
CHANGED
|
@@ -409,6 +409,128 @@ function MyComponent() {
|
|
|
409
409
|
|
|
410
410
|
See [React Hooks Guide](../../react-event-tracker/REACT_MCP_TOOLS.md) for details.
|
|
411
411
|
|
|
412
|
+
## Multi-Tab Applications
|
|
413
|
+
|
|
414
|
+
The library automatically handles multiple browser tabs with intelligent routing.
|
|
415
|
+
|
|
416
|
+
### How It Works
|
|
417
|
+
|
|
418
|
+
Each tab gets a unique ID (via `crypto.randomUUID()`) stored in `sessionStorage`. Tools are registered per-tab, and the worker routes calls intelligently.
|
|
419
|
+
|
|
420
|
+
### Automatic Routing (Smart Strategy)
|
|
421
|
+
|
|
422
|
+
**Without `tabId` parameter**: Intelligently routes based on availability and focus
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
// In any tab
|
|
426
|
+
await workerClient.registerTool(
|
|
427
|
+
'get_page_url',
|
|
428
|
+
'Get current page URL',
|
|
429
|
+
{ type: 'object', properties: {} },
|
|
430
|
+
async () => ({
|
|
431
|
+
content: [{
|
|
432
|
+
type: 'text',
|
|
433
|
+
text: window.location.href
|
|
434
|
+
}]
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Scenario 1: Only one tab has the tool
|
|
439
|
+
// Tab A: Has get_page_url
|
|
440
|
+
// Tab B: Doesn't have get_page_url (active)
|
|
441
|
+
get_page_url()
|
|
442
|
+
// → Automatically routes to Tab A (even though Tab B is active!)
|
|
443
|
+
// No error, tool "just works"
|
|
444
|
+
|
|
445
|
+
// Scenario 2: Multiple tabs have the tool
|
|
446
|
+
// Tab A: Has get_page_url
|
|
447
|
+
// Tab B: Has get_page_url (active)
|
|
448
|
+
get_page_url()
|
|
449
|
+
// → Routes to Tab B (active tab preferred when multiple available)
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**With `tabId` parameter**: Routes to specific tab precisely
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// First, discover available tabs
|
|
456
|
+
list_browser_tabs()
|
|
457
|
+
// → [
|
|
458
|
+
// { tabId: "abc-123", url: "/dashboard", title: "Dashboard", isActive: true },
|
|
459
|
+
// { tabId: "def-456", url: "/settings", title: "Settings", isActive: false }
|
|
460
|
+
// ]
|
|
461
|
+
|
|
462
|
+
// Then target a specific tab
|
|
463
|
+
get_page_url({ tabId: "def-456" })
|
|
464
|
+
// → Routes to Settings tab specifically
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Built-in Meta Tool
|
|
468
|
+
|
|
469
|
+
**`list_browser_tabs`**: Always available, lists all active tabs
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
// Returns array of:
|
|
473
|
+
{
|
|
474
|
+
tabId: string; // Unique tab identifier
|
|
475
|
+
url: string; // Current URL
|
|
476
|
+
title: string; // Page title
|
|
477
|
+
isActive: boolean; // Is this the focused tab?
|
|
478
|
+
lastSeen: string; // ISO timestamp of last activity
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Reference Counting
|
|
483
|
+
|
|
484
|
+
When multiple tabs register the same tool:
|
|
485
|
+
- Tool is registered once with MCP
|
|
486
|
+
- Worker tracks all tabs that have the tool
|
|
487
|
+
- Unregistration happens when last tab unmounts
|
|
488
|
+
- Handler is always from the target tab's context
|
|
489
|
+
|
|
490
|
+
### Tab Persistence
|
|
491
|
+
|
|
492
|
+
Tab IDs persist across page refreshes (stored in `sessionStorage`):
|
|
493
|
+
- Same tab keeps same ID after F5
|
|
494
|
+
- Duplicate tab (Ctrl+K) gets new ID
|
|
495
|
+
- New tab/window gets new ID
|
|
496
|
+
|
|
497
|
+
### Use Cases
|
|
498
|
+
|
|
499
|
+
**1. Compare data across tabs:**
|
|
500
|
+
```typescript
|
|
501
|
+
// Get state from multiple tabs
|
|
502
|
+
const dashboardState = await get_react_state({ tabId: "tab-1" });
|
|
503
|
+
const settingsState = await get_react_state({ tabId: "tab-2" });
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**2. Debug specific tab:**
|
|
507
|
+
```typescript
|
|
508
|
+
// Agent: "Show me the state of the Settings tab"
|
|
509
|
+
list_browser_tabs()
|
|
510
|
+
// Find tabId for Settings
|
|
511
|
+
get_react_state({ tabId: "settings-tab-id" })
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**3. Focus-driven interaction:**
|
|
515
|
+
```typescript
|
|
516
|
+
// Agent: "What's on the current page?"
|
|
517
|
+
get_page_info() // No tabId needed - uses focused tab
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Debugging Multi-Tab
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
import { workerClient } from '@mcp-fe/mcp-worker';
|
|
524
|
+
|
|
525
|
+
// Get current tab info
|
|
526
|
+
console.log(workerClient.getTabInfo());
|
|
527
|
+
// → { tabId: "abc-123", url: "/dashboard", title: "Dashboard" }
|
|
528
|
+
|
|
529
|
+
// Clear and regenerate tab ID (for testing)
|
|
530
|
+
WorkerClient.clearTabId();
|
|
531
|
+
// Refresh page to get new ID
|
|
532
|
+
```
|
|
533
|
+
|
|
412
534
|
## Troubleshooting
|
|
413
535
|
|
|
414
536
|
### Tool doesn't register
|
package/docs/index.md
CHANGED
|
@@ -10,6 +10,7 @@ Complete guide to all documentation and examples in the MCP Worker library.
|
|
|
10
10
|
| **See advanced patterns** | [examples/dynamic-tools.ts](../examples/dynamic-tools.ts) |
|
|
11
11
|
| **Learn step-by-step** | [Guide](./guide.md) |
|
|
12
12
|
| **Complete API reference** | [API Reference](./api.md) |
|
|
13
|
+
| **Multi-tab applications** | [Multi-Tab Guide](./multi-tab.md) |
|
|
13
14
|
| **Worker implementation** | [Worker Details](./worker-details.md) |
|
|
14
15
|
| **Use with React** | [React Hooks Guide](../../react-event-tracker/REACT_MCP_TOOLS.md) |
|
|
15
16
|
| **Understand architecture** | [Architecture](./architecture.md) |
|
|
@@ -25,6 +26,7 @@ libs/mcp-worker/
|
|
|
25
26
|
│ ├── index.md ← This file
|
|
26
27
|
│ ├── guide.md ← Complete guide
|
|
27
28
|
│ ├── api.md ← API reference
|
|
29
|
+
│ ├── multi-tab.md ← Multi-tab support
|
|
28
30
|
│ ├── worker-details.md ← Worker implementation
|
|
29
31
|
│ ├── architecture.md ← Technical architecture
|
|
30
32
|
│ └── initialization.md ← Init handling
|
|
@@ -79,6 +81,12 @@ libs/react-event-tracker/
|
|
|
79
81
|
### "How does the proxy pattern work?"
|
|
80
82
|
→ [Architecture](./architecture.md)
|
|
81
83
|
|
|
84
|
+
### "How do I handle multiple tabs?"
|
|
85
|
+
→ [Multi-Tab Guide](./multi-tab.md)
|
|
86
|
+
|
|
87
|
+
### "What tools are built-in?"
|
|
88
|
+
→ [Multi-Tab Guide](./multi-tab.md#built-in-meta-tool) (list_browser_tabs)
|
|
89
|
+
|
|
82
90
|
### "How do I handle initialization?"
|
|
83
91
|
→ [Initialization](./initialization.md)
|
|
84
92
|
|