mcp-camoufox 0.2.0 → 0.2.2
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 +30 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,24 +35,25 @@ function ensureDirs() {
|
|
|
35
35
|
mkdirSync(PROFILE_DIR, { recursive: true });
|
|
36
36
|
mkdirSync(SCREENSHOT_DIR, { recursive: true });
|
|
37
37
|
}
|
|
38
|
-
// DOM snapshot JS —
|
|
39
|
-
const SNAPSHOT_JS = `() => {
|
|
40
|
-
|
|
38
|
+
// DOM snapshot JS — IIFE so page.evaluate runs it immediately
|
|
39
|
+
const SNAPSHOT_JS = `(() => {
|
|
40
|
+
var sels = 'button, a, input:not([type="hidden"]), textarea, select, '
|
|
41
41
|
+ '[role="button"], [role="link"], [role="textbox"], [role="checkbox"], '
|
|
42
42
|
+ '[role="radio"], [role="tab"], [role="menuitem"], [contenteditable="true"], '
|
|
43
43
|
+ 'img[alt], h1, h2, h3, h4, h5, h6, label, [role="dialog"], [role="alert"], [role="status"]';
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
els.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
var els = document.querySelectorAll(sels);
|
|
45
|
+
var results = [];
|
|
46
|
+
var refId = 0;
|
|
47
|
+
for (var i = 0; i < els.length; i++) {
|
|
48
|
+
var el = els[i];
|
|
49
|
+
var r = el.getBoundingClientRect();
|
|
50
|
+
if (r.width < 1 || r.height < 1) continue;
|
|
51
|
+
var cs = getComputedStyle(el);
|
|
52
|
+
if (cs.display === 'none' || cs.visibility === 'hidden') continue;
|
|
53
|
+
var ref = 'e' + refId++;
|
|
53
54
|
el.setAttribute('data-mcp-ref', ref);
|
|
54
|
-
|
|
55
|
-
ref,
|
|
55
|
+
var entry = {
|
|
56
|
+
ref: ref,
|
|
56
57
|
tag: el.tagName.toLowerCase(),
|
|
57
58
|
role: el.getAttribute('role') || '',
|
|
58
59
|
text: (el.innerText || el.value || '').trim().slice(0, 100),
|
|
@@ -64,15 +65,19 @@ const SNAPSHOT_JS = `() => {
|
|
|
64
65
|
checked: el.checked || false,
|
|
65
66
|
disabled: el.disabled || false,
|
|
66
67
|
};
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
var clean = {};
|
|
69
|
+
var keys = Object.keys(entry);
|
|
70
|
+
for (var j = 0; j < keys.length; j++) {
|
|
71
|
+
var k = keys[j], v = entry[k];
|
|
69
72
|
if (v !== '' && v !== false && v !== undefined) clean[k] = v;
|
|
70
73
|
}
|
|
71
74
|
results.push(clean);
|
|
72
|
-
}
|
|
75
|
+
}
|
|
73
76
|
return results;
|
|
74
|
-
}`;
|
|
77
|
+
})()`;
|
|
75
78
|
function formatSnapshot(elements, url, title) {
|
|
79
|
+
if (!elements || !Array.isArray(elements))
|
|
80
|
+
elements = [];
|
|
76
81
|
const lines = [
|
|
77
82
|
`Page: ${title}`,
|
|
78
83
|
`URL: ${url}`,
|
|
@@ -123,7 +128,7 @@ server.tool("browser_launch", "Launch Camoufox stealth browser and navigate to U
|
|
|
123
128
|
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
124
129
|
await page.waitForTimeout(1500);
|
|
125
130
|
}
|
|
126
|
-
return { content: [{ type: "text", text: `Already running. Navigated to: ${page.url}` }] };
|
|
131
|
+
return { content: [{ type: "text", text: `Already running. Navigated to: ${page.url()}` }] };
|
|
127
132
|
}
|
|
128
133
|
ensureDirs();
|
|
129
134
|
const w = width > 0 ? width : 1280;
|
|
@@ -154,7 +159,7 @@ server.tool("browser_launch", "Launch Camoufox stealth browser and navigate to U
|
|
|
154
159
|
await page.waitForTimeout(1500);
|
|
155
160
|
}
|
|
156
161
|
const title = await page.title();
|
|
157
|
-
return { content: [{ type: "text", text: `Browser launched. URL: ${page.url}\nTitle: ${title}` }] };
|
|
162
|
+
return { content: [{ type: "text", text: `Browser launched. URL: ${page.url()}\nTitle: ${title}` }] };
|
|
158
163
|
});
|
|
159
164
|
server.tool("browser_close", "Close the browser. Cookies are preserved in profile.", {}, async () => {
|
|
160
165
|
if (browserContext) {
|
|
@@ -178,27 +183,27 @@ server.tool("navigate", "Navigate to a URL.", {
|
|
|
178
183
|
const page = getPage();
|
|
179
184
|
await page.goto(url, { waitUntil: wait_until, timeout });
|
|
180
185
|
await page.waitForTimeout(1000);
|
|
181
|
-
return { content: [{ type: "text", text: `Navigated to: ${page.url}\nTitle: ${await page.title()}` }] };
|
|
186
|
+
return { content: [{ type: "text", text: `Navigated to: ${page.url()}\nTitle: ${await page.title()}` }] };
|
|
182
187
|
});
|
|
183
188
|
server.tool("go_back", "Navigate back in history.", {}, async () => {
|
|
184
189
|
const page = getPage();
|
|
185
190
|
await page.goBack({ waitUntil: "domcontentloaded", timeout: 15000 });
|
|
186
|
-
return { content: [{ type: "text", text: `Went back. URL: ${page.url}` }] };
|
|
191
|
+
return { content: [{ type: "text", text: `Went back. URL: ${page.url()}` }] };
|
|
187
192
|
});
|
|
188
193
|
server.tool("go_forward", "Navigate forward in history.", {}, async () => {
|
|
189
194
|
const page = getPage();
|
|
190
195
|
await page.goForward({ waitUntil: "domcontentloaded", timeout: 15000 });
|
|
191
|
-
return { content: [{ type: "text", text: `Went forward. URL: ${page.url}` }] };
|
|
196
|
+
return { content: [{ type: "text", text: `Went forward. URL: ${page.url()}` }] };
|
|
192
197
|
});
|
|
193
198
|
server.tool("reload", "Reload the current page.", {}, async () => {
|
|
194
199
|
const page = getPage();
|
|
195
200
|
await page.reload({ waitUntil: "domcontentloaded", timeout: 15000 });
|
|
196
|
-
return { content: [{ type: "text", text: `Reloaded. URL: ${page.url}` }] };
|
|
201
|
+
return { content: [{ type: "text", text: `Reloaded. URL: ${page.url()}` }] };
|
|
197
202
|
});
|
|
198
203
|
// ── Tools: Snapshot & Screenshot ───────────────────────────────────────────
|
|
199
204
|
server.tool("browser_snapshot", "Get visible interactive elements with ref IDs. Use refs with click/fill. Always call after navigation.", {}, async () => {
|
|
200
205
|
const page = getPage();
|
|
201
|
-
const elements = await page.evaluate(SNAPSHOT_JS);
|
|
206
|
+
const elements = await page.evaluate(SNAPSHOT_JS) || [];
|
|
202
207
|
const text = formatSnapshot(elements, page.url(), await page.title());
|
|
203
208
|
return { content: [{ type: "text", text }] };
|
|
204
209
|
});
|
package/package.json
CHANGED