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.
Files changed (2) hide show
  1. package/dist/index.js +30 -25
  2. 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 — returns interactive elements with ref IDs
39
- const SNAPSHOT_JS = `() => {
40
- const sels = 'button, a, input:not([type="hidden"]), textarea, select, '
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
- const els = document.querySelectorAll(sels);
45
- const results = [];
46
- let refId = 0;
47
- els.forEach(el => {
48
- const r = el.getBoundingClientRect();
49
- if (r.width < 1 || r.height < 1) return;
50
- const cs = getComputedStyle(el);
51
- if (cs.display === 'none' || cs.visibility === 'hidden') return;
52
- const ref = 'e' + refId++;
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
- const entry = {
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
- const clean = {};
68
- for (const [k, v] of Object.entries(entry)) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-camoufox",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "MCP server for stealth browser automation via Camoufox — 39 tools, Chrome DevTools MCP-level power with anti-bot stealth",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",