browser-debug-mcp-bridge 1.9.0 → 1.10.0
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 +11 -0
- package/apps/mcp-server/dist/db/automation-repository.js +199 -0
- package/apps/mcp-server/dist/db/automation-repository.js.map +1 -0
- package/apps/mcp-server/dist/db/connection.js +1 -5
- package/apps/mcp-server/dist/db/connection.js.map +1 -1
- package/apps/mcp-server/dist/db/events-repository.js +18 -1
- package/apps/mcp-server/dist/db/events-repository.js.map +1 -1
- package/apps/mcp-server/dist/db/index.js +2 -0
- package/apps/mcp-server/dist/db/index.js.map +1 -1
- package/apps/mcp-server/dist/db/migrations.js +93 -0
- package/apps/mcp-server/dist/db/migrations.js.map +1 -1
- package/apps/mcp-server/dist/db/schema.js +59 -1
- package/apps/mcp-server/dist/db/schema.js.map +1 -1
- package/apps/mcp-server/dist/main.js +36 -2
- package/apps/mcp-server/dist/main.js.map +1 -1
- package/apps/mcp-server/dist/mcp/server.js +1903 -15
- package/apps/mcp-server/dist/mcp/server.js.map +1 -1
- package/apps/mcp-server/dist/mcp-bridge.js +46 -3
- package/apps/mcp-server/dist/mcp-bridge.js.map +1 -1
- package/apps/mcp-server/dist/runtime-paths.js +33 -0
- package/apps/mcp-server/dist/runtime-paths.js.map +1 -0
- package/apps/mcp-server/dist/websocket/messages.js +8 -0
- package/apps/mcp-server/dist/websocket/messages.js.map +1 -1
- package/package.json +5 -1
- package/scripts/mcp-start.cjs +201 -11
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { pathToFileURL } from 'url';
|
|
2
2
|
import { createMCPServer } from './mcp/server.js';
|
|
3
|
+
import { initializeDatabase, getConnection } from './db/index.js';
|
|
3
4
|
let stopServerFn = null;
|
|
4
5
|
let getWebSocketManager = null;
|
|
5
6
|
let isShuttingDown = false;
|
|
7
|
+
const ATTACH_EXISTING_BRIDGE = process.env.MCP_ATTACH_EXISTING_BRIDGE === '1';
|
|
8
|
+
const BRIDGE_HTTP_BASE_URL = process.env.MCP_ATTACH_HTTP_BASE_URL || 'http://127.0.0.1:8065';
|
|
6
9
|
function ensureWebSocketManager() {
|
|
7
10
|
if (!getWebSocketManager) {
|
|
8
11
|
throw new Error('WebSocket manager resolver is not initialized yet.');
|
|
@@ -20,6 +23,17 @@ async function bootstrapMainRuntime() {
|
|
|
20
23
|
stopServerFn = mainRuntime.stopServer;
|
|
21
24
|
getWebSocketManager = () => mainRuntime.wsManager;
|
|
22
25
|
}
|
|
26
|
+
async function ensureAttachableRuntime() {
|
|
27
|
+
initializeDatabase(getConnection().db);
|
|
28
|
+
}
|
|
29
|
+
async function fetchBridgeJson(pathname, init) {
|
|
30
|
+
const response = await fetch(`${BRIDGE_HTTP_BASE_URL}${pathname}`, init);
|
|
31
|
+
const payload = await response.json();
|
|
32
|
+
if (!response.ok || payload.ok === false) {
|
|
33
|
+
throw new Error(typeof payload.error === 'string' ? payload.error : `Bridge request failed: ${pathname}`);
|
|
34
|
+
}
|
|
35
|
+
return payload;
|
|
36
|
+
}
|
|
23
37
|
function writeStderr(message) {
|
|
24
38
|
process.stderr.write(`${message}\n`);
|
|
25
39
|
}
|
|
@@ -48,15 +62,39 @@ function registerLifecycleGuards() {
|
|
|
48
62
|
process.on('disconnect', () => shutdownFromHostDisconnect('parent-disconnect'));
|
|
49
63
|
}
|
|
50
64
|
async function startBridge() {
|
|
51
|
-
|
|
65
|
+
if (ATTACH_EXISTING_BRIDGE) {
|
|
66
|
+
await ensureAttachableRuntime();
|
|
67
|
+
writeStderr(`[MCPServer][Bridge] Attaching MCP stdio to existing bridge runtime at ${BRIDGE_HTTP_BASE_URL}.`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
await bootstrapMainRuntime();
|
|
71
|
+
}
|
|
52
72
|
const runtime = createMCPServer({}, {
|
|
53
73
|
captureClient: {
|
|
54
74
|
execute: async (sessionId, command, payload, timeoutMs) => {
|
|
75
|
+
if (ATTACH_EXISTING_BRIDGE) {
|
|
76
|
+
const response = await fetchBridgeJson('/internal/capture-command', {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
body: JSON.stringify({ sessionId, command, payload, timeoutMs }),
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
ok: response.ok === true,
|
|
83
|
+
payload: response.payload && typeof response.payload === 'object' && !Array.isArray(response.payload)
|
|
84
|
+
? response.payload
|
|
85
|
+
: {},
|
|
86
|
+
truncated: response.truncated === true,
|
|
87
|
+
error: typeof response.error === 'string' ? response.error : undefined,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
55
90
|
const manager = ensureWebSocketManager();
|
|
56
91
|
return manager.sendCaptureCommand(sessionId, command, payload, timeoutMs);
|
|
57
92
|
},
|
|
58
93
|
},
|
|
59
94
|
getSessionConnectionState: (sessionId) => {
|
|
95
|
+
if (ATTACH_EXISTING_BRIDGE) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
60
98
|
const manager = ensureWebSocketManager();
|
|
61
99
|
const state = manager.getSessionConnectionState(sessionId);
|
|
62
100
|
if (!state) {
|
|
@@ -72,8 +110,13 @@ async function startBridge() {
|
|
|
72
110
|
},
|
|
73
111
|
});
|
|
74
112
|
await runtime.start();
|
|
75
|
-
|
|
76
|
-
|
|
113
|
+
if (ATTACH_EXISTING_BRIDGE) {
|
|
114
|
+
writeStderr('[MCPServer][Bridge] Ready: MCP stdio attached to existing HTTP/WebSocket ingest runtime.');
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
writeStderr('[MCPServer][Bridge] Ready: MCP stdio connected and HTTP/WebSocket ingest is active.');
|
|
118
|
+
writeStderr('[MCPServer][Bridge] Health check: http://127.0.0.1:8065/health');
|
|
119
|
+
}
|
|
77
120
|
writeStderr('[MCPServer][Bridge] Next steps:');
|
|
78
121
|
writeStderr('[MCPServer][Bridge] 1) Start a session in the Chrome extension');
|
|
79
122
|
writeStderr('[MCPServer][Bridge] 2) Ask your MCP client to call list_sessions');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-bridge.js","sourceRoot":"","sources":["../src/mcp-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,eAAe,EAAuB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-bridge.js","sourceRoot":"","sources":["../src/mcp-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
|
+
const APP_RUNTIME_DIR = 'browser-debug-mcp-bridge';
|
|
4
|
+
export function getRuntimeDataDir() {
|
|
5
|
+
const explicitDataDir = process.env.DATA_DIR?.trim();
|
|
6
|
+
if (explicitDataDir) {
|
|
7
|
+
return resolve(explicitDataDir);
|
|
8
|
+
}
|
|
9
|
+
const home = process.env.HOME || homedir();
|
|
10
|
+
if (process.platform === 'win32') {
|
|
11
|
+
const appDataRoot = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
12
|
+
if (appDataRoot) {
|
|
13
|
+
return resolve(appDataRoot, APP_RUNTIME_DIR);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (process.platform === 'darwin' && home) {
|
|
17
|
+
return resolve(home, 'Library', 'Application Support', APP_RUNTIME_DIR);
|
|
18
|
+
}
|
|
19
|
+
if (process.env.XDG_STATE_HOME) {
|
|
20
|
+
return resolve(process.env.XDG_STATE_HOME, APP_RUNTIME_DIR);
|
|
21
|
+
}
|
|
22
|
+
if (process.env.XDG_DATA_HOME) {
|
|
23
|
+
return resolve(process.env.XDG_DATA_HOME, APP_RUNTIME_DIR);
|
|
24
|
+
}
|
|
25
|
+
if (home) {
|
|
26
|
+
return resolve(home, '.local', 'share', APP_RUNTIME_DIR);
|
|
27
|
+
}
|
|
28
|
+
return resolve(process.cwd(), '.browser-debug-mcp-bridge');
|
|
29
|
+
}
|
|
30
|
+
export function getDatabasePath() {
|
|
31
|
+
return join(getRuntimeDataDir(), 'browser-debug.db');
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=runtime-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-paths.js","sourceRoot":"","sources":["../src/runtime-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,eAAe,GAAG,0BAA0B,CAAC;AAEnD,MAAM,UAAU,iBAAiB;IAC/B,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrD,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;IAE3C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QACpE,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,OAAO,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,eAAe,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,2BAA2B,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,iBAAiB,EAAE,EAAE,kBAAkB,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -13,6 +13,11 @@ export const EventTypeSchema = z.enum([
|
|
|
13
13
|
'blur',
|
|
14
14
|
'keydown',
|
|
15
15
|
'ui_snapshot',
|
|
16
|
+
'automation_requested',
|
|
17
|
+
'automation_started',
|
|
18
|
+
'automation_succeeded',
|
|
19
|
+
'automation_failed',
|
|
20
|
+
'automation_stopped',
|
|
16
21
|
'custom',
|
|
17
22
|
]);
|
|
18
23
|
export const WebSocketMessageTypeSchema = z.enum([
|
|
@@ -33,8 +38,11 @@ export const CaptureCommandSchema = z.enum([
|
|
|
33
38
|
'CAPTURE_DOM_DOCUMENT',
|
|
34
39
|
'CAPTURE_COMPUTED_STYLES',
|
|
35
40
|
'CAPTURE_LAYOUT_METRICS',
|
|
41
|
+
'CAPTURE_PAGE_STATE',
|
|
36
42
|
'CAPTURE_UI_SNAPSHOT',
|
|
37
43
|
'CAPTURE_GET_LIVE_CONSOLE_LOGS',
|
|
44
|
+
'SET_VIEWPORT',
|
|
45
|
+
'EXECUTE_UI_ACTION',
|
|
38
46
|
]);
|
|
39
47
|
export const BaseWebSocketMessageSchema = z.object({
|
|
40
48
|
type: WebSocketMessageTypeSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/websocket/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC;IACpC,YAAY;IACZ,SAAS;IACT,OAAO;IACP,SAAS;IACT,OAAO;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,MAAM;IACN,SAAS;IACT,aAAa;IACb,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,IAAI,CAAC;IAC/C,MAAM;IACN,MAAM;IACN,OAAO;IACP,aAAa;IACb,eAAe;IACf,eAAe;IACf,gBAAgB;IAChB,aAAa;IACb,iBAAiB;IACjB,gBAAgB;IAChB,OAAO;CACR,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,qBAAqB;IACrB,sBAAsB;IACtB,yBAAyB;IACzB,wBAAwB;IACxB,qBAAqB;IACrB,+BAA+B;
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/websocket/messages.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-debug-mcp-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Chrome Extension + Node.js MCP server for browser debugging",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"verify": "pnpm typecheck && pnpm lint && pnpm test && pnpm build && pnpm docs:ci && pnpm mcp:check-stdio-guard",
|
|
27
27
|
"serve": "nx serve mcp-server",
|
|
28
28
|
"mcp:start": "node scripts/mcp-start.cjs",
|
|
29
|
+
"mcp:diagnose": "node tools/mcp/diagnose-bridge.mjs",
|
|
30
|
+
"mcp:doctor": "node tools/mcp/diagnose-bridge.mjs",
|
|
31
|
+
"mcp:doctor:json": "node tools/mcp/diagnose-bridge.mjs --json",
|
|
32
|
+
"mcp:smoke": "node tools/mcp/diagnose-bridge.mjs --smoke",
|
|
29
33
|
"build:mcp-runtime": "pnpm -C apps/mcp-server build",
|
|
30
34
|
"prepack": "pnpm run build:mcp-runtime",
|
|
31
35
|
"docs:dev": "nx serve docs",
|
package/scripts/mcp-start.cjs
CHANGED
|
@@ -5,8 +5,10 @@ const { dirname, join, resolve } = require('node:path');
|
|
|
5
5
|
const { createRequire } = require('node:module');
|
|
6
6
|
const net = require('node:net');
|
|
7
7
|
const http = require('node:http');
|
|
8
|
+
const { homedir } = require('node:os');
|
|
8
9
|
|
|
9
10
|
const repoRoot = resolve(__dirname, '..');
|
|
11
|
+
const runtimeDirName = 'browser-debug-mcp-bridge';
|
|
10
12
|
const packageJson = join(repoRoot, 'package.json');
|
|
11
13
|
const mcpBridgeDistEntry = join(repoRoot, 'apps', 'mcp-server', 'dist', 'mcp-bridge.js');
|
|
12
14
|
const mainServerDistEntry = join(repoRoot, 'apps', 'mcp-server', 'dist', 'main.js');
|
|
@@ -22,10 +24,45 @@ const localRequire = createRequire(join(repoRoot, 'package.json'));
|
|
|
22
24
|
const supportsColor = Boolean(process.stderr.isTTY) && !process.env.NO_COLOR;
|
|
23
25
|
const greenBackground = '\x1b[42m\x1b[30m';
|
|
24
26
|
const ansiReset = '\x1b[0m';
|
|
25
|
-
const
|
|
27
|
+
const resolvedDataDir = resolveRuntimeDataDir();
|
|
28
|
+
const launchLockPath = join(resolvedDataDir, '.mcp-start.lock');
|
|
26
29
|
|
|
27
30
|
let launchLockHeld = false;
|
|
28
31
|
|
|
32
|
+
function resolveRuntimeDataDir() {
|
|
33
|
+
const explicitDataDir = process.env.DATA_DIR && process.env.DATA_DIR.trim();
|
|
34
|
+
if (explicitDataDir) {
|
|
35
|
+
return resolve(explicitDataDir);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const home = process.env.HOME || homedir();
|
|
39
|
+
|
|
40
|
+
if (process.platform === 'win32') {
|
|
41
|
+
const appDataRoot = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
42
|
+
if (appDataRoot) {
|
|
43
|
+
return resolve(appDataRoot, runtimeDirName);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (process.platform === 'darwin' && home) {
|
|
48
|
+
return resolve(home, 'Library', 'Application Support', runtimeDirName);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (process.env.XDG_STATE_HOME) {
|
|
52
|
+
return resolve(process.env.XDG_STATE_HOME, runtimeDirName);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (process.env.XDG_DATA_HOME) {
|
|
56
|
+
return resolve(process.env.XDG_DATA_HOME, runtimeDirName);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (home) {
|
|
60
|
+
return resolve(home, '.local', 'share', runtimeDirName);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return resolve(process.cwd(), '.browser-debug-mcp-bridge');
|
|
64
|
+
}
|
|
65
|
+
|
|
29
66
|
function resolveRuntimePath(specifier) {
|
|
30
67
|
try {
|
|
31
68
|
return localRequire.resolve(specifier);
|
|
@@ -181,6 +218,115 @@ function releaseLaunchLock(lockPath) {
|
|
|
181
218
|
}
|
|
182
219
|
}
|
|
183
220
|
|
|
221
|
+
function clearLaunchLockForPid(lockPath, pid) {
|
|
222
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const existing = readLaunchLock(lockPath);
|
|
228
|
+
const lockPid = Number(existing && existing.pid);
|
|
229
|
+
if (lockPid !== pid) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
unlinkSync(lockPath);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (error && error.code !== 'ENOENT') {
|
|
235
|
+
process.stderr.write(`[mcp-start] Warning: failed to clear startup lock ${lockPath}.\n`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isStandaloneLauncherCommand(command) {
|
|
241
|
+
const normalized = String(command || '').toLowerCase();
|
|
242
|
+
return (
|
|
243
|
+
(normalized.includes('scripts\\mcp-start.cjs') || normalized.includes('scripts/mcp-start.cjs'))
|
|
244
|
+
&& normalized.includes('--standalone')
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function tryRecoverLockedStandaloneForMcpStdio(lockPath) {
|
|
249
|
+
if (standalone || dryRun || stopRequested) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const existing = readLaunchLock(lockPath);
|
|
254
|
+
const lockPid = Number(existing && existing.pid);
|
|
255
|
+
if (!Number.isInteger(lockPid) || lockPid <= 0 || lockPid === process.pid) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!isProcessAlive(lockPid) || !isStandaloneLauncherCommand(existing && existing.command)) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
process.stderr.write(
|
|
264
|
+
`[mcp-start] Replacing running standalone launcher (pid ${lockPid}) so MCP stdio can own the bridge runtime.\n`,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (!terminateProcess(lockPid)) {
|
|
268
|
+
process.stderr.write(`[mcp-start] Failed to terminate standalone launcher ${lockPid}.\n`);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
273
|
+
await delay(200);
|
|
274
|
+
if (!isProcessAlive(lockPid)) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (isProcessAlive(lockPid)) {
|
|
280
|
+
process.stderr.write(
|
|
281
|
+
`[mcp-start] Standalone launcher ${lockPid} did not exit after termination request.\n`,
|
|
282
|
+
);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
clearLaunchLockForPid(lockPath, lockPid);
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function tryReplaceExistingBridgeForMcpStdio(port) {
|
|
291
|
+
if (standalone || dryRun || stopRequested) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const endpointLooksLikeBridge = await isBridgeHttpEndpoint(port);
|
|
296
|
+
if (!endpointLooksLikeBridge) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const listenerPids = getListeningPids(port).filter((pid) => pid !== process.pid);
|
|
301
|
+
if (listenerPids.length === 0) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
process.stderr.write(
|
|
306
|
+
`[mcp-start] Replacing existing Browser Debug MCP Bridge listener(s) on port ${port} so MCP stdio can own the runtime.\n`,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
for (const pid of listenerPids) {
|
|
310
|
+
if (!terminateProcess(pid)) {
|
|
311
|
+
process.stderr.write(`[mcp-start] Failed to terminate bridge listener ${pid} on port ${port}.\n`);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
317
|
+
await delay(200);
|
|
318
|
+
const inUse = await isPortInUse(port);
|
|
319
|
+
if (!inUse) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
process.stderr.write(
|
|
325
|
+
`[mcp-start] Existing bridge listeners stopped, but port ${port} is still in use.\n`,
|
|
326
|
+
);
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
|
|
184
330
|
function getStartupTimeoutMs() {
|
|
185
331
|
const timeoutMs = Number(process.env.MCP_STARTUP_TIMEOUT_MS || '15000');
|
|
186
332
|
if (!Number.isFinite(timeoutMs) || timeoutMs < 1000) {
|
|
@@ -264,7 +410,7 @@ function getWindowsProcessCommandLine(pid) {
|
|
|
264
410
|
const result = spawnSync(
|
|
265
411
|
'powershell.exe',
|
|
266
412
|
['-NoProfile', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`],
|
|
267
|
-
{ encoding: 'utf8' },
|
|
413
|
+
{ encoding: 'utf8', timeout: 2000 },
|
|
268
414
|
);
|
|
269
415
|
|
|
270
416
|
if (result.status !== 0) {
|
|
@@ -285,7 +431,16 @@ function isLikelyBridgeCommandLine(commandLine) {
|
|
|
285
431
|
}
|
|
286
432
|
|
|
287
433
|
function killWindowsProcess(pid) {
|
|
288
|
-
const
|
|
434
|
+
const stopResult = spawnSync(
|
|
435
|
+
'powershell.exe',
|
|
436
|
+
['-NoProfile', '-Command', `Stop-Process -Id ${pid} -Force`],
|
|
437
|
+
{ encoding: 'utf8', timeout: 5000 },
|
|
438
|
+
);
|
|
439
|
+
if (stopResult.status === 0) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const result = spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { encoding: 'utf8', timeout: 5000 });
|
|
289
444
|
return result.status === 0;
|
|
290
445
|
}
|
|
291
446
|
|
|
@@ -477,6 +632,7 @@ if (!existsSync(packageJson)) {
|
|
|
477
632
|
}
|
|
478
633
|
|
|
479
634
|
async function spawnRuntime(runtime, port) {
|
|
635
|
+
const attachExistingBridge = !standalone && process.env.MCP_ATTACH_EXISTING_BRIDGE === '1';
|
|
480
636
|
const nxTarget = standalone ? 'mcp-server:serve' : 'mcp-server:serve-mcp';
|
|
481
637
|
const entryScript =
|
|
482
638
|
runtime === 'dist'
|
|
@@ -506,14 +662,24 @@ async function spawnRuntime(runtime, port) {
|
|
|
506
662
|
nxBin.endsWith('.cmd') ? ['run', nxTarget] : [nxBin, 'run', nxTarget],
|
|
507
663
|
{
|
|
508
664
|
cwd: repoRoot,
|
|
509
|
-
env: {
|
|
665
|
+
env: {
|
|
666
|
+
...process.env,
|
|
667
|
+
DATA_DIR: resolvedDataDir,
|
|
668
|
+
MCP_ATTACH_EXISTING_BRIDGE: attachExistingBridge ? '1' : '',
|
|
669
|
+
MCP_ATTACH_HTTP_BASE_URL: `http://127.0.0.1:${port}`,
|
|
670
|
+
},
|
|
510
671
|
stdio: 'inherit',
|
|
511
672
|
},
|
|
512
673
|
)
|
|
513
674
|
: runtime === 'dist'
|
|
514
675
|
? spawn(process.execPath, [entryScript], {
|
|
515
676
|
cwd: repoRoot,
|
|
516
|
-
env: {
|
|
677
|
+
env: {
|
|
678
|
+
...process.env,
|
|
679
|
+
DATA_DIR: resolvedDataDir,
|
|
680
|
+
MCP_ATTACH_EXISTING_BRIDGE: attachExistingBridge ? '1' : '',
|
|
681
|
+
MCP_ATTACH_HTTP_BASE_URL: `http://127.0.0.1:${port}`,
|
|
682
|
+
},
|
|
517
683
|
stdio: 'inherit',
|
|
518
684
|
})
|
|
519
685
|
: spawn(
|
|
@@ -521,7 +687,12 @@ async function spawnRuntime(runtime, port) {
|
|
|
521
687
|
tsxCli.endsWith('.cmd') ? [entryScript] : [tsxCli, entryScript],
|
|
522
688
|
{
|
|
523
689
|
cwd: repoRoot,
|
|
524
|
-
env: {
|
|
690
|
+
env: {
|
|
691
|
+
...process.env,
|
|
692
|
+
DATA_DIR: resolvedDataDir,
|
|
693
|
+
MCP_ATTACH_EXISTING_BRIDGE: attachExistingBridge ? '1' : '',
|
|
694
|
+
MCP_ATTACH_HTTP_BASE_URL: `http://127.0.0.1:${port}`,
|
|
695
|
+
},
|
|
525
696
|
stdio: 'inherit',
|
|
526
697
|
},
|
|
527
698
|
);
|
|
@@ -604,7 +775,9 @@ async function spawnRuntime(runtime, port) {
|
|
|
604
775
|
startupFinished = true;
|
|
605
776
|
const startedMessage = standalone
|
|
606
777
|
? `[mcp-start] Started Browser Debug MCP Bridge (runtime: ${runtime}, mode: standalone). Keep this terminal open.`
|
|
607
|
-
:
|
|
778
|
+
: attachExistingBridge
|
|
779
|
+
? `[mcp-start] Attached MCP stdio to existing Browser Debug MCP Bridge (runtime: ${runtime}, mode: attach).`
|
|
780
|
+
: `[mcp-start] Started Browser Debug MCP Bridge (runtime: ${runtime}, mode: mcp-stdio).`;
|
|
608
781
|
process.stderr.write(`${supportsColor ? `${greenBackground}${startedMessage}${ansiReset}` : startedMessage}\n`);
|
|
609
782
|
if (!standalone && process.stdin.isTTY) {
|
|
610
783
|
process.stderr.write(
|
|
@@ -612,6 +785,13 @@ async function spawnRuntime(runtime, port) {
|
|
|
612
785
|
'Use --standalone for manual keep-alive testing.\n',
|
|
613
786
|
);
|
|
614
787
|
}
|
|
788
|
+
|
|
789
|
+
if (standalone) {
|
|
790
|
+
await new Promise((resolve) => {
|
|
791
|
+
child.once('exit', () => resolve());
|
|
792
|
+
child.once('close', () => resolve());
|
|
793
|
+
});
|
|
794
|
+
}
|
|
615
795
|
}
|
|
616
796
|
|
|
617
797
|
async function main() {
|
|
@@ -626,12 +806,22 @@ async function main() {
|
|
|
626
806
|
return;
|
|
627
807
|
}
|
|
628
808
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
809
|
+
let attachExistingBridge = false;
|
|
810
|
+
if (!standalone && !dryRun) {
|
|
811
|
+
attachExistingBridge = await isBridgeHttpEndpoint(port);
|
|
812
|
+
if (attachExistingBridge) {
|
|
813
|
+
process.env.MCP_ATTACH_EXISTING_BRIDGE = '1';
|
|
814
|
+
process.stderr.write(
|
|
815
|
+
`[mcp-start] Found existing Browser Debug MCP Bridge on port ${port}; attaching MCP stdio instead of replacing it.\n`,
|
|
816
|
+
);
|
|
817
|
+
}
|
|
632
818
|
}
|
|
633
819
|
|
|
634
|
-
if (
|
|
820
|
+
if (!dryRun && !attachExistingBridge) {
|
|
821
|
+
await tryRecoverLockedStandaloneForMcpStdio(launchLockPath);
|
|
822
|
+
await tryReplaceExistingBridgeForMcpStdio(port);
|
|
823
|
+
acquireLaunchLock(launchLockPath);
|
|
824
|
+
process.on('exit', () => releaseLaunchLock(launchLockPath));
|
|
635
825
|
let inUse = await isPortInUse(port);
|
|
636
826
|
if (inUse) {
|
|
637
827
|
const recovered = await tryRecoverStaleBridgeOnWindowsPort(port);
|