firefox-devtools-mcp 0.5.3 → 0.6.1
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 +40 -10
- package/dist/snapshot.injected.global.js +1 -1
- package/package.json +3 -2
- package/plugins/claude/firefox-devtools/.claude-plugin/plugin.json +11 -0
- package/plugins/claude/firefox-devtools/.mcp.json +8 -0
- package/plugins/claude/firefox-devtools/README.md +82 -0
- package/plugins/claude/firefox-devtools/agents/e2e-tester.md +36 -0
- package/plugins/claude/firefox-devtools/agents/web-scraper.md +35 -0
- package/plugins/claude/firefox-devtools/commands/debug.md +32 -0
- package/plugins/claude/firefox-devtools/commands/navigate.md +31 -0
- package/plugins/claude/firefox-devtools/commands/screenshot.md +30 -0
- package/plugins/claude/firefox-devtools/skills/browser-automation/SKILL.md +65 -0
package/dist/index.js
CHANGED
|
@@ -23301,12 +23301,12 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23301
23301
|
* Take a snapshot of the current page
|
|
23302
23302
|
* Returns text and JSON with snapshotId, no DOM mutations
|
|
23303
23303
|
*/
|
|
23304
|
-
async takeSnapshot() {
|
|
23304
|
+
async takeSnapshot(options) {
|
|
23305
23305
|
const snapshotId = ++this.currentSnapshotId;
|
|
23306
23306
|
this.resolver.setSnapshotId(snapshotId);
|
|
23307
23307
|
this.resolver.clear();
|
|
23308
23308
|
logDebug(`Taking snapshot (ID: ${snapshotId})...`);
|
|
23309
|
-
const result = await this.executeInjectedScript(snapshotId);
|
|
23309
|
+
const result = await this.executeInjectedScript(snapshotId, options);
|
|
23310
23310
|
logDebug(
|
|
23311
23311
|
`Snapshot executeScript result: hasResult=${!!result}, hasTree=${!!result?.tree}, truncated=${result?.truncated || false}`
|
|
23312
23312
|
);
|
|
@@ -23319,6 +23319,10 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23319
23319
|
logDebug(` ... and ${result.debugLog.length - 20} more`);
|
|
23320
23320
|
}
|
|
23321
23321
|
}
|
|
23322
|
+
if (result?.selectorError) {
|
|
23323
|
+
logDebug(`Snapshot generation failed: ${result.selectorError}`);
|
|
23324
|
+
throw new Error(result.selectorError);
|
|
23325
|
+
}
|
|
23322
23326
|
if (!result?.tree) {
|
|
23323
23327
|
const errorMsg = "Unknown error";
|
|
23324
23328
|
logDebug(`Snapshot generation failed: ${errorMsg}`);
|
|
@@ -23362,7 +23366,7 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23362
23366
|
/**
|
|
23363
23367
|
* Execute bundled injected snapshot script
|
|
23364
23368
|
*/
|
|
23365
|
-
async executeInjectedScript(snapshotId) {
|
|
23369
|
+
async executeInjectedScript(snapshotId, options) {
|
|
23366
23370
|
const scriptSource = this.getInjectedScript();
|
|
23367
23371
|
const result = await this.driver.executeScript(
|
|
23368
23372
|
`
|
|
@@ -23374,10 +23378,11 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23374
23378
|
window.__createSnapshot = __SnapshotInjected.createSnapshot;
|
|
23375
23379
|
}
|
|
23376
23380
|
}
|
|
23377
|
-
// Call it
|
|
23378
|
-
return window.__createSnapshot(arguments[0]);
|
|
23381
|
+
// Call it with options
|
|
23382
|
+
return window.__createSnapshot(arguments[0], arguments[1]);
|
|
23379
23383
|
`,
|
|
23380
|
-
snapshotId
|
|
23384
|
+
snapshotId,
|
|
23385
|
+
options || {}
|
|
23381
23386
|
);
|
|
23382
23387
|
return result;
|
|
23383
23388
|
}
|
|
@@ -23649,11 +23654,11 @@ var init_firefox = __esm({
|
|
|
23649
23654
|
// ============================================================================
|
|
23650
23655
|
// Snapshot
|
|
23651
23656
|
// ============================================================================
|
|
23652
|
-
async takeSnapshot() {
|
|
23657
|
+
async takeSnapshot(options) {
|
|
23653
23658
|
if (!this.snapshot) {
|
|
23654
23659
|
throw new Error("Not connected");
|
|
23655
23660
|
}
|
|
23656
|
-
return await this.snapshot.takeSnapshot();
|
|
23661
|
+
return await this.snapshot.takeSnapshot(options);
|
|
23657
23662
|
}
|
|
23658
23663
|
resolveUidToSelector(uid) {
|
|
23659
23664
|
if (!this.snapshot) {
|
|
@@ -24506,13 +24511,24 @@ async function handleTakeSnapshot(args2) {
|
|
|
24506
24511
|
maxLines: requestedMaxLines = DEFAULT_SNAPSHOT_LINES,
|
|
24507
24512
|
includeAttributes = false,
|
|
24508
24513
|
includeText = true,
|
|
24509
|
-
maxDepth
|
|
24514
|
+
maxDepth,
|
|
24515
|
+
includeAll = false,
|
|
24516
|
+
selector
|
|
24510
24517
|
} = args2 || {};
|
|
24511
24518
|
const maxLines = Math.min(Math.max(1, requestedMaxLines), TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP);
|
|
24512
24519
|
const wasCapped = requestedMaxLines > TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP;
|
|
24513
24520
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
24514
24521
|
const firefox3 = await getFirefox2();
|
|
24515
|
-
const
|
|
24522
|
+
const snapshotOptions = {};
|
|
24523
|
+
if (includeAll) {
|
|
24524
|
+
snapshotOptions.includeAll = includeAll;
|
|
24525
|
+
}
|
|
24526
|
+
if (selector) {
|
|
24527
|
+
snapshotOptions.selector = selector;
|
|
24528
|
+
}
|
|
24529
|
+
const snapshot = await firefox3.takeSnapshot(
|
|
24530
|
+
Object.keys(snapshotOptions).length > 0 ? snapshotOptions : void 0
|
|
24531
|
+
);
|
|
24516
24532
|
const { formatSnapshotTree: formatSnapshotTree2 } = await Promise.resolve().then(() => (init_formatter(), formatter_exports));
|
|
24517
24533
|
const options = {
|
|
24518
24534
|
includeAttributes,
|
|
@@ -24526,6 +24542,12 @@ async function handleTakeSnapshot(args2) {
|
|
|
24526
24542
|
const truncated = lines.length > maxLines;
|
|
24527
24543
|
const displayLines = truncated ? lines.slice(0, maxLines) : lines;
|
|
24528
24544
|
let output = `\u{1F4F8} Snapshot (id=${snapshot.json.snapshotId})`;
|
|
24545
|
+
if (selector) {
|
|
24546
|
+
output += ` [selector: ${selector}]`;
|
|
24547
|
+
}
|
|
24548
|
+
if (includeAll) {
|
|
24549
|
+
output += " [includeAll: true]";
|
|
24550
|
+
}
|
|
24529
24551
|
if (wasCapped) {
|
|
24530
24552
|
output += ` [maxLines capped: ${TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP}]`;
|
|
24531
24553
|
}
|
|
@@ -24606,6 +24628,14 @@ var init_snapshot2 = __esm({
|
|
|
24606
24628
|
maxDepth: {
|
|
24607
24629
|
type: "number",
|
|
24608
24630
|
description: "Max tree depth"
|
|
24631
|
+
},
|
|
24632
|
+
includeAll: {
|
|
24633
|
+
type: "boolean",
|
|
24634
|
+
description: "Include all visible elements without relevance filtering. Useful for Vue/Livewire apps (default: false)"
|
|
24635
|
+
},
|
|
24636
|
+
selector: {
|
|
24637
|
+
type: "string",
|
|
24638
|
+
description: 'CSS selector to scope snapshot to specific element (e.g., "#app")'
|
|
24609
24639
|
}
|
|
24610
24640
|
}
|
|
24611
24641
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __SnapshotInjected=(()=>{var
|
|
1
|
+
"use strict";var __SnapshotInjected=(()=>{var N=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var Q=(e,n)=>{for(var t in n)N(e,t,{get:n[t],enumerable:!0})},Y=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of J(n))!K.call(e,i)&&i!==t&&N(e,i,{get:()=>n[i],enumerable:!(r=B(n,i))||r.enumerable});return e};var Z=e=>Y(N({},"__esModule",{value:!0}),e);var ue={};Q(ue,{createSnapshot:()=>U});var T=["a","button","input","select","textarea","img","video","audio","iframe"],ee=["nav","main","section","article","header","footer","form"],te=["div","span","p","li","ul","ol"];function y(e){if(!e||e.nodeType!==Node.ELEMENT_NODE)return!1;let n=e;for(;n&&n!==document.documentElement;){try{let t=window.getComputedStyle(n),r=parseFloat(t.opacity);if(t.display==="none"||t.visibility==="hidden"||r===0||isNaN(r))return!1}catch{return!1}n=n.parentElement}return!0}function ne(e){let n="";for(let t=0;t<e.childNodes.length;t++){let r=e.childNodes[t];r&&r.nodeType===Node.TEXT_NODE&&(n+=r.textContent||"")}return n.trim()}function re(e){for(let n=0;n<e.children.length;n++){let t=e.children[n];if(t){let r=t.tagName.toLowerCase();if(T.indexOf(r)!==-1||t.hasAttribute("role"))return!0}}return!1}function R(e){if(!e||e.nodeType!==Node.ELEMENT_NODE||!y(e))return!1;let n=e.tagName.toLowerCase();if(T.indexOf(n)!==-1||e.hasAttribute("role")||e.hasAttribute("aria-label")||/^h[1-6]$/.test(n)||ee.indexOf(n)!==-1)return!0;if(te.indexOf(n)!==-1){let t=ne(e);if(t.length>0&&t.length<500||e.id||e.className||re(e))return!0}return!1}function $(e){if(e.tabIndex>=0)return!0;let t=e.tagName.toLowerCase();return["a","button","input","select","textarea"].indexOf(t)!==-1}function D(e){let n=e.tagName.toLowerCase();if(T.indexOf(n)!==-1)return!0;let t=e.getAttribute("role");return!!(t&&["button","link","menuitem","tab"].indexOf(t)!==-1||e.hasAttribute("onclick"))}var ie=100;function X(e){if(e.hasAttribute("aria-label"))return e.getAttribute("aria-label")||void 0;let t=e.id;if(t){let i=document.querySelector(`label[for="${t}"]`);if(i?.textContent)return i.textContent.trim()}if(e.hasAttribute("placeholder"))return e.getAttribute("placeholder")||void 0;if(e.hasAttribute("title"))return e.getAttribute("title")||void 0;if(e.hasAttribute("alt"))return e.getAttribute("alt")||void 0;let r=e.tagName.toLowerCase();if(["button","a","h1","h2","h3","h4","h5","h6"].indexOf(r)!==-1)return x(e)}function x(e){let n="";for(let r=0;r<e.childNodes.length;r++){let i=e.childNodes[r];i&&i.nodeType===Node.TEXT_NODE&&(n+=i.textContent||"")}let t=n.trim();if(t)return t.substring(0,ie)}function W(e){let n={},t=!1,r=["disabled","hidden","selected","expanded"];for(let o of r){let a=e.getAttribute(`aria-${o}`);a!==null&&(n[o]=a==="true",t=!0)}let i=["checked","pressed"];for(let o of i){let a=e.getAttribute(`aria-${o}`);a!==null&&(a==="mixed"?n[o]="mixed":n[o]=a==="true",t=!0)}let l=["autocomplete","haspopup","invalid","label","labelledby","describedby","controls"];for(let o of l){let a=e.getAttribute(`aria-${o}`);a&&(n[o]=a,t=!0)}let u=e.getAttribute("aria-level");if(u){let o=parseInt(u,10);isNaN(o)||(n.level=o,t=!0)}return t?n:void 0}function G(e){let n={};try{let t=window.getComputedStyle(e),r=parseFloat(t.opacity);n.visible=t.display!=="none"&&t.visibility!=="hidden"&&r!==0&&!isNaN(r)}catch{n.visible=!1}return n.accessible=n.visible&&!e.getAttribute("aria-hidden"),n.focusable=$(e),n.interactive=D(e),n}var oe=["id","data-testid","data-test-id"];function P(e){let n=[],t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE;){let r=t.nodeName.toLowerCase(),i=!1;for(let a of oe){let d=t.getAttribute(a);if(d){a==="id"?r+="#"+CSS.escape(d):r+=`[${a}="${H(d)}"]`,n.unshift(r),i=!0;break}}if(i)break;let l=t.getAttribute("aria-label"),u=t.getAttribute("role");if(l&&u){r+=`[role="${u}"][aria-label="${H(l)}"]`,n.unshift(r),t=t.parentElement;continue}let o=t.parentElement?.children;if(o&&o.length>1){let a=1;for(let d=0;d<o.length;d++){let s=o[d];if(s){if(s===t)break;s.nodeName===t.nodeName&&a++}}(a>1||o.length>1&&o[0]!==t)&&(r+=`:nth-of-type(${a})`)}if(n.unshift(se(r)),t=t.parentElement,t&&t.nodeName.toLowerCase()==="body"){n.unshift("body");break}}return n.join(" > ")}function F(e){let n=e.id;if(n)return`//*[@id="${ae(n)}"]`;let t=[],r=e;for(;r&&r.nodeType===Node.ELEMENT_NODE;){let i=r.nodeName.toLowerCase(),l=1,u=r.previousElementSibling;for(;u;)u.nodeName.toLowerCase()===i&&l++,u=u.previousElementSibling;let o=r.parentElement,a=!1;o&&(a=Array.from(o.children).filter(h=>h.nodeName.toLowerCase()===i).length>1);let d=a?`${i}[${l}]`:i;if(t.unshift(d),r=r.parentElement,r&&r.nodeName.toLowerCase()==="html"){t.unshift("html");break}}return"/"+t.join("/")}function H(e){return e.replace(/"/g,'\\"').substring(0,64)}function ae(e){return e.indexOf('"')===-1||e.indexOf("'")===-1?e:`concat(${e.split('"').map((t,r,i)=>r===i.length-1?t?`"${t}"`:"":t?`"${t}",'"'`:`"'"`).filter(t=>t).join(",")})`}function se(e){return e.length<=64?e:e.substring(0,64)}var le=10,j=1e3;function V(e,n,t={}){let{includeAll:r=!1,includeIframes:i=!0}=t,l=0,u=[],o=!1;function a(s,h){if(h>le)return o=!0,{node:null,relevantChildren:[]};if(l>=j)return o=!0,{node:null,relevantChildren:[]};let b=s.tagName.toLowerCase(),C=b==="body"||b==="html",g;r?g=C||y(s):g=C||R(s);let E=[];if(b==="iframe"&&i&&g)try{let c=s,p=c.contentDocument||c.contentWindow?.document;if(p?.body){let f=a(p.body,h+1);f.node&&(f.node.isIframe=!0,f.node.frameSrc=c.src,E.push(f.node))}}catch{}else for(let c=0;c<s.children.length;c++){if(l>=j){o=!0;break}let p=s.children[c];if(!p)continue;let f=a(p,h+1);f.node?E.push(f.node):f.relevantChildren.length>0&&E.push(...f.relevantChildren)}if(!g)return{node:null,relevantChildren:E};let S=`${n}_${l++}`,q=P(s),z=F(s);u.push({uid:S,css:q,xpath:z});let A=s,v=s.getAttribute("role"),O=X(s),_=x(s),w=A.value,M=A.href,L=A.src,I=W(s),k=G(s),m={uid:S,tag:b,...v&&{role:v},...O&&{name:O},...w&&{value:w},...M&&{href:M},...L&&{src:L},..._&&{text:_},...I&&{aria:I},...k&&{computed:k},children:E};if(b==="iframe"&&i)try{let c=s;(c.contentDocument||c.contentWindow?.document)?.body||(m.isIframe=!0,m.frameSrc=c.src,m.crossOrigin=!0)}catch{m.isIframe=!0,m.frameSrc=s.src,m.crossOrigin=!0}return{node:m,relevantChildren:[]}}return{tree:a(e,0).node,uidMap:u,truncated:o}}function U(e,n){try{let t=document.body;if(n?.selector)try{let l=document.querySelector(n.selector);if(!l)return{tree:null,uidMap:[],truncated:!1,selectorError:`Selector "${n.selector}" not found`};t=l}catch{return{tree:null,uidMap:[],truncated:!1,selectorError:`Invalid selector syntax: "${n.selector}"`}}let r={includeIframes:n?.includeIframes??!0};n?.includeAll!==void 0&&(r.includeAll=n.includeAll);let i=V(t,e,r);if(!i.tree)throw new Error("Failed to generate tree");return i}catch{return{tree:null,uidMap:[],truncated:!1}}}typeof window<"u"&&(window.__createSnapshot=U);return Z(ue);})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firefox-devtools-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for Firefox DevTools automation",
|
|
5
5
|
"author": "freema",
|
|
6
6
|
"license": "MIT",
|
|
@@ -88,7 +88,8 @@
|
|
|
88
88
|
"dist",
|
|
89
89
|
"README.md",
|
|
90
90
|
"LICENSE",
|
|
91
|
-
"scripts"
|
|
91
|
+
"scripts",
|
|
92
|
+
"plugins"
|
|
92
93
|
],
|
|
93
94
|
"repository": {
|
|
94
95
|
"type": "git",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "firefox-devtools",
|
|
3
|
+
"description": "Firefox browser automation via WebDriver BiDi. Automate Firefox for testing, scraping, form filling, screenshots, and debugging.",
|
|
4
|
+
"version": "0.6.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Tomáš Grasl",
|
|
7
|
+
"url": "https://www.tomasgrasl.cz/"
|
|
8
|
+
},
|
|
9
|
+
"repository": "https://github.com/freema/firefox-devtools-mcp",
|
|
10
|
+
"license": "MIT"
|
|
11
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Firefox DevTools Plugin for Claude Code
|
|
2
|
+
|
|
3
|
+
Firefox browser automation via WebDriver BiDi. Navigate pages, fill forms, click elements, take screenshots, and monitor console/network activity.
|
|
4
|
+
|
|
5
|
+
## What's Included
|
|
6
|
+
|
|
7
|
+
This plugin provides:
|
|
8
|
+
|
|
9
|
+
- **MCP Server** - Connects Claude Code to Firefox automation
|
|
10
|
+
- **Skills** - Auto-triggers for browser automation, testing, and scraping tasks
|
|
11
|
+
- **Agents** - Dedicated `e2e-tester` and `web-scraper` agents for focused tasks
|
|
12
|
+
- **Commands** - `/firefox:navigate`, `/firefox:screenshot`, `/firefox:debug`
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
claude plugin install firefox-devtools
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
### /firefox:navigate
|
|
23
|
+
|
|
24
|
+
Navigate to a URL and take a DOM snapshot:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
/firefox:navigate https://example.com
|
|
28
|
+
/firefox:navigate https://github.com/login
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### /firefox:screenshot
|
|
32
|
+
|
|
33
|
+
Capture the current page or a specific element:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
/firefox:screenshot
|
|
37
|
+
/firefox:screenshot e15
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### /firefox:debug
|
|
41
|
+
|
|
42
|
+
Show console errors and failed network requests:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
/firefox:debug
|
|
46
|
+
/firefox:debug console
|
|
47
|
+
/firefox:debug network
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Agents
|
|
51
|
+
|
|
52
|
+
Spawn agents to keep your main context clean:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
spawn e2e-tester to test the login flow on https://app.example.com
|
|
56
|
+
spawn web-scraper to extract product prices from https://shop.example.com
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage Examples
|
|
60
|
+
|
|
61
|
+
The plugin works automatically when you ask about browser tasks:
|
|
62
|
+
|
|
63
|
+
- "Navigate to example.com and take a screenshot"
|
|
64
|
+
- "Fill out the login form and submit"
|
|
65
|
+
- "Check for JavaScript errors on this page"
|
|
66
|
+
- "Scrape all product prices from this page"
|
|
67
|
+
|
|
68
|
+
## Key Workflow
|
|
69
|
+
|
|
70
|
+
1. `take_snapshot` - Creates DOM snapshot with UIDs (e.g., `e42`)
|
|
71
|
+
2. Interact using UIDs - `click_by_uid`, `fill_by_uid`, etc.
|
|
72
|
+
3. Re-snapshot after DOM changes
|
|
73
|
+
|
|
74
|
+
## Requirements
|
|
75
|
+
|
|
76
|
+
- Firefox 120+
|
|
77
|
+
- Node.js 20.19.0+
|
|
78
|
+
|
|
79
|
+
## Links
|
|
80
|
+
|
|
81
|
+
- [Repository](https://github.com/freema/firefox-devtools-mcp)
|
|
82
|
+
- [npm](https://www.npmjs.com/package/firefox-devtools-mcp)
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
- `take_snapshot` - Get DOM with UIDs
|
|
26
|
+
- `fill_by_uid` / `fill_form_by_uid` - Enter text
|
|
27
|
+
- `click_by_uid` - Click elements
|
|
28
|
+
- `screenshot_page` - Capture evidence
|
|
29
|
+
- `list_console_messages` - Check for JS errors
|
|
30
|
+
|
|
31
|
+
## Guidelines
|
|
32
|
+
|
|
33
|
+
- Always snapshot before AND after interactions
|
|
34
|
+
- Take screenshots at key checkpoints
|
|
35
|
+
- Report console errors as test failures
|
|
36
|
+
- Be specific about what passed or failed
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-scraper
|
|
3
|
+
description: Agent for extracting structured data from web pages. Navigates, handles pagination, and extracts content.
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a web scraping agent specializing in data extraction using Firefox DevTools MCP.
|
|
8
|
+
|
|
9
|
+
## Your Task
|
|
10
|
+
|
|
11
|
+
When given a scraping task, navigate to pages, extract the requested data, 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 data 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
|
+
- `take_snapshot` - Get DOM with content and UIDs
|
|
26
|
+
- `click_by_uid` - Navigate pagination
|
|
27
|
+
- `list_network_requests` - Monitor API calls (sometimes easier to scrape)
|
|
28
|
+
|
|
29
|
+
## Guidelines
|
|
30
|
+
|
|
31
|
+
- Snapshots contain element text - no need for separate "get text" calls
|
|
32
|
+
- Check network requests for API endpoints (often cleaner than DOM scraping)
|
|
33
|
+
- Respect rate limits - don't hammer the server
|
|
34
|
+
- Handle "load more" buttons and infinite scroll patterns
|
|
35
|
+
- Return structured data, not raw HTML
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show console errors and failed network requests
|
|
3
|
+
argument-hint: [console|network|all]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /firefox:debug
|
|
7
|
+
|
|
8
|
+
Displays debugging information from the current page.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/firefox:debug # Show all (console errors + failed requests)
|
|
14
|
+
/firefox:debug console # Console messages only
|
|
15
|
+
/firefox:debug network # Network requests only
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
/firefox:debug
|
|
22
|
+
/firefox:debug console
|
|
23
|
+
/firefox:debug network
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## What Happens
|
|
27
|
+
|
|
28
|
+
- `console`: Calls `list_console_messages` with `level="error"`
|
|
29
|
+
- `network`: Calls `list_network_requests` with `status="failed"`
|
|
30
|
+
- `all` (default): Shows both console errors and failed network requests
|
|
31
|
+
|
|
32
|
+
Useful for debugging page issues, JavaScript errors, and API failures.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Navigate Firefox to a URL and take a snapshot
|
|
3
|
+
argument-hint: <url>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /firefox:navigate
|
|
7
|
+
|
|
8
|
+
Opens a URL in Firefox and takes a DOM snapshot for interaction.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/firefox:navigate <url>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
/firefox:navigate https://example.com
|
|
20
|
+
/firefox:navigate https://github.com/login
|
|
21
|
+
/firefox:navigate file:///path/to/local.html
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What Happens
|
|
25
|
+
|
|
26
|
+
1. Calls `navigate_page` with the URL
|
|
27
|
+
2. Waits for page load
|
|
28
|
+
3. Calls `take_snapshot` to create UID mappings
|
|
29
|
+
4. Returns the DOM snapshot with interactive elements marked
|
|
30
|
+
|
|
31
|
+
After navigating, you can interact with elements using their UIDs (e.g., `e42`).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Take a screenshot of the current page or element
|
|
3
|
+
argument-hint: [uid]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /firefox:screenshot
|
|
7
|
+
|
|
8
|
+
Captures a screenshot of the page or a specific element.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/firefox:screenshot # Full page
|
|
14
|
+
/firefox:screenshot <uid> # Specific element
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
/firefox:screenshot
|
|
21
|
+
/firefox:screenshot e15
|
|
22
|
+
/firefox:screenshot e42
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What Happens
|
|
26
|
+
|
|
27
|
+
- Without UID: Calls `screenshot_page` for full page capture
|
|
28
|
+
- With UID: Calls `screenshot_by_uid` for element-specific capture
|
|
29
|
+
|
|
30
|
+
Screenshots are saved and displayed in the conversation.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: browser-automation
|
|
3
|
+
description: This skill should be used when the user asks about browser automation, testing web pages, scraping content, filling forms, taking screenshots, or monitoring console/network activity. Activates for E2E testing, web scraping, form automation, 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
|
+
## When to Use This Skill
|
|
9
|
+
|
|
10
|
+
Activate this skill when the user:
|
|
11
|
+
|
|
12
|
+
- Wants to automate browser interactions ("Fill out this form", "Click the login button")
|
|
13
|
+
- Needs E2E testing ("Test the checkout flow", "Verify the login works")
|
|
14
|
+
- Requests web scraping ("Extract prices from this page", "Get all links")
|
|
15
|
+
- Needs screenshots ("Screenshot this page", "Capture the error state")
|
|
16
|
+
- Wants to debug ("Check for JS errors", "Show failed network requests")
|
|
17
|
+
|
|
18
|
+
## Core Workflow
|
|
19
|
+
|
|
20
|
+
### Step 1: Navigate and Snapshot
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
navigate_page url="https://example.com"
|
|
24
|
+
take_snapshot
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The snapshot returns a DOM representation with UIDs (e.g., `e42`) for each interactive element.
|
|
28
|
+
|
|
29
|
+
### Step 2: Interact with Elements
|
|
30
|
+
|
|
31
|
+
Use UIDs from the snapshot:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
fill_by_uid uid="e5" text="user@example.com"
|
|
35
|
+
click_by_uid uid="e8"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Step 3: Re-snapshot After Changes
|
|
39
|
+
|
|
40
|
+
DOM changes invalidate UIDs. Always re-snapshot after:
|
|
41
|
+
- Page navigation
|
|
42
|
+
- Form submissions
|
|
43
|
+
- Dynamic content loads
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
take_snapshot # Get fresh UIDs
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Reference
|
|
50
|
+
|
|
51
|
+
| Task | Tools |
|
|
52
|
+
|------|-------|
|
|
53
|
+
| Navigate | `navigate_page` |
|
|
54
|
+
| See DOM | `take_snapshot` |
|
|
55
|
+
| Click | `click_by_uid` |
|
|
56
|
+
| Type | `fill_by_uid`, `fill_form_by_uid` |
|
|
57
|
+
| Screenshot | `screenshot_page`, `screenshot_by_uid` |
|
|
58
|
+
| Debug | `list_console_messages`, `list_network_requests` |
|
|
59
|
+
|
|
60
|
+
## Guidelines
|
|
61
|
+
|
|
62
|
+
- **Always snapshot first**: UIDs only exist after `take_snapshot`
|
|
63
|
+
- **Re-snapshot after DOM changes**: UIDs become stale after interactions
|
|
64
|
+
- **Check for errors**: Use `list_console_messages level="error"` to catch JS issues
|
|
65
|
+
- **Firefox only**: This MCP controls Firefox, not Chrome or Safari
|