@roxybrowser/openapi 1.0.4 → 1.0.7

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 (47) hide show
  1. package/README.md +228 -226
  2. package/lib/index.js +93 -1610
  3. package/lib/index.js.map +1 -1
  4. package/lib/modules/account.d.ts +491 -0
  5. package/lib/modules/account.d.ts.map +1 -0
  6. package/lib/modules/account.js +442 -0
  7. package/lib/modules/account.js.map +1 -0
  8. package/lib/modules/browser.d.ts +3011 -0
  9. package/lib/modules/browser.d.ts.map +1 -0
  10. package/lib/modules/browser.js +1076 -0
  11. package/lib/modules/browser.js.map +1 -0
  12. package/lib/modules/other.d.ts +102 -0
  13. package/lib/modules/other.d.ts.map +1 -0
  14. package/lib/modules/other.js +194 -0
  15. package/lib/modules/other.js.map +1 -0
  16. package/lib/modules/proxy.d.ts +576 -0
  17. package/lib/modules/proxy.d.ts.map +1 -0
  18. package/lib/modules/proxy.js +713 -0
  19. package/lib/modules/proxy.js.map +1 -0
  20. package/lib/types.d.ts +860 -155
  21. package/lib/types.d.ts.map +1 -1
  22. package/lib/types.js +4 -4
  23. package/lib/types.js.map +1 -1
  24. package/lib/utils/error-analyzer.d.ts.map +1 -1
  25. package/lib/utils/error-analyzer.js +17 -15
  26. package/lib/utils/error-analyzer.js.map +1 -1
  27. package/lib/utils/index.d.ts +24 -0
  28. package/lib/utils/index.d.ts.map +1 -0
  29. package/lib/utils/index.js +45 -0
  30. package/lib/utils/index.js.map +1 -0
  31. package/package.json +50 -50
  32. package/lib/browser/browser-creator.d.ts +0 -54
  33. package/lib/browser/browser-creator.d.ts.map +0 -1
  34. package/lib/browser/browser-creator.js +0 -263
  35. package/lib/browser/browser-creator.js.map +0 -1
  36. package/lib/proxy/proxy-manager.d.ts +0 -67
  37. package/lib/proxy/proxy-manager.d.ts.map +0 -1
  38. package/lib/proxy/proxy-manager.js +0 -278
  39. package/lib/proxy/proxy-manager.js.map +0 -1
  40. package/lib/proxy/proxy-validator.d.ts +0 -66
  41. package/lib/proxy/proxy-validator.d.ts.map +0 -1
  42. package/lib/proxy/proxy-validator.js +0 -273
  43. package/lib/proxy/proxy-validator.js.map +0 -1
  44. package/lib/roxy-client.d.ts +0 -117
  45. package/lib/roxy-client.d.ts.map +0 -1
  46. package/lib/roxy-client.js +0 -467
  47. package/lib/roxy-client.js.map +0 -1
package/lib/index.js CHANGED
@@ -8,853 +8,59 @@
8
8
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
9
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
10
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
11
- import { RoxyClient } from './roxy-client.js';
12
- import { BrowserCreator } from './browser/browser-creator.js';
13
- import { ProxyManager } from './proxy/proxy-manager.js';
11
+ import { batchCreateAccounts, createAccount, deleteAccounts, listAccounts, modifyAccount } from './modules/account.js';
12
+ import { batchCreateBrowsers, clearLocalCache, clearServerCache, closeBrowsers, createBrowser, deleteBrowsers, getBrowserDetail, getConnectionInfo, listBrowsers, listLabels, openBrowser, randomFingerprint, updateBrowser } from './modules/browser.js';
13
+ import { healthCheck, listWorkspaces } from './modules/other.js';
14
+ import { batchCreateProxies, createProxy, deleteProxies, detectProxy, modifyProxy, proxyList, proxyStore } from './modules/proxy.js';
15
+ import { ConfigError } from './types.js';
14
16
  import { ErrorAnalyzer } from './utils/error-analyzer.js';
15
- import { ConfigError, RoxyApiError, BrowserCreationError, LATEST_CORE_VERSION, } from './types.js';
16
- // ========== Configuration ==========
17
- function getConfig() {
18
- const apiHost = process.env.ROXY_API_HOST || 'http://127.0.0.1:50000';
19
- const apiKey = process.env.ROXY_API_KEY || '';
20
- const timeout = process.env.ROXY_TIMEOUT ? parseInt(process.env.ROXY_TIMEOUT) : 30000;
21
- if (!apiKey) {
22
- throw new ConfigError('ROXY_API_KEY environment variable is required. ' +
23
- 'Get your API key from RoxyBrowser: API -> API配置 -> API Key');
24
- }
25
- return { apiHost, apiKey, timeout };
26
- }
27
17
  // ========== Tool Definitions ==========
28
18
  const TOOLS = [
29
- {
30
- name: 'roxy_list_workspaces',
31
- description: 'Get list of all workspaces and their projects from RoxyBrowser',
32
- inputSchema: {
33
- type: 'object',
34
- properties: {
35
- pageIndex: {
36
- type: 'number',
37
- description: 'Page index for pagination (default: 1)',
38
- default: 1,
39
- },
40
- pageSize: {
41
- type: 'number',
42
- description: 'Number of items per page (default: 15)',
43
- default: 15,
44
- },
45
- },
46
- },
47
- },
48
- {
49
- name: 'roxy_list_browsers',
50
- description: 'Get list of browsers in specified workspace/project',
51
- inputSchema: {
52
- type: 'object',
53
- properties: {
54
- workspaceId: {
55
- type: 'number',
56
- description: 'Workspace ID (required)',
57
- },
58
- projectIds: {
59
- type: 'string',
60
- description: 'Comma-separated project IDs (optional)',
61
- },
62
- windowName: {
63
- type: 'string',
64
- description: 'Filter by browser window name (optional)',
65
- },
66
- pageIndex: {
67
- type: 'number',
68
- description: 'Page index for pagination (default: 1)',
69
- default: 1,
70
- },
71
- pageSize: {
72
- type: 'number',
73
- description: 'Number of items per page (default: 15)',
74
- default: 15,
75
- },
76
- },
77
- required: ['workspaceId'],
78
- },
79
- },
80
- {
81
- name: 'roxy_open_browsers',
82
- description: 'Open multiple browsers and return their CDP WebSocket endpoints for automation',
83
- inputSchema: {
84
- type: 'object',
85
- properties: {
86
- workspaceId: {
87
- type: 'number',
88
- description: 'Workspace ID (required)',
89
- },
90
- dirIds: {
91
- type: 'array',
92
- items: { type: 'string' },
93
- description: 'Array of browser directory IDs to open (required)',
94
- },
95
- args: {
96
- type: 'array',
97
- items: { type: 'string' },
98
- description: 'Optional browser startup arguments',
99
- },
100
- },
101
- required: ['workspaceId', 'dirIds'],
102
- },
103
- },
104
- {
105
- name: 'roxy_close_browsers',
106
- description: 'Close multiple browsers by their directory IDs',
107
- inputSchema: {
108
- type: 'object',
109
- properties: {
110
- dirIds: {
111
- type: 'array',
112
- items: { type: 'string' },
113
- description: 'Array of browser directory IDs to close (required)',
114
- },
115
- },
116
- required: ['dirIds'],
117
- },
118
- },
119
- {
120
- name: 'roxy_delete_browsers',
121
- description: 'Delete multiple browsers permanently by their directory IDs',
122
- inputSchema: {
123
- type: 'object',
124
- properties: {
125
- workspaceId: {
126
- type: 'number',
127
- description: 'Workspace ID (required)',
128
- },
129
- dirIds: {
130
- type: 'array',
131
- items: { type: 'string' },
132
- description: 'Array of browser directory IDs to delete (required)',
133
- },
134
- },
135
- required: ['workspaceId', 'dirIds'],
136
- },
137
- },
138
- {
139
- name: 'roxy_create_browser_simple',
140
- description: 'Create a browser with simple configuration - ideal for quick setup with basic proxy and common options',
141
- inputSchema: {
142
- type: 'object',
143
- properties: {
144
- workspaceId: {
145
- type: 'number',
146
- description: 'Workspace ID (required)',
147
- },
148
- windowName: {
149
- type: 'string',
150
- description: 'Browser window name (optional)',
151
- },
152
- projectId: {
153
- type: 'number',
154
- description: 'Project ID (optional)',
155
- },
156
- windowRemark: {
157
- type: 'string',
158
- description: 'Window remarks/notes (optional)',
159
- },
160
- proxyHost: {
161
- type: 'string',
162
- description: 'Proxy server host/IP address (optional)',
163
- },
164
- proxyPort: {
165
- type: 'string',
166
- description: 'Proxy server port (optional)',
167
- },
168
- proxyUserName: {
169
- type: 'string',
170
- description: 'Proxy username (optional)',
171
- },
172
- proxyPassword: {
173
- type: 'string',
174
- description: 'Proxy password (optional)',
175
- },
176
- proxyType: {
177
- type: 'string',
178
- enum: ['HTTP', 'HTTPS', 'SOCKS5'],
179
- description: 'Proxy type (optional, default: HTTP)',
180
- },
181
- cookie: {
182
- type: 'array',
183
- description: 'Cookie list (optional)',
184
- },
185
- searchEngine: {
186
- type: 'string',
187
- enum: ['Google', 'Microsoft Bing', 'Yahoo', 'Yandex', 'DuckDuckGo'],
188
- description: 'Default search engine (optional, default: Google)',
189
- },
190
- labelIds: {
191
- type: 'array',
192
- items: { type: 'number' },
193
- description: 'Label IDs to assign (optional)',
194
- },
195
- },
196
- required: ['workspaceId'],
197
- },
198
- },
199
- {
200
- name: 'roxy_create_browser_standard',
201
- description: 'Create a browser with standard configuration - covers most common use cases with commonly used fingerprint settings',
202
- inputSchema: {
203
- type: 'object',
204
- properties: {
205
- workspaceId: {
206
- type: 'number',
207
- description: 'Workspace ID (required)',
208
- },
209
- windowName: {
210
- type: 'string',
211
- description: 'Browser window name (optional)',
212
- },
213
- projectId: {
214
- type: 'number',
215
- description: 'Project ID (optional)',
216
- },
217
- windowRemark: {
218
- type: 'string',
219
- description: 'Window remarks/notes (optional)',
220
- },
221
- os: {
222
- type: 'string',
223
- enum: ['Windows', 'macOS', 'Linux', 'IOS', 'Android'],
224
- description: 'Operating system (optional, default: Windows)',
225
- },
226
- osVersion: {
227
- type: 'string',
228
- description: 'OS version (optional, auto-selected based on OS)',
229
- },
230
- coreVersion: {
231
- type: 'string',
232
- enum: ['140', '138', '137', '136', '135', '133', '130', '125', '117', '109'],
233
- description: 'Browser core version (optional, default: 140)',
234
- },
235
- userAgent: {
236
- type: 'string',
237
- description: 'Custom user agent (optional)',
238
- },
239
- cookie: {
240
- type: 'array',
241
- description: 'Cookie list (optional)',
242
- },
243
- searchEngine: {
244
- type: 'string',
245
- enum: ['Google', 'Microsoft Bing', 'Yahoo', 'Yandex', 'DuckDuckGo'],
246
- description: 'Default search engine (optional)',
247
- },
248
- labelIds: {
249
- type: 'array',
250
- items: { type: 'number' },
251
- description: 'Label IDs to assign (optional)',
252
- },
253
- defaultOpenUrl: {
254
- type: 'array',
255
- items: { type: 'string' },
256
- description: 'URLs to open by default (optional)',
257
- },
258
- windowPlatformList: {
259
- type: 'array',
260
- items: {
261
- type: 'object',
262
- properties: {
263
- platformUrl: { type: 'string', description: 'Platform URL' },
264
- platformUserName: { type: 'string', description: 'Platform username' },
265
- platformPassword: { type: 'string', description: 'Platform password' },
266
- platformEfa: { type: 'string', description: 'Platform EFA' },
267
- platformRemarks: { type: 'string', description: 'Platform remarks' },
268
- },
269
- },
270
- description: 'Platform account information (optional)',
271
- },
272
- proxyInfo: {
273
- type: 'object',
274
- description: 'Complete proxy configuration object (optional)',
275
- properties: {
276
- proxyMethod: { type: 'string', enum: ['custom', 'choose', 'api'] },
277
- proxyCategory: { type: 'string', enum: ['noproxy', 'HTTP', 'HTTPS', 'SOCKS5', 'SSH'] },
278
- ipType: { type: 'string', enum: ['IPV4', 'IPV6'] },
279
- protocol: { type: 'string', enum: ['HTTP', 'HTTPS', 'SOCKS5'] },
280
- host: { type: 'string' },
281
- port: { type: 'string' },
282
- proxyUserName: { type: 'string' },
283
- proxyPassword: { type: 'string' },
284
- refreshUrl: { type: 'string' },
285
- checkChannel: { type: 'string', enum: ['IPRust.io', 'IP-API', 'IP123.in'] },
286
- },
287
- },
288
- fingerInfo: {
289
- type: 'object',
290
- description: 'Common fingerprint configuration (optional) - for full control use roxy_create_browser_advanced',
291
- properties: {
292
- // Language and timezone
293
- language: { type: 'string', description: 'Browser language (e.g., en-US)' },
294
- timeZone: { type: 'string', description: 'Browser timezone (e.g., GMT-5:00 America/New_York)' },
295
- // Window settings (commonly used)
296
- openWidth: { type: 'string', description: 'Window width (default: 1000)' },
297
- openHeight: { type: 'string', description: 'Window height (default: 1000)' },
298
- // Media settings (commonly adjusted)
299
- forbidAudio: { type: 'boolean', description: 'Enable/disable sound' },
300
- forbidImage: { type: 'boolean', description: 'Enable/disable image loading' },
301
- forbidMedia: { type: 'boolean', description: 'Enable/disable video playback' },
302
- // Common fingerprint settings
303
- webRTC: { type: 'number', enum: [0, 1, 2], description: 'WebRTC: 0=replace, 1=real, 2=disable' },
304
- canvas: { type: 'boolean', description: 'Canvas: random vs real' },
305
- webGL: { type: 'boolean', description: 'WebGL: random vs real' },
306
- },
307
- },
308
- },
309
- required: ['workspaceId'],
310
- },
311
- },
312
- {
313
- name: 'roxy_create_browser_advanced',
314
- description: 'Create a browser with complete configuration control - for expert users needing full parameter access',
315
- inputSchema: {
316
- type: 'object',
317
- properties: {
318
- workspaceId: {
319
- type: 'number',
320
- description: 'Workspace ID (required)',
321
- },
322
- windowName: {
323
- type: 'string',
324
- description: 'Browser window name (optional)',
325
- },
326
- projectId: {
327
- type: 'number',
328
- description: 'Project ID (optional)',
329
- },
330
- windowRemark: {
331
- type: 'string',
332
- description: 'Window remarks/notes (optional)',
333
- },
334
- os: {
335
- type: 'string',
336
- enum: ['Windows', 'macOS', 'Linux', 'IOS', 'Android'],
337
- description: 'Operating system (optional)',
338
- },
339
- osVersion: {
340
- type: 'string',
341
- description: 'OS version (optional)',
342
- },
343
- coreVersion: {
344
- type: 'string',
345
- enum: ['140', '138', '137', '136', '135', '133', '130', '125', '117', '109'],
346
- description: 'Browser core version (optional)',
347
- },
348
- userAgent: {
349
- type: 'string',
350
- description: 'Custom user agent (optional)',
351
- },
352
- cookie: {
353
- type: 'array',
354
- description: 'Cookie list (optional)',
355
- },
356
- searchEngine: {
357
- type: 'string',
358
- enum: ['Google', 'Microsoft Bing', 'Yahoo', 'Yandex', 'DuckDuckGo'],
359
- description: 'Default search engine (optional)',
360
- },
361
- labelIds: {
362
- type: 'array',
363
- items: { type: 'number' },
364
- description: 'Label IDs to assign (optional)',
365
- },
366
- defaultOpenUrl: {
367
- type: 'array',
368
- items: { type: 'string' },
369
- description: 'Default URLs to open (optional)',
370
- },
371
- windowPlatformList: {
372
- type: 'array',
373
- items: {
374
- type: 'object',
375
- properties: {
376
- platformUrl: { type: 'string', description: 'Platform URL' },
377
- platformUserName: { type: 'string', description: 'Platform username' },
378
- platformPassword: { type: 'string', description: 'Platform password' },
379
- platformEfa: { type: 'string', description: 'Platform EFA' },
380
- platformRemarks: { type: 'string', description: 'Platform remarks' },
381
- },
382
- },
383
- description: 'Platform account information (optional)',
384
- },
385
- proxyInfo: {
386
- type: 'object',
387
- description: 'Complete proxy configuration (optional)',
388
- properties: {
389
- proxyMethod: { type: 'string', enum: ['custom', 'choose', 'api'] },
390
- proxyCategory: { type: 'string', enum: ['noproxy', 'HTTP', 'HTTPS', 'SOCKS5', 'SSH'] },
391
- ipType: { type: 'string', enum: ['IPV4', 'IPV6'] },
392
- protocol: { type: 'string', enum: ['HTTP', 'HTTPS', 'SOCKS5'] },
393
- host: { type: 'string' },
394
- port: { type: 'string' },
395
- proxyUserName: { type: 'string' },
396
- proxyPassword: { type: 'string' },
397
- refreshUrl: { type: 'string' },
398
- checkChannel: { type: 'string', enum: ['IPRust.io', 'IP-API', 'IP123.in'] },
399
- },
400
- },
401
- fingerInfo: {
402
- type: 'object',
403
- description: 'Complete fingerprint configuration (optional)',
404
- properties: {
405
- // Language and timezone
406
- isLanguageBaseIp: { type: 'boolean', description: 'Follow IP for browser language' },
407
- language: { type: 'string', description: 'Custom browser language' },
408
- isDisplayLanguageBaseIp: { type: 'boolean', description: 'Follow IP for display language' },
409
- displayLanguage: { type: 'string', description: 'Custom display language' },
410
- isTimeZone: { type: 'boolean', description: 'Follow IP for timezone' },
411
- timeZone: { type: 'string', description: 'Custom timezone' },
412
- // Geolocation
413
- position: { type: 'number', enum: [0, 1, 2], description: 'Geolocation prompt: 0=ask, 1=allow, 2=deny' },
414
- isPositionBaseIp: { type: 'boolean', description: 'Follow IP for geolocation' },
415
- longitude: { type: 'string', description: 'Custom longitude' },
416
- latitude: { type: 'string', description: 'Custom latitude' },
417
- precisionPos: { type: 'string', description: 'Precision in meters' },
418
- // Media settings
419
- forbidAudio: { type: 'boolean', description: 'Enable/disable sound' },
420
- forbidImage: { type: 'boolean', description: 'Enable/disable image loading' },
421
- forbidMedia: { type: 'boolean', description: 'Enable/disable video playback' },
422
- // Window settings
423
- openWidth: { type: 'string', description: 'Window width' },
424
- openHeight: { type: 'string', description: 'Window height' },
425
- openBookmarks: { type: 'boolean', description: 'Enable bookmarks' },
426
- positionSwitch: { type: 'boolean', description: 'Window position switch' },
427
- windowRatioPosition: { type: 'string', description: 'Window position ratio' },
428
- isDisplayName: { type: 'boolean', description: 'Show window name in title bar' },
429
- // Sync settings
430
- syncBookmark: { type: 'boolean', description: 'Sync bookmarks' },
431
- syncHistory: { type: 'boolean', description: 'Sync history' },
432
- syncTab: { type: 'boolean', description: 'Sync tabs' },
433
- syncCookie: { type: 'boolean', description: 'Sync cookies' },
434
- syncExtensions: { type: 'boolean', description: 'Sync extensions' },
435
- syncPassword: { type: 'boolean', description: 'Sync saved passwords' },
436
- syncIndexedDb: { type: 'boolean', description: 'Sync IndexedDB' },
437
- syncLocalStorage: { type: 'boolean', description: 'Sync LocalStorage' },
438
- // Cleanup settings
439
- clearCacheFile: { type: 'boolean', description: 'Clear cache files on startup' },
440
- clearCookie: { type: 'boolean', description: 'Clear cookies on startup' },
441
- clearLocalStorage: { type: 'boolean', description: 'Clear LocalStorage on startup' },
442
- // Advanced settings
443
- randomFingerprint: { type: 'boolean', description: 'Generate random fingerprint' },
444
- forbidSavePassword: { type: 'boolean', description: 'Disable password save prompts' },
445
- stopOpenNet: { type: 'boolean', description: 'Stop opening if network fails' },
446
- stopOpenIP: { type: 'boolean', description: 'Stop opening if IP changes' },
447
- stopOpenPosition: { type: 'boolean', description: 'Stop opening if IP location changes' },
448
- openWorkbench: { type: 'number', enum: [0, 1, 2], description: 'Open workbench: 0=close, 1=open, 2=follow app' },
449
- // Display settings
450
- resolutionType: { type: 'boolean', description: 'Custom resolution vs follow system' },
451
- resolutionX: { type: 'string', description: 'Custom resolution width' },
452
- resolutionY: { type: 'string', description: 'Custom resolution height' },
453
- fontType: { type: 'boolean', description: 'Random fonts vs system fonts' },
454
- // Browser fingerprint settings
455
- webRTC: { type: 'number', enum: [0, 1, 2], description: 'WebRTC: 0=replace, 1=real, 2=disable' },
456
- webGL: { type: 'boolean', description: 'WebGL: random vs real' },
457
- webGLInfo: { type: 'boolean', description: 'WebGL info: custom vs real' },
458
- webGLManufacturer: { type: 'string', description: 'Custom WebGL manufacturer' },
459
- webGLRender: { type: 'string', description: 'Custom WebGL renderer' },
460
- webGpu: { type: 'string', enum: ['webgl', 'real', 'block'], description: 'WebGPU setting' },
461
- canvas: { type: 'boolean', description: 'Canvas: random vs real' },
462
- audioContext: { type: 'boolean', description: 'AudioContext: random vs real' },
463
- speechVoices: { type: 'boolean', description: 'Speech Voices: random vs real' },
464
- doNotTrack: { type: 'boolean', description: 'Enable Do Not Track' },
465
- clientRects: { type: 'boolean', description: 'ClientRects: random vs real' },
466
- deviceInfo: { type: 'boolean', description: 'Media devices: random vs real' },
467
- deviceNameSwitch: { type: 'boolean', description: 'Device names: random vs real' },
468
- macInfo: { type: 'boolean', description: 'MAC address: custom vs real' },
469
- // Hardware settings
470
- hardwareConcurrent: { type: 'string', description: 'Hardware concurrency' },
471
- deviceMemory: { type: 'string', description: 'Device memory' },
472
- // Security settings
473
- disableSsl: { type: 'boolean', description: 'SSL fingerprint settings' },
474
- disableSslList: { type: 'array', items: { type: 'string' }, description: 'SSL feature list' },
475
- portScanProtect: { type: 'boolean', description: 'Port scan protection' },
476
- portScanList: { type: 'string', description: 'Port scan whitelist' },
477
- useGpu: { type: 'boolean', description: 'Use GPU acceleration' },
478
- sandboxPermission: { type: 'boolean', description: 'Disable sandbox' },
479
- startupParam: { type: 'string', description: 'Browser startup parameters' },
480
- },
481
- },
482
- },
483
- required: ['workspaceId'],
484
- },
485
- },
486
- {
487
- name: 'roxy_validate_proxy_config',
488
- description: 'Validate proxy configuration before using it',
489
- inputSchema: {
490
- type: 'object',
491
- properties: {
492
- proxyInfo: {
493
- type: 'object',
494
- description: 'Proxy configuration to validate (required)',
495
- properties: {
496
- proxyMethod: { type: 'string', enum: ['custom', 'choose', 'api'] },
497
- proxyCategory: { type: 'string', enum: ['noproxy', 'HTTP', 'HTTPS', 'SOCKS5', 'SSH'] },
498
- ipType: { type: 'string', enum: ['IPV4', 'IPV6'] },
499
- protocol: { type: 'string', enum: ['HTTP', 'HTTPS', 'SOCKS5'] },
500
- host: { type: 'string' },
501
- port: { type: 'string' },
502
- proxyUserName: { type: 'string' },
503
- proxyPassword: { type: 'string' },
504
- refreshUrl: { type: 'string' },
505
- checkChannel: { type: 'string', enum: ['IPRust.io', 'IP-API', 'IP123.in'] },
506
- },
507
- },
508
- },
509
- required: ['proxyInfo'],
510
- },
511
- },
512
- {
513
- name: 'roxy_system_diagnostics',
514
- description: 'Perform comprehensive system diagnostics and health checks',
515
- inputSchema: {
516
- type: 'object',
517
- properties: {
518
- includeWorkspaceCheck: {
519
- type: 'boolean',
520
- description: 'Include workspace connectivity tests (optional, default: true)',
521
- default: true,
522
- },
523
- includeBrowserCheck: {
524
- type: 'boolean',
525
- description: 'Include browser availability checks (optional, default: true)',
526
- default: true,
527
- },
528
- verbose: {
529
- type: 'boolean',
530
- description: 'Include detailed diagnostic information (optional, default: false)',
531
- default: false,
532
- },
533
- },
534
- },
535
- },
536
- {
537
- name: 'roxy_list_accounts',
538
- description: 'Get list of accounts (platform credentials) in specified workspace',
539
- inputSchema: {
540
- type: 'object',
541
- properties: {
542
- workspaceId: {
543
- type: 'number',
544
- description: 'Workspace ID (required)',
545
- },
546
- accountId: {
547
- type: 'number',
548
- description: 'Account ID to filter by (optional)',
549
- },
550
- pageIndex: {
551
- type: 'number',
552
- description: 'Page index for pagination (default: 1)',
553
- default: 1,
554
- },
555
- pageSize: {
556
- type: 'number',
557
- description: 'Number of items per page (default: 15)',
558
- default: 15,
559
- },
560
- },
561
- required: ['workspaceId'],
562
- },
563
- },
564
- {
565
- name: 'roxy_list_labels',
566
- description: 'Get list of labels in specified workspace',
567
- inputSchema: {
568
- type: 'object',
569
- properties: {
570
- workspaceId: {
571
- type: 'number',
572
- description: 'Workspace ID (required)',
573
- },
574
- },
575
- required: ['workspaceId'],
576
- },
577
- },
578
- {
579
- name: 'roxy_get_connection_info',
580
- description: 'Get connection information (CDP endpoints, PIDs) for currently opened browsers',
581
- inputSchema: {
582
- type: 'object',
583
- properties: {
584
- dirIds: {
585
- type: 'array',
586
- items: { type: 'string' },
587
- description: 'Array of browser directory IDs to query (optional, returns all if not specified)',
588
- },
589
- },
590
- },
591
- },
592
- {
593
- name: 'roxy_get_browser_detail',
594
- description: 'Get detailed information for a specific browser window',
595
- inputSchema: {
596
- type: 'object',
597
- properties: {
598
- workspaceId: {
599
- type: 'number',
600
- description: 'Workspace ID (required)',
601
- },
602
- dirId: {
603
- type: 'string',
604
- description: 'Browser directory ID (required)',
605
- },
606
- },
607
- required: ['workspaceId', 'dirId'],
608
- },
609
- },
610
- {
611
- name: 'roxy_update_browser',
612
- description: 'Update/modify an existing browser configuration with full control over all settings',
613
- inputSchema: {
614
- type: 'object',
615
- properties: {
616
- workspaceId: {
617
- type: 'number',
618
- description: 'Workspace ID (required)',
619
- },
620
- dirId: {
621
- type: 'string',
622
- description: 'Browser directory ID to update (required)',
623
- },
624
- windowName: {
625
- type: 'string',
626
- description: 'Browser window name (optional)',
627
- },
628
- projectId: {
629
- type: 'number',
630
- description: 'Project ID (optional)',
631
- },
632
- windowRemark: {
633
- type: 'string',
634
- description: 'Window remarks/notes (optional)',
635
- },
636
- os: {
637
- type: 'string',
638
- enum: ['Windows', 'macOS', 'Linux', 'IOS', 'Android'],
639
- description: 'Operating system (optional)',
640
- },
641
- osVersion: {
642
- type: 'string',
643
- description: 'OS version (optional)',
644
- },
645
- coreVersion: {
646
- type: 'string',
647
- enum: ['140', '138', '137', '136', '135', '133', '130', '125', '117', '109'],
648
- description: 'Browser core version (optional)',
649
- },
650
- userAgent: {
651
- type: 'string',
652
- description: 'Custom user agent (optional)',
653
- },
654
- cookie: {
655
- type: 'array',
656
- description: 'Cookie list (optional)',
657
- },
658
- searchEngine: {
659
- type: 'string',
660
- enum: ['Google', 'Microsoft Bing', 'Yahoo', 'Yandex', 'DuckDuckGo'],
661
- description: 'Default search engine (optional)',
662
- },
663
- labelIds: {
664
- type: 'array',
665
- items: { type: 'number' },
666
- description: 'Label IDs to assign (optional)',
667
- },
668
- defaultOpenUrl: {
669
- type: 'array',
670
- items: { type: 'string' },
671
- description: 'Default URLs to open (optional)',
672
- },
673
- windowPlatformList: {
674
- type: 'array',
675
- items: {
676
- type: 'object',
677
- properties: {
678
- platformUrl: { type: 'string', description: 'Platform URL' },
679
- platformUserName: { type: 'string', description: 'Platform username' },
680
- platformPassword: { type: 'string', description: 'Platform password' },
681
- platformEfa: { type: 'string', description: 'Platform EFA' },
682
- platformRemarks: { type: 'string', description: 'Platform remarks' },
683
- },
684
- },
685
- description: 'Platform account information (optional)',
686
- },
687
- proxyInfo: {
688
- type: 'object',
689
- description: 'Complete proxy configuration (optional)',
690
- properties: {
691
- proxyMethod: { type: 'string', enum: ['custom', 'choose', 'api'] },
692
- proxyCategory: { type: 'string', enum: ['noproxy', 'HTTP', 'HTTPS', 'SOCKS5', 'SSH'] },
693
- ipType: { type: 'string', enum: ['IPV4', 'IPV6'] },
694
- protocol: { type: 'string', enum: ['HTTP', 'HTTPS', 'SOCKS5'] },
695
- host: { type: 'string' },
696
- port: { type: 'string' },
697
- proxyUserName: { type: 'string' },
698
- proxyPassword: { type: 'string' },
699
- refreshUrl: { type: 'string' },
700
- checkChannel: { type: 'string', enum: ['IPRust.io', 'IP-API', 'IP123.in'] },
701
- },
702
- },
703
- fingerInfo: {
704
- type: 'object',
705
- description: 'Complete fingerprint configuration (optional)',
706
- properties: {
707
- // Language and timezone
708
- isLanguageBaseIp: { type: 'boolean', description: 'Follow IP for browser language' },
709
- language: { type: 'string', description: 'Custom browser language' },
710
- isDisplayLanguageBaseIp: { type: 'boolean', description: 'Follow IP for display language' },
711
- displayLanguage: { type: 'string', description: 'Custom display language' },
712
- isTimeZone: { type: 'boolean', description: 'Follow IP for timezone' },
713
- timeZone: { type: 'string', description: 'Custom timezone' },
714
- // Geolocation
715
- position: { type: 'number', enum: [0, 1, 2], description: 'Geolocation prompt: 0=ask, 1=allow, 2=deny' },
716
- isPositionBaseIp: { type: 'boolean', description: 'Follow IP for geolocation' },
717
- longitude: { type: 'string', description: 'Custom longitude' },
718
- latitude: { type: 'string', description: 'Custom latitude' },
719
- precisionPos: { type: 'string', description: 'Precision in meters' },
720
- // Media settings
721
- forbidAudio: { type: 'boolean', description: 'Enable/disable sound' },
722
- forbidImage: { type: 'boolean', description: 'Enable/disable image loading' },
723
- forbidMedia: { type: 'boolean', description: 'Enable/disable video playback' },
724
- // Window settings
725
- openWidth: { type: 'string', description: 'Window width' },
726
- openHeight: { type: 'string', description: 'Window height' },
727
- openBookmarks: { type: 'boolean', description: 'Enable bookmarks' },
728
- positionSwitch: { type: 'boolean', description: 'Window position switch' },
729
- windowRatioPosition: { type: 'string', description: 'Window position ratio' },
730
- isDisplayName: { type: 'boolean', description: 'Show window name in title bar' },
731
- // Sync settings
732
- syncBookmark: { type: 'boolean', description: 'Sync bookmarks' },
733
- syncHistory: { type: 'boolean', description: 'Sync history' },
734
- syncTab: { type: 'boolean', description: 'Sync tabs' },
735
- syncCookie: { type: 'boolean', description: 'Sync cookies' },
736
- syncExtensions: { type: 'boolean', description: 'Sync extensions' },
737
- syncPassword: { type: 'boolean', description: 'Sync saved passwords' },
738
- syncIndexedDb: { type: 'boolean', description: 'Sync IndexedDB' },
739
- syncLocalStorage: { type: 'boolean', description: 'Sync LocalStorage' },
740
- // Cleanup settings
741
- clearCacheFile: { type: 'boolean', description: 'Clear cache files on startup' },
742
- clearCookie: { type: 'boolean', description: 'Clear cookies on startup' },
743
- clearLocalStorage: { type: 'boolean', description: 'Clear LocalStorage on startup' },
744
- // Advanced settings
745
- randomFingerprint: { type: 'boolean', description: 'Generate random fingerprint' },
746
- forbidSavePassword: { type: 'boolean', description: 'Disable password save prompts' },
747
- stopOpenNet: { type: 'boolean', description: 'Stop opening if network fails' },
748
- stopOpenIP: { type: 'boolean', description: 'Stop opening if IP changes' },
749
- stopOpenPosition: { type: 'boolean', description: 'Stop opening if IP location changes' },
750
- openWorkbench: { type: 'number', enum: [0, 1, 2], description: 'Open workbench: 0=close, 1=open, 2=follow app' },
751
- // Display settings
752
- resolutionType: { type: 'boolean', description: 'Custom resolution vs follow system' },
753
- resolutionX: { type: 'string', description: 'Custom resolution width' },
754
- resolutionY: { type: 'string', description: 'Custom resolution height' },
755
- fontType: { type: 'boolean', description: 'Random fonts vs system fonts' },
756
- // Browser fingerprint settings
757
- webRTC: { type: 'number', enum: [0, 1, 2], description: 'WebRTC: 0=replace, 1=real, 2=disable' },
758
- webGL: { type: 'boolean', description: 'WebGL: random vs real' },
759
- webGLInfo: { type: 'boolean', description: 'WebGL info: custom vs real' },
760
- webGLManufacturer: { type: 'string', description: 'Custom WebGL manufacturer' },
761
- webGLRender: { type: 'string', description: 'Custom WebGL renderer' },
762
- webGpu: { type: 'string', enum: ['webgl', 'real', 'block'], description: 'WebGPU setting' },
763
- canvas: { type: 'boolean', description: 'Canvas: random vs real' },
764
- audioContext: { type: 'boolean', description: 'AudioContext: random vs real' },
765
- speechVoices: { type: 'boolean', description: 'Speech Voices: random vs real' },
766
- doNotTrack: { type: 'boolean', description: 'Enable Do Not Track' },
767
- clientRects: { type: 'boolean', description: 'ClientRects: random vs real' },
768
- deviceInfo: { type: 'boolean', description: 'Media devices: random vs real' },
769
- deviceNameSwitch: { type: 'boolean', description: 'Device names: random vs real' },
770
- macInfo: { type: 'boolean', description: 'MAC address: custom vs real' },
771
- // Hardware settings
772
- hardwareConcurrent: { type: 'string', description: 'Hardware concurrency' },
773
- deviceMemory: { type: 'string', description: 'Device memory' },
774
- // Security settings
775
- disableSsl: { type: 'boolean', description: 'SSL fingerprint settings' },
776
- disableSslList: { type: 'array', items: { type: 'string' }, description: 'SSL feature list' },
777
- portScanProtect: { type: 'boolean', description: 'Port scan protection' },
778
- portScanList: { type: 'string', description: 'Port scan whitelist' },
779
- useGpu: { type: 'boolean', description: 'Use GPU acceleration' },
780
- sandboxPermission: { type: 'boolean', description: 'Disable sandbox' },
781
- startupParam: { type: 'string', description: 'Browser startup parameters' },
782
- },
783
- },
784
- },
785
- required: ['workspaceId', 'dirId'],
786
- },
787
- },
788
- {
789
- name: 'roxy_random_fingerprint',
790
- description: 'Randomize browser fingerprint for a specific browser',
791
- inputSchema: {
792
- type: 'object',
793
- properties: {
794
- workspaceId: {
795
- type: 'number',
796
- description: 'Workspace ID (required)',
797
- },
798
- dirId: {
799
- type: 'string',
800
- description: 'Browser directory ID (required)',
801
- },
802
- },
803
- required: ['workspaceId', 'dirId'],
804
- },
805
- },
806
- {
807
- name: 'roxy_clear_local_cache',
808
- description: 'Clear local cache for specified browsers',
809
- inputSchema: {
810
- type: 'object',
811
- properties: {
812
- dirIds: {
813
- type: 'array',
814
- items: { type: 'string' },
815
- description: 'Array of browser directory IDs (required)',
816
- },
817
- },
818
- required: ['dirIds'],
819
- },
820
- },
821
- {
822
- name: 'roxy_clear_server_cache',
823
- description: 'Clear server-side cache for specified browsers',
824
- inputSchema: {
825
- type: 'object',
826
- properties: {
827
- workspaceId: {
828
- type: 'number',
829
- description: 'Workspace ID (required)',
830
- },
831
- dirIds: {
832
- type: 'array',
833
- items: { type: 'string' },
834
- description: 'Array of browser directory IDs (required)',
835
- },
836
- },
837
- required: ['workspaceId', 'dirIds'],
838
- },
839
- },
19
+ listBrowsers.schema,
20
+ batchCreateBrowsers.schema,
21
+ createBrowser.schema,
22
+ openBrowser.schema,
23
+ updateBrowser.schema,
24
+ closeBrowsers.schema,
25
+ deleteBrowsers.schema,
26
+ getBrowserDetail.schema,
27
+ clearLocalCache.schema,
28
+ clearServerCache.schema,
29
+ randomFingerprint.schema,
30
+ listLabels.schema,
31
+ getConnectionInfo.schema,
32
+ proxyList.schema,
33
+ proxyStore.schema,
34
+ createProxy.schema,
35
+ batchCreateProxies.schema,
36
+ detectProxy.schema,
37
+ modifyProxy.schema,
38
+ deleteProxies.schema,
39
+ // getDetectChannels.schema,
40
+ listAccounts.schema,
41
+ createAccount.schema,
42
+ batchCreateAccounts.schema,
43
+ modifyAccount.schema,
44
+ deleteAccounts.schema,
45
+ listWorkspaces.schema,
46
+ healthCheck.schema,
840
47
  ];
841
48
  // ========== MCP Server ==========
842
49
  class RoxyBrowserMCPServer {
843
50
  server;
844
- roxyClient;
845
51
  constructor() {
846
52
  this.server = new Server({
847
- name: 'roxy-browser-mcp',
53
+ name: 'roxybrowser-openapi-mcp',
848
54
  version: '1.0.0',
849
55
  }, {
850
56
  capabilities: {
851
57
  tools: {},
852
58
  },
853
59
  });
854
- // Initialize RoxyBrowser client
855
- const config = getConfig();
856
- this.roxyClient = new RoxyClient(config);
857
60
  this.setupHandlers();
61
+ // Initialize RoxyBrowser client
62
+ // const config = getConfig()
63
+ // this.roxyClient = new RoxyClient(config)
858
64
  }
859
65
  setupHandlers() {
860
66
  // List available tools
@@ -866,42 +72,62 @@ class RoxyBrowserMCPServer {
866
72
  const { name, arguments: args } = request.params;
867
73
  try {
868
74
  switch (name) {
869
- case 'roxy_list_workspaces':
870
- return await this.handleListWorkspaces(args);
871
- case 'roxy_list_browsers':
872
- return await this.handleListBrowsers(args);
873
- case 'roxy_open_browsers':
874
- return await this.handleOpenBrowsers(args);
875
- case 'roxy_close_browsers':
876
- return await this.handleCloseBrowsers(args);
877
- case 'roxy_delete_browsers':
878
- return await this.handleDeleteBrowsers(args);
879
- case 'roxy_create_browser_simple':
880
- return await this.handleCreateBrowserSimple(args);
881
- case 'roxy_create_browser_standard':
882
- return await this.handleCreateBrowserStandard(args);
883
- case 'roxy_create_browser_advanced':
884
- return await this.handleCreateBrowserAdvanced(args);
885
- case 'roxy_validate_proxy_config':
886
- return await this.handleValidateProxyConfig(args);
887
- case 'roxy_system_diagnostics':
888
- return await this.handleSystemDiagnostics(args);
889
- case 'roxy_list_accounts':
890
- return await this.handleListAccounts(args);
891
- case 'roxy_list_labels':
892
- return await this.handleListLabels(args);
893
- case 'roxy_get_connection_info':
894
- return await this.handleGetConnectionInfo(args);
895
- case 'roxy_get_browser_detail':
896
- return await this.handleGetBrowserDetail(args);
897
- case 'roxy_update_browser':
898
- return await this.handleUpdateBrowser(args);
899
- case 'roxy_random_fingerprint':
900
- return await this.handleRandomFingerprint(args);
901
- case 'roxy_clear_local_cache':
902
- return await this.handleClearLocalCache(args);
903
- case 'roxy_clear_server_cache':
904
- return await this.handleClearServerCache(args);
75
+ case listBrowsers.name:
76
+ return await listBrowsers.handle(args);
77
+ case createBrowser.name:
78
+ return await createBrowser.handle(args);
79
+ case openBrowser.name:
80
+ return await openBrowser.handle(args);
81
+ case updateBrowser.name:
82
+ return await updateBrowser.handle(args);
83
+ case closeBrowsers.name:
84
+ return await closeBrowsers.handle(args);
85
+ case deleteBrowsers.name:
86
+ return await deleteBrowsers.handle(args);
87
+ case batchCreateBrowsers.name:
88
+ return await batchCreateBrowsers.handle(args);
89
+ case proxyList.name:
90
+ return await proxyList.handle(args);
91
+ case proxyStore.name:
92
+ return await proxyStore.handle(args);
93
+ case listWorkspaces.name:
94
+ return await listWorkspaces.handle(args);
95
+ case healthCheck.name:
96
+ return await healthCheck.handle(args);
97
+ case listAccounts.name:
98
+ return await listAccounts.handle(args);
99
+ case createAccount.name:
100
+ return await createAccount.handle(args);
101
+ case batchCreateAccounts.name:
102
+ return await batchCreateAccounts.handle(args);
103
+ case modifyAccount.name:
104
+ return await modifyAccount.handle(args);
105
+ case deleteAccounts.name:
106
+ return await deleteAccounts.handle(args);
107
+ case listLabels.name:
108
+ return await listLabels.handle(args);
109
+ case getConnectionInfo.name:
110
+ return await getConnectionInfo.handle(args);
111
+ case getBrowserDetail.name:
112
+ return await getBrowserDetail.handle(args);
113
+ case randomFingerprint.name:
114
+ return await randomFingerprint.handle(args);
115
+ case clearLocalCache.name:
116
+ return await clearLocalCache.handle(args);
117
+ case clearServerCache.name:
118
+ return await clearServerCache.handle(args);
119
+ case createProxy.name:
120
+ return await createProxy.handle(args);
121
+ case batchCreateProxies.name:
122
+ return await batchCreateProxies.handle(args);
123
+ case detectProxy.name:
124
+ return await detectProxy.handle(args);
125
+ case modifyProxy.name:
126
+ return await modifyProxy.handle(args);
127
+ case deleteProxies.name:
128
+ return await deleteProxies.handle(args);
129
+ // case getDetectChannels.name:
130
+ // return await getDetectChannels.handle(args)
905
131
  default:
906
132
  throw new Error(`Unknown tool: ${name}`);
907
133
  }
@@ -920,750 +146,7 @@ class RoxyBrowserMCPServer {
920
146
  }
921
147
  });
922
148
  }
923
- async handleCreateBrowserSimple(args) {
924
- const params = args;
925
- if (!params.workspaceId) {
926
- throw new Error('workspaceId is required');
927
- }
928
- try {
929
- // Build configuration from simple parameters
930
- const config = BrowserCreator.buildSimpleConfig(params);
931
- const finalConfig = BrowserCreator.applyDefaults(config);
932
- // Validate configuration
933
- const validation = BrowserCreator.validateConfig(finalConfig);
934
- if (!validation.valid) {
935
- throw new BrowserCreationError(`Configuration validation failed: ${validation.errors.join(', ')}`);
936
- }
937
- // Create browser
938
- const result = await this.roxyClient.createBrowser(finalConfig);
939
- const response = {
940
- browser: {
941
- dirId: result.dirId,
942
- windowName: finalConfig.windowName || 'Simple Browser',
943
- workspaceId: params.workspaceId,
944
- projectId: params.projectId,
945
- proxyConfigured: !!(params.proxyHost && params.proxyPort),
946
- },
947
- message: `Browser created successfully with ID: ${result.dirId}`,
948
- };
949
- return {
950
- content: [
951
- {
952
- type: 'text',
953
- text: `✅ **Simple Browser Created**\n\n` +
954
- `**Browser ID:** \`${response.browser.dirId}\`\n` +
955
- `**Name:** ${response.browser.windowName}\n` +
956
- `**Workspace:** ${response.browser.workspaceId}\n` +
957
- `${response.browser.projectId ? `**Project:** ${response.browser.projectId}\n` : ''}` +
958
- `**Proxy:** ${response.browser.proxyConfigured ? '✅ Configured' : '❌ Not configured'}\n\n` +
959
- `*Use this browser ID with \`roxy_open_browsers\` to start the browser and get CDP endpoints for automation.*`,
960
- },
961
- ],
962
- };
963
- }
964
- catch (error) {
965
- // Check for quota error specifically
966
- if (error instanceof RoxyApiError && error.code === 409 &&
967
- error.message.includes('额度不足')) {
968
- return {
969
- content: [{
970
- type: 'text',
971
- text: `❌ **浏览器创建失败 - 窗口额度不足 / Browser Creation Failed - Insufficient Profiles Quota**\n\n` +
972
- `**错误信息 / Error:** ${error.message}\n\n` +
973
- `**解决步骤 / Solution Steps:**\n` +
974
- `1. 打开 RoxyBrowser 应用 / Open RoxyBrowser app\n` +
975
- `2. 前往费用中心 / Go to Billing Center\n` +
976
- `3. 购买或升级窗口套餐 / Purchase or upgrade profiles plan\n` +
977
- `4. 或者删除不需要的浏览器窗口以释放额度 / Or delete unused browser profiles to free up quota\n` +
978
- `5. 等待生效后重试创建 / Retry creation after quota is available\n\n` +
979
- `💡 **重要提示 / Important:** 必须使用 \`roxy_delete_browsers\` 删除浏览器才能释放额度,仅关闭浏览器无法释放额度。\n` +
980
- `You must use \`roxy_delete_browsers\` to delete profiles to free up quota. Simply closing browsers will NOT free up quota.`,
981
- }],
982
- };
983
- }
984
- // Use enhanced error analysis for other errors
985
- const formattedError = ErrorAnalyzer.formatErrorForDisplay(error instanceof Error ? error : new Error('Unknown error'));
986
- return {
987
- content: [{ type: 'text', text: formattedError }],
988
- };
989
- }
990
- }
991
- async handleCreateBrowserStandard(args) {
992
- const params = args;
993
- if (!params.workspaceId) {
994
- throw new Error('workspaceId is required');
995
- }
996
- try {
997
- // Build configuration from standard parameters
998
- const config = BrowserCreator.buildStandardConfig(params);
999
- const finalConfig = BrowserCreator.applyDefaults(config);
1000
- // Validate configuration
1001
- const validation = BrowserCreator.validateConfig(finalConfig);
1002
- if (!validation.valid) {
1003
- throw new BrowserCreationError(`Configuration validation failed: ${validation.errors.join(', ')}`);
1004
- }
1005
- // Create browser
1006
- const result = await this.roxyClient.createBrowser(finalConfig);
1007
- const response = {
1008
- browser: {
1009
- dirId: result.dirId,
1010
- windowName: finalConfig.windowName || 'Standard Browser',
1011
- workspaceId: params.workspaceId,
1012
- projectId: params.projectId,
1013
- os: finalConfig.os || 'Windows',
1014
- coreVersion: finalConfig.coreVersion || LATEST_CORE_VERSION,
1015
- proxyInfo: params.proxyInfo,
1016
- windowSize: `${params.openWidth || '1000'}x${params.openHeight || '1000'}`,
1017
- },
1018
- message: `Standard browser created successfully with ID: ${result.dirId}`,
1019
- };
1020
- return {
1021
- content: [
1022
- {
1023
- type: 'text',
1024
- text: `✅ **Standard Browser Created**\n\n` +
1025
- `**Browser ID:** \`${response.browser.dirId}\`\n` +
1026
- `**Name:** ${response.browser.windowName}\n` +
1027
- `**OS:** ${response.browser.os} ${finalConfig.osVersion || ''}\n` +
1028
- `**Core Version:** ${response.browser.coreVersion}\n` +
1029
- `**Window Size:** ${response.browser.windowSize}\n` +
1030
- `**Workspace:** ${response.browser.workspaceId}\n` +
1031
- `${response.browser.projectId ? `**Project:** ${response.browser.projectId}\n` : ''}` +
1032
- `**Proxy:** ${response.browser.proxyInfo ? '✅ Configured' : '❌ Not configured'}\n\n` +
1033
- `*Browser is ready for automation. Use \`roxy_open_browsers\` to start it.*`,
1034
- },
1035
- ],
1036
- };
1037
- }
1038
- catch (error) {
1039
- // Check for quota error specifically
1040
- if (error instanceof RoxyApiError && error.code === 409 &&
1041
- error.message.includes('额度不足')) {
1042
- return {
1043
- content: [{
1044
- type: 'text',
1045
- text: `❌ **浏览器创建失败 - 窗口额度不足 / Browser Creation Failed - Insufficient Profiles Quota**\n\n` +
1046
- `**错误信息 / Error:** ${error.message}\n\n` +
1047
- `**解决步骤 / Solution Steps:**\n` +
1048
- `1. 打开 RoxyBrowser 应用 / Open RoxyBrowser app\n` +
1049
- `2. 前往费用中心 / Go to Billing Center\n` +
1050
- `3. 购买或升级窗口套餐 / Purchase or upgrade profiles plan\n` +
1051
- `4. 或者删除不需要的浏览器窗口以释放额度 / Or delete unused browser profiles to free up quota\n` +
1052
- `5. 等待生效后重试创建 / Retry creation after quota is available\n\n` +
1053
- `💡 **重要提示 / Important:** 必须使用 \`roxy_delete_browsers\` 删除浏览器才能释放额度,仅关闭浏览器无法释放额度。\n` +
1054
- `You must use \`roxy_delete_browsers\` to delete profiles to free up quota. Simply closing browsers will NOT free up quota.`,
1055
- }],
1056
- };
1057
- }
1058
- // Use enhanced error analysis for other errors
1059
- const formattedError = ErrorAnalyzer.formatErrorForDisplay(error instanceof Error ? error : new Error('Unknown error'));
1060
- return {
1061
- content: [{ type: 'text', text: formattedError }],
1062
- };
1063
- }
1064
- }
1065
- async handleCreateBrowserAdvanced(args) {
1066
- const params = args;
1067
- if (!params.workspaceId) {
1068
- throw new Error('workspaceId is required');
1069
- }
1070
- try {
1071
- // Build configuration from advanced parameters
1072
- const config = BrowserCreator.buildAdvancedConfig(params);
1073
- const finalConfig = BrowserCreator.applyDefaults(config);
1074
- // Validate configuration
1075
- const validation = BrowserCreator.validateConfig(finalConfig);
1076
- if (!validation.valid) {
1077
- throw new BrowserCreationError(`Configuration validation failed: ${validation.errors.join(', ')}`);
1078
- }
1079
- // Create browser
1080
- const result = await this.roxyClient.createBrowser(finalConfig);
1081
- const response = {
1082
- browser: {
1083
- dirId: result.dirId,
1084
- config: finalConfig,
1085
- },
1086
- message: `Advanced browser created successfully with ID: ${result.dirId}`,
1087
- };
1088
- // Create detailed status text
1089
- const configSummary = [
1090
- `**Browser ID:** \`${response.browser.dirId}\``,
1091
- `**Name:** ${finalConfig.windowName || 'Advanced Browser'}`,
1092
- `**OS:** ${finalConfig.os || 'Windows'} ${finalConfig.osVersion || ''}`,
1093
- `**Core Version:** ${finalConfig.coreVersion || LATEST_CORE_VERSION}`,
1094
- finalConfig.userAgent ? `**User Agent:** ${finalConfig.userAgent.substring(0, 50)}...` : '',
1095
- `**Search Engine:** ${finalConfig.searchEngine || 'Google'}`,
1096
- finalConfig.proxyInfo?.proxyCategory !== 'noproxy' ? `**Proxy:** ✅ ${finalConfig.proxyInfo?.proxyCategory} ${finalConfig.proxyInfo?.host}:${finalConfig.proxyInfo?.port}` : '**Proxy:** ❌ No proxy',
1097
- finalConfig.fingerInfo?.randomFingerprint ? '**Fingerprint:** 🎲 Random' : '**Fingerprint:** 🔒 Fixed',
1098
- finalConfig.defaultOpenUrl?.length ? `**Default URLs:** ${finalConfig.defaultOpenUrl.length} URL(s)` : '',
1099
- ].filter(Boolean).join('\n');
1100
- return {
1101
- content: [
1102
- {
1103
- type: 'text',
1104
- text: `✅ **Advanced Browser Created**\n\n${configSummary}\n\n` +
1105
- `*Advanced browser configured with complete control. Use \`roxy_open_browsers\` to start it.*`,
1106
- },
1107
- ],
1108
- };
1109
- }
1110
- catch (error) {
1111
- // Check for quota error specifically
1112
- if (error instanceof RoxyApiError && error.code === 409 &&
1113
- error.message.includes('额度不足')) {
1114
- return {
1115
- content: [{
1116
- type: 'text',
1117
- text: `❌ **浏览器创建失败 - 窗口额度不足 / Browser Creation Failed - Insufficient Profiles Quota**\n\n` +
1118
- `**错误信息 / Error:** ${error.message}\n\n` +
1119
- `**解决步骤 / Solution Steps:**\n` +
1120
- `1. 打开 RoxyBrowser 应用 / Open RoxyBrowser app\n` +
1121
- `2. 前往费用中心 / Go to Billing Center\n` +
1122
- `3. 购买或升级窗口套餐 / Purchase or upgrade profiles plan\n` +
1123
- `4. 或者删除不需要的浏览器窗口以释放额度 / Or delete unused browser profiles to free up quota\n` +
1124
- `5. 等待生效后重试创建 / Retry creation after quota is available\n\n` +
1125
- `💡 **重要提示 / Important:** 必须使用 \`roxy_delete_browsers\` 删除浏览器才能释放额度,仅关闭浏览器无法释放额度。\n` +
1126
- `You must use \`roxy_delete_browsers\` to delete profiles to free up quota. Simply closing browsers will NOT free up quota.`,
1127
- }],
1128
- };
1129
- }
1130
- // Use enhanced error analysis for other errors
1131
- const formattedError = ErrorAnalyzer.formatErrorForDisplay(error instanceof Error ? error : new Error('Unknown error'));
1132
- return {
1133
- content: [{ type: 'text', text: formattedError }],
1134
- };
1135
- }
1136
- }
1137
- async handleValidateProxyConfig(args) {
1138
- const { proxyInfo } = args;
1139
- if (!proxyInfo) {
1140
- throw new Error('proxyInfo is required');
1141
- }
1142
- const validation = ProxyManager.validateProxy(proxyInfo);
1143
- let resultText = '';
1144
- if (validation.valid) {
1145
- resultText = `✅ **Proxy Configuration Valid**\n\n` +
1146
- `**Type:** ${proxyInfo.proxyCategory || 'Not specified'}\n` +
1147
- `**Host:** ${proxyInfo.host || 'Not specified'}\n` +
1148
- `**Port:** ${proxyInfo.port || 'Not specified'}\n` +
1149
- `**Authentication:** ${proxyInfo.proxyUserName ? '✅ Yes' : '❌ No'}\n` +
1150
- `**IP Type:** ${proxyInfo.ipType || 'IPV4'}`;
1151
- }
1152
- else {
1153
- resultText = `❌ **Proxy Configuration Invalid**\n\n` +
1154
- `**Errors:**\n` +
1155
- validation.errors.map(error => ` • ${error}`).join('\n');
1156
- }
1157
- if (validation.warnings.length > 0) {
1158
- resultText += `\n\n**⚠️ Warnings:**\n` +
1159
- validation.warnings.map(warning => ` • ${warning}`).join('\n');
1160
- }
1161
- return {
1162
- content: [
1163
- {
1164
- type: 'text',
1165
- text: resultText,
1166
- },
1167
- ],
1168
- };
1169
- }
1170
- async handleListWorkspaces(args) {
1171
- const { pageIndex = 1, pageSize = 15 } = args || {};
1172
- const data = await this.roxyClient.getWorkspaces(pageIndex, pageSize);
1173
- return {
1174
- content: [
1175
- {
1176
- type: 'text',
1177
- text: `Found ${data.total} workspaces:\n\n` +
1178
- data.rows.map(ws => `**${ws.workspaceName}** (ID: ${ws.id})\n` +
1179
- ws.project_details.map(proj => ` - ${proj.projectName} (ID: ${proj.projectId})`).join('\n')).join('\n\n'),
1180
- },
1181
- ],
1182
- };
1183
- }
1184
- async handleListBrowsers(args) {
1185
- const params = args;
1186
- if (!params.workspaceId) {
1187
- throw new Error('workspaceId is required');
1188
- }
1189
- const data = await this.roxyClient.getBrowsers({
1190
- workspaceId: params.workspaceId,
1191
- projectIds: params.projectIds,
1192
- windowName: params.windowName,
1193
- page_index: params.pageIndex || 1,
1194
- page_size: params.pageSize || 15,
1195
- });
1196
- return {
1197
- content: [
1198
- {
1199
- type: 'text',
1200
- text: `Found ${data.total} browsers in workspace ${params.workspaceId}:\n\n` +
1201
- data.rows.map(browser => `**${browser.windowName || 'Unnamed'}** (ID: ${browser.dirId})\n` +
1202
- ` - Project: ${browser.projectId}\n` +
1203
- ` - Sort: ${browser.sortNum}\n` +
1204
- ` - OS: ${browser.os}\n` +
1205
- ` - Status: ${browser.status}`).join('\n\n'),
1206
- },
1207
- ],
1208
- };
1209
- }
1210
- async handleOpenBrowsers(args) {
1211
- const params = args;
1212
- if (!params.workspaceId || !params.dirIds || params.dirIds.length === 0) {
1213
- throw new Error('workspaceId and dirIds are required');
1214
- }
1215
- const { successes, failures } = await this.roxyClient.openBrowsers(params.workspaceId, params.dirIds, params.args);
1216
- // Build success message
1217
- let message = '';
1218
- if (successes.length > 0) {
1219
- message += `✅ **Successfully opened ${successes.length} browser(s):**\n\n` +
1220
- successes.map(result => `**Browser ${result.dirId || 'Unknown'}** (PID: ${result.pid})\n` +
1221
- ` - CDP WebSocket: \`${result.ws}\`\n` +
1222
- ` - HTTP Endpoint: \`${result.http}\`\n` +
1223
- ` - Core Version: ${result.coreVersion}`).join('\n\n') +
1224
- '\n\n**Use these WebSocket URLs with playwright-mcp:**\n' +
1225
- '```bash\n' +
1226
- successes.map(result => `npx @playwright/mcp@latest --cdp-endpoint "${result.ws}"`).join('\n') +
1227
- '\n```';
1228
- }
1229
- // Build failure message with special handling for quota errors
1230
- if (failures.length > 0) {
1231
- if (successes.length > 0) {
1232
- message += '\n\n---\n\n';
1233
- }
1234
- // Check if any failures are quota errors (code 101 or 409)
1235
- const quotaErrors = failures.filter(f => f.errorCode === 101 || (f.errorCode === 409 && f.error.includes('额度不足')));
1236
- const otherErrors = failures.filter(f => !quotaErrors.includes(f));
1237
- if (quotaErrors.length > 0) {
1238
- message += `❌ **Failed to open ${quotaErrors.length} browser(s) - Insufficient Profiles Quota / 窗口额度不足:**\n\n`;
1239
- quotaErrors.forEach(failure => {
1240
- message += ` - Browser ID: \`${failure.dirId}\`\n Error: ${failure.error}\n`;
1241
- });
1242
- message += '\n**解决步骤 / Solution Steps:**\n';
1243
- message += '1. 打开 RoxyBrowser 应用 / Open RoxyBrowser app\n';
1244
- message += '2. 前往费用中心 / Go to Billing Center\n';
1245
- message += '3. 购买或升级窗口套餐 / Purchase or upgrade profiles plan\n';
1246
- message += '4. 或者删除不需要的浏览器窗口以释放额度 / Or delete unused browser profiles to free up quota\n\n';
1247
- message += '💡 **重要提示 / Important:** 必须使用 `roxy_delete_browsers` 删除浏览器才能释放额度,仅关闭浏览器无法释放额度。\n';
1248
- message += 'You must use `roxy_delete_browsers` to delete profiles to free up quota. Simply closing browsers will NOT free up quota.';
1249
- }
1250
- if (otherErrors.length > 0) {
1251
- if (quotaErrors.length > 0) {
1252
- message += '\n\n';
1253
- }
1254
- message += `❌ **Failed to open ${otherErrors.length} browser(s) - Other Errors:**\n\n`;
1255
- otherErrors.forEach(failure => {
1256
- message += ` - Browser ID: \`${failure.dirId}\`\n`;
1257
- message += ` Error: ${failure.error}\n`;
1258
- message += ` Retryable: ${failure.retryable ? '✅ Yes' : '❌ No'}\n`;
1259
- });
1260
- }
1261
- }
1262
- // If all failed
1263
- if (successes.length === 0 && failures.length > 0) {
1264
- message = `❌ **Failed to open all ${failures.length} browser(s)**\n\n` + message;
1265
- }
1266
- return {
1267
- content: [
1268
- {
1269
- type: 'text',
1270
- text: message,
1271
- },
1272
- ],
1273
- };
1274
- }
1275
- async handleCloseBrowsers(args) {
1276
- const params = args;
1277
- if (!params.dirIds || params.dirIds.length === 0) {
1278
- throw new Error('dirIds are required');
1279
- }
1280
- const results = await this.roxyClient.closeBrowsers(params.dirIds);
1281
- const successCount = results.filter(r => r.success).length;
1282
- const failureCount = results.filter(r => !r.success).length;
1283
- const successText = successCount > 0
1284
- ? `✅ Successfully closed ${successCount} browsers`
1285
- : '';
1286
- const failureText = failureCount > 0
1287
- ? `❌ Failed to close ${failureCount} browsers:\n` +
1288
- results.filter(r => !r.success).map(r => ` - ${r.dirId}: ${r.error}`).join('\n')
1289
- : '';
1290
- return {
1291
- content: [
1292
- {
1293
- type: 'text',
1294
- text: [successText, failureText].filter(Boolean).join('\n\n'),
1295
- },
1296
- ],
1297
- };
1298
- }
1299
- async handleDeleteBrowsers(args) {
1300
- const params = args;
1301
- if (!params.workspaceId || !params.dirIds || params.dirIds.length === 0) {
1302
- throw new Error('workspaceId and dirIds are required');
1303
- }
1304
- try {
1305
- await this.roxyClient.deleteBrowsers(params.workspaceId, params.dirIds);
1306
- const response = {
1307
- results: params.dirIds.map(dirId => ({
1308
- dirId,
1309
- success: true,
1310
- })),
1311
- successCount: params.dirIds.length,
1312
- failureCount: 0,
1313
- message: `Successfully deleted ${params.dirIds.length} browser(s)`,
1314
- };
1315
- return {
1316
- content: [
1317
- {
1318
- type: 'text',
1319
- text: `✅ **Browsers Deleted Successfully**\n\n` +
1320
- `**Count:** ${response.successCount} browser(s)\n` +
1321
- `**Workspace:** ${params.workspaceId}\n\n` +
1322
- `**Deleted Browsers:**\n` +
1323
- params.dirIds.map((dirId, index) => ` ${index + 1}. \`${dirId}\``).join('\n') +
1324
- `\n\n⚠️ **Warning:** Deleted browsers cannot be recovered. All browser data, profiles, and configurations have been permanently removed.`,
1325
- },
1326
- ],
1327
- };
1328
- }
1329
- catch (error) {
1330
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1331
- return {
1332
- content: [
1333
- {
1334
- type: 'text',
1335
- text: `❌ **Browser Deletion Failed**\n\n` +
1336
- `**Error:** ${errorMessage}\n` +
1337
- `**Workspace:** ${params.workspaceId}\n` +
1338
- `**Failed Browsers:** ${params.dirIds.length}\n\n` +
1339
- `**Browser IDs:**\n` +
1340
- params.dirIds.map((dirId, index) => ` ${index + 1}. \`${dirId}\``).join('\n'),
1341
- },
1342
- ],
1343
- };
1344
- }
1345
- }
1346
- async handleSystemDiagnostics(args) {
1347
- const { includeWorkspaceCheck = true, includeBrowserCheck = true, verbose = false } = args || {};
1348
- // Perform comprehensive diagnostics
1349
- const diagnostics = await this.roxyClient.performDiagnostics();
1350
- let diagnosticText = `## 🔍 系统诊断报告 / System Diagnostics Report\n\n`;
1351
- // Basic connectivity
1352
- diagnosticText += `### 🌐 连接状态 / Connectivity Status\n`;
1353
- diagnosticText += `- **API连接 / API Connection**: ${diagnostics.connected ? '✅ 已连接' : '❌ 连接失败'}\n`;
1354
- diagnosticText += `- **认证状态 / Authentication**: ${diagnostics.authentication ? '✅ 成功' : '❌ 失败'}\n`;
1355
- diagnosticText += `- **工作区访问 / Workspace Access**: ${diagnostics.workspaceAccess ? '✅ 正常' : '⚠️ 受限'}\n\n`;
1356
- // Additional workspace checks
1357
- if (includeWorkspaceCheck && diagnostics.connected && diagnostics.authentication) {
1358
- try {
1359
- const workspaces = await this.roxyClient.getWorkspaces(1, 5);
1360
- diagnosticText += `### 📁 工作区信息 / Workspace Information\n`;
1361
- diagnosticText += `- **可用工作区 / Available Workspaces**: ${workspaces.total}\n`;
1362
- if (workspaces.rows.length > 0) {
1363
- diagnosticText += `- **工作区详情 / Workspace Details**:\n`;
1364
- workspaces.rows.slice(0, 3).forEach(ws => {
1365
- const projectCount = ws.project_details.length;
1366
- diagnosticText += ` - ${ws.workspaceName} (ID: ${ws.id}) - ${projectCount} projects\n`;
1367
- });
1368
- if (workspaces.total > 3) {
1369
- diagnosticText += ` - ... and ${workspaces.total - 3} more\n`;
1370
- }
1371
- }
1372
- diagnosticText += '\n';
1373
- }
1374
- catch (error) {
1375
- diagnosticText += `### 📁 工作区信息 / Workspace Information\n`;
1376
- diagnosticText += `- **状态**: ❌ 无法获取工作区信息\n`;
1377
- diagnosticText += `- **错误**: ${error instanceof Error ? error.message : 'Unknown error'}\n\n`;
1378
- }
1379
- }
1380
- // Browser availability checks
1381
- if (includeBrowserCheck && diagnostics.connected && diagnostics.authentication && diagnostics.workspaceAccess) {
1382
- try {
1383
- // Get first workspace and check browsers
1384
- const workspaces = await this.roxyClient.getWorkspaces(1, 1);
1385
- if (workspaces.rows.length > 0) {
1386
- const firstWorkspace = workspaces.rows[0];
1387
- const browsers = await this.roxyClient.getBrowsers({
1388
- workspaceId: firstWorkspace.id,
1389
- page_index: 1,
1390
- page_size: 5,
1391
- });
1392
- diagnosticText += `### 🖥️ 浏览器状态 / Browser Status\n`;
1393
- diagnosticText += `- **检查工作区 / Checked Workspace**: ${firstWorkspace.workspaceName} (ID: ${firstWorkspace.id})\n`;
1394
- diagnosticText += `- **可用浏览器 / Available Browsers**: ${browsers.total}\n`;
1395
- if (browsers.rows.length > 0) {
1396
- diagnosticText += `- **浏览器示例 / Browser Examples**:\n`;
1397
- browsers.rows.slice(0, 3).forEach(browser => {
1398
- diagnosticText += ` - ${browser.windowName || 'Unnamed'} (${browser.os}) - Status: ${browser.status}\n`;
1399
- });
1400
- }
1401
- diagnosticText += '\n';
1402
- }
1403
- }
1404
- catch (error) {
1405
- diagnosticText += `### 🖥️ 浏览器状态 / Browser Status\n`;
1406
- diagnosticText += `- **状态**: ⚠️ 无法检查浏览器状态\n`;
1407
- diagnosticText += `- **原因**: ${error instanceof Error ? error.message : 'Unknown error'}\n\n`;
1408
- }
1409
- }
1410
- // Error details
1411
- if (diagnostics.errors.length > 0) {
1412
- diagnosticText += `### ❌ 检测到的问题 / Detected Issues\n`;
1413
- diagnostics.errors.forEach((error, index) => {
1414
- diagnosticText += `${index + 1}. ${error}\n`;
1415
- });
1416
- diagnosticText += '\n';
1417
- }
1418
- // Recommendations
1419
- if (diagnostics.recommendations.length > 0) {
1420
- diagnosticText += `### 💡 建议操作 / Recommendations\n`;
1421
- diagnostics.recommendations.slice(0, 8).forEach((rec, index) => {
1422
- diagnosticText += `${index + 1}. ${rec}\n`;
1423
- });
1424
- if (diagnostics.recommendations.length > 8) {
1425
- diagnosticText += `... and ${diagnostics.recommendations.length - 8} more\n`;
1426
- }
1427
- diagnosticText += '\n';
1428
- }
1429
- // Verbose information
1430
- if (verbose) {
1431
- diagnosticText += `### 🔧 详细信息 / Detailed Information\n`;
1432
- diagnosticText += `- **API主机 / API Host**: ${this.roxyClient.config.apiHost}\n`;
1433
- diagnosticText += `- **超时设置 / Timeout**: ${this.roxyClient.config.timeout}ms\n`;
1434
- diagnosticText += `- **诊断时间 / Diagnosis Time**: ${new Date().toISOString()}\n\n`;
1435
- }
1436
- // Overall status
1437
- const overallStatus = diagnostics.connected && diagnostics.authentication;
1438
- diagnosticText += `### 📋 总体状态 / Overall Status\n`;
1439
- diagnosticText += `**${overallStatus ? '✅ 系统正常运行' : '❌ 系统存在问题'}** / `;
1440
- diagnosticText += `**${overallStatus ? 'System Operating Normally' : 'System Issues Detected'}**\n\n`;
1441
- if (overallStatus) {
1442
- diagnosticText += `*系统已准备就绪,可以进行浏览器自动化操作。*\n`;
1443
- diagnosticText += `*System ready for browser automation operations.*`;
1444
- }
1445
- else {
1446
- diagnosticText += `*请解决上述问题后重新运行诊断。*\n`;
1447
- diagnosticText += `*Please resolve the issues above and run diagnostics again.*`;
1448
- }
1449
- return {
1450
- content: [
1451
- {
1452
- type: 'text',
1453
- text: diagnosticText,
1454
- },
1455
- ],
1456
- };
1457
- }
1458
- async handleListAccounts(args) {
1459
- const params = {
1460
- workspaceId: args.workspaceId,
1461
- accountId: args.accountId,
1462
- page_index: args.pageIndex || 1,
1463
- page_size: args.pageSize || 15,
1464
- };
1465
- if (!params.workspaceId) {
1466
- throw new Error('workspaceId is required');
1467
- }
1468
- const data = await this.roxyClient.getAccounts(params);
1469
- return {
1470
- content: [
1471
- {
1472
- type: 'text',
1473
- text: `Found ${data.total} accounts in workspace ${params.workspaceId}:\n\n` +
1474
- data.rows.map(account => `**${account.platformName}** (ID: ${account.id})\n` +
1475
- ` - Username: ${account.platformUserName}\n` +
1476
- ` - Platform URL: ${account.platformUrl}\n` +
1477
- ` - Remarks: ${account.platformRemarks || 'N/A'}\n` +
1478
- ` - Created: ${account.createTime}`).join('\n\n'),
1479
- },
1480
- ],
1481
- };
1482
- }
1483
- async handleListLabels(args) {
1484
- const { workspaceId } = args;
1485
- if (!workspaceId) {
1486
- throw new Error('workspaceId is required');
1487
- }
1488
- const labels = await this.roxyClient.getLabels(workspaceId);
1489
- return {
1490
- content: [
1491
- {
1492
- type: 'text',
1493
- text: `Found ${labels.length} labels in workspace ${workspaceId}:\n\n` +
1494
- labels.map(label => `**${label.name}** (ID: ${label.id})\n` +
1495
- ` - Color: ${label.color}`).join('\n\n'),
1496
- },
1497
- ],
1498
- };
1499
- }
1500
- async handleGetConnectionInfo(args) {
1501
- const { dirIds } = args;
1502
- const connections = await this.roxyClient.getConnectionInfo(dirIds);
1503
- if (connections.length === 0) {
1504
- return {
1505
- content: [
1506
- {
1507
- type: 'text',
1508
- text: '⚠️ No opened browsers found.\n\nUse `roxy_open_browsers` to open browsers first.',
1509
- },
1510
- ],
1511
- };
1512
- }
1513
- return {
1514
- content: [
1515
- {
1516
- type: 'text',
1517
- text: `Found ${connections.length} opened browser(s):\n\n` +
1518
- connections.map(conn => `**${conn.windowName || 'Unnamed'}** (${conn.dirId})\n` +
1519
- ` - PID: ${conn.pid}\n` +
1520
- ` - CDP WebSocket: \`${conn.ws}\`\n` +
1521
- ` - HTTP Endpoint: \`${conn.http}\`\n` +
1522
- ` - Core Version: ${conn.coreVersion}\n` +
1523
- ` - Driver: ${conn.driver}`).join('\n\n'),
1524
- },
1525
- ],
1526
- };
1527
- }
1528
- async handleGetBrowserDetail(args) {
1529
- const { workspaceId, dirId } = args;
1530
- if (!workspaceId || !dirId) {
1531
- throw new Error('workspaceId and dirId are required');
1532
- }
1533
- const detail = await this.roxyClient.getBrowserDetail(workspaceId, dirId);
1534
- // Save cookie count before removing cookies to save tokens
1535
- const cookieCount = detail.cookie?.length || 0;
1536
- // Create a copy without cookies (cookies can be very large)
1537
- const { cookie, ...detailWithoutCookies } = detail;
1538
- // Create summary
1539
- const summary = `**Browser Details Summary**\n\n` +
1540
- `**ID:** \`${detail.dirId}\`\n` +
1541
- `**Name:** ${detail.windowName}\n` +
1542
- `**Sort Number:** ${detail.windowSortNum}\n` +
1543
- `**Project:** ${detail.projectName} (ID: ${detail.projectId})\n` +
1544
- `**OS:** ${detail.os} ${detail.osVersion}\n` +
1545
- `**Core Version:** ${detail.coreVersion}\n` +
1546
- `**Search Engine:** ${detail.searchEngine}\n` +
1547
- `**Open Status:** ${detail.openStatus ? '✅ Opened' : '❌ Closed'}\n` +
1548
- `**Cookies:** ${cookieCount} stored (excluded from response to save tokens)\n\n` +
1549
- `**Full Details (JSON):**\n` +
1550
- '```json\n' +
1551
- JSON.stringify(detailWithoutCookies, null, 2) +
1552
- '\n```';
1553
- return {
1554
- content: [
1555
- {
1556
- type: 'text',
1557
- text: summary,
1558
- },
1559
- ],
1560
- };
1561
- }
1562
- async handleUpdateBrowser(args) {
1563
- const params = args;
1564
- if (!params.workspaceId || !params.dirId) {
1565
- throw new Error('workspaceId and dirId are required');
1566
- }
1567
- await this.roxyClient.updateBrowser(params);
1568
- return {
1569
- content: [
1570
- {
1571
- type: 'text',
1572
- text: `✅ **Browser Updated Successfully**\n\n` +
1573
- `**Browser ID:** \`${params.dirId}\`\n` +
1574
- `**Workspace:** ${params.workspaceId}\n\n` +
1575
- `*Browser configuration has been updated.*`,
1576
- },
1577
- ],
1578
- };
1579
- }
1580
- async handleRandomFingerprint(args) {
1581
- const { workspaceId, dirId } = args;
1582
- if (!workspaceId || !dirId) {
1583
- throw new Error('workspaceId and dirId are required');
1584
- }
1585
- await this.roxyClient.randomBrowserFingerprint(workspaceId, dirId);
1586
- return {
1587
- content: [
1588
- {
1589
- type: 'text',
1590
- text: `✅ **Browser Fingerprint Randomized**\n\n` +
1591
- `**Browser ID:** \`${dirId}\`\n` +
1592
- `**Workspace:** ${workspaceId}\n\n` +
1593
- `*Browser fingerprint has been randomized. Restart the browser to apply changes.*`,
1594
- },
1595
- ],
1596
- };
1597
- }
1598
- async handleClearLocalCache(args) {
1599
- const { dirIds } = args;
1600
- if (!dirIds || dirIds.length === 0) {
1601
- throw new Error('dirIds are required');
1602
- }
1603
- await this.roxyClient.clearBrowserLocalCache(dirIds);
1604
- return {
1605
- content: [
1606
- {
1607
- type: 'text',
1608
- text: `✅ **Local Cache Cleared**\n\n` +
1609
- `**Browser Count:** ${dirIds.length}\n\n` +
1610
- `**Browser IDs:**\n` +
1611
- dirIds.map((id, index) => ` ${index + 1}. \`${id}\``).join('\n'),
1612
- },
1613
- ],
1614
- };
1615
- }
1616
- async handleClearServerCache(args) {
1617
- const { workspaceId, dirIds } = args;
1618
- if (!workspaceId || !dirIds || dirIds.length === 0) {
1619
- throw new Error('workspaceId and dirIds are required');
1620
- }
1621
- await this.roxyClient.clearBrowserServerCache(workspaceId, dirIds);
1622
- return {
1623
- content: [
1624
- {
1625
- type: 'text',
1626
- text: `✅ **Server Cache Cleared**\n\n` +
1627
- `**Workspace:** ${workspaceId}\n` +
1628
- `**Browser Count:** ${dirIds.length}\n\n` +
1629
- `**Browser IDs:**\n` +
1630
- dirIds.map((id, index) => ` ${index + 1}. \`${id}\``).join('\n'),
1631
- },
1632
- ],
1633
- };
1634
- }
1635
149
  async run() {
1636
- // Enhanced connection testing with diagnostics
1637
- console.error('🔗 Performing RoxyBrowser API diagnostics...');
1638
- const diagnostics = await this.roxyClient.performDiagnostics();
1639
- if (!diagnostics.connected) {
1640
- console.error('❌ Failed to connect to RoxyBrowser API');
1641
- console.error('\n📋 Diagnostic Results:');
1642
- diagnostics.errors.forEach(error => console.error(` ❌ ${error}`));
1643
- if (diagnostics.recommendations.length > 0) {
1644
- console.error('\n💡 Recommendations:');
1645
- diagnostics.recommendations.slice(0, 5).forEach((rec, index) => {
1646
- console.error(` ${index + 1}. ${rec}`);
1647
- });
1648
- }
1649
- process.exit(1);
1650
- }
1651
- if (!diagnostics.authentication) {
1652
- console.error('❌ Authentication failed');
1653
- console.error('\n📋 Diagnostic Results:');
1654
- diagnostics.errors.forEach(error => console.error(` ❌ ${error}`));
1655
- if (diagnostics.recommendations.length > 0) {
1656
- console.error('\n💡 Recommendations:');
1657
- diagnostics.recommendations.slice(0, 5).forEach((rec, index) => {
1658
- console.error(` ${index + 1}. ${rec}`);
1659
- });
1660
- }
1661
- process.exit(1);
1662
- }
1663
- console.error('✅ API Diagnostics Passed:');
1664
- console.error(' ✓ Connection established');
1665
- console.error(' ✓ Authentication successful');
1666
- console.error(` ✓ Workspace access: ${diagnostics.workspaceAccess ? 'Yes' : 'Limited'}`);
1667
150
  console.error('🚀 Starting RoxyBrowser MCP Server...');
1668
151
  const transport = new StdioServerTransport();
1669
152
  await this.server.connect(transport);