@mcp-fe/mcp-worker 0.1.7 → 0.1.8
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 +26 -0
- package/docs/index.md +15 -4
- package/docs/initialization.md +2 -2
- package/docs/project-structure.md +172 -0
- package/index.js +34 -250
- package/mcp-service-worker.js +526 -7468
- package/mcp-shared-worker.js +528 -7470
- package/package.json +1 -1
- package/src/client/index.d.ts +10 -0
- package/src/client/index.d.ts.map +1 -0
- package/src/client/worker-client.d.ts.map +1 -0
- package/src/index.d.ts +3 -7
- package/src/index.d.ts.map +1 -1
- package/src/shared/database.d.ts +8 -0
- package/src/shared/database.d.ts.map +1 -0
- package/src/shared/logger.d.ts.map +1 -0
- package/src/{lib/tool-registry.d.ts → shared/types.d.ts} +27 -13
- package/src/shared/types.d.ts.map +1 -0
- package/src/worker/built-in-tools.d.ts.map +1 -0
- package/src/worker/index.d.ts +12 -0
- package/src/worker/index.d.ts.map +1 -0
- package/src/{lib → worker}/mcp-controller.d.ts +2 -1
- package/src/worker/mcp-controller.d.ts.map +1 -0
- package/src/worker/mcp-server.d.ts.map +1 -0
- package/src/{lib → worker}/tab-manager.d.ts +2 -5
- package/src/worker/tab-manager.d.ts.map +1 -0
- package/src/worker/tool-registry.d.ts +17 -0
- package/src/worker/tool-registry.d.ts.map +1 -0
- package/src/worker/validation.d.ts +31 -0
- package/src/worker/validation.d.ts.map +1 -0
- package/src/worker/websocket-transport.d.ts.map +1 -0
- package/src/lib/built-in-tools.d.ts.map +0 -1
- package/src/lib/database.d.ts +0 -27
- package/src/lib/database.d.ts.map +0 -1
- package/src/lib/logger.d.ts.map +0 -1
- package/src/lib/mcp-controller.d.ts.map +0 -1
- package/src/lib/mcp-server.d.ts.map +0 -1
- package/src/lib/tab-manager.d.ts.map +0 -1
- package/src/lib/tool-registry.d.ts.map +0 -1
- package/src/lib/websocket-transport.d.ts.map +0 -1
- package/src/lib/worker-client.d.ts.map +0 -1
- /package/src/{lib → client}/worker-client.d.ts +0 -0
- /package/src/{lib → shared}/logger.d.ts +0 -0
- /package/src/{lib → worker}/built-in-tools.d.ts +0 -0
- /package/src/{lib → worker}/mcp-server.d.ts +0 -0
- /package/src/{lib → worker}/websocket-transport.d.ts +0 -0
package/README.md
CHANGED
|
@@ -67,6 +67,31 @@ Frontend App ←→ WorkerClient ←→ Web Worker ←→ WebSocket ←→ MCP P
|
|
|
67
67
|
5. **MCP Proxy** - Bridges browser with AI agents
|
|
68
68
|
6. **AI Agent** - Queries your app via MCP protocol
|
|
69
69
|
|
|
70
|
+
### Project Structure
|
|
71
|
+
|
|
72
|
+
The library is organized into three main directories:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
libs/mcp-worker/src/
|
|
76
|
+
├── client/ # Client-side code (application runtime)
|
|
77
|
+
│ ├── worker-client.ts # Main WorkerClient class
|
|
78
|
+
│ └── index.ts # Client API exports
|
|
79
|
+
├── worker/ # Worker-side code (background processing)
|
|
80
|
+
│ ├── mcp-controller.ts # MCP server controller
|
|
81
|
+
│ ├── mcp-server.ts # MCP server setup
|
|
82
|
+
│ ├── tab-manager.ts # Multi-tab coordination
|
|
83
|
+
│ ├── tool-registry.ts # Dynamic tool management
|
|
84
|
+
│ └── built-in-tools.ts # Default MCP tools
|
|
85
|
+
└── shared/ # Shared code (both contexts)
|
|
86
|
+
├── types.ts # TypeScript type definitions
|
|
87
|
+
├── logger.ts # Logging utilities
|
|
88
|
+
└── database.ts # IndexedDB operations
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Entry points:**
|
|
92
|
+
- `mcp-shared-worker.ts` - SharedWorker implementation (preferred)
|
|
93
|
+
- `mcp-service-worker.ts` - ServiceWorker fallback
|
|
94
|
+
|
|
70
95
|
## Quick Start
|
|
71
96
|
|
|
72
97
|
### Installation
|
|
@@ -130,6 +155,7 @@ await workerClient.registerTool(
|
|
|
130
155
|
|
|
131
156
|
- **[Quick Start Guide](./docs/guide.md)** - Complete guide to dynamic tool registration
|
|
132
157
|
- **[API Reference](./docs/api.md)** - Full API documentation
|
|
158
|
+
- **[Project Structure](./docs/project-structure.md)** - Codebase organization explained
|
|
133
159
|
- **[Worker Details](./docs/worker-details.md)** - Implementation details
|
|
134
160
|
- **[Architecture](./docs/architecture.md)** - How the proxy pattern works
|
|
135
161
|
- **[Initialization](./docs/initialization.md)** - Init queue handling
|
package/docs/index.md
CHANGED
|
@@ -12,6 +12,7 @@ Complete guide to all documentation and examples in the MCP Worker library.
|
|
|
12
12
|
| **Complete API reference** | [API Reference](./api.md) |
|
|
13
13
|
| **Multi-tab applications** | [Multi-Tab Guide](./multi-tab.md) |
|
|
14
14
|
| **Worker implementation** | [Worker Details](./worker-details.md) |
|
|
15
|
+
| **Understand codebase structure** | [Project Structure](./project-structure.md) |
|
|
15
16
|
| **Use with React** | [React Hooks Guide](../../react-event-tracker/REACT_MCP_TOOLS.md) |
|
|
16
17
|
| **Understand architecture** | [Architecture](./architecture.md) |
|
|
17
18
|
| **Handle initialization** | [Initialization](./initialization.md) |
|
|
@@ -29,16 +30,26 @@ libs/mcp-worker/
|
|
|
29
30
|
│ ├── multi-tab.md ← Multi-tab support
|
|
30
31
|
│ ├── worker-details.md ← Worker implementation
|
|
31
32
|
│ ├── architecture.md ← Technical architecture
|
|
32
|
-
│
|
|
33
|
+
│ ├── initialization.md ← Init handling
|
|
34
|
+
│ ├── project-structure.md ← Codebase organization
|
|
35
|
+
│ └── tab-manager.md ← Tab management
|
|
33
36
|
│
|
|
34
37
|
├── examples/ ← Code examples
|
|
35
38
|
│ ├── README.md ← Examples guide
|
|
36
39
|
│ ├── quick-start.ts ← Simple examples
|
|
37
|
-
│
|
|
40
|
+
│ ├── dynamic-tools.ts ← Advanced patterns
|
|
41
|
+
│ └── structured-output.ts ← Structured outputs
|
|
38
42
|
│
|
|
39
43
|
└── src/ ← Source code
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
├── client/ ← Application runtime
|
|
45
|
+
│ └── worker-client.ts
|
|
46
|
+
├── worker/ ← Worker implementation
|
|
47
|
+
│ ├── mcp-controller.ts
|
|
48
|
+
│ └── ...
|
|
49
|
+
└── shared/ ← Shared utilities
|
|
50
|
+
├── types.ts
|
|
51
|
+
├── logger.ts
|
|
52
|
+
└── database.ts
|
|
42
53
|
|
|
43
54
|
libs/react-event-tracker/
|
|
44
55
|
├── REACT_MCP_TOOLS.md ← React hooks docs
|
package/docs/initialization.md
CHANGED
|
@@ -182,7 +182,7 @@ For contributors:
|
|
|
182
182
|
- Queues are processed in order (FIFO)
|
|
183
183
|
|
|
184
184
|
See source code for implementation details:
|
|
185
|
-
- `libs/mcp-worker/src/
|
|
186
|
-
- `libs/mcp-worker/src/
|
|
185
|
+
- `libs/mcp-worker/src/client/worker-client.ts`
|
|
186
|
+
- `libs/mcp-worker/src/worker/mcp-controller.ts`
|
|
187
187
|
|
|
188
188
|
|
|
@@ -0,0 +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
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// libs/mcp-worker/src/
|
|
1
|
+
// libs/mcp-worker/src/shared/logger.ts
|
|
2
2
|
var isProduction = typeof process !== "undefined" && true;
|
|
3
3
|
var mcpDebug = typeof process !== "undefined" && process.env?.["MCP_DEBUG"];
|
|
4
4
|
var debugEnabled = mcpDebug === "true" || !isProduction && mcpDebug !== "false";
|
|
@@ -26,7 +26,7 @@ var logger = {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
// libs/mcp-worker/src/
|
|
29
|
+
// libs/mcp-worker/src/client/worker-client.ts
|
|
30
30
|
var WorkerClient = class _WorkerClient {
|
|
31
31
|
// Configurable worker script URLs (defaults kept for backward compatibility)
|
|
32
32
|
sharedWorkerUrl = "/mcp-shared-worker.js";
|
|
@@ -1046,7 +1046,10 @@ var WorkerClient = class _WorkerClient {
|
|
|
1046
1046
|
}
|
|
1047
1047
|
};
|
|
1048
1048
|
|
|
1049
|
-
// libs/mcp-worker/src/
|
|
1049
|
+
// libs/mcp-worker/src/client/index.ts
|
|
1050
|
+
var workerClient = new WorkerClient();
|
|
1051
|
+
|
|
1052
|
+
// libs/mcp-worker/src/shared/database.ts
|
|
1050
1053
|
var DB_NAME = "user-activity-db";
|
|
1051
1054
|
var DB_VERSION = 1;
|
|
1052
1055
|
var STORE_NAME = "user-events";
|
|
@@ -1066,6 +1069,33 @@ async function initDB() {
|
|
|
1066
1069
|
};
|
|
1067
1070
|
});
|
|
1068
1071
|
}
|
|
1072
|
+
async function storeEvent(event) {
|
|
1073
|
+
const db = await initDB();
|
|
1074
|
+
const transaction = db.transaction([STORE_NAME], "readwrite");
|
|
1075
|
+
const store = transaction.objectStore(STORE_NAME);
|
|
1076
|
+
const eventWithId = {
|
|
1077
|
+
...event,
|
|
1078
|
+
id: `${event.timestamp}-${Math.random().toString(36).substr(2, 9)}`
|
|
1079
|
+
};
|
|
1080
|
+
await new Promise((resolve, reject) => {
|
|
1081
|
+
const request = store.add(eventWithId);
|
|
1082
|
+
request.onsuccess = () => resolve();
|
|
1083
|
+
request.onerror = () => reject(request.error);
|
|
1084
|
+
});
|
|
1085
|
+
const countRequest = store.count();
|
|
1086
|
+
countRequest.onsuccess = () => {
|
|
1087
|
+
if (countRequest.result > 1e3) {
|
|
1088
|
+
const index = store.index("timestamp");
|
|
1089
|
+
const getAllRequest = index.getAll();
|
|
1090
|
+
getAllRequest.onsuccess = () => {
|
|
1091
|
+
const events = getAllRequest.result;
|
|
1092
|
+
events.sort((a, b) => a.timestamp - b.timestamp);
|
|
1093
|
+
const toDelete = events.slice(0, events.length - 1e3);
|
|
1094
|
+
toDelete.forEach((event2) => store.delete(event2.id));
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1069
1099
|
async function queryEvents(filters) {
|
|
1070
1100
|
const db = await initDB();
|
|
1071
1101
|
const transaction = db.transaction([STORE_NAME], "readonly");
|
|
@@ -1096,256 +1126,10 @@ async function queryEvents(filters) {
|
|
|
1096
1126
|
request.onerror = () => reject(request.error);
|
|
1097
1127
|
});
|
|
1098
1128
|
}
|
|
1099
|
-
|
|
1100
|
-
// libs/mcp-worker/src/lib/tab-manager.ts
|
|
1101
|
-
var TabManager = class {
|
|
1102
|
-
// Registry of all active tabs
|
|
1103
|
-
tabRegistry = /* @__PURE__ */ new Map();
|
|
1104
|
-
// Currently active/focused tab
|
|
1105
|
-
activeTabId = null;
|
|
1106
|
-
// Tool handlers per tab: Map<ToolName, Set<TabId>>
|
|
1107
|
-
toolHandlersByTab = /* @__PURE__ */ new Map();
|
|
1108
|
-
/**
|
|
1109
|
-
* Register a new tab
|
|
1110
|
-
*/
|
|
1111
|
-
registerTab(tabId, url, title) {
|
|
1112
|
-
if (!tabId) {
|
|
1113
|
-
logger.warn("[TabManager] Cannot register tab: missing tabId");
|
|
1114
|
-
return;
|
|
1115
|
-
}
|
|
1116
|
-
this.tabRegistry.set(tabId, {
|
|
1117
|
-
url: url || "",
|
|
1118
|
-
title: title || "",
|
|
1119
|
-
lastSeen: Date.now()
|
|
1120
|
-
});
|
|
1121
|
-
logger.log(`[TabManager] Registered tab: ${tabId} (${title})`);
|
|
1122
|
-
}
|
|
1123
|
-
/**
|
|
1124
|
-
* Set the active/focused tab
|
|
1125
|
-
*/
|
|
1126
|
-
setActiveTab(tabId) {
|
|
1127
|
-
if (!tabId) {
|
|
1128
|
-
logger.warn("[TabManager] Cannot set active tab: missing tabId");
|
|
1129
|
-
return;
|
|
1130
|
-
}
|
|
1131
|
-
this.activeTabId = tabId;
|
|
1132
|
-
logger.log(`[TabManager] Active tab changed: ${tabId}`);
|
|
1133
|
-
const tab = this.tabRegistry.get(tabId);
|
|
1134
|
-
if (tab) {
|
|
1135
|
-
tab.lastSeen = Date.now();
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
/**
|
|
1139
|
-
* Get the currently active tab ID
|
|
1140
|
-
*/
|
|
1141
|
-
getActiveTabId() {
|
|
1142
|
-
return this.activeTabId;
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Get all registered tabs
|
|
1146
|
-
*/
|
|
1147
|
-
getAllTabs() {
|
|
1148
|
-
return Array.from(this.tabRegistry.entries()).map(([tabId, info]) => ({
|
|
1149
|
-
tabId,
|
|
1150
|
-
...info
|
|
1151
|
-
}));
|
|
1152
|
-
}
|
|
1153
|
-
/**
|
|
1154
|
-
* Get tab info by ID
|
|
1155
|
-
*/
|
|
1156
|
-
getTabInfo(tabId) {
|
|
1157
|
-
return this.tabRegistry.get(tabId);
|
|
1158
|
-
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Check if tab exists
|
|
1161
|
-
*/
|
|
1162
|
-
hasTab(tabId) {
|
|
1163
|
-
return this.tabRegistry.has(tabId);
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* Remove a tab from registry
|
|
1167
|
-
*/
|
|
1168
|
-
removeTab(tabId) {
|
|
1169
|
-
const existed = this.tabRegistry.delete(tabId);
|
|
1170
|
-
if (existed) {
|
|
1171
|
-
logger.log(`[TabManager] Removed tab: ${tabId}`);
|
|
1172
|
-
if (this.activeTabId === tabId) {
|
|
1173
|
-
this.activeTabId = null;
|
|
1174
|
-
}
|
|
1175
|
-
this.cleanupTabTools(tabId);
|
|
1176
|
-
}
|
|
1177
|
-
return existed;
|
|
1178
|
-
}
|
|
1179
|
-
/**
|
|
1180
|
-
* Register a tool for a specific tab
|
|
1181
|
-
*/
|
|
1182
|
-
registerToolForTab(toolName, tabId) {
|
|
1183
|
-
if (!this.toolHandlersByTab.has(toolName)) {
|
|
1184
|
-
this.toolHandlersByTab.set(toolName, /* @__PURE__ */ new Set());
|
|
1185
|
-
}
|
|
1186
|
-
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
1187
|
-
const isNewTab = !tabHandlers.has(tabId);
|
|
1188
|
-
tabHandlers.add(tabId);
|
|
1189
|
-
if (isNewTab) {
|
|
1190
|
-
logger.log(
|
|
1191
|
-
`[TabManager] Tab ${tabId} registered tool '${toolName}' (${tabHandlers.size} tab(s) total)`
|
|
1192
|
-
);
|
|
1193
|
-
} else {
|
|
1194
|
-
logger.log(
|
|
1195
|
-
`[TabManager] Tab ${tabId} re-registered tool '${toolName}' (already tracked)`
|
|
1196
|
-
);
|
|
1197
|
-
}
|
|
1198
|
-
return isNewTab;
|
|
1199
|
-
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Unregister a tool from a specific tab
|
|
1202
|
-
*/
|
|
1203
|
-
unregisterToolFromTab(toolName, tabId) {
|
|
1204
|
-
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
1205
|
-
if (!tabHandlers || !tabHandlers.has(tabId)) {
|
|
1206
|
-
return { wasRemoved: false, remainingTabs: 0, wasActiveTab: false };
|
|
1207
|
-
}
|
|
1208
|
-
const wasActiveTab = tabId === this.activeTabId;
|
|
1209
|
-
tabHandlers.delete(tabId);
|
|
1210
|
-
const remainingTabs = tabHandlers.size;
|
|
1211
|
-
logger.log(
|
|
1212
|
-
`[TabManager] Removed tab ${tabId} from tool '${toolName}' (${remainingTabs} tab(s) remaining)`
|
|
1213
|
-
);
|
|
1214
|
-
if (remainingTabs === 0) {
|
|
1215
|
-
this.toolHandlersByTab.delete(toolName);
|
|
1216
|
-
}
|
|
1217
|
-
return { wasRemoved: true, remainingTabs, wasActiveTab };
|
|
1218
|
-
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Get all tabs that have a specific tool
|
|
1221
|
-
*/
|
|
1222
|
-
getTabsForTool(toolName) {
|
|
1223
|
-
return this.toolHandlersByTab.get(toolName) || /* @__PURE__ */ new Set();
|
|
1224
|
-
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Check if a tab has a specific tool
|
|
1227
|
-
*/
|
|
1228
|
-
tabHasTool(toolName, tabId) {
|
|
1229
|
-
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
1230
|
-
return tabHandlers ? tabHandlers.has(tabId) : false;
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Smart routing: determine which tab should handle a tool call
|
|
1234
|
-
*
|
|
1235
|
-
* Priority:
|
|
1236
|
-
* 1. Explicit tabId parameter (if provided and valid)
|
|
1237
|
-
* 2. Only one tab has tool -> use it (regardless of focus)
|
|
1238
|
-
* 3. Active tab has tool -> use it
|
|
1239
|
-
* 4. Active tab doesn't have tool -> use first available
|
|
1240
|
-
* 5. No active tab -> use first available
|
|
1241
|
-
*/
|
|
1242
|
-
routeToolCall(toolName, explicitTabId) {
|
|
1243
|
-
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
1244
|
-
if (!tabHandlers || tabHandlers.size === 0) {
|
|
1245
|
-
return null;
|
|
1246
|
-
}
|
|
1247
|
-
if (explicitTabId) {
|
|
1248
|
-
if (tabHandlers.has(explicitTabId)) {
|
|
1249
|
-
return {
|
|
1250
|
-
targetTabId: explicitTabId,
|
|
1251
|
-
reason: "explicit tabId parameter"
|
|
1252
|
-
};
|
|
1253
|
-
} else {
|
|
1254
|
-
return null;
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
if (tabHandlers.size === 1) {
|
|
1258
|
-
const targetTabId = tabHandlers.values().next().value;
|
|
1259
|
-
return {
|
|
1260
|
-
targetTabId,
|
|
1261
|
-
reason: "only one tab has tool"
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
if (this.activeTabId && tabHandlers.has(this.activeTabId)) {
|
|
1265
|
-
return {
|
|
1266
|
-
targetTabId: this.activeTabId,
|
|
1267
|
-
reason: "active tab has tool"
|
|
1268
|
-
};
|
|
1269
|
-
}
|
|
1270
|
-
const firstTab = tabHandlers.values().next().value;
|
|
1271
|
-
const reason = this.activeTabId ? "active tab lacks tool, using first available" : "no active tab, using first available";
|
|
1272
|
-
return {
|
|
1273
|
-
targetTabId: firstTab,
|
|
1274
|
-
reason
|
|
1275
|
-
};
|
|
1276
|
-
}
|
|
1277
|
-
/**
|
|
1278
|
-
* Get routing statistics for debugging
|
|
1279
|
-
*/
|
|
1280
|
-
getRoutingInfo(toolName) {
|
|
1281
|
-
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
1282
|
-
const tabsWithTool = tabHandlers ? Array.from(tabHandlers) : [];
|
|
1283
|
-
return {
|
|
1284
|
-
toolExists: tabsWithTool.length > 0,
|
|
1285
|
-
tabsWithTool,
|
|
1286
|
-
activeTabHasTool: this.activeTabId ? this.tabHasTool(toolName, this.activeTabId) : false,
|
|
1287
|
-
recommendedTab: this.routeToolCall(toolName)?.targetTabId || null
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Clean up all tools for a specific tab
|
|
1292
|
-
* @private
|
|
1293
|
-
*/
|
|
1294
|
-
cleanupTabTools(tabId) {
|
|
1295
|
-
const removedTools = [];
|
|
1296
|
-
for (const [toolName, tabHandlers] of this.toolHandlersByTab.entries()) {
|
|
1297
|
-
if (tabHandlers.has(tabId)) {
|
|
1298
|
-
tabHandlers.delete(tabId);
|
|
1299
|
-
removedTools.push(toolName);
|
|
1300
|
-
if (tabHandlers.size === 0) {
|
|
1301
|
-
this.toolHandlersByTab.delete(toolName);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
if (removedTools.length > 0) {
|
|
1306
|
-
logger.log(
|
|
1307
|
-
`[TabManager] Cleaned up ${removedTools.length} tool(s) for tab ${tabId}: ${removedTools.join(", ")}`
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
/**
|
|
1312
|
-
* Get statistics for monitoring
|
|
1313
|
-
*/
|
|
1314
|
-
getStats() {
|
|
1315
|
-
const toolsPerTab = {};
|
|
1316
|
-
for (const [tabId] of this.tabRegistry) {
|
|
1317
|
-
let toolCount = 0;
|
|
1318
|
-
for (const tabHandlers of this.toolHandlersByTab.values()) {
|
|
1319
|
-
if (tabHandlers.has(tabId)) {
|
|
1320
|
-
toolCount++;
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
toolsPerTab[tabId] = toolCount;
|
|
1324
|
-
}
|
|
1325
|
-
return {
|
|
1326
|
-
totalTabs: this.tabRegistry.size,
|
|
1327
|
-
activeTabId: this.activeTabId,
|
|
1328
|
-
totalTools: this.toolHandlersByTab.size,
|
|
1329
|
-
toolsPerTab
|
|
1330
|
-
};
|
|
1331
|
-
}
|
|
1332
|
-
/**
|
|
1333
|
-
* Clear all data (for testing)
|
|
1334
|
-
*/
|
|
1335
|
-
clear() {
|
|
1336
|
-
this.tabRegistry.clear();
|
|
1337
|
-
this.activeTabId = null;
|
|
1338
|
-
this.toolHandlersByTab.clear();
|
|
1339
|
-
logger.log("[TabManager] Cleared all data");
|
|
1340
|
-
}
|
|
1341
|
-
};
|
|
1342
|
-
|
|
1343
|
-
// libs/mcp-worker/src/index.ts
|
|
1344
|
-
var workerClient = new WorkerClient();
|
|
1345
1129
|
export {
|
|
1346
|
-
TabManager,
|
|
1347
1130
|
WorkerClient,
|
|
1348
1131
|
logger,
|
|
1349
1132
|
queryEvents,
|
|
1133
|
+
storeEvent,
|
|
1350
1134
|
workerClient
|
|
1351
1135
|
};
|