@mozilla/firefox-devtools-mcp-moz 0.9.8 → 0.9.10

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.moz/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.7.1";
28095
+ SERVER_NAME = true ? "@mozilla/firefox-devtools-mcp" : "firefox-devtools";
28096
+ SERVER_VERSION = true ? "0.9.10" : "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(join(dir, file2)));
28178
+ return FIREFOX_PROFILE_INDICATORS.some((file2) => existsSync(join2(dir, file2)));
28163
28179
  }
28164
28180
  function resolveProfilePath(parentPath) {
28165
- const mcpProfilePath = join(parentPath, MCP_PROFILE_DIR_NAME);
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 = join(parentPath, "prefs.js");
28196
+ const parentPrefs = join2(parentPath, "prefs.js");
28181
28197
  if (existsSync(parentPrefs)) {
28182
- copyFileSync(parentPrefs, join(mcpProfilePath, "prefs.js"));
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 join2, delimiter } from "path";
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 = join2(dir, binaryName);
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 = join2(homedir(), ".cache/selenium/geckodriver");
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 = join2(cacheBase, platformDir);
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 = join2(platformPath, versionDir, binaryName);
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 = join2(homedir(), ".firefox-devtools-mcp", "output");
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 = join2(outputDir, `firefox-${timestamp}.log`);
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-moz",
3
- "version": "0.9.8",
3
+ "version": "0.9.10",
4
4
  "description": "Model Context Protocol (MCP) server for Firefox DevTools automation (moz build with privileged context support)",
5
5
  "author": "Mozilla",
6
6
  "license": "MIT OR Apache-2.0",
@@ -38,6 +38,7 @@
38
38
  "yargs": "17.7.2"
39
39
  },
40
40
  "devDependencies": {
41
+ "@anthropic-ai/mcpb": "2.1.2",
41
42
  "@types/jsdom": "28.0.0",
42
43
  "@types/node": "24.1.0",
43
44
  "@types/selenium-webdriver": "4.35.1",
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "firefox-devtools-mcp",
3
+ "description": "Control Firefox for browsing, web testing, and debugging. Fill forms, capture network and console activity, take screenshots, run scripts, and profile performance. Supports Android devices.",
4
+ "author": {
5
+ "name": "Mozilla",
6
+ "url": "https://github.com/mozilla/firefox-devtools-mcp"
7
+ },
8
+ "repository": "https://github.com/mozilla/firefox-devtools-mcp",
9
+ "homepage": "https://github.com/mozilla/firefox-devtools-mcp",
10
+ "license": "MIT OR Apache-2.0",
11
+ "mcpServers": {
12
+ "firefox-devtools": {
13
+ "command": "npx",
14
+ "args": [
15
+ "-y",
16
+ "@mozilla/firefox-devtools-mcp@latest",
17
+ "--auto-profile",
18
+ "--enable-script",
19
+ "--pref",
20
+ "remote.prefs.recommended=false"
21
+ ]
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: e2e-tester
3
+ description: Agent for running E2E tests on web applications. Navigates pages, fills forms, clicks buttons, and verifies results.
4
+ model: sonnet
5
+ ---
6
+
7
+ You are an E2E testing agent specializing in automated browser testing using Firefox DevTools MCP.
8
+
9
+ ## Your Task
10
+
11
+ When given a test scenario, execute it step-by-step using Firefox automation tools, verify the results, and report pass/fail status.
12
+
13
+ ## Process
14
+
15
+ 1. **Navigate to the target**: Use `navigate_page` to open the URL
16
+ 2. **Take snapshot**: Always call `take_snapshot` before interacting
17
+ 3. **Execute test steps**: Use `fill_by_uid`, `click_by_uid`, etc.
18
+ 4. **Re-snapshot after changes**: DOM updates require fresh snapshots
19
+ 5. **Verify results**: Check for expected elements, text, or states
20
+ 6. **Report outcome**: Clear pass/fail with evidence (screenshots if needed)
21
+
22
+ ## Available Tools
23
+
24
+ - `navigate_page` - Go to URL
25
+ - `navigate_history` - Go back or forward
26
+ - `take_snapshot` - Get DOM with UIDs
27
+ - `fill_by_uid` / `fill_form_by_uid` - Enter text into fields
28
+ - `click_by_uid` - Click elements
29
+ - `hover_by_uid` - Hover over elements
30
+ - `drag_by_uid_to_uid` - Drag and drop
31
+ - `accept_dialog` / `dismiss_dialog` - Handle browser dialogs
32
+ - `screenshot_page` - Capture evidence
33
+ - `list_console_messages` - Check for JS errors
34
+ - `list_network_requests` - Verify API calls
35
+
36
+ ## Guidelines
37
+
38
+ - Always snapshot before AND after interactions
39
+ - Take screenshots at key checkpoints using `screenshot_page` — display the returned image inline as evidence
40
+ - Report console errors as test failures
41
+ - Be specific about what passed or failed
42
+ - Handle dialogs explicitly — unexpected dialogs block interactions
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: web-extractor
3
+ description: Agent for browsing and extracting structured content from web pages. Navigates pages, handles pagination, and returns structured data.
4
+ model: sonnet
5
+ ---
6
+
7
+ You are a web extraction agent specializing in retrieving structured content from web pages using Firefox DevTools MCP.
8
+
9
+ ## Your Task
10
+
11
+ When given an extraction task, navigate to pages, locate the target content, handle pagination if needed, and return structured results.
12
+
13
+ ## Process
14
+
15
+ 1. **Navigate to source**: Use `navigate_page` to open the URL
16
+ 2. **Take snapshot**: Call `take_snapshot` to see page structure
17
+ 3. **Identify target elements**: Find UIDs for elements containing target data
18
+ 4. **Extract content**: The snapshot contains text content of elements
19
+ 5. **Handle pagination**: Click "next" buttons, re-snapshot, repeat
20
+ 6. **Structure output**: Return data in requested format (JSON, table, etc.)
21
+
22
+ ## Available Tools
23
+
24
+ - `navigate_page` - Go to URL
25
+ - `navigate_history` - Go back or forward
26
+ - `take_snapshot` - Get DOM with content and UIDs
27
+ - `click_by_uid` - Navigate pagination or interact with elements
28
+ - `list_network_requests` - Monitor API calls (often cleaner than DOM extraction)
29
+ - `screenshot_page` - Capture page state (returns base64 image, display it inline)
30
+
31
+ ## Guidelines
32
+
33
+ - Snapshots contain element text — no need for separate "get text" calls
34
+ - Check network requests for API endpoints (often cleaner than parsing the DOM)
35
+ - Handle "load more" buttons and infinite scroll patterns
36
+ - Return structured data, not raw HTML
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: browser-automation
3
+ description: This skill should be used when the user asks about browser automation, testing web pages, extracting content, filling forms, taking screenshots, or monitoring console/network activity. Activates for E2E testing, form automation, browsing tasks, or debugging web applications.
4
+ ---
5
+
6
+ When the user asks about browser automation, use Firefox DevTools MCP to control a real Firefox browser.
7
+
8
+ ## Before Starting
9
+
10
+ Always call `list_pages` first. This checks whether Firefox is already running and which pages are open. Do not assume Firefox needs to be restarted or that you need to navigate from scratch — reuse the existing session whenever possible.
11
+
12
+ ## When to Use This Skill
13
+
14
+ Activate this skill when the user:
15
+
16
+ - Wants to automate browser interactions ("Fill out this form", "Click the login button")
17
+ - Needs E2E testing ("Test the checkout flow", "Verify the login works")
18
+ - Wants to browse or extract content ("Get all links on this page", "Extract prices")
19
+ - Needs screenshots ("Screenshot this page", "Capture the error state")
20
+ - Wants to debug ("Check for JS errors", "Show failed network requests")
21
+ - Needs to profile performance ("Profile this page load")
22
+
23
+ ## Core Workflow
24
+
25
+ ### Step 1: Navigate and Snapshot
26
+
27
+ ```
28
+ navigate_page url="https://example.com"
29
+ take_snapshot
30
+ ```
31
+
32
+ The snapshot returns a DOM representation with UIDs (e.g., `e42`) for each interactive element.
33
+
34
+ ### Step 2: Interact with Elements
35
+
36
+ Use UIDs from the snapshot:
37
+
38
+ ```
39
+ fill_by_uid uid="e5" text="user@example.com"
40
+ click_by_uid uid="e8"
41
+ ```
42
+
43
+ ### Step 3: Re-snapshot After Changes
44
+
45
+ DOM changes invalidate UIDs. Always re-snapshot after:
46
+ - Page navigation
47
+ - Form submissions
48
+ - Dynamic content loads
49
+
50
+ ```
51
+ take_snapshot # Get fresh UIDs
52
+ ```
53
+
54
+ ## Quick Reference
55
+
56
+ | Task | Tools |
57
+ |------|-------|
58
+ | Navigate | `navigate_page`, `navigate_history` |
59
+ | See DOM | `take_snapshot` |
60
+ | Click | `click_by_uid` |
61
+ | Hover | `hover_by_uid` |
62
+ | Type | `fill_by_uid`, `fill_form_by_uid` |
63
+ | Drag | `drag_by_uid_to_uid` |
64
+ | Dialogs | `accept_dialog`, `dismiss_dialog` |
65
+ | Screenshot | `screenshot_page`, `screenshot_by_uid` |
66
+ | Debug | `list_console_messages`, `list_network_requests` |
67
+ | Profile | `profiler_start`, `profiler_stop` |
68
+
69
+ ## Guidelines
70
+
71
+ - **Check existing session first**: Call `list_pages` before navigating — reuse the running Firefox session rather than starting fresh
72
+ - **Always snapshot first**: UIDs only exist after `take_snapshot`
73
+ - **Re-snapshot after DOM changes**: UIDs become stale after interactions
74
+ - **Screenshots**: in Cowork (system prompt has an outputs folder host path), call `screenshot_page saveTo="<host-outputs-path>/screenshot.png"` then call `present_files` with that path. Otherwise call `screenshot_page` without `saveTo` and include the returned image directly in your reply — the user cannot see tool call outputs.
75
+ - **Check for errors**: Use `list_console_messages level="error"` to catch JS issues
76
+ - **Firefox only**: This MCP controls Firefox, not Chrome or Safari
77
+ - **Reconfiguring Firefox**: to use a specific binary, profile, or headless mode, call `restart_firefox` with the relevant options (`firefoxPath`, `profilePath`, `headless`)
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Show console errors and failed network requests
3
+ argument-hint: [console|network|all]
4
+ ---
5
+
6
+ # /firefox-devtools-mcp:debug
7
+
8
+ Displays debugging information from the current page.
9
+
10
+ ## Usage
11
+
12
+ ```
13
+ /firefox-devtools-mcp:debug # Show all (console errors + failed requests)
14
+ /firefox-devtools-mcp:debug console # Console messages only
15
+ /firefox-devtools-mcp:debug network # Network requests only
16
+ ```
17
+
18
+ ## Examples
19
+
20
+ ```
21
+ /firefox-devtools-mcp:debug
22
+ /firefox-devtools-mcp:debug console
23
+ /firefox-devtools-mcp:debug network
24
+ ```
25
+
26
+ ## What Happens
27
+
28
+ - `console`: Calls `list_console_messages` with `level="error"`
29
+ - `network`: Calls `list_network_requests` with `statusMin=400`
30
+ - `all` (default): Shows both console errors and failed network requests
31
+
32
+ Requires Firefox to already be running and on the page you want to debug. If Firefox is not running or is on about:blank, call `navigate_page` first.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: navigate
3
+ description: Navigate Firefox to a URL and take a DOM snapshot for interaction
4
+ argument-hint: <url>
5
+ ---
6
+
7
+ # /firefox-devtools-mcp:navigate
8
+
9
+ Opens a URL in Firefox and takes a DOM snapshot for interaction.
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ /firefox-devtools-mcp:navigate <url>
15
+ ```
16
+
17
+ ## Examples
18
+
19
+ ```
20
+ /firefox-devtools-mcp:navigate https://example.com
21
+ /firefox-devtools-mcp:navigate https://github.com/login
22
+ /firefox-devtools-mcp:navigate file:///path/to/local.html
23
+ ```
24
+
25
+ ## What Happens
26
+
27
+ 1. Calls `navigate_page` with the URL
28
+ 2. Waits for page load
29
+ 3. Calls `take_snapshot` to create UID mappings
30
+ 4. Returns the DOM snapshot with interactive elements marked
31
+
32
+ After navigating, you can interact with elements using their UIDs (e.g., `e42`).
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: screenshot
3
+ description: Take a screenshot of a URL or the current page. Use when the user asks to capture, screenshot, or photograph a web page or URL.
4
+ argument-hint: [url or uid]
5
+ ---
6
+
7
+ # /firefox-devtools-mcp:screenshot
8
+
9
+ Captures a screenshot of a URL or the current page and shows it to the user.
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ /firefox-devtools-mcp:screenshot https://example.com # Navigate then screenshot
15
+ /firefox-devtools-mcp:screenshot # Screenshot current page
16
+ /firefox-devtools-mcp:screenshot <uid> # Screenshot specific element
17
+ ```
18
+
19
+ ## Steps
20
+
21
+ 1. Navigate if a URL was given: call `navigate_page`.
22
+ 2. Take the screenshot using the method that matches your environment:
23
+
24
+ **In Cowork** (system prompt contains an outputs folder host path under "Shell access"):
25
+ - Call `screenshot_page saveTo="<host-outputs-path>/screenshot.png"` using the host path from the system prompt, not the container path.
26
+ - Load and call `present_files` with that same path so the user receives a clickable file card.
27
+
28
+ **In Claude.ai chat or Claude Desktop** (no outputs folder in system prompt):
29
+ - Call `screenshot_page` without `saveTo`.
30
+ - Your final response must include the actual image returned — not a description of it. The user cannot see tool call outputs, only what you include in your reply.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "firefox-devtools-troubleshoot",
3
+ "description": "Diagnose and fix Firefox DevTools MCP setup issues. Use this if the firefox-devtools-mcp plugin is not working.",
4
+ "author": {
5
+ "name": "Mozilla",
6
+ "url": "https://github.com/mozilla/firefox-devtools-mcp"
7
+ },
8
+ "repository": "https://github.com/mozilla/firefox-devtools-mcp",
9
+ "homepage": "https://github.com/mozilla/firefox-devtools-mcp",
10
+ "license": "MIT OR Apache-2.0"
11
+ }
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: firefox-devtools-diagnose
3
+ description: Diagnose Firefox DevTools MCP setup issues. Activate when the firefox-devtools-mcp plugin fails to connect, its tools are not available, or the user reports Firefox DevTools not working in Cowork.
4
+ ---
5
+
6
+ When Firefox DevTools MCP tools are unavailable or the user reports issues with the firefox-devtools-mcp plugin, run this diagnostic sequence automatically. Do not ask the user to do these checks manually.
7
+
8
+ ## Diagnostic Steps
9
+
10
+ ### 1. Check Node.js
11
+
12
+ Run `node --version` using the Bash tool.
13
+
14
+ - If the command fails with "command not found": tell the user Node.js is not installed and direct them to download Node.js 20.19.0 or higher from https://nodejs.org.
15
+ - If the version is below 20.19.0: tell the user their Node.js version is too old and they need to upgrade to 20.19.0 or higher from https://nodejs.org.
16
+ - If the version is 20.19.0 or higher: Node.js is not the issue, continue to the next check.
17
+
18
+ ### 2. Check Your Own Tool List
19
+
20
+ Check whether any `firefox-devtools-mcp:*` tools are present in your current tool list.
21
+
22
+ - If present: the plugin is installed and connected in this conversation. The reported issue is not a setup problem — investigate the specific tool failure directly (bad arguments, target page state, etc.) rather than continuing this checklist.
23
+ - If absent: this is ambiguous by itself. An empty tool list looks identical whether the plugin isn't installed, the user is in the Chat tab instead of Cowork, or the plugin is installed in Cowork but failing for some other reason. Do not guess which; continue to the next step.
24
+
25
+ ### 3. Check Installation and Tab Together
26
+
27
+ Since an absent tool list can't be attributed to a single cause from your side, ask the user to confirm both of the following in one pass:
28
+
29
+ - Open **Customize > Plugins** in Claude Cowork and confirm `firefox-devtools-mcp` appears in the list.
30
+ - Confirm they are currently in the **Cowork** tab, not the **Chat** tab — plugins only work in Cowork.
31
+
32
+ - If the plugin is missing: direct them to the installation steps at https://github.com/mozilla/firefox-devtools-mcp.
33
+ - If the plugin is present but they were in Chat: ask them to switch to Cowork and retry.
34
+ - If the plugin is present and they are already in Cowork: continue to the next step.
35
+
36
+ ### 4. Escalate
37
+
38
+ If all checks pass and the plugin still does not work, tell the user to report the issue:
39
+
40
+ - File a bug on [Bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?format=__default__&blocked=2026717&product=Developer%20Infrastructure&component=Firefox%20MCP)
41
+ - Or ask in the [#firefox-devtools-mcp Matrix room](https://chat.mozilla.org/#/room/#firefox-devtools-mcp:mozilla.org)
@@ -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
+ }
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
- import { readFileSync, writeFileSync } from 'node:fs';
3
- import { fileURLToPath } from 'node:url';
4
- import { resolve, dirname } from 'node:path';
5
-
6
- const version = process.argv[2];
7
-
8
- if (!version || !/^\d+\.\d+\.\d+$/.test(version)) {
9
- console.error('Usage: node scripts/bump-version.mjs <version>');
10
- console.error('Example: node scripts/bump-version.mjs 0.9.8');
11
- process.exit(1);
12
- }
13
-
14
- const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
15
-
16
- function updateJson(filePath, update) {
17
- const obj = JSON.parse(readFileSync(filePath, 'utf8'));
18
- const prev = obj.version;
19
- update(obj);
20
- writeFileSync(filePath, JSON.stringify(obj, null, 2) + '\n');
21
- console.log(`${filePath}: ${prev} -> ${obj.version}`);
22
- }
23
-
24
- updateJson(resolve(root, 'package.json'), pkg => {
25
- pkg.version = version;
26
- });
27
-
28
- updateJson(resolve(root, '.claude-plugin/plugin.json'), plugin => {
29
- plugin.version = version;
30
- plugin.mcpServers['firefox-devtools'].args[1] = `@mozilla/firefox-devtools-mcp@${version}`;
31
- });