@mcp-fe/mcp-worker 0.2.0 → 0.2.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/LICENSE +27 -29
- package/docs/multi-tab.md +637 -637
- package/docs/native-webmcp.md +232 -232
- package/docs/project-structure.md +172 -172
- package/docs/tab-manager.md +150 -150
- package/index.js +68 -59
- package/mcp-service-worker.js +43 -2
- package/mcp-shared-worker.js +43 -2
- package/package.json +1 -1
- package/src/client/worker-client.d.ts.map +1 -1
- package/src/worker/mcp-controller.d.ts +5 -0
- package/src/worker/mcp-controller.d.ts.map +1 -1
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
# Project Structure
|
|
2
|
-
|
|
3
|
-
This document explains the organization of the `@mcp-fe/mcp-worker` codebase.
|
|
4
|
-
|
|
5
|
-
## Directory Layout
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
libs/mcp-worker/src/
|
|
9
|
-
├── index.ts # Main entry point (re-exports from client/)
|
|
10
|
-
├── mcp-shared-worker.ts # SharedWorker entry point
|
|
11
|
-
├── mcp-service-worker.ts # ServiceWorker entry point
|
|
12
|
-
├── client/ # Client-side code (application runtime)
|
|
13
|
-
│ ├── index.ts # Client API exports
|
|
14
|
-
│ └── worker-client.ts # Main WorkerClient class
|
|
15
|
-
├── worker/ # Worker-side code (background processing)
|
|
16
|
-
│ ├── index.ts # Worker internal exports
|
|
17
|
-
│ ├── mcp-controller.ts # MCP server controller & lifecycle
|
|
18
|
-
│ ├── mcp-server.ts # MCP server setup & handlers
|
|
19
|
-
│ ├── websocket-transport.ts # WebSocket transport for MCP
|
|
20
|
-
│ ├── tool-registry.ts # Dynamic tool registration
|
|
21
|
-
│ ├── tab-manager.ts # Multi-tab coordination
|
|
22
|
-
│ ├── built-in-tools.ts # Default MCP tools
|
|
23
|
-
│ └── tab-manager.spec.ts # Tests for TabManager
|
|
24
|
-
└── shared/ # Shared code (both contexts)
|
|
25
|
-
├── types.ts # TypeScript type definitions
|
|
26
|
-
├── logger.ts # Logging utilities
|
|
27
|
-
└── database.ts # IndexedDB operations
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Module Responsibilities
|
|
31
|
-
|
|
32
|
-
### Client (`client/`)
|
|
33
|
-
|
|
34
|
-
**Purpose:** Code that runs in the main browser thread (your application).
|
|
35
|
-
|
|
36
|
-
**Key Files:**
|
|
37
|
-
- `worker-client.ts` - Main API for communicating with workers
|
|
38
|
-
- Handles worker initialization (SharedWorker vs ServiceWorker)
|
|
39
|
-
- Manages tool registration and lifecycle
|
|
40
|
-
- Handles multi-tab coordination
|
|
41
|
-
- Provides request/response messaging
|
|
42
|
-
|
|
43
|
-
**Used by:** Your application code
|
|
44
|
-
|
|
45
|
-
**Example:**
|
|
46
|
-
```typescript
|
|
47
|
-
import { workerClient } from '@mcp-fe/mcp-worker';
|
|
48
|
-
await workerClient.init();
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Worker (`worker/`)
|
|
52
|
-
|
|
53
|
-
**Purpose:** Code that runs inside Web Workers (background processing).
|
|
54
|
-
|
|
55
|
-
**Key Files:**
|
|
56
|
-
- `mcp-controller.ts` - Main controller for MCP server lifecycle
|
|
57
|
-
- WebSocket connection management
|
|
58
|
-
- Tool call routing and execution
|
|
59
|
-
- Tab management coordination
|
|
60
|
-
- Event storage and querying
|
|
61
|
-
|
|
62
|
-
- `mcp-server.ts` - MCP server setup using @modelcontextprotocol/sdk
|
|
63
|
-
- Request handlers (ListTools, CallTool)
|
|
64
|
-
- Server configuration and capabilities
|
|
65
|
-
|
|
66
|
-
- `tool-registry.ts` - Dynamic tool management
|
|
67
|
-
- Tool definition storage
|
|
68
|
-
- Handler registration and lookup
|
|
69
|
-
- Tool lifecycle management
|
|
70
|
-
|
|
71
|
-
- `tab-manager.ts` - Multi-tab coordination
|
|
72
|
-
- Tab registration and tracking
|
|
73
|
-
- Active tab management
|
|
74
|
-
- Smart tool routing across tabs
|
|
75
|
-
|
|
76
|
-
- `built-in-tools.ts` - Default MCP tools
|
|
77
|
-
- Event querying tools
|
|
78
|
-
- Tab listing tools
|
|
79
|
-
- Navigation history tools
|
|
80
|
-
|
|
81
|
-
**Used by:** Worker entry points (`mcp-shared-worker.ts`, `mcp-service-worker.ts`)
|
|
82
|
-
|
|
83
|
-
### Shared (`shared/`)
|
|
84
|
-
|
|
85
|
-
**Purpose:** Code used by both client and worker contexts.
|
|
86
|
-
|
|
87
|
-
**Key Files:**
|
|
88
|
-
- `types.ts` - TypeScript type definitions
|
|
89
|
-
- `UserEvent`, `ToolDefinition`, `ToolHandler`
|
|
90
|
-
- `TabInfo`, `EventFilters`, etc.
|
|
91
|
-
- Ensures type consistency across contexts
|
|
92
|
-
|
|
93
|
-
- `logger.ts` - Logging utilities
|
|
94
|
-
- Environment-aware logging (dev vs production)
|
|
95
|
-
- Consistent logging interface
|
|
96
|
-
- Works in both main thread and workers
|
|
97
|
-
|
|
98
|
-
- `database.ts` - IndexedDB operations
|
|
99
|
-
- Event storage and retrieval
|
|
100
|
-
- Query filtering and pagination
|
|
101
|
-
- Available in both contexts (IndexedDB works everywhere)
|
|
102
|
-
|
|
103
|
-
**Used by:** Both client and worker code
|
|
104
|
-
|
|
105
|
-
## Communication Flow
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
Application Code
|
|
109
|
-
↓ (imports)
|
|
110
|
-
client/worker-client.ts
|
|
111
|
-
↓ (postMessage)
|
|
112
|
-
mcp-shared-worker.ts or mcp-service-worker.ts
|
|
113
|
-
↓ (uses)
|
|
114
|
-
worker/mcp-controller.ts
|
|
115
|
-
↓ (uses)
|
|
116
|
-
worker/mcp-server.ts
|
|
117
|
-
↓ (uses)
|
|
118
|
-
worker/tool-registry.ts
|
|
119
|
-
↓ (WebSocket)
|
|
120
|
-
MCP Proxy Server
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Import Patterns
|
|
124
|
-
|
|
125
|
-
### For Application Code
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
// Import from the main package
|
|
129
|
-
import { workerClient, type ToolDefinition } from '@mcp-fe/mcp-worker';
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Internal Worker Code
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
// Worker modules import from shared/
|
|
136
|
-
import { logger } from '../shared/logger';
|
|
137
|
-
import type { UserEvent } from '../shared/types';
|
|
138
|
-
|
|
139
|
-
// Worker modules import from other worker modules
|
|
140
|
-
import { toolRegistry } from './tool-registry';
|
|
141
|
-
import { TabManager } from './tab-manager';
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Internal Client Code
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
// Client modules import from shared/
|
|
148
|
-
import { logger } from '../shared/logger';
|
|
149
|
-
import type { ToolDefinition } from '../shared/types';
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Why This Structure?
|
|
153
|
-
|
|
154
|
-
### Clear Separation of Concerns
|
|
155
|
-
- **Client code** only deals with communication and API
|
|
156
|
-
- **Worker code** handles MCP protocol and business logic
|
|
157
|
-
- **Shared code** provides common utilities and types
|
|
158
|
-
|
|
159
|
-
### Better Tree-Shaking
|
|
160
|
-
- Applications only import client code
|
|
161
|
-
- Worker bundles only include worker code
|
|
162
|
-
- No unnecessary code in either bundle
|
|
163
|
-
|
|
164
|
-
### Maintainability
|
|
165
|
-
- Easy to find where specific functionality lives
|
|
166
|
-
- Clear boundaries between contexts
|
|
167
|
-
- Prevents accidental mixing of client/worker code
|
|
168
|
-
|
|
169
|
-
### Future-Proof
|
|
170
|
-
- Ready for splitting into separate npm packages if needed
|
|
171
|
-
- Can add more worker types (e.g., dedicated workers)
|
|
172
|
-
- Easy to add more shared utilities
|
|
1
|
+
# Project Structure
|
|
2
|
+
|
|
3
|
+
This document explains the organization of the `@mcp-fe/mcp-worker` codebase.
|
|
4
|
+
|
|
5
|
+
## Directory Layout
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
libs/mcp-worker/src/
|
|
9
|
+
├── index.ts # Main entry point (re-exports from client/)
|
|
10
|
+
├── mcp-shared-worker.ts # SharedWorker entry point
|
|
11
|
+
├── mcp-service-worker.ts # ServiceWorker entry point
|
|
12
|
+
├── client/ # Client-side code (application runtime)
|
|
13
|
+
│ ├── index.ts # Client API exports
|
|
14
|
+
│ └── worker-client.ts # Main WorkerClient class
|
|
15
|
+
├── worker/ # Worker-side code (background processing)
|
|
16
|
+
│ ├── index.ts # Worker internal exports
|
|
17
|
+
│ ├── mcp-controller.ts # MCP server controller & lifecycle
|
|
18
|
+
│ ├── mcp-server.ts # MCP server setup & handlers
|
|
19
|
+
│ ├── websocket-transport.ts # WebSocket transport for MCP
|
|
20
|
+
│ ├── tool-registry.ts # Dynamic tool registration
|
|
21
|
+
│ ├── tab-manager.ts # Multi-tab coordination
|
|
22
|
+
│ ├── built-in-tools.ts # Default MCP tools
|
|
23
|
+
│ └── tab-manager.spec.ts # Tests for TabManager
|
|
24
|
+
└── shared/ # Shared code (both contexts)
|
|
25
|
+
├── types.ts # TypeScript type definitions
|
|
26
|
+
├── logger.ts # Logging utilities
|
|
27
|
+
└── database.ts # IndexedDB operations
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Module Responsibilities
|
|
31
|
+
|
|
32
|
+
### Client (`client/`)
|
|
33
|
+
|
|
34
|
+
**Purpose:** Code that runs in the main browser thread (your application).
|
|
35
|
+
|
|
36
|
+
**Key Files:**
|
|
37
|
+
- `worker-client.ts` - Main API for communicating with workers
|
|
38
|
+
- Handles worker initialization (SharedWorker vs ServiceWorker)
|
|
39
|
+
- Manages tool registration and lifecycle
|
|
40
|
+
- Handles multi-tab coordination
|
|
41
|
+
- Provides request/response messaging
|
|
42
|
+
|
|
43
|
+
**Used by:** Your application code
|
|
44
|
+
|
|
45
|
+
**Example:**
|
|
46
|
+
```typescript
|
|
47
|
+
import { workerClient } from '@mcp-fe/mcp-worker';
|
|
48
|
+
await workerClient.init();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Worker (`worker/`)
|
|
52
|
+
|
|
53
|
+
**Purpose:** Code that runs inside Web Workers (background processing).
|
|
54
|
+
|
|
55
|
+
**Key Files:**
|
|
56
|
+
- `mcp-controller.ts` - Main controller for MCP server lifecycle
|
|
57
|
+
- WebSocket connection management
|
|
58
|
+
- Tool call routing and execution
|
|
59
|
+
- Tab management coordination
|
|
60
|
+
- Event storage and querying
|
|
61
|
+
|
|
62
|
+
- `mcp-server.ts` - MCP server setup using @modelcontextprotocol/sdk
|
|
63
|
+
- Request handlers (ListTools, CallTool)
|
|
64
|
+
- Server configuration and capabilities
|
|
65
|
+
|
|
66
|
+
- `tool-registry.ts` - Dynamic tool management
|
|
67
|
+
- Tool definition storage
|
|
68
|
+
- Handler registration and lookup
|
|
69
|
+
- Tool lifecycle management
|
|
70
|
+
|
|
71
|
+
- `tab-manager.ts` - Multi-tab coordination
|
|
72
|
+
- Tab registration and tracking
|
|
73
|
+
- Active tab management
|
|
74
|
+
- Smart tool routing across tabs
|
|
75
|
+
|
|
76
|
+
- `built-in-tools.ts` - Default MCP tools
|
|
77
|
+
- Event querying tools
|
|
78
|
+
- Tab listing tools
|
|
79
|
+
- Navigation history tools
|
|
80
|
+
|
|
81
|
+
**Used by:** Worker entry points (`mcp-shared-worker.ts`, `mcp-service-worker.ts`)
|
|
82
|
+
|
|
83
|
+
### Shared (`shared/`)
|
|
84
|
+
|
|
85
|
+
**Purpose:** Code used by both client and worker contexts.
|
|
86
|
+
|
|
87
|
+
**Key Files:**
|
|
88
|
+
- `types.ts` - TypeScript type definitions
|
|
89
|
+
- `UserEvent`, `ToolDefinition`, `ToolHandler`
|
|
90
|
+
- `TabInfo`, `EventFilters`, etc.
|
|
91
|
+
- Ensures type consistency across contexts
|
|
92
|
+
|
|
93
|
+
- `logger.ts` - Logging utilities
|
|
94
|
+
- Environment-aware logging (dev vs production)
|
|
95
|
+
- Consistent logging interface
|
|
96
|
+
- Works in both main thread and workers
|
|
97
|
+
|
|
98
|
+
- `database.ts` - IndexedDB operations
|
|
99
|
+
- Event storage and retrieval
|
|
100
|
+
- Query filtering and pagination
|
|
101
|
+
- Available in both contexts (IndexedDB works everywhere)
|
|
102
|
+
|
|
103
|
+
**Used by:** Both client and worker code
|
|
104
|
+
|
|
105
|
+
## Communication Flow
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Application Code
|
|
109
|
+
↓ (imports)
|
|
110
|
+
client/worker-client.ts
|
|
111
|
+
↓ (postMessage)
|
|
112
|
+
mcp-shared-worker.ts or mcp-service-worker.ts
|
|
113
|
+
↓ (uses)
|
|
114
|
+
worker/mcp-controller.ts
|
|
115
|
+
↓ (uses)
|
|
116
|
+
worker/mcp-server.ts
|
|
117
|
+
↓ (uses)
|
|
118
|
+
worker/tool-registry.ts
|
|
119
|
+
↓ (WebSocket)
|
|
120
|
+
MCP Proxy Server
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Import Patterns
|
|
124
|
+
|
|
125
|
+
### For Application Code
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Import from the main package
|
|
129
|
+
import { workerClient, type ToolDefinition } from '@mcp-fe/mcp-worker';
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Internal Worker Code
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Worker modules import from shared/
|
|
136
|
+
import { logger } from '../shared/logger';
|
|
137
|
+
import type { UserEvent } from '../shared/types';
|
|
138
|
+
|
|
139
|
+
// Worker modules import from other worker modules
|
|
140
|
+
import { toolRegistry } from './tool-registry';
|
|
141
|
+
import { TabManager } from './tab-manager';
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Internal Client Code
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Client modules import from shared/
|
|
148
|
+
import { logger } from '../shared/logger';
|
|
149
|
+
import type { ToolDefinition } from '../shared/types';
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Why This Structure?
|
|
153
|
+
|
|
154
|
+
### Clear Separation of Concerns
|
|
155
|
+
- **Client code** only deals with communication and API
|
|
156
|
+
- **Worker code** handles MCP protocol and business logic
|
|
157
|
+
- **Shared code** provides common utilities and types
|
|
158
|
+
|
|
159
|
+
### Better Tree-Shaking
|
|
160
|
+
- Applications only import client code
|
|
161
|
+
- Worker bundles only include worker code
|
|
162
|
+
- No unnecessary code in either bundle
|
|
163
|
+
|
|
164
|
+
### Maintainability
|
|
165
|
+
- Easy to find where specific functionality lives
|
|
166
|
+
- Clear boundaries between contexts
|
|
167
|
+
- Prevents accidental mixing of client/worker code
|
|
168
|
+
|
|
169
|
+
### Future-Proof
|
|
170
|
+
- Ready for splitting into separate npm packages if needed
|
|
171
|
+
- Can add more worker types (e.g., dedicated workers)
|
|
172
|
+
- Easy to add more shared utilities
|
package/docs/tab-manager.md
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
# TabManager
|
|
2
|
-
|
|
3
|
-
Manages browser tabs and tool registration routing.
|
|
4
|
-
|
|
5
|
-
## API
|
|
6
|
-
|
|
7
|
-
### Tab Management
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
// Register tab
|
|
11
|
-
tabManager.registerTab(tabId, url, title);
|
|
12
|
-
|
|
13
|
-
// Set active tab
|
|
14
|
-
tabManager.setActiveTab(tabId);
|
|
15
|
-
|
|
16
|
-
// Get active tab
|
|
17
|
-
const activeId = tabManager.getActiveTabId(); // → string | null
|
|
18
|
-
|
|
19
|
-
// Get all tabs
|
|
20
|
-
const tabs = tabManager.getAllTabs(); // → TabInfo[]
|
|
21
|
-
|
|
22
|
-
// Remove tab
|
|
23
|
-
tabManager.removeTab(tabId); // → boolean
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Tool Registration
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
// Register tool for tab
|
|
30
|
-
const isNew = tabManager.registerToolForTab(toolName, tabId);
|
|
31
|
-
|
|
32
|
-
// Unregister tool from tab
|
|
33
|
-
const result = tabManager.unregisterToolFromTab(toolName, tabId);
|
|
34
|
-
// → { wasRemoved, remainingTabs, wasActiveTab }
|
|
35
|
-
|
|
36
|
-
// Get tabs with tool
|
|
37
|
-
const tabs = tabManager.getTabsForTool(toolName); // → Set<string>
|
|
38
|
-
|
|
39
|
-
// Check if tab has tool
|
|
40
|
-
const has = tabManager.tabHasTool(toolName, tabId); // → boolean
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Smart Routing
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
const route = tabManager.routeToolCall(toolName, explicitTabId?);
|
|
47
|
-
// → { targetTabId: string, reason: string } | null
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Priority:**
|
|
51
|
-
1. Explicit `tabId` (if valid)
|
|
52
|
-
2. Only one tab has tool → use it
|
|
53
|
-
3. Active tab has tool → use it
|
|
54
|
-
4. Use first available
|
|
55
|
-
|
|
56
|
-
### Diagnostics
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
// Debug routing
|
|
60
|
-
const info = tabManager.getRoutingInfo(toolName);
|
|
61
|
-
|
|
62
|
-
// Get stats
|
|
63
|
-
const stats = tabManager.getStats();
|
|
64
|
-
|
|
65
|
-
// Clear all (testing)
|
|
66
|
-
tabManager.clear();
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Usage
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
import { TabManager } from '@mcp-fe/mcp-worker';
|
|
73
|
-
|
|
74
|
-
const tabs = new TabManager();
|
|
75
|
-
|
|
76
|
-
// Register tabs
|
|
77
|
-
tabs.registerTab('tab-1', '/dashboard', 'Dashboard');
|
|
78
|
-
tabs.registerTab('tab-2', '/settings', 'Settings');
|
|
79
|
-
|
|
80
|
-
// Register tools
|
|
81
|
-
tabs.registerToolForTab('get_data', 'tab-1');
|
|
82
|
-
tabs.registerToolForTab('get_data', 'tab-2');
|
|
83
|
-
|
|
84
|
-
// Set active
|
|
85
|
-
tabs.setActiveTab('tab-1');
|
|
86
|
-
|
|
87
|
-
// Route
|
|
88
|
-
const route = tabs.routeToolCall('get_data');
|
|
89
|
-
// → { targetTabId: 'tab-1', reason: 'active tab has tool' }
|
|
90
|
-
|
|
91
|
-
// Route to specific tab
|
|
92
|
-
const route2 = tabs.routeToolCall('get_data', 'tab-2');
|
|
93
|
-
// → { targetTabId: 'tab-2', reason: 'explicit tabId parameter' }
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Integration
|
|
97
|
-
|
|
98
|
-
MCPController uses TabManager internally:
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
class MCPController {
|
|
102
|
-
private tabManager = new TabManager();
|
|
103
|
-
|
|
104
|
-
handleRegisterTab(data) {
|
|
105
|
-
this.tabManager.registerTab(data.tabId, data.url, data.title);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async handleRegisterToolInternal(toolData) {
|
|
109
|
-
const isNew = this.tabManager.registerToolForTab(toolData.name, toolData.tabId);
|
|
110
|
-
|
|
111
|
-
if (!isNew) return;
|
|
112
|
-
|
|
113
|
-
// Create handler with routing
|
|
114
|
-
const handler = async (args) => {
|
|
115
|
-
const route = this.tabManager.routeToolCall(toolData.name, args.tabId);
|
|
116
|
-
if (!route) throw new Error('Tool not available');
|
|
117
|
-
|
|
118
|
-
return this.sendToolCall(route.targetTabId, args);
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
toolRegistry.register({ name: toolData.name }, handler);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Why Separate Class?
|
|
127
|
-
|
|
128
|
-
- **Separation of Concerns**: Tab logic separate from MCP protocol
|
|
129
|
-
- **Testable**: Easy to unit test without MCP dependencies
|
|
130
|
-
- **Reusable**: Can be used in other contexts
|
|
131
|
-
- **Type Safe**: Strong typing throughout
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
// Example test
|
|
135
|
-
test('routes to only available tab', () => {
|
|
136
|
-
const tabs = new TabManager();
|
|
137
|
-
tabs.registerTab('tab-1', '/page', 'Page');
|
|
138
|
-
tabs.registerTab('tab-2', '/page', 'Page');
|
|
139
|
-
tabs.registerToolForTab('tool', 'tab-1');
|
|
140
|
-
tabs.setActiveTab('tab-2'); // Active tab doesn't have tool
|
|
141
|
-
|
|
142
|
-
const route = tabs.routeToolCall('tool');
|
|
143
|
-
expect(route.targetTabId).toBe('tab-1');
|
|
144
|
-
});
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## See Also
|
|
148
|
-
|
|
149
|
-
- [Multi-Tab Guide](./multi-tab.md) - Complete multi-tab docs
|
|
150
|
-
- [Architecture](./architecture.md) - System architecture
|
|
1
|
+
# TabManager
|
|
2
|
+
|
|
3
|
+
Manages browser tabs and tool registration routing.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
### Tab Management
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Register tab
|
|
11
|
+
tabManager.registerTab(tabId, url, title);
|
|
12
|
+
|
|
13
|
+
// Set active tab
|
|
14
|
+
tabManager.setActiveTab(tabId);
|
|
15
|
+
|
|
16
|
+
// Get active tab
|
|
17
|
+
const activeId = tabManager.getActiveTabId(); // → string | null
|
|
18
|
+
|
|
19
|
+
// Get all tabs
|
|
20
|
+
const tabs = tabManager.getAllTabs(); // → TabInfo[]
|
|
21
|
+
|
|
22
|
+
// Remove tab
|
|
23
|
+
tabManager.removeTab(tabId); // → boolean
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Tool Registration
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Register tool for tab
|
|
30
|
+
const isNew = tabManager.registerToolForTab(toolName, tabId);
|
|
31
|
+
|
|
32
|
+
// Unregister tool from tab
|
|
33
|
+
const result = tabManager.unregisterToolFromTab(toolName, tabId);
|
|
34
|
+
// → { wasRemoved, remainingTabs, wasActiveTab }
|
|
35
|
+
|
|
36
|
+
// Get tabs with tool
|
|
37
|
+
const tabs = tabManager.getTabsForTool(toolName); // → Set<string>
|
|
38
|
+
|
|
39
|
+
// Check if tab has tool
|
|
40
|
+
const has = tabManager.tabHasTool(toolName, tabId); // → boolean
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Smart Routing
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const route = tabManager.routeToolCall(toolName, explicitTabId?);
|
|
47
|
+
// → { targetTabId: string, reason: string } | null
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Priority:**
|
|
51
|
+
1. Explicit `tabId` (if valid)
|
|
52
|
+
2. Only one tab has tool → use it
|
|
53
|
+
3. Active tab has tool → use it
|
|
54
|
+
4. Use first available
|
|
55
|
+
|
|
56
|
+
### Diagnostics
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Debug routing
|
|
60
|
+
const info = tabManager.getRoutingInfo(toolName);
|
|
61
|
+
|
|
62
|
+
// Get stats
|
|
63
|
+
const stats = tabManager.getStats();
|
|
64
|
+
|
|
65
|
+
// Clear all (testing)
|
|
66
|
+
tabManager.clear();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { TabManager } from '@mcp-fe/mcp-worker';
|
|
73
|
+
|
|
74
|
+
const tabs = new TabManager();
|
|
75
|
+
|
|
76
|
+
// Register tabs
|
|
77
|
+
tabs.registerTab('tab-1', '/dashboard', 'Dashboard');
|
|
78
|
+
tabs.registerTab('tab-2', '/settings', 'Settings');
|
|
79
|
+
|
|
80
|
+
// Register tools
|
|
81
|
+
tabs.registerToolForTab('get_data', 'tab-1');
|
|
82
|
+
tabs.registerToolForTab('get_data', 'tab-2');
|
|
83
|
+
|
|
84
|
+
// Set active
|
|
85
|
+
tabs.setActiveTab('tab-1');
|
|
86
|
+
|
|
87
|
+
// Route
|
|
88
|
+
const route = tabs.routeToolCall('get_data');
|
|
89
|
+
// → { targetTabId: 'tab-1', reason: 'active tab has tool' }
|
|
90
|
+
|
|
91
|
+
// Route to specific tab
|
|
92
|
+
const route2 = tabs.routeToolCall('get_data', 'tab-2');
|
|
93
|
+
// → { targetTabId: 'tab-2', reason: 'explicit tabId parameter' }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Integration
|
|
97
|
+
|
|
98
|
+
MCPController uses TabManager internally:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
class MCPController {
|
|
102
|
+
private tabManager = new TabManager();
|
|
103
|
+
|
|
104
|
+
handleRegisterTab(data) {
|
|
105
|
+
this.tabManager.registerTab(data.tabId, data.url, data.title);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async handleRegisterToolInternal(toolData) {
|
|
109
|
+
const isNew = this.tabManager.registerToolForTab(toolData.name, toolData.tabId);
|
|
110
|
+
|
|
111
|
+
if (!isNew) return;
|
|
112
|
+
|
|
113
|
+
// Create handler with routing
|
|
114
|
+
const handler = async (args) => {
|
|
115
|
+
const route = this.tabManager.routeToolCall(toolData.name, args.tabId);
|
|
116
|
+
if (!route) throw new Error('Tool not available');
|
|
117
|
+
|
|
118
|
+
return this.sendToolCall(route.targetTabId, args);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
toolRegistry.register({ name: toolData.name }, handler);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Why Separate Class?
|
|
127
|
+
|
|
128
|
+
- **Separation of Concerns**: Tab logic separate from MCP protocol
|
|
129
|
+
- **Testable**: Easy to unit test without MCP dependencies
|
|
130
|
+
- **Reusable**: Can be used in other contexts
|
|
131
|
+
- **Type Safe**: Strong typing throughout
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Example test
|
|
135
|
+
test('routes to only available tab', () => {
|
|
136
|
+
const tabs = new TabManager();
|
|
137
|
+
tabs.registerTab('tab-1', '/page', 'Page');
|
|
138
|
+
tabs.registerTab('tab-2', '/page', 'Page');
|
|
139
|
+
tabs.registerToolForTab('tool', 'tab-1');
|
|
140
|
+
tabs.setActiveTab('tab-2'); // Active tab doesn't have tool
|
|
141
|
+
|
|
142
|
+
const route = tabs.routeToolCall('tool');
|
|
143
|
+
expect(route.targetTabId).toBe('tab-1');
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## See Also
|
|
148
|
+
|
|
149
|
+
- [Multi-Tab Guide](./multi-tab.md) - Complete multi-tab docs
|
|
150
|
+
- [Architecture](./architecture.md) - System architecture
|