@mcp-fe/mcp-worker 0.1.3 → 0.1.5
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/mcp-service-worker.js
CHANGED
|
@@ -27808,6 +27808,33 @@ function registerBuiltInTools() {
|
|
|
27808
27808
|
toolRegistry.register(definition, handler);
|
|
27809
27809
|
});
|
|
27810
27810
|
}
|
|
27811
|
+
function registerTabManagementTool(tabManager) {
|
|
27812
|
+
toolRegistry.register(
|
|
27813
|
+
{
|
|
27814
|
+
name: "list_browser_tabs",
|
|
27815
|
+
description: "List all active browser tabs running this application. Returns tab IDs, URLs, titles, and active status. Use this to discover available tabs before calling tools with specific tabId parameters.",
|
|
27816
|
+
inputSchema: {
|
|
27817
|
+
type: "object",
|
|
27818
|
+
properties: {}
|
|
27819
|
+
}
|
|
27820
|
+
},
|
|
27821
|
+
async () => {
|
|
27822
|
+
const tabs = tabManager.getAllTabs().map((tab) => ({
|
|
27823
|
+
...tab,
|
|
27824
|
+
isActive: tab.tabId === tabManager.getActiveTabId(),
|
|
27825
|
+
lastSeen: new Date(tab.lastSeen).toISOString()
|
|
27826
|
+
}));
|
|
27827
|
+
return {
|
|
27828
|
+
content: [
|
|
27829
|
+
{
|
|
27830
|
+
type: "text",
|
|
27831
|
+
text: JSON.stringify(tabs, null, 2)
|
|
27832
|
+
}
|
|
27833
|
+
]
|
|
27834
|
+
};
|
|
27835
|
+
}
|
|
27836
|
+
);
|
|
27837
|
+
}
|
|
27811
27838
|
|
|
27812
27839
|
// libs/mcp-worker/src/lib/mcp-server.ts
|
|
27813
27840
|
function createMCPServer(options = {}) {
|
|
@@ -27912,6 +27939,249 @@ var logger = {
|
|
|
27912
27939
|
}
|
|
27913
27940
|
};
|
|
27914
27941
|
|
|
27942
|
+
// libs/mcp-worker/src/lib/tab-manager.ts
|
|
27943
|
+
var TabManager = class {
|
|
27944
|
+
// Registry of all active tabs
|
|
27945
|
+
tabRegistry = /* @__PURE__ */ new Map();
|
|
27946
|
+
// Currently active/focused tab
|
|
27947
|
+
activeTabId = null;
|
|
27948
|
+
// Tool handlers per tab: Map<ToolName, Set<TabId>>
|
|
27949
|
+
toolHandlersByTab = /* @__PURE__ */ new Map();
|
|
27950
|
+
/**
|
|
27951
|
+
* Register a new tab
|
|
27952
|
+
*/
|
|
27953
|
+
registerTab(tabId, url2, title) {
|
|
27954
|
+
if (!tabId) {
|
|
27955
|
+
logger.warn("[TabManager] Cannot register tab: missing tabId");
|
|
27956
|
+
return;
|
|
27957
|
+
}
|
|
27958
|
+
this.tabRegistry.set(tabId, {
|
|
27959
|
+
url: url2 || "",
|
|
27960
|
+
title: title || "",
|
|
27961
|
+
lastSeen: Date.now()
|
|
27962
|
+
});
|
|
27963
|
+
logger.log(`[TabManager] Registered tab: ${tabId} (${title})`);
|
|
27964
|
+
}
|
|
27965
|
+
/**
|
|
27966
|
+
* Set the active/focused tab
|
|
27967
|
+
*/
|
|
27968
|
+
setActiveTab(tabId) {
|
|
27969
|
+
if (!tabId) {
|
|
27970
|
+
logger.warn("[TabManager] Cannot set active tab: missing tabId");
|
|
27971
|
+
return;
|
|
27972
|
+
}
|
|
27973
|
+
this.activeTabId = tabId;
|
|
27974
|
+
logger.log(`[TabManager] Active tab changed: ${tabId}`);
|
|
27975
|
+
const tab = this.tabRegistry.get(tabId);
|
|
27976
|
+
if (tab) {
|
|
27977
|
+
tab.lastSeen = Date.now();
|
|
27978
|
+
}
|
|
27979
|
+
}
|
|
27980
|
+
/**
|
|
27981
|
+
* Get the currently active tab ID
|
|
27982
|
+
*/
|
|
27983
|
+
getActiveTabId() {
|
|
27984
|
+
return this.activeTabId;
|
|
27985
|
+
}
|
|
27986
|
+
/**
|
|
27987
|
+
* Get all registered tabs
|
|
27988
|
+
*/
|
|
27989
|
+
getAllTabs() {
|
|
27990
|
+
return Array.from(this.tabRegistry.entries()).map(([tabId, info]) => ({
|
|
27991
|
+
tabId,
|
|
27992
|
+
...info
|
|
27993
|
+
}));
|
|
27994
|
+
}
|
|
27995
|
+
/**
|
|
27996
|
+
* Get tab info by ID
|
|
27997
|
+
*/
|
|
27998
|
+
getTabInfo(tabId) {
|
|
27999
|
+
return this.tabRegistry.get(tabId);
|
|
28000
|
+
}
|
|
28001
|
+
/**
|
|
28002
|
+
* Check if tab exists
|
|
28003
|
+
*/
|
|
28004
|
+
hasTab(tabId) {
|
|
28005
|
+
return this.tabRegistry.has(tabId);
|
|
28006
|
+
}
|
|
28007
|
+
/**
|
|
28008
|
+
* Remove a tab from registry
|
|
28009
|
+
*/
|
|
28010
|
+
removeTab(tabId) {
|
|
28011
|
+
const existed = this.tabRegistry.delete(tabId);
|
|
28012
|
+
if (existed) {
|
|
28013
|
+
logger.log(`[TabManager] Removed tab: ${tabId}`);
|
|
28014
|
+
if (this.activeTabId === tabId) {
|
|
28015
|
+
this.activeTabId = null;
|
|
28016
|
+
}
|
|
28017
|
+
this.cleanupTabTools(tabId);
|
|
28018
|
+
}
|
|
28019
|
+
return existed;
|
|
28020
|
+
}
|
|
28021
|
+
/**
|
|
28022
|
+
* Register a tool for a specific tab
|
|
28023
|
+
*/
|
|
28024
|
+
registerToolForTab(toolName, tabId) {
|
|
28025
|
+
if (!this.toolHandlersByTab.has(toolName)) {
|
|
28026
|
+
this.toolHandlersByTab.set(toolName, /* @__PURE__ */ new Set());
|
|
28027
|
+
}
|
|
28028
|
+
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
28029
|
+
const isNewTab = !tabHandlers.has(tabId);
|
|
28030
|
+
tabHandlers.add(tabId);
|
|
28031
|
+
if (isNewTab) {
|
|
28032
|
+
logger.log(
|
|
28033
|
+
`[TabManager] Tab ${tabId} registered tool '${toolName}' (${tabHandlers.size} tab(s) total)`
|
|
28034
|
+
);
|
|
28035
|
+
} else {
|
|
28036
|
+
logger.log(
|
|
28037
|
+
`[TabManager] Tab ${tabId} re-registered tool '${toolName}' (already tracked)`
|
|
28038
|
+
);
|
|
28039
|
+
}
|
|
28040
|
+
return isNewTab;
|
|
28041
|
+
}
|
|
28042
|
+
/**
|
|
28043
|
+
* Unregister a tool from a specific tab
|
|
28044
|
+
*/
|
|
28045
|
+
unregisterToolFromTab(toolName, tabId) {
|
|
28046
|
+
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
28047
|
+
if (!tabHandlers || !tabHandlers.has(tabId)) {
|
|
28048
|
+
return { wasRemoved: false, remainingTabs: 0, wasActiveTab: false };
|
|
28049
|
+
}
|
|
28050
|
+
const wasActiveTab = tabId === this.activeTabId;
|
|
28051
|
+
tabHandlers.delete(tabId);
|
|
28052
|
+
const remainingTabs = tabHandlers.size;
|
|
28053
|
+
logger.log(
|
|
28054
|
+
`[TabManager] Removed tab ${tabId} from tool '${toolName}' (${remainingTabs} tab(s) remaining)`
|
|
28055
|
+
);
|
|
28056
|
+
if (remainingTabs === 0) {
|
|
28057
|
+
this.toolHandlersByTab.delete(toolName);
|
|
28058
|
+
}
|
|
28059
|
+
return { wasRemoved: true, remainingTabs, wasActiveTab };
|
|
28060
|
+
}
|
|
28061
|
+
/**
|
|
28062
|
+
* Get all tabs that have a specific tool
|
|
28063
|
+
*/
|
|
28064
|
+
getTabsForTool(toolName) {
|
|
28065
|
+
return this.toolHandlersByTab.get(toolName) || /* @__PURE__ */ new Set();
|
|
28066
|
+
}
|
|
28067
|
+
/**
|
|
28068
|
+
* Check if a tab has a specific tool
|
|
28069
|
+
*/
|
|
28070
|
+
tabHasTool(toolName, tabId) {
|
|
28071
|
+
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
28072
|
+
return tabHandlers ? tabHandlers.has(tabId) : false;
|
|
28073
|
+
}
|
|
28074
|
+
/**
|
|
28075
|
+
* Smart routing: determine which tab should handle a tool call
|
|
28076
|
+
*
|
|
28077
|
+
* Priority:
|
|
28078
|
+
* 1. Explicit tabId parameter (if provided and valid)
|
|
28079
|
+
* 2. Only one tab has tool -> use it (regardless of focus)
|
|
28080
|
+
* 3. Active tab has tool -> use it
|
|
28081
|
+
* 4. Active tab doesn't have tool -> use first available
|
|
28082
|
+
* 5. No active tab -> use first available
|
|
28083
|
+
*/
|
|
28084
|
+
routeToolCall(toolName, explicitTabId) {
|
|
28085
|
+
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
28086
|
+
if (!tabHandlers || tabHandlers.size === 0) {
|
|
28087
|
+
return null;
|
|
28088
|
+
}
|
|
28089
|
+
if (explicitTabId) {
|
|
28090
|
+
if (tabHandlers.has(explicitTabId)) {
|
|
28091
|
+
return {
|
|
28092
|
+
targetTabId: explicitTabId,
|
|
28093
|
+
reason: "explicit tabId parameter"
|
|
28094
|
+
};
|
|
28095
|
+
} else {
|
|
28096
|
+
return null;
|
|
28097
|
+
}
|
|
28098
|
+
}
|
|
28099
|
+
if (tabHandlers.size === 1) {
|
|
28100
|
+
const targetTabId = tabHandlers.values().next().value;
|
|
28101
|
+
return {
|
|
28102
|
+
targetTabId,
|
|
28103
|
+
reason: "only one tab has tool"
|
|
28104
|
+
};
|
|
28105
|
+
}
|
|
28106
|
+
if (this.activeTabId && tabHandlers.has(this.activeTabId)) {
|
|
28107
|
+
return {
|
|
28108
|
+
targetTabId: this.activeTabId,
|
|
28109
|
+
reason: "active tab has tool"
|
|
28110
|
+
};
|
|
28111
|
+
}
|
|
28112
|
+
const firstTab = tabHandlers.values().next().value;
|
|
28113
|
+
const reason = this.activeTabId ? "active tab lacks tool, using first available" : "no active tab, using first available";
|
|
28114
|
+
return {
|
|
28115
|
+
targetTabId: firstTab,
|
|
28116
|
+
reason
|
|
28117
|
+
};
|
|
28118
|
+
}
|
|
28119
|
+
/**
|
|
28120
|
+
* Get routing statistics for debugging
|
|
28121
|
+
*/
|
|
28122
|
+
getRoutingInfo(toolName) {
|
|
28123
|
+
const tabHandlers = this.toolHandlersByTab.get(toolName);
|
|
28124
|
+
const tabsWithTool = tabHandlers ? Array.from(tabHandlers) : [];
|
|
28125
|
+
return {
|
|
28126
|
+
toolExists: tabsWithTool.length > 0,
|
|
28127
|
+
tabsWithTool,
|
|
28128
|
+
activeTabHasTool: this.activeTabId ? this.tabHasTool(toolName, this.activeTabId) : false,
|
|
28129
|
+
recommendedTab: this.routeToolCall(toolName)?.targetTabId || null
|
|
28130
|
+
};
|
|
28131
|
+
}
|
|
28132
|
+
/**
|
|
28133
|
+
* Clean up all tools for a specific tab
|
|
28134
|
+
* @private
|
|
28135
|
+
*/
|
|
28136
|
+
cleanupTabTools(tabId) {
|
|
28137
|
+
const removedTools = [];
|
|
28138
|
+
for (const [toolName, tabHandlers] of this.toolHandlersByTab.entries()) {
|
|
28139
|
+
if (tabHandlers.has(tabId)) {
|
|
28140
|
+
tabHandlers.delete(tabId);
|
|
28141
|
+
removedTools.push(toolName);
|
|
28142
|
+
if (tabHandlers.size === 0) {
|
|
28143
|
+
this.toolHandlersByTab.delete(toolName);
|
|
28144
|
+
}
|
|
28145
|
+
}
|
|
28146
|
+
}
|
|
28147
|
+
if (removedTools.length > 0) {
|
|
28148
|
+
logger.log(
|
|
28149
|
+
`[TabManager] Cleaned up ${removedTools.length} tool(s) for tab ${tabId}: ${removedTools.join(", ")}`
|
|
28150
|
+
);
|
|
28151
|
+
}
|
|
28152
|
+
}
|
|
28153
|
+
/**
|
|
28154
|
+
* Get statistics for monitoring
|
|
28155
|
+
*/
|
|
28156
|
+
getStats() {
|
|
28157
|
+
const toolsPerTab = {};
|
|
28158
|
+
for (const [tabId] of this.tabRegistry) {
|
|
28159
|
+
let toolCount = 0;
|
|
28160
|
+
for (const tabHandlers of this.toolHandlersByTab.values()) {
|
|
28161
|
+
if (tabHandlers.has(tabId)) {
|
|
28162
|
+
toolCount++;
|
|
28163
|
+
}
|
|
28164
|
+
}
|
|
28165
|
+
toolsPerTab[tabId] = toolCount;
|
|
28166
|
+
}
|
|
28167
|
+
return {
|
|
28168
|
+
totalTabs: this.tabRegistry.size,
|
|
28169
|
+
activeTabId: this.activeTabId,
|
|
28170
|
+
totalTools: this.toolHandlersByTab.size,
|
|
28171
|
+
toolsPerTab
|
|
28172
|
+
};
|
|
28173
|
+
}
|
|
28174
|
+
/**
|
|
28175
|
+
* Clear all data (for testing)
|
|
28176
|
+
*/
|
|
28177
|
+
clear() {
|
|
28178
|
+
this.tabRegistry.clear();
|
|
28179
|
+
this.activeTabId = null;
|
|
28180
|
+
this.toolHandlersByTab.clear();
|
|
28181
|
+
logger.log("[TabManager] Cleared all data");
|
|
28182
|
+
}
|
|
28183
|
+
};
|
|
28184
|
+
|
|
27915
28185
|
// libs/mcp-worker/src/lib/mcp-controller.ts
|
|
27916
28186
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
27917
28187
|
var INITIAL_RECONNECT_DELAY = 1e3;
|
|
@@ -27920,6 +28190,7 @@ var MCPController = class _MCPController {
|
|
|
27920
28190
|
this.backendUrl = backendUrl2;
|
|
27921
28191
|
this.broadcastFn = broadcastFn;
|
|
27922
28192
|
this.requireAuth = requireAuth;
|
|
28193
|
+
registerTabManagementTool(this.tabManager);
|
|
27923
28194
|
}
|
|
27924
28195
|
socket = null;
|
|
27925
28196
|
transport = null;
|
|
@@ -27931,6 +28202,40 @@ var MCPController = class _MCPController {
|
|
|
27931
28202
|
// Queue for tool registrations that arrive before MCP server is ready
|
|
27932
28203
|
pendingToolRegistrations = [];
|
|
27933
28204
|
isMCPServerReady = false;
|
|
28205
|
+
// Multi-tab support via TabManager
|
|
28206
|
+
tabManager = new TabManager();
|
|
28207
|
+
// Map to track pending tool calls
|
|
28208
|
+
pendingToolCalls = /* @__PURE__ */ new Map();
|
|
28209
|
+
/**
|
|
28210
|
+
* Handle tab registration from WorkerClient
|
|
28211
|
+
* @public
|
|
28212
|
+
*/
|
|
28213
|
+
handleRegisterTab(data) {
|
|
28214
|
+
const tabId = data["tabId"];
|
|
28215
|
+
const url2 = data["url"];
|
|
28216
|
+
const title = data["title"];
|
|
28217
|
+
if (!tabId) {
|
|
28218
|
+
logger.warn("[MCPController] REGISTER_TAB missing tabId");
|
|
28219
|
+
return;
|
|
28220
|
+
}
|
|
28221
|
+
this.tabManager.registerTab(tabId, url2, title);
|
|
28222
|
+
this.broadcastFn({
|
|
28223
|
+
type: "TAB_LIST_UPDATED",
|
|
28224
|
+
tabs: this.tabManager.getAllTabs()
|
|
28225
|
+
});
|
|
28226
|
+
}
|
|
28227
|
+
/**
|
|
28228
|
+
* Handle active tab change from WorkerClient
|
|
28229
|
+
* @public
|
|
28230
|
+
*/
|
|
28231
|
+
handleSetActiveTab(data) {
|
|
28232
|
+
const tabId = data["tabId"];
|
|
28233
|
+
if (!tabId) {
|
|
28234
|
+
logger.warn("[MCPController] SET_ACTIVE_TAB missing tabId");
|
|
28235
|
+
return;
|
|
28236
|
+
}
|
|
28237
|
+
this.tabManager.setActiveTab(tabId);
|
|
28238
|
+
}
|
|
27934
28239
|
startKeepAlive() {
|
|
27935
28240
|
if (this.keepAliveInterval) {
|
|
27936
28241
|
clearInterval(this.keepAliveInterval);
|
|
@@ -28122,6 +28427,7 @@ var MCPController = class _MCPController {
|
|
|
28122
28427
|
const description = toolData["description"];
|
|
28123
28428
|
const inputSchema = toolData["inputSchema"];
|
|
28124
28429
|
const handlerType = toolData["handlerType"];
|
|
28430
|
+
const tabId = toolData["tabId"];
|
|
28125
28431
|
if (!name || !description || !inputSchema) {
|
|
28126
28432
|
throw new Error(
|
|
28127
28433
|
"Missing required tool fields: name, description, inputSchema"
|
|
@@ -28132,49 +28438,72 @@ var MCPController = class _MCPController {
|
|
|
28132
28438
|
`Unsupported handler type: ${handlerType}. Only 'proxy' handlers are supported.`
|
|
28133
28439
|
);
|
|
28134
28440
|
}
|
|
28135
|
-
const
|
|
28136
|
-
|
|
28137
|
-
|
|
28138
|
-
|
|
28139
|
-
|
|
28140
|
-
|
|
28141
|
-
|
|
28142
|
-
const
|
|
28143
|
-
|
|
28144
|
-
|
|
28145
|
-
|
|
28146
|
-
|
|
28147
|
-
|
|
28148
|
-
|
|
28149
|
-
|
|
28150
|
-
|
|
28151
|
-
|
|
28152
|
-
|
|
28153
|
-
|
|
28154
|
-
|
|
28155
|
-
|
|
28156
|
-
|
|
28157
|
-
|
|
28158
|
-
|
|
28441
|
+
const isNewTab = this.tabManager.registerToolForTab(name, tabId);
|
|
28442
|
+
if (!isNewTab) {
|
|
28443
|
+
return;
|
|
28444
|
+
}
|
|
28445
|
+
const tabsWithTool = this.tabManager.getTabsForTool(name);
|
|
28446
|
+
if (tabsWithTool.size === 1) {
|
|
28447
|
+
const handler = async (args) => {
|
|
28448
|
+
const argsObj = args;
|
|
28449
|
+
const explicitTabId = argsObj["tabId"];
|
|
28450
|
+
const routingResult = this.tabManager.routeToolCall(
|
|
28451
|
+
name,
|
|
28452
|
+
explicitTabId
|
|
28453
|
+
);
|
|
28454
|
+
if (!routingResult) {
|
|
28455
|
+
const available = Array.from(this.tabManager.getTabsForTool(name));
|
|
28456
|
+
if (explicitTabId) {
|
|
28457
|
+
throw new Error(
|
|
28458
|
+
`Tool '${name}' not available in tab '${explicitTabId}'. Available tabs: ${available.join(", ")}`
|
|
28459
|
+
);
|
|
28460
|
+
} else {
|
|
28461
|
+
throw new Error(
|
|
28462
|
+
`Tool '${name}' has no registered tabs. Please specify tabId parameter.`
|
|
28463
|
+
);
|
|
28464
|
+
}
|
|
28465
|
+
}
|
|
28466
|
+
const { targetTabId, reason } = routingResult;
|
|
28467
|
+
logger.log(
|
|
28468
|
+
`[MCPController] Routing '${name}' to tab ${targetTabId}: ${reason}`
|
|
28469
|
+
);
|
|
28470
|
+
const callId = `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
28471
|
+
return new Promise((resolve, reject) => {
|
|
28472
|
+
const pendingCall = {
|
|
28473
|
+
resolve,
|
|
28474
|
+
reject,
|
|
28475
|
+
timeout: setTimeout(() => {
|
|
28476
|
+
this.pendingToolCalls.delete(callId);
|
|
28477
|
+
reject(
|
|
28478
|
+
new Error(`Tool call timeout: ${name} (tab: ${targetTabId})`)
|
|
28479
|
+
);
|
|
28480
|
+
}, 3e4)
|
|
28481
|
+
// 30 second timeout
|
|
28482
|
+
};
|
|
28483
|
+
this.pendingToolCalls.set(callId, pendingCall);
|
|
28484
|
+
this.broadcastFn({
|
|
28485
|
+
type: "CALL_TOOL",
|
|
28486
|
+
toolName: name,
|
|
28487
|
+
args,
|
|
28488
|
+
callId,
|
|
28489
|
+
targetTabId
|
|
28490
|
+
});
|
|
28159
28491
|
});
|
|
28160
|
-
}
|
|
28161
|
-
|
|
28162
|
-
|
|
28163
|
-
|
|
28164
|
-
|
|
28165
|
-
|
|
28166
|
-
|
|
28167
|
-
|
|
28168
|
-
|
|
28169
|
-
|
|
28170
|
-
|
|
28171
|
-
|
|
28172
|
-
|
|
28492
|
+
};
|
|
28493
|
+
toolRegistry.register(
|
|
28494
|
+
{
|
|
28495
|
+
name,
|
|
28496
|
+
description,
|
|
28497
|
+
inputSchema
|
|
28498
|
+
},
|
|
28499
|
+
handler
|
|
28500
|
+
);
|
|
28501
|
+
logger.log(
|
|
28502
|
+
`[MCPController] Registered proxy tool: ${name} with smart multi-tab routing`
|
|
28503
|
+
);
|
|
28504
|
+
}
|
|
28173
28505
|
}
|
|
28174
|
-
pendingToolCalls;
|
|
28175
28506
|
handleToolCallResult(callId, result) {
|
|
28176
|
-
if (!this.pendingToolCalls)
|
|
28177
|
-
return;
|
|
28178
28507
|
const pendingCall = this.pendingToolCalls.get(callId);
|
|
28179
28508
|
if (!pendingCall) {
|
|
28180
28509
|
logger.warn(
|
|
@@ -28191,14 +28520,35 @@ var MCPController = class _MCPController {
|
|
|
28191
28520
|
pendingCall.reject(new Error(resultData.error || "Tool call failed"));
|
|
28192
28521
|
}
|
|
28193
28522
|
}
|
|
28194
|
-
async handleUnregisterTool(toolName) {
|
|
28195
|
-
|
|
28196
|
-
|
|
28197
|
-
|
|
28198
|
-
|
|
28199
|
-
|
|
28523
|
+
async handleUnregisterTool(toolName, tabId) {
|
|
28524
|
+
if (!tabId) {
|
|
28525
|
+
logger.warn(
|
|
28526
|
+
`[MCPController] UNREGISTER_TOOL missing tabId for '${toolName}'`
|
|
28527
|
+
);
|
|
28528
|
+
return false;
|
|
28529
|
+
}
|
|
28530
|
+
const result = this.tabManager.unregisterToolFromTab(toolName, tabId);
|
|
28531
|
+
if (!result.wasRemoved) {
|
|
28532
|
+
logger.warn(
|
|
28533
|
+
`[MCPController] Tool '${toolName}' not found in tab ${tabId}`
|
|
28534
|
+
);
|
|
28535
|
+
return false;
|
|
28536
|
+
}
|
|
28537
|
+
if (result.wasActiveTab && result.remainingTabs > 0) {
|
|
28538
|
+
logger.log(
|
|
28539
|
+
`[MCPController] Active tab ${tabId} unregistered '${toolName}', but ${result.remainingTabs} other tab(s) still have it. Future calls will route to available tabs.`
|
|
28540
|
+
);
|
|
28541
|
+
}
|
|
28542
|
+
if (result.remainingTabs === 0) {
|
|
28543
|
+
const success2 = toolRegistry.unregister(toolName);
|
|
28544
|
+
if (success2) {
|
|
28545
|
+
logger.log(
|
|
28546
|
+
`[MCPController] Unregistered tool from MCP: ${toolName} (no tabs remaining)`
|
|
28547
|
+
);
|
|
28548
|
+
}
|
|
28549
|
+
return success2;
|
|
28200
28550
|
}
|
|
28201
|
-
return
|
|
28551
|
+
return true;
|
|
28202
28552
|
}
|
|
28203
28553
|
getConnectionStatus() {
|
|
28204
28554
|
return this.socket?.readyState === WebSocket.OPEN;
|
|
@@ -28232,27 +28582,44 @@ var getController = () => {
|
|
|
28232
28582
|
return controller;
|
|
28233
28583
|
};
|
|
28234
28584
|
var setBackendUrl = (url2) => {
|
|
28235
|
-
backendUrl
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
|
|
28242
|
-
|
|
28243
|
-
|
|
28244
|
-
|
|
28245
|
-
|
|
28246
|
-
|
|
28247
|
-
|
|
28585
|
+
if (controller && backendUrl === url2) {
|
|
28586
|
+
logger.log(
|
|
28587
|
+
"[ServiceWorker] Controller already initialized with same URL, reusing"
|
|
28588
|
+
);
|
|
28589
|
+
return controller;
|
|
28590
|
+
}
|
|
28591
|
+
if (backendUrl !== url2) {
|
|
28592
|
+
logger.log(
|
|
28593
|
+
`[ServiceWorker] Initializing/updating controller with URL: ${url2}`
|
|
28594
|
+
);
|
|
28595
|
+
backendUrl = url2;
|
|
28596
|
+
if (controller) {
|
|
28597
|
+
try {
|
|
28598
|
+
controller.dispose();
|
|
28599
|
+
} catch (e) {
|
|
28600
|
+
logger.warn("[ServiceWorker] Failed to dispose old controller:", e);
|
|
28601
|
+
}
|
|
28602
|
+
}
|
|
28603
|
+
controller = MCPController.create(url2, (message) => {
|
|
28604
|
+
self.clients.matchAll().then((clients) => {
|
|
28605
|
+
clients.forEach((client) => {
|
|
28606
|
+
try {
|
|
28607
|
+
client.postMessage(message);
|
|
28608
|
+
} catch (e) {
|
|
28609
|
+
logger.error(
|
|
28610
|
+
"[ServiceWorker] Failed to post message to client:",
|
|
28611
|
+
e
|
|
28612
|
+
);
|
|
28613
|
+
}
|
|
28614
|
+
});
|
|
28615
|
+
}).catch((err) => {
|
|
28616
|
+
logger.error(
|
|
28617
|
+
"[ServiceWorker] Failed to match clients for broadcast:",
|
|
28618
|
+
err
|
|
28619
|
+
);
|
|
28248
28620
|
});
|
|
28249
|
-
}).catch((err) => {
|
|
28250
|
-
logger.error(
|
|
28251
|
-
"[ServiceWorker] Failed to match clients for broadcast:",
|
|
28252
|
-
err
|
|
28253
|
-
);
|
|
28254
28621
|
});
|
|
28255
|
-
}
|
|
28622
|
+
}
|
|
28256
28623
|
return controller;
|
|
28257
28624
|
};
|
|
28258
28625
|
self.addEventListener("message", async (event) => {
|
|
@@ -28372,6 +28739,26 @@ self.addEventListener("message", async (event) => {
|
|
|
28372
28739
|
}
|
|
28373
28740
|
return;
|
|
28374
28741
|
}
|
|
28742
|
+
if (msg["type"] === "REGISTER_TAB") {
|
|
28743
|
+
try {
|
|
28744
|
+
if (controller) {
|
|
28745
|
+
getController().handleRegisterTab(msg);
|
|
28746
|
+
}
|
|
28747
|
+
} catch (error48) {
|
|
28748
|
+
logger.error("[ServiceWorker] Failed to register tab:", error48);
|
|
28749
|
+
}
|
|
28750
|
+
return;
|
|
28751
|
+
}
|
|
28752
|
+
if (msg["type"] === "SET_ACTIVE_TAB") {
|
|
28753
|
+
try {
|
|
28754
|
+
if (controller) {
|
|
28755
|
+
getController().handleSetActiveTab(msg);
|
|
28756
|
+
}
|
|
28757
|
+
} catch (error48) {
|
|
28758
|
+
logger.error("[ServiceWorker] Failed to set active tab:", error48);
|
|
28759
|
+
}
|
|
28760
|
+
return;
|
|
28761
|
+
}
|
|
28375
28762
|
if (msg["type"] === "REGISTER_TOOL") {
|
|
28376
28763
|
event.waitUntil(
|
|
28377
28764
|
(async () => {
|
|
@@ -28416,6 +28803,7 @@ self.addEventListener("message", async (event) => {
|
|
|
28416
28803
|
return;
|
|
28417
28804
|
}
|
|
28418
28805
|
const toolName = msg["name"];
|
|
28806
|
+
const tabId = msg["tabId"];
|
|
28419
28807
|
if (!toolName) {
|
|
28420
28808
|
if (event.ports && event.ports[0]) {
|
|
28421
28809
|
event.ports[0].postMessage({
|
|
@@ -28425,7 +28813,10 @@ self.addEventListener("message", async (event) => {
|
|
|
28425
28813
|
}
|
|
28426
28814
|
return;
|
|
28427
28815
|
}
|
|
28428
|
-
const success2 = await getController().handleUnregisterTool(
|
|
28816
|
+
const success2 = await getController().handleUnregisterTool(
|
|
28817
|
+
toolName,
|
|
28818
|
+
tabId
|
|
28819
|
+
);
|
|
28429
28820
|
if (event.ports && event.ports[0]) {
|
|
28430
28821
|
event.ports[0].postMessage({ success: success2 });
|
|
28431
28822
|
}
|