const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { ListToolsRequestSchema, CallToolRequestSchema } = require('@modelcontextprotocol/sdk/types.js'); const tools = { openUrl: require('./tools/openUrl'), click: require('./tools/click'), type: require('./tools/type'), screenshot: require('./tools/screenshot'), getText: require('./tools/getText'), getLinks: require('./tools/getLinks'), waitForSelector: require('./tools/waitForSelector'), extractElements: require('./tools/extractElements'), smartClick: require('./tools/smartClick'), smartType: require('./tools/smartType'), getButtons: require('./tools/getButtons'), smartWaitNavigation: require('./tools/smartWaitNavigation'), getForms: require('./tools/getForms'), fillFormAuto: require('./tools/fillFormAuto'), agentFlow: require('./tools/agentFlow'), agentFlowV2: require('./tools/agentFlowV2'), closeBrowser: require('./tools/closeBrowser'), getURL: require('./tools/getURL'), getTitle: require('./tools/getTitle'), getHTML: require('./tools/getHTML'), goBack: require('./tools/goBack'), goForward: require('./tools/goForward'), reload: require('./tools/reload'), evaluateJS: require('./tools/evaluateJS'), evaluateJs: require('./tools/evaluateJS'), waitForURL: require('./tools/waitForURL'), newTab: require('./tools/newTab'), switchTab: require('./tools/switchTab'), listTabs: require('./tools/listTabs'), closeTab: require('./tools/closeTab'), setViewport: require('./tools/setViewport'), emulateDevice: require('./tools/emulateDevice'), hover: require('./tools/hover'), select: require('./tools/select'), pressKey: require('./tools/pressKey'), scrollTo: require('./tools/scrollTo'), pdf: require('./tools/pdf'), getCookies: require('./tools/getCookies'), setCookies: require('./tools/setCookies'), clearCookies: require('./tools/clearCookies'), getStorage: require('./tools/getStorage'), setStorage: require('./tools/setStorage'), clearStorage: require('./tools/clearStorage'), getNetworkLogs: require('./tools/getNetworkLogs'), blockResources: require('./tools/blockResources'), uploadFile: require('./tools/uploadFile'), getPerformanceMetrics: require('./tools/getPerformanceMetrics'), wait: require('./tools/wait'), enableVisualOverlay: require('./tools/enableVisualOverlay'), showToast: require('./tools/showToast'), highlightElement: require('./tools/highlightElement'), dragAndDrop: require('./tools/dragAndDrop'), annotatedScreenshot: require('./tools/annotatedScreenshot'), getInputValues: require('./tools/getInputValues'), waitForClickable: require('./tools/waitForClickable'), waitForElementVisible: require('./tools/waitForElementVisible'), waitForText: require('./tools/waitForText') }; const server = new Server({ name: 'browser', version: '3.1.1' }, { capabilities: { tools: {} } }); const toolSchemas = [ { name: 'open_url', description: 'Abre uma URL no navegador', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL para abrir' }, waitUntil: { type: 'string', enum: ['domcontentloaded', 'load', 'networkidle'] } }, required: ['url'] } }, { name: 'click', description: 'Clica em elemento pelo seletor CSS', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, button: { type: 'string', enum: ['left', 'right', 'middle'] }, double: { type: 'boolean' } }, required: ['selector'] } }, { name: 'type', description: 'Digita texto em campo', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, text: { type: 'string' }, clear: { type: 'boolean' }, slowly: { type: 'boolean' } }, required: ['selector', 'text'] } }, { name: 'screenshot', description: 'Captura screenshot', inputSchema: { type: 'object', properties: { fullPage: { type: 'boolean' }, element: { type: 'string' }, path: { type: 'string' } } } }, { name: 'get_text', description: 'Extrai texto', inputSchema: { type: 'object', properties: { selector: { type: 'string' } } } }, { name: 'get_links', description: 'Lista links', inputSchema: { type: 'object', properties: { selector: { type: 'string' } } } }, { name: 'wait_for_selector', description: 'Espera elemento', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, timeout: { type: 'number' }, state: { type: 'string', enum: ['attached', 'detached', 'visible', 'hidden'] } }, required: ['selector'] } }, { name: 'extract_elements', description: 'Extrai elementos com atributos', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, attributes: { type: 'array', items: { type: 'string' } } }, required: ['selector'] } }, { name: 'smart_click', description: 'Clica por texto visível', inputSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] } }, { name: 'smart_type', description: 'Digita por label/placeholder', inputSchema: { type: 'object', properties: { label: { type: 'string' }, text: { type: 'string' } }, required: ['label', 'text'] } }, { name: 'get_buttons', description: 'Lista botões', inputSchema: { type: 'object', properties: {} } }, { name: 'smart_wait_navigation', description: 'Espera mudança de URL', inputSchema: { type: 'object', properties: { timeout: { type: 'number' } } } }, { name: 'get_forms', description: 'Lista formulários', inputSchema: { type: 'object', properties: {} } }, { name: 'fill_form_auto', description: 'Preenche formulário', inputSchema: { type: 'object', properties: { data: { type: 'object' } }, required: ['data'] } }, { name: 'agent_flow', description: 'Fluxo automático simples', inputSchema: { type: 'object', properties: { goal: { type: 'string' }, data: { type: 'object' } }, required: ['goal'] } }, { name: 'agent_flow_v2', description: 'Fluxo com retry', inputSchema: { type: 'object', properties: { goal: { type: 'string' }, data: { type: 'object' }, retries: { type: 'number' } }, required: ['goal'] } }, { name: 'close_browser', description: 'Fecha navegador', inputSchema: { type: 'object', properties: {} } }, { name: 'get_url', description: 'Obtém URL atual', inputSchema: { type: 'object', properties: {} } }, { name: 'get_title', description: 'Obtém título', inputSchema: { type: 'object', properties: {} } }, { name: 'get_html', description: 'Obtém HTML', inputSchema: { type: 'object', properties: { selector: { type: 'string' } } } }, { name: 'go_back', description: 'Voltar no histórico', inputSchema: { type: 'object', properties: {} } }, { name: 'go_forward', description: 'Avançar no histórico', inputSchema: { type: 'object', properties: {} } }, { name: 'reload', description: 'Recarrega página', inputSchema: { type: 'object', properties: {} } }, { name: 'evaluate_js', description: 'Executa JavaScript', inputSchema: { type: 'object', properties: { expression: { type: 'string' }, selector: { type: 'string' } }, required: ['expression'] } }, { name: 'wait_for_url', description: 'Espera URL específica', inputSchema: { type: 'object', properties: { pattern: { type: 'string' }, timeout: { type: 'number' } }, required: ['pattern'] } }, { name: 'new_tab', description: 'Nova aba', inputSchema: { type: 'object', properties: { url: { type: 'string' } } } }, { name: 'switch_tab', description: 'Muda aba', inputSchema: { type: 'object', properties: { index: { type: 'number' } }, required: ['index'] } }, { name: 'list_tabs', description: 'Lista abas', inputSchema: { type: 'object', properties: {} } }, { name: 'close_tab', description: 'Fecha aba', inputSchema: { type: 'object', properties: { index: { type: 'number' } } } }, { name: 'set_viewport', description: 'Define viewport', inputSchema: { type: 'object', properties: { width: { type: 'number' }, height: { type: 'number' } }, required: ['width', 'height'] } }, { name: 'emulate_device', description: 'Emula dispositivo', inputSchema: { type: 'object', properties: { device: { type: 'string' } }, required: ['device'] } }, { name: 'hover', description: 'Faz hover', inputSchema: { type: 'object', properties: { selector: { type: 'string' } }, required: ['selector'] } }, { name: 'select', description: 'Seleciona opções', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, values: { type: 'array', items: { type: 'string' } } }, required: ['selector', 'values'] } }, { name: 'press_key', description: 'Pressiona tecla', inputSchema: { type: 'object', properties: { key: { type: 'string' }, selector: { type: 'string' } }, required: ['key'] } }, { name: 'scroll_to', description: 'Rola página', inputSchema: { type: 'object', properties: { position: { oneOf: [{ type: 'number' }, { type: 'string', enum: ['top', 'bottom'] }, { type: 'object', properties: { selector: { type: 'string' } } }] } } } }, { name: 'pdf', description: 'Gera PDF', inputSchema: { type: 'object', properties: { path: { type: 'string' }, format: { type: 'string' }, landscape: { type: 'boolean' } } } }, { name: 'get_cookies', description: 'Obtém cookies', inputSchema: { type: 'object', properties: { urls: { type: 'array', items: { type: 'string' } } } } }, { name: 'set_cookies', description: 'Define cookies', inputSchema: { type: 'object', properties: { cookies: { type: 'array' } }, required: ['cookies'] } }, { name: 'clear_cookies', description: 'Limpa cookies', inputSchema: { type: 'object', properties: {} } }, { name: 'get_storage', description: 'Obtém storage', inputSchema: { type: 'object', properties: {} } }, { name: 'set_storage', description: 'Define storage', inputSchema: { type: 'object', properties: { localStorage: { type: 'object' }, sessionStorage: { type: 'object' } } } }, { name: 'clear_storage', description: 'Limpa storage', inputSchema: { type: 'object', properties: {} } }, { name: 'get_network_logs', description: 'Logs de rede', inputSchema: { type: 'object', properties: { type: { type: 'string' }, url: { type: 'string' } } } }, { name: 'block_resources', description: 'Bloqueia recursos', inputSchema: { type: 'object', properties: { types: { type: 'array', items: { type: 'string' } } }, required: ['types'] } }, { name: 'upload_file', description: 'Upload de arquivo', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, filePaths: { oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }] } }, required: ['selector', 'filePaths'] } }, { name: 'get_performance_metrics', description: 'Métricas de performance', inputSchema: { type: 'object', properties: {} } }, { name: 'wait', description: 'Aguarda ms', inputSchema: { type: 'object', properties: { ms: { type: 'number' } }, required: ['ms'] } }, { name: 'enable_visual_overlay', description: 'Ativa cursor customizado e indicadores visuais de rolagem', inputSchema: { type: 'object', properties: {} } }, { name: 'show_toast', description: 'Mostra notificação toast na página', inputSchema: { type: 'object', properties: { message: { type: 'string' }, duration: { type: 'number' } } } }, { name: 'highlight_element', description: 'Destaca elemento com animação visual', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, color: { type: 'string' }, duration: { type: 'number' } }, required: ['selector'] } }, { name: 'drag_and_drop', description: 'Arrasta e solta elemento', inputSchema: { type: 'object', properties: { sourceSelector: { type: 'string' }, targetSelector: { type: 'string' } }, required: ['sourceSelector', 'targetSelector'] } }, { name: 'annotated_screenshot', description: 'Screenshot com elementos destacados', inputSchema: { type: 'object', properties: { fullPage: { type: 'boolean' }, path: { type: 'string' }, highlight: { oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }] } } } }, { name: 'get_input_values', description: 'Obtém valores de todos os inputs', inputSchema: { type: 'object', properties: { selector: { type: 'string' } } } }, { name: 'wait_for_clickable', description: 'Espera elemento ser clicável', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, timeout: { type: 'number' } }, required: ['selector'] } }, { name: 'wait_for_element_visible', description: 'Espera elemento visível', inputSchema: { type: 'object', properties: { selector: { type: 'string' }, timeout: { type: 'number' }, state: { type: 'string', enum: ['attached', 'detached', 'visible', 'hidden'] } }, required: ['selector'] } }, { name: 'wait_for_text', description: 'Espera texto aparecer na página', inputSchema: { type: 'object', properties: { text: { type: 'string' }, timeout: { type: 'number' } }, required: ['text'] } } ]; server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: toolSchemas })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; let toolName = name.replace(/-/g, '').replace(/_([a-z])/g, (_, c) => c.toUpperCase()); // Debug log console.error('[MCP] Request:', name, '→ Converted:', toolName); const nameMap = { // camelCase sem underscores 'geturl': 'getURL', 'gethtml': 'getHTML', 'goback': 'goBack', 'goforward': 'goForward', 'newtab': 'newTab', 'switchtab': 'switchTab', 'listtabs': 'listTabs', 'closetab': 'closeTab', 'setviewport': 'setViewport', 'emulatedevice': 'emulateDevice', 'waitforurl': 'waitForURL', 'waitforselector': 'waitForSelector', 'getcookies': 'getCookies', 'setcookies': 'setCookies', 'clearcookies': 'clearCookies', 'getstorage': 'getStorage', 'setstorage': 'setStorage', 'clearstorage': 'clearStorage', 'getnetworklogs': 'getNetworkLogs', 'blockresources': 'blockResources', 'getperformancemetrics': 'getPerformanceMetrics', 'waitfortimeout': 'waitForTimeout', 'enablevisualoverlay': 'enableVisualOverlay', 'showtoast': 'showToast', 'highlightelement': 'highlightElement', 'draganddrop': 'dragAndDrop', 'annotatedscreenshot': 'annotatedScreenshot', 'getinputvalues': 'getInputValues', 'waitforclickable': 'waitForClickable', 'waitforelementvisible': 'waitForElementVisible', 'waitfortext': 'waitForText', 'fillformauto': 'fillFormAuto', 'agentflow': 'agentFlow', 'agentflowv2': 'agentFlowV2', 'smartclick': 'smartClick', 'smarttype': 'smartType', 'smartwaitnavigation': 'smartWaitNavigation', 'getbuttons': 'getButtons', 'getforms': 'getForms', 'extractelements': 'extractElements', 'gettext': 'getText', 'getlinks': 'getLinks', 'openurl': 'openUrl', 'closebrowser': 'closeBrowser', 'gettitle': 'getTitle' }; if (nameMap[toolName]) toolName = nameMap[toolName]; const tool = tools[toolName] || tools[name]; if (!tool) throw new Error(`Tool "${name}" não encontrada`); try { const result = await tool(args || {}); return { content: [{ type: 'text', text: JSON.stringify(result) }] }; } catch (error) { return { content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }], isError: true }; } }); const transport = new StdioServerTransport(); server.connect(transport).catch(console.error);