@mozilla/firefox-devtools-mcp 0.9.9 → 0.9.12
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/dist/index.js +32 -16
- package/package.json +3 -1
- package/scripts/build-mcpb.mjs +52 -0
package/dist/index.js
CHANGED
|
@@ -39,8 +39,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
));
|
|
40
40
|
|
|
41
41
|
// src/cli.ts
|
|
42
|
+
import { createHash } from "crypto";
|
|
43
|
+
import { homedir } from "os";
|
|
44
|
+
import { join } from "path";
|
|
42
45
|
import yargs from "yargs";
|
|
43
46
|
import { hideBin } from "yargs/helpers";
|
|
47
|
+
function defaultProfileDir(firefoxPath) {
|
|
48
|
+
const base = join(homedir(), ".firefox-devtools-mcp");
|
|
49
|
+
if (!firefoxPath) {
|
|
50
|
+
return join(base, "profile");
|
|
51
|
+
}
|
|
52
|
+
const hash2 = createHash("sha1").update(firefoxPath).digest("hex").slice(0, 8);
|
|
53
|
+
return join(base, `profile-${hash2}`);
|
|
54
|
+
}
|
|
44
55
|
function parsePrefs(prefs) {
|
|
45
56
|
const result = {};
|
|
46
57
|
if (!prefs || prefs.length === 0) {
|
|
@@ -122,6 +133,11 @@ var init_cli = __esm({
|
|
|
122
133
|
type: "string",
|
|
123
134
|
description: "Path to Firefox profile directory (optional, for persistent profile)"
|
|
124
135
|
},
|
|
136
|
+
autoProfile: {
|
|
137
|
+
type: "boolean",
|
|
138
|
+
description: "Automatically use a persistent profile stored in ~/.firefox-devtools-mcp/. When --firefox-path is set, the profile is scoped to that binary so different Firefox builds stay isolated.",
|
|
139
|
+
default: (process.env.AUTO_PROFILE ?? "false") === "true"
|
|
140
|
+
},
|
|
125
141
|
firefoxArg: {
|
|
126
142
|
type: "array",
|
|
127
143
|
description: "Additional arguments for Firefox. Only applies when Firefox is launched by firefox-devtools-mcp."
|
|
@@ -28076,8 +28092,8 @@ var SERVER_NAME, SERVER_VERSION;
|
|
|
28076
28092
|
var init_constants = __esm({
|
|
28077
28093
|
"src/config/constants.ts"() {
|
|
28078
28094
|
"use strict";
|
|
28079
|
-
SERVER_NAME = "firefox-devtools";
|
|
28080
|
-
SERVER_VERSION = "0.
|
|
28095
|
+
SERVER_NAME = true ? "@mozilla/firefox-devtools-mcp" : "firefox-devtools";
|
|
28096
|
+
SERVER_VERSION = true ? "0.9.12" : "dev";
|
|
28081
28097
|
}
|
|
28082
28098
|
});
|
|
28083
28099
|
|
|
@@ -28157,12 +28173,12 @@ var init_logger = __esm({
|
|
|
28157
28173
|
|
|
28158
28174
|
// src/firefox/profile.ts
|
|
28159
28175
|
import { existsSync, mkdirSync, copyFileSync } from "fs";
|
|
28160
|
-
import { join } from "path";
|
|
28176
|
+
import { join as join2 } from "path";
|
|
28161
28177
|
function isFirefoxProfile(dir) {
|
|
28162
|
-
return FIREFOX_PROFILE_INDICATORS.some((file2) => existsSync(
|
|
28178
|
+
return FIREFOX_PROFILE_INDICATORS.some((file2) => existsSync(join2(dir, file2)));
|
|
28163
28179
|
}
|
|
28164
28180
|
function resolveProfilePath(parentPath) {
|
|
28165
|
-
const mcpProfilePath =
|
|
28181
|
+
const mcpProfilePath = join2(parentPath, MCP_PROFILE_DIR_NAME);
|
|
28166
28182
|
let warning = null;
|
|
28167
28183
|
if (isFirefoxProfile(parentPath)) {
|
|
28168
28184
|
warning = `Warning: The path "${parentPath}" looks like an existing Firefox profile.
|
|
@@ -28177,9 +28193,9 @@ function resolveProfilePath(parentPath) {
|
|
|
28177
28193
|
if (isNew) {
|
|
28178
28194
|
mkdirSync(mcpProfilePath, { recursive: true });
|
|
28179
28195
|
log(`Created MCP profile directory: ${mcpProfilePath}`);
|
|
28180
|
-
const parentPrefs =
|
|
28196
|
+
const parentPrefs = join2(parentPath, "prefs.js");
|
|
28181
28197
|
if (existsSync(parentPrefs)) {
|
|
28182
|
-
copyFileSync(parentPrefs,
|
|
28198
|
+
copyFileSync(parentPrefs, join2(mcpProfilePath, "prefs.js"));
|
|
28183
28199
|
log(` Copied prefs.js from parent profile for initial preferences.`);
|
|
28184
28200
|
}
|
|
28185
28201
|
}
|
|
@@ -28199,14 +28215,14 @@ var init_profile = __esm({
|
|
|
28199
28215
|
import { Builder, Browser, Capabilities } from "selenium-webdriver";
|
|
28200
28216
|
import firefox from "selenium-webdriver/firefox.js";
|
|
28201
28217
|
import { mkdirSync as mkdirSync2, openSync, closeSync, existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
28202
|
-
import { homedir } from "os";
|
|
28203
|
-
import { join as
|
|
28218
|
+
import { homedir as homedir2 } from "os";
|
|
28219
|
+
import { join as join3, delimiter } from "path";
|
|
28204
28220
|
function findGeckodriverInPath(binaryName) {
|
|
28205
28221
|
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
28206
28222
|
if (!dir) {
|
|
28207
28223
|
continue;
|
|
28208
28224
|
}
|
|
28209
|
-
const candidate =
|
|
28225
|
+
const candidate = join3(dir, binaryName);
|
|
28210
28226
|
if (existsSync2(candidate)) {
|
|
28211
28227
|
return candidate;
|
|
28212
28228
|
}
|
|
@@ -28214,18 +28230,18 @@ function findGeckodriverInPath(binaryName) {
|
|
|
28214
28230
|
return null;
|
|
28215
28231
|
}
|
|
28216
28232
|
function findGeckodriverInSeleniumCache(binaryName) {
|
|
28217
|
-
const cacheBase =
|
|
28233
|
+
const cacheBase = join3(homedir2(), ".cache/selenium/geckodriver");
|
|
28218
28234
|
try {
|
|
28219
28235
|
if (!existsSync2(cacheBase)) {
|
|
28220
28236
|
return null;
|
|
28221
28237
|
}
|
|
28222
28238
|
for (const platformDir of readdirSync(cacheBase)) {
|
|
28223
|
-
const platformPath =
|
|
28239
|
+
const platformPath = join3(cacheBase, platformDir);
|
|
28224
28240
|
if (!statSync(platformPath).isDirectory()) {
|
|
28225
28241
|
continue;
|
|
28226
28242
|
}
|
|
28227
28243
|
for (const versionDir of readdirSync(platformPath).sort().reverse()) {
|
|
28228
|
-
const candidate =
|
|
28244
|
+
const candidate = join3(platformPath, versionDir, binaryName);
|
|
28229
28245
|
if (existsSync2(candidate)) {
|
|
28230
28246
|
return candidate;
|
|
28231
28247
|
}
|
|
@@ -28315,10 +28331,10 @@ var init_core3 = __esm({
|
|
|
28315
28331
|
if (this.options.logFile) {
|
|
28316
28332
|
this.logFilePath = this.options.logFile;
|
|
28317
28333
|
} else if (this.options.env && Object.keys(this.options.env).length > 0) {
|
|
28318
|
-
const outputDir =
|
|
28334
|
+
const outputDir = join3(homedir2(), ".firefox-devtools-mcp", "output");
|
|
28319
28335
|
mkdirSync2(outputDir, { recursive: true });
|
|
28320
28336
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28321
|
-
this.logFilePath =
|
|
28337
|
+
this.logFilePath = join3(outputDir, `firefox-${timestamp}.log`);
|
|
28322
28338
|
}
|
|
28323
28339
|
if (this.options.env) {
|
|
28324
28340
|
for (const [key, value] of Object.entries(this.options.env)) {
|
|
@@ -33419,7 +33435,7 @@ async function getFirefox() {
|
|
|
33419
33435
|
options = {
|
|
33420
33436
|
firefoxPath: args.firefoxPath ?? void 0,
|
|
33421
33437
|
headless: args.headless,
|
|
33422
|
-
profilePath: args.profilePath ?? void 0,
|
|
33438
|
+
profilePath: args.profilePath ?? (args.autoProfile ? defaultProfileDir(args.firefoxPath) : void 0),
|
|
33423
33439
|
viewport: args.viewport ?? void 0,
|
|
33424
33440
|
args: args.firefoxArg ?? void 0,
|
|
33425
33441
|
startUrl: args.startUrl ?? void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mozilla/firefox-devtools-mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for Firefox DevTools automation",
|
|
5
5
|
"author": "Mozilla",
|
|
6
6
|
"license": "MIT OR Apache-2.0",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"build": "tsup",
|
|
16
16
|
"build:moz": "node scripts/generate-moz-package.mjs && tsup --config tsup.config.moz.ts",
|
|
17
17
|
"build:all": "npm run build && npm run build:moz",
|
|
18
|
+
"build:mcpb": "node scripts/build-mcpb.mjs",
|
|
18
19
|
"publish:moz": "node scripts/publish-moz-package.mjs",
|
|
19
20
|
"start": "node dist/index.js",
|
|
20
21
|
"setup": "node scripts/setup-mcp-config.js",
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
"yargs": "17.7.2"
|
|
76
77
|
},
|
|
77
78
|
"devDependencies": {
|
|
79
|
+
"@anthropic-ai/mcpb": "2.1.2",
|
|
78
80
|
"@types/jsdom": "28.0.0",
|
|
79
81
|
"@types/node": "24.1.0",
|
|
80
82
|
"@types/selenium-webdriver": "4.35.1",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Builds the firefox-devtools-mcp .mcpb bundle (Claude Desktop extension).
|
|
4
|
+
* Assembles a staging directory with dist/, production node_modules and
|
|
5
|
+
* manifest.json, then runs `mcpb pack` on it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync, cpSync, mkdtempSync, rmSync } from 'node:fs';
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import { resolve, dirname } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
|
|
14
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
|
+
const pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
|
16
|
+
const manifest = JSON.parse(readFileSync(resolve(root, 'manifest.json'), 'utf8'));
|
|
17
|
+
|
|
18
|
+
console.log('Building server...');
|
|
19
|
+
execSync('npm run build', { cwd: root, stdio: 'inherit' });
|
|
20
|
+
|
|
21
|
+
const stagingDir = mkdtempSync(resolve(tmpdir(), 'firefox-devtools-mcpb-'));
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
cpSync(resolve(root, 'dist'), resolve(stagingDir, 'dist'), { recursive: true });
|
|
25
|
+
for (const file of ['README.md', 'LICENSE-MIT', 'LICENSE-APACHE', 'package.json', 'package-lock.json']) {
|
|
26
|
+
cpSync(resolve(root, file), resolve(stagingDir, file));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log('Installing production dependencies...');
|
|
30
|
+
execSync('npm ci --omit=dev', { cwd: stagingDir, stdio: 'inherit' });
|
|
31
|
+
|
|
32
|
+
writeFileSync(
|
|
33
|
+
resolve(stagingDir, 'manifest.json'),
|
|
34
|
+
JSON.stringify({ ...manifest, version: pkg.version }, null, 2) + '\n'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const outDir = resolve(root, 'dist-mcpb');
|
|
38
|
+
if (!existsSync(outDir)) {
|
|
39
|
+
execSync(`mkdir -p ${outDir}`);
|
|
40
|
+
}
|
|
41
|
+
const outFile = resolve(outDir, `firefox-devtools-mcp-${pkg.version}.mcpb`);
|
|
42
|
+
|
|
43
|
+
console.log('Packing .mcpb bundle...');
|
|
44
|
+
execSync(`npx mcpb pack "${stagingDir}" "${outFile}"`, {
|
|
45
|
+
cwd: root,
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
console.log(`Written ${outFile}`);
|
|
50
|
+
} finally {
|
|
51
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
52
|
+
}
|