mcpbrowser 0.2.7 → 0.2.9
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/extension/package.json +1 -1
- package/extension/src/extension.js +15 -2
- package/package.json +1 -1
- package/src/mcp-browser.js +48 -16
package/extension/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "mcpbrowser",
|
|
3
3
|
"displayName": "MCP Browser",
|
|
4
4
|
"description": "Extends Copilot's web access to protected pages - handles login, SSO, and anti-crawler restrictions",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.9",
|
|
6
6
|
"publisher": "cherchyk",
|
|
7
7
|
"icon": "icon.png",
|
|
8
8
|
"engines": {
|
|
@@ -39,8 +39,21 @@ async function installMcpBrowser() {
|
|
|
39
39
|
try {
|
|
40
40
|
vscode.window.showInformationMessage('Installing MCPBrowser npm package...');
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
|
|
42
|
+
// Try with sudo if in Linux/Mac environment (like dev containers)
|
|
43
|
+
let installCmd = 'npm install -g mcpbrowser@latest';
|
|
44
|
+
|
|
45
|
+
// Check if we need sudo (Linux/Mac and not running as root)
|
|
46
|
+
if (process.platform !== 'win32' && process.getuid && process.getuid() !== 0) {
|
|
47
|
+
// Check if sudo is available
|
|
48
|
+
try {
|
|
49
|
+
await execPromise('which sudo');
|
|
50
|
+
installCmd = 'sudo ' + installCmd;
|
|
51
|
+
} catch {
|
|
52
|
+
// sudo not available, try without it
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await execPromise(installCmd);
|
|
44
57
|
|
|
45
58
|
vscode.window.showInformationMessage('MCPBrowser package installed successfully!');
|
|
46
59
|
return true;
|
package/package.json
CHANGED
package/src/mcp-browser.js
CHANGED
|
@@ -60,6 +60,7 @@ const defaultChromePaths = getDefaultChromePaths();
|
|
|
60
60
|
|
|
61
61
|
let cachedBrowser = null;
|
|
62
62
|
let lastKeptPage = null; // reuse the same tab when requested
|
|
63
|
+
let chromeLaunchPromise = null; // prevent multiple simultaneous launches
|
|
63
64
|
|
|
64
65
|
async function devtoolsAvailable() {
|
|
65
66
|
try {
|
|
@@ -80,24 +81,47 @@ function findChromePath() {
|
|
|
80
81
|
|
|
81
82
|
async function launchChromeIfNeeded() {
|
|
82
83
|
if (explicitWSEndpoint) return; // user provided explicit endpoint; assume managed externally
|
|
84
|
+
|
|
85
|
+
// If Chrome is already available, don't launch
|
|
83
86
|
if (await devtoolsAvailable()) return;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
87
|
+
|
|
88
|
+
// If a launch is already in progress, wait for it
|
|
89
|
+
if (chromeLaunchPromise) {
|
|
90
|
+
return await chromeLaunchPromise;
|
|
88
91
|
}
|
|
92
|
+
|
|
93
|
+
// Create a new launch promise to prevent multiple simultaneous launches
|
|
94
|
+
chromeLaunchPromise = (async () => {
|
|
95
|
+
try {
|
|
96
|
+
// Double-check after acquiring the launch lock
|
|
97
|
+
if (await devtoolsAvailable()) return;
|
|
98
|
+
|
|
99
|
+
const chromePath = findChromePath();
|
|
100
|
+
if (!chromePath) {
|
|
101
|
+
throw new Error("Chrome/Edge not found. Set CHROME_PATH to your browser executable.");
|
|
102
|
+
}
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
const args = [
|
|
105
|
+
`--remote-debugging-port=${chromePort}`,
|
|
106
|
+
`--user-data-dir=${userDataDir}`,
|
|
107
|
+
'about:blank' // Open with a blank page
|
|
108
|
+
];
|
|
109
|
+
const child = spawn(chromePath, args, { detached: true, stdio: "ignore" });
|
|
110
|
+
child.unref();
|
|
93
111
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
// Wait for DevTools to come up
|
|
113
|
+
const deadline = Date.now() + 20000;
|
|
114
|
+
while (Date.now() < deadline) {
|
|
115
|
+
if (await devtoolsAvailable()) return;
|
|
116
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
117
|
+
}
|
|
118
|
+
throw new Error("Chrome did not become available on DevTools port; check CHROME_PATH/port/profile.");
|
|
119
|
+
} finally {
|
|
120
|
+
chromeLaunchPromise = null;
|
|
121
|
+
}
|
|
122
|
+
})();
|
|
123
|
+
|
|
124
|
+
return await chromeLaunchPromise;
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
async function resolveWSEndpoint() {
|
|
@@ -173,9 +197,17 @@ async function fetchPage({
|
|
|
173
197
|
}
|
|
174
198
|
}
|
|
175
199
|
|
|
176
|
-
//
|
|
200
|
+
// Try to reuse existing pages first (when Chrome opened with profile)
|
|
177
201
|
if (!page) {
|
|
178
|
-
|
|
202
|
+
const pages = await browser.pages();
|
|
203
|
+
// Find an existing page that's not being used
|
|
204
|
+
if (pages.length > 0) {
|
|
205
|
+
// Use the first available page (usually the blank tab Chrome opens with)
|
|
206
|
+
page = pages[0];
|
|
207
|
+
} else {
|
|
208
|
+
// Create new tab if no existing pages
|
|
209
|
+
page = await browser.newPage();
|
|
210
|
+
}
|
|
179
211
|
}
|
|
180
212
|
|
|
181
213
|
let shouldKeepOpen = keepPageOpen || page === lastKeptPage;
|