brave-real-browser-mcp-server 2.29.0 โ†’ 2.30.0

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/README.md CHANGED
@@ -107,6 +107,115 @@ npm run dev
107
107
 
108
108
  ---
109
109
 
110
+ ## ๐Ÿ†• New in v2.28.1 - Advanced Enhancements
111
+
112
+ ### ๐ŸŒ Advanced Navigation
113
+ | Option | Description |
114
+ |--------|-------------|
115
+ | `blockResources` | Block images, fonts, CSS for faster loading |
116
+ | `customHeaders` | Set custom HTTP headers |
117
+ | `referrer` | Custom referrer URL |
118
+ | `waitForSelector` | Wait for specific element after load |
119
+ | `waitForContent` | Wait for specific text content |
120
+ | `scrollToBottom` | Auto-scroll for lazy loading |
121
+ | `randomDelay` | Human-like delay (100-500ms) |
122
+ | `bypassCSP` | Bypass Content Security Policy |
123
+
124
+ **Example:**
125
+ ```json
126
+ {
127
+ "url": "https://example.com",
128
+ "blockResources": ["image", "font"],
129
+ "waitForSelector": ".main-content",
130
+ "scrollToBottom": true,
131
+ "randomDelay": true
132
+ }
133
+ ```
134
+
135
+ ### ๐Ÿš€ Parallel Scraping (js_scrape)
136
+ | Option | Description |
137
+ |--------|-------------|
138
+ | `urls` | Array of multiple URLs to scrape |
139
+ | `concurrency` | Max concurrent scrapes (1-10) |
140
+ | `continueOnError` | Continue even if some URLs fail |
141
+ | `delayBetween` | Delay between scrapes (ms) |
142
+
143
+ **Example:**
144
+ ```json
145
+ {
146
+ "urls": ["https://site1.com", "https://site2.com", "https://site3.com"],
147
+ "concurrency": 3,
148
+ "extractSelector": "article"
149
+ }
150
+ ```
151
+
152
+ ### ๐Ÿ“„ Auto-Pagination (link_harvester)
153
+ | Option | Description |
154
+ |--------|-------------|
155
+ | `followPagination` | Auto-follow pagination links |
156
+ | `maxPages` | Maximum pages to scrape (1-20) |
157
+ | `paginationSelector` | Custom next page selector |
158
+ | `delayBetweenPages` | Delay between pages (ms) |
159
+
160
+ **Example:**
161
+ ```json
162
+ {
163
+ "followPagination": true,
164
+ "maxPages": 10,
165
+ "filter": "movie"
166
+ }
167
+ ```
168
+
169
+ ### ๐Ÿ”Œ API Interceptor (network_recorder)
170
+ | Option | Description |
171
+ |--------|-------------|
172
+ | `interceptMode` | `record`, `intercept`, or `mock` |
173
+ | `blockPatterns` | URL patterns to block |
174
+ | `mockResponses` | Fake responses for URLs |
175
+ | `modifyHeaders` | Modify request headers |
176
+ | `capturePayloads` | Capture POST/PUT bodies |
177
+
178
+ **Example:**
179
+ ```json
180
+ {
181
+ "interceptMode": "intercept",
182
+ "blockPatterns": ["ads", "tracking"],
183
+ "capturePayloads": true
184
+ }
185
+ ```
186
+
187
+ ### ๐Ÿ›ก๏ธ Anti-Detection Enhancements
188
+
189
+ | Feature | Description |
190
+ |---------|-------------|
191
+ | **Fingerprint Randomizer** | Canvas, Audio, Hardware randomization |
192
+ | **Human Behavior Simulation** | Natural mouse/scroll patterns |
193
+ | **WebGL Spoofing** | Random GPU from 8 configs (Intel, NVIDIA, AMD) |
194
+ | **Proxy Rotation** | round-robin, random, least-used, on-error strategies |
195
+ | **Rate Limiter** | Per-second/minute limits, domain-specific |
196
+ | **Error Auto-Recovery** | Smart retry with different strategies |
197
+
198
+ ### ๐Ÿ”„ Internal Systems
199
+
200
+ ```javascript
201
+ // Rate Limiter
202
+ initRateLimiter({ requestsPerSecond: 5, requestsPerMinute: 100 });
203
+ setDomainRateLimit('api.example.com', 2); // 2 req/sec for this domain
204
+
205
+ // Proxy Rotation
206
+ initProxyRotation(['proxy1:8080', 'proxy2:8080'], 'round-robin');
207
+ rotateProxy('error'); // Rotate on error
208
+
209
+ // Error Recovery
210
+ executeWithRecovery(operation, {
211
+ toolName: 'navigate',
212
+ page: pageInstance,
213
+ restartBrowser: () => browser.restart()
214
+ });
215
+ ```
216
+
217
+ ---
218
+
110
219
  ## ๐ŸŒ Unified MCP+LSP+SSE Ecosystem
111
220
 
112
221
  **เคเค• command เคธเฅ‡ เคธเคฌ active:**
@@ -117,7 +226,7 @@ npm run dev
117
226
 
118
227
  ### Output:
119
228
  ```
120
- ๐Ÿฆ Brave Real Browser - Unified Server v2.22.0
229
+ ๐Ÿฆ Brave Real Browser - Unified Server v2.28.1
121
230
  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
122
231
  ๐Ÿ“ก HTTP Server: http://localhost:3000
123
232
 
@@ -185,14 +294,14 @@ brave-real-launcher
185
294
  ### Navigation
186
295
  | Tool | Description |
187
296
  |------|-------------|
188
- | `navigate` | Navigate to URL |
297
+ | `navigate` | Navigate to URL with advanced options (resource blocking, custom headers, auto-scroll) |
189
298
  | `wait` | Wait for conditions |
190
299
 
191
300
  ### Content
192
301
  | Tool | Description |
193
302
  |------|-------------|
194
303
  | `get_content` | Get page HTML/text |
195
- | `find_element` | Find by selector/text/AI |
304
+ | `find_element` | Find by selector/text/AI with batch mode |
196
305
  | `save_content_as_markdown` | Save as markdown file |
197
306
 
198
307
  ### Interaction
@@ -204,34 +313,32 @@ brave-real-launcher
204
313
  | `random_scroll` | Natural scrolling |
205
314
  | `solve_captcha` | Solve CAPTCHAs |
206
315
 
207
- ### Media Extraction
316
+ ### Media & Streaming
317
+ | Tool | Description |
318
+ |------|-------------|
319
+ | `stream_extractor` | Extract streams with multi-quality selector, VidSrc/Filemoon/StreamWish support |
320
+ | `player_api_hook` | Hook into JWPlayer, Video.js, HLS.js, Plyr, Vidstack, DASH.js |
321
+ | `iframe_handler` | Deep iframe scraping with video source extraction |
322
+
323
+ ### Scraping
208
324
  | Tool | Description |
209
325
  |------|-------------|
210
- | `media_extractor` | Extract video/audio |
211
- | `m3u8_parser` | Parse HLS streams |
212
- | `stream_extractor` | Direct download URLs |
326
+ | `js_scrape` | JavaScript-rendered scraping with parallel URL support |
327
+ | `link_harvester` | Harvest links with auto-pagination |
328
+ | `network_recorder` | Record traffic with API interception |
329
+ | `extract_json` | Extract embedded JSON with AES decryption |
330
+ | `search_regex` | Regex search like regex101.com |
213
331
 
214
332
  ### Advanced
215
333
  | Tool | Description |
216
334
  |------|-------------|
217
- | `search_content` | Search patterns |
218
- | `extract_json` | Extract embedded JSON |
219
335
  | `scrape_meta_tags` | Meta/OG tags |
220
- | `deep_analysis` | Full page analysis |
221
- | `network_recorder` | Record traffic |
222
- | `api_finder` | Discover APIs |
223
- | `ajax_content_waiter` | Wait for AJAX |
224
- | `link_harvester` | Harvest links |
225
- | `batch_element_scraper` | Batch scrape |
226
- | `extract_schema` | Schema.org data |
227
- | `element_screenshot` | Screenshot element |
228
- | `breadcrumb_navigator` | Navigate breadcrumbs |
336
+ | `deep_analysis` | Full page analysis with screenshot |
229
337
  | `redirect_tracer` | Trace redirects |
230
338
  | `progress_tracker` | Track progress |
231
339
  | `cookie_manager` | Manage cookies |
232
340
  | `file_downloader` | Download files |
233
- | `iframe_handler` | Handle iframes |
234
- | `popup_handler` | Handle popups |
341
+ | `execute_js` | Run custom JavaScript |
235
342
 
236
343
  ---
237
344
 
@@ -5962,3 +5962,321 @@ export async function handlePlayerApiHook(page, args) {
5962
5962
  };
5963
5963
  }
5964
5964
  }
5965
+ // Field type detection patterns
5966
+ const FIELD_PATTERNS = {
5967
+ email: [/email/i, /e-mail/i, /mail/i],
5968
+ password: [/password/i, /pass/i, /pwd/i],
5969
+ username: [/username/i, /user/i, /login/i, /uname/i],
5970
+ firstName: [/first.?name/i, /fname/i, /given.?name/i],
5971
+ lastName: [/last.?name/i, /lname/i, /surname/i, /family.?name/i],
5972
+ fullName: [/full.?name/i, /name/i],
5973
+ phone: [/phone/i, /tel/i, /mobile/i, /cell/i],
5974
+ address: [/address/i, /street/i, /addr/i],
5975
+ city: [/city/i, /town/i],
5976
+ state: [/state/i, /province/i, /region/i],
5977
+ zip: [/zip/i, /postal/i, /postcode/i],
5978
+ country: [/country/i, /nation/i],
5979
+ cardNumber: [/card.?number/i, /cc.?num/i, /credit.?card/i],
5980
+ cvv: [/cvv/i, /cvc/i, /security.?code/i],
5981
+ expiry: [/expir/i, /exp.?date/i],
5982
+ search: [/search/i, /query/i, /q/i],
5983
+ message: [/message/i, /comment/i, /feedback/i, /note/i],
5984
+ company: [/company/i, /organization/i, /org/i],
5985
+ website: [/website/i, /url/i, /web/i],
5986
+ age: [/age/i, /birth/i, /dob/i],
5987
+ };
5988
+ // Detect field type from attributes
5989
+ function detectFieldType(attributes) {
5990
+ const { name = '', id = '', type = '', placeholder = '', label = '', autocomplete = '' } = attributes;
5991
+ const combined = `${name} ${id} ${type} ${placeholder} ${label} ${autocomplete}`.toLowerCase();
5992
+ for (const [fieldType, patterns] of Object.entries(FIELD_PATTERNS)) {
5993
+ for (const pattern of patterns) {
5994
+ if (pattern.test(combined)) {
5995
+ return fieldType;
5996
+ }
5997
+ }
5998
+ }
5999
+ return 'unknown';
6000
+ }
6001
+ export async function handleFormAutomator(page, args) {
6002
+ const action = args.action || 'fill';
6003
+ const formSelector = args.formSelector || 'form';
6004
+ const humanLike = args.humanLike !== false;
6005
+ const clearFields = args.clearFields !== false;
6006
+ const delayBetweenFields = args.delayBetweenFields || 500;
6007
+ try {
6008
+ // Get form fields
6009
+ const getFormFields = async () => {
6010
+ return await page.evaluate((selector) => {
6011
+ const form = document.querySelector(selector);
6012
+ if (!form)
6013
+ return [];
6014
+ const inputs = form.querySelectorAll('input, textarea, select');
6015
+ const fields = [];
6016
+ inputs.forEach((input, index) => {
6017
+ // Find associated label
6018
+ let label = '';
6019
+ if (input.id) {
6020
+ const labelEl = document.querySelector(`label[for="${input.id}"]`);
6021
+ if (labelEl)
6022
+ label = labelEl.textContent?.trim() || '';
6023
+ }
6024
+ if (!label) {
6025
+ const parent = input.closest('label');
6026
+ if (parent)
6027
+ label = parent.textContent?.replace(input.value || '', '').trim() || '';
6028
+ }
6029
+ fields.push({
6030
+ index,
6031
+ tagName: input.tagName.toLowerCase(),
6032
+ type: input.type || 'text',
6033
+ name: input.name || '',
6034
+ id: input.id || '',
6035
+ placeholder: input.placeholder || '',
6036
+ label,
6037
+ autocomplete: input.autocomplete || '',
6038
+ required: input.required,
6039
+ value: input.value || '',
6040
+ visible: input.offsetWidth > 0 && input.offsetHeight > 0,
6041
+ selector: input.id ? `#${input.id}` :
6042
+ input.name ? `[name="${input.name}"]` :
6043
+ `${selector} ${input.tagName.toLowerCase()}:nth-of-type(${index + 1})`
6044
+ });
6045
+ });
6046
+ return fields;
6047
+ }, formSelector);
6048
+ };
6049
+ // Human-like typing function
6050
+ const humanType = async (selector, text) => {
6051
+ const element = await page.$(selector);
6052
+ if (!element)
6053
+ throw new Error(`Element not found: ${selector}`);
6054
+ // Focus and clear field
6055
+ await element.click();
6056
+ await page.keyboard.down('Control');
6057
+ await page.keyboard.press('a');
6058
+ await page.keyboard.up('Control');
6059
+ await page.keyboard.press('Backspace');
6060
+ const minDelay = 50;
6061
+ const maxDelay = 150;
6062
+ const errorRate = 0.02;
6063
+ for (let i = 0; i < text.length; i++) {
6064
+ const char = text[i];
6065
+ // Random delay between keystrokes
6066
+ const delay = minDelay + Math.random() * (maxDelay - minDelay);
6067
+ await new Promise(r => setTimeout(r, delay));
6068
+ // Simulate occasional typo
6069
+ if (Math.random() < errorRate && i > 0 && i < text.length - 1) {
6070
+ const wrongChar = String.fromCharCode(char.charCodeAt(0) + (Math.random() > 0.5 ? 1 : -1));
6071
+ await page.keyboard.type(wrongChar);
6072
+ await new Promise(r => setTimeout(r, 200));
6073
+ await page.keyboard.press('Backspace');
6074
+ await new Promise(r => setTimeout(r, 50 + Math.random() * 50));
6075
+ }
6076
+ await page.keyboard.type(char);
6077
+ }
6078
+ };
6079
+ // Handle different actions
6080
+ switch (action) {
6081
+ case 'getFields': {
6082
+ const fields = await getFormFields();
6083
+ const detectedFields = fields.map(f => ({
6084
+ ...f,
6085
+ detectedType: detectFieldType(f)
6086
+ }));
6087
+ return {
6088
+ success: true,
6089
+ formFound: fields.length > 0,
6090
+ totalFields: fields.length,
6091
+ visibleFields: fields.filter(f => f.visible).length,
6092
+ fields: detectedFields,
6093
+ message: `Found ${fields.length} fields in form`
6094
+ };
6095
+ }
6096
+ case 'fillField': {
6097
+ if (!args.fieldSelector || !args.fieldValue) {
6098
+ throw new Error('fieldSelector and fieldValue are required for fillField action');
6099
+ }
6100
+ if (humanLike) {
6101
+ await humanType(args.fieldSelector, args.fieldValue);
6102
+ }
6103
+ else {
6104
+ if (clearFields) {
6105
+ await page.$eval(args.fieldSelector, (el) => el.value = '');
6106
+ }
6107
+ await page.type(args.fieldSelector, args.fieldValue);
6108
+ }
6109
+ return {
6110
+ success: true,
6111
+ field: args.fieldSelector,
6112
+ value: args.fieldValue,
6113
+ message: `Field filled successfully`
6114
+ };
6115
+ }
6116
+ case 'submit': {
6117
+ const submitSelector = args.submitSelector || 'button[type="submit"], input[type="submit"]';
6118
+ const waitForNav = args.waitForNavigation !== false;
6119
+ const submitButton = await page.$(submitSelector);
6120
+ if (waitForNav) {
6121
+ const navigationPromise = page.waitForNavigation({ timeout: 30000 }).catch(() => null);
6122
+ if (submitButton) {
6123
+ await submitButton.click();
6124
+ }
6125
+ else {
6126
+ await page.$eval(formSelector, (f) => f.submit());
6127
+ }
6128
+ await navigationPromise;
6129
+ }
6130
+ else {
6131
+ if (submitButton) {
6132
+ await submitButton.click();
6133
+ }
6134
+ else {
6135
+ await page.$eval(formSelector, (f) => f.submit());
6136
+ }
6137
+ }
6138
+ return {
6139
+ success: true,
6140
+ submitted: true,
6141
+ currentUrl: page.url(),
6142
+ message: 'Form submitted successfully'
6143
+ };
6144
+ }
6145
+ case 'autoFill': {
6146
+ // Default test data
6147
+ const defaultData = {
6148
+ email: 'test@example.com',
6149
+ password: 'SecurePass123!',
6150
+ username: 'testuser',
6151
+ firstName: 'John',
6152
+ lastName: 'Doe',
6153
+ fullName: 'John Doe',
6154
+ phone: '+1-555-123-4567',
6155
+ address: '123 Main Street',
6156
+ city: 'New York',
6157
+ state: 'NY',
6158
+ zip: '10001',
6159
+ country: 'United States',
6160
+ company: 'Acme Corp',
6161
+ website: 'https://example.com',
6162
+ message: 'This is a test message.',
6163
+ ...args.data
6164
+ };
6165
+ args.data = defaultData;
6166
+ // Fall through to fill action
6167
+ }
6168
+ case 'fill':
6169
+ default: {
6170
+ const data = args.data || {};
6171
+ const fields = await getFormFields();
6172
+ const filledFields = [];
6173
+ const errors = [];
6174
+ for (const field of fields) {
6175
+ if (!field.visible)
6176
+ continue;
6177
+ // Detect field type
6178
+ const fieldType = detectFieldType(field);
6179
+ // Find matching data
6180
+ let value = null;
6181
+ if (data[field.name])
6182
+ value = data[field.name];
6183
+ else if (data[field.id])
6184
+ value = data[field.id];
6185
+ else if (data[fieldType])
6186
+ value = data[fieldType];
6187
+ if (!value)
6188
+ continue;
6189
+ try {
6190
+ if (field.tagName === 'select') {
6191
+ await page.select(field.selector, value);
6192
+ }
6193
+ else if (field.type === 'checkbox') {
6194
+ const isChecked = await page.$eval(field.selector, (el) => el.checked);
6195
+ if ((value === true || value === 'true' || value === '1') && !isChecked) {
6196
+ await page.click(field.selector);
6197
+ }
6198
+ else if ((value === false || value === 'false' || value === '0') && isChecked) {
6199
+ await page.click(field.selector);
6200
+ }
6201
+ }
6202
+ else if (field.type === 'radio') {
6203
+ const radioSelector = `${field.selector}[value="${value}"]`;
6204
+ await page.click(radioSelector);
6205
+ }
6206
+ else {
6207
+ if (humanLike) {
6208
+ await humanType(field.selector, String(value));
6209
+ }
6210
+ else {
6211
+ if (clearFields) {
6212
+ await page.$eval(field.selector, (el) => el.value = '');
6213
+ }
6214
+ await page.type(field.selector, String(value));
6215
+ }
6216
+ }
6217
+ filledFields.push({
6218
+ field: field.name || field.id || field.selector,
6219
+ type: fieldType,
6220
+ success: true
6221
+ });
6222
+ // Delay between fields
6223
+ if (humanLike) {
6224
+ await new Promise(r => setTimeout(r, delayBetweenFields * (0.5 + Math.random())));
6225
+ }
6226
+ }
6227
+ catch (err) {
6228
+ errors.push({
6229
+ field: field.name || field.id || field.selector,
6230
+ error: err.message
6231
+ });
6232
+ }
6233
+ }
6234
+ // Submit if requested
6235
+ let submitted = false;
6236
+ if (args.submitAfter) {
6237
+ try {
6238
+ const submitSelector = args.submitSelector || 'button[type="submit"], input[type="submit"]';
6239
+ const submitButton = await page.$(submitSelector);
6240
+ if (args.waitForNavigation !== false) {
6241
+ const navigationPromise = page.waitForNavigation({ timeout: 30000 }).catch(() => null);
6242
+ if (submitButton) {
6243
+ await submitButton.click();
6244
+ }
6245
+ else {
6246
+ await page.$eval(formSelector, (f) => f.submit());
6247
+ }
6248
+ await navigationPromise;
6249
+ }
6250
+ else {
6251
+ if (submitButton) {
6252
+ await submitButton.click();
6253
+ }
6254
+ else {
6255
+ await page.$eval(formSelector, (f) => f.submit());
6256
+ }
6257
+ }
6258
+ submitted = true;
6259
+ }
6260
+ catch (err) {
6261
+ errors.push({ field: 'submit', error: err.message });
6262
+ }
6263
+ }
6264
+ return {
6265
+ success: errors.length === 0,
6266
+ filledFields,
6267
+ errors,
6268
+ submitted,
6269
+ totalFields: fields.filter(f => f.visible).length,
6270
+ currentUrl: page.url(),
6271
+ message: `Filled ${filledFields.length} fields${submitted ? ' and submitted form' : ''}`
6272
+ };
6273
+ }
6274
+ }
6275
+ }
6276
+ catch (error) {
6277
+ return {
6278
+ success: false,
6279
+ message: `Form automator error: ${error instanceof Error ? error.message : String(error)}`
6280
+ };
6281
+ }
6282
+ }
package/dist/index.js CHANGED
@@ -67,7 +67,9 @@ handleFileDownloader,
67
67
  // Enhanced streaming/download tools
68
68
  handleIframeHandler, handleStreamExtractor, handleJsScrape,
69
69
  // New JS extraction tools
70
- handleExecuteJs, handlePlayerApiHook, } from './handlers/advanced-tools.js';
70
+ handleExecuteJs, handlePlayerApiHook,
71
+ // Form automation
72
+ handleFormAutomator, } from './handlers/advanced-tools.js';
71
73
  // State for video recording
72
74
  const recorderState = new Map();
73
75
  debug('All modules loaded successfully');
@@ -256,6 +258,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
256
258
  if (!page)
257
259
  throw new Error('Browser not initialized. Call browser_init first.');
258
260
  return { content: [{ type: 'text', text: JSON.stringify(await handlePlayerApiHook(page, args)) }] };
261
+ case TOOL_NAMES.FORM_AUTOMATOR:
262
+ if (!page)
263
+ throw new Error('Browser not initialized. Call browser_init first.');
264
+ return { content: [{ type: 'text', text: JSON.stringify(await handleFormAutomator(page, args)) }] };
259
265
  default:
260
266
  throw new Error(`Unknown tool: ${name}`);
261
267
  }
@@ -914,6 +914,71 @@ export const TOOLS = [
914
914
  },
915
915
  },
916
916
  },
917
+ // Form Automator Tool
918
+ {
919
+ name: 'form_automator',
920
+ description: 'Auto-fill forms with human-like typing, field detection, and smart form submission. Supports auto-detection of field types (email, password, name, phone, address, etc.)',
921
+ inputSchema: {
922
+ type: 'object',
923
+ additionalProperties: false,
924
+ properties: {
925
+ action: {
926
+ type: 'string',
927
+ enum: ['fill', 'autoFill', 'getFields', 'submit', 'fillField'],
928
+ description: 'Action to perform: fill (with custom data), autoFill (with test data), getFields (detect fields), submit, fillField (single field)',
929
+ default: 'fill'
930
+ },
931
+ formSelector: {
932
+ type: 'string',
933
+ description: 'CSS selector for the form (default: "form")',
934
+ default: 'form'
935
+ },
936
+ data: {
937
+ type: 'object',
938
+ description: 'Data to fill in form. Keys can be field names, IDs, or types (email, password, username, firstName, lastName, phone, address, etc.)',
939
+ additionalProperties: true
940
+ },
941
+ fieldSelector: {
942
+ type: 'string',
943
+ description: 'CSS selector for specific field (used with fillField action)'
944
+ },
945
+ fieldValue: {
946
+ type: 'string',
947
+ description: 'Value to fill in specific field (used with fillField action)'
948
+ },
949
+ humanLike: {
950
+ type: 'boolean',
951
+ description: 'Use human-like typing with variable delays and occasional typos',
952
+ default: true
953
+ },
954
+ clearFields: {
955
+ type: 'boolean',
956
+ description: 'Clear existing field values before filling',
957
+ default: true
958
+ },
959
+ submitAfter: {
960
+ type: 'boolean',
961
+ description: 'Submit form after filling',
962
+ default: false
963
+ },
964
+ submitSelector: {
965
+ type: 'string',
966
+ description: 'CSS selector for submit button',
967
+ default: 'button[type="submit"], input[type="submit"]'
968
+ },
969
+ waitForNavigation: {
970
+ type: 'boolean',
971
+ description: 'Wait for page navigation after submit',
972
+ default: true
973
+ },
974
+ delayBetweenFields: {
975
+ type: 'number',
976
+ description: 'Delay in ms between filling fields (for human-like behavior)',
977
+ default: 500
978
+ }
979
+ }
980
+ },
981
+ },
917
982
  ];
918
983
  // Tool name constants for type safety
919
984
  export const TOOL_NAMES = {
@@ -956,6 +1021,8 @@ export const TOOL_NAMES = {
956
1021
  // New JS extraction tools
957
1022
  EXECUTE_JS: 'execute_js',
958
1023
  PLAYER_API_HOOK: 'player_api_hook',
1024
+ // Form automation
1025
+ FORM_AUTOMATOR: 'form_automator',
959
1026
  };
960
1027
  // Tool categories for organization
961
1028
  export const TOOL_CATEGORIES = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.29.0",
3
+ "version": "2.30.0",
4
4
  "description": "๐Ÿฆ MCP server for Brave Real Browser - NPM Workspaces Monorepo with anti-detection features, SSE streaming, and LSP compatibility",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "@modelcontextprotocol/sdk": "latest",
52
52
  "@types/turndown": "latest",
53
- "brave-real-browser": "^2.9.0",
53
+ "brave-real-browser": "^2.10.0",
54
54
  "puppeteer-core": "^24.35.0",
55
55
  "turndown": "latest",
56
56
  "vscode-languageserver": "^9.0.1",