feat: adapta CdpHandleDialog (nueva firma + DialogLog) y reporta diálogos en disconnect
CdpHandleDialog ahora devuelve (cancel, *DialogLog, error). El pool guarda el DialogLog por puerto y browser_disconnect reporta cuántos diálogos se auto-respondieron y el último (tipo + mensaje). drop/closeAll usan CdpDisconnect (alias legible de CdpClose(c,0)).
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
name: browser_mcp
|
name: browser_mcp
|
||||||
lang: go
|
lang: go
|
||||||
domain: infra
|
domain: infra
|
||||||
version: 0.3.0
|
version: 0.4.0
|
||||||
description: "Servidor MCP que expone control total del navegador via CDP (39 tools: navegación, DOM, cookies, iframes, teclado/scroll, diálogos, estado de sesión, selección determinista de pestaña, lectura compacta texto/AX + bucle percibir→actuar por #ref con auto-observe) reusando funciones del dominio browser del registry con un pool de conexiones CDP vivas. Por defecto opera sobre un Chrome aislado (puerto 9333) separado del navegador diario."
|
description: "Servidor MCP que expone control total del navegador via CDP (40 tools: navegación, DOM, cookies, iframes, teclado/scroll, diálogos, estado de sesión, selección determinista de pestaña, lectura compacta texto/AX + bucle percibir→actuar por #ref con auto-observe, incluyendo find-ref-by-text) reusando funciones del dominio browser del registry con un pool de conexiones CDP vivas. Por defecto opera sobre un Chrome aislado (puerto 9333) separado del navegador diario."
|
||||||
tags: [mcp, browser, cdp, automation, scraping]
|
tags: [mcp, browser, cdp, automation, scraping]
|
||||||
uses_functions:
|
uses_functions:
|
||||||
- chrome_launch_go_browser
|
- chrome_launch_go_browser
|
||||||
@@ -26,6 +26,7 @@ uses_functions:
|
|||||||
- cdp_click_text_go_browser
|
- cdp_click_text_go_browser
|
||||||
- cdp_type_text_go_browser
|
- cdp_type_text_go_browser
|
||||||
- cdp_find_by_text_go_browser
|
- cdp_find_by_text_go_browser
|
||||||
|
- cdp_find_ref_by_text_go_browser
|
||||||
- cdp_wait_element_go_browser
|
- cdp_wait_element_go_browser
|
||||||
- cdp_press_key_go_browser
|
- cdp_press_key_go_browser
|
||||||
- cdp_scroll_go_browser
|
- cdp_scroll_go_browser
|
||||||
|
|||||||
@@ -14,10 +14,15 @@ type connPool struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
conns map[int]*browser.CDPConn
|
conns map[int]*browser.CDPConn
|
||||||
cancels map[int]func() // cancels de handlers persistentes (handle_dialog)
|
cancels map[int]func() // cancels de handlers persistentes (handle_dialog)
|
||||||
|
dialogLogs map[int]*browser.DialogLog // log de diálogos auto-respondidos por puerto
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConnPool() *connPool {
|
func newConnPool() *connPool {
|
||||||
return &connPool{conns: map[int]*browser.CDPConn{}, cancels: map[int]func(){}}
|
return &connPool{
|
||||||
|
conns: map[int]*browser.CDPConn{},
|
||||||
|
cancels: map[int]func(){},
|
||||||
|
dialogLogs: map[int]*browser.DialogLog{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *connPool) get(port int) (*browser.CDPConn, error) {
|
func (p *connPool) get(port int) (*browser.CDPConn, error) {
|
||||||
@@ -41,8 +46,11 @@ func (p *connPool) drop(port int) {
|
|||||||
cancel()
|
cancel()
|
||||||
delete(p.cancels, port)
|
delete(p.cancels, port)
|
||||||
}
|
}
|
||||||
|
delete(p.dialogLogs, port)
|
||||||
if c, ok := p.conns[port]; ok && c != nil {
|
if c, ok := p.conns[port]; ok && c != nil {
|
||||||
_ = browser.CdpClose(c, 0)
|
// CdpDisconnect = cerrar el WebSocket sin matar Chrome (el navegador
|
||||||
|
// sigue vivo; solo soltamos la sesión pooled).
|
||||||
|
_ = browser.CdpDisconnect(c)
|
||||||
delete(p.conns, port)
|
delete(p.conns, port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,13 +70,27 @@ func (p *connPool) connectTarget(port int, match string) (*browser.CDPConn, erro
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *connPool) setCancel(port int, cancel func()) {
|
// setDialog guarda el cancel y el DialogLog del auto-handler de diálogos del
|
||||||
|
// puerto. Si ya había uno armado, lo cancela primero.
|
||||||
|
func (p *connPool) setDialog(port int, cancel func(), dlog *browser.DialogLog) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
if old := p.cancels[port]; old != nil {
|
if old := p.cancels[port]; old != nil {
|
||||||
old()
|
old()
|
||||||
}
|
}
|
||||||
p.cancels[port] = cancel
|
p.cancels[port] = cancel
|
||||||
|
p.dialogLogs[port] = dlog
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialogSnapshot devuelve el estado del log de diálogos del puerto (0,"","" si
|
||||||
|
// no hay handler armado).
|
||||||
|
func (p *connPool) dialogSnapshot(port int) (int, string, string) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if dl := p.dialogLogs[port]; dl != nil {
|
||||||
|
return dl.Snapshot()
|
||||||
|
}
|
||||||
|
return 0, "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *connPool) closeAll() {
|
func (p *connPool) closeAll() {
|
||||||
@@ -79,11 +101,12 @@ func (p *connPool) closeAll() {
|
|||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
if c != nil {
|
if c != nil {
|
||||||
_ = browser.CdpClose(c, 0)
|
_ = browser.CdpDisconnect(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.conns = map[int]*browser.CDPConn{}
|
p.conns = map[int]*browser.CDPConn{}
|
||||||
p.cancels = map[int]func(){}
|
p.cancels = map[int]func(){}
|
||||||
|
p.dialogLogs = map[int]*browser.DialogLog{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isConnErr reconoce errores de conexión CDP muerta para reintentar UNA vez.
|
// isConnErr reconoce errores de conexión CDP muerta para reintentar UNA vez.
|
||||||
|
|||||||
+4
-2
@@ -101,10 +101,12 @@ func (d *deps) handleHandleDialog(_ context.Context, _ mcp.CallToolRequest, a ha
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp.NewToolResultError(err.Error()), nil
|
return mcp.NewToolResultError(err.Error()), nil
|
||||||
}
|
}
|
||||||
cancel, err := browser.CdpHandleDialog(c, a.Accept, a.PromptText)
|
cancel, dlog, err := browser.CdpHandleDialog(c, a.Accept, a.PromptText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mcp.NewToolResultError(err.Error()), nil
|
return mcp.NewToolResultError(err.Error()), nil
|
||||||
}
|
}
|
||||||
d.pool.setCancel(port, cancel)
|
// Guardamos el DialogLog junto al cancel para que browser_disconnect pueda
|
||||||
|
// reportar cuántos diálogos se auto-respondieron y cuál fue el último.
|
||||||
|
d.pool.setDialog(port, cancel, dlog)
|
||||||
return mcp.NewToolResultText("dialog auto-handler armed"), nil
|
return mcp.NewToolResultText("dialog auto-handler armed"), nil
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -99,6 +99,12 @@ func disconnectTool() mcp.Tool {
|
|||||||
|
|
||||||
func (d *deps) handleDisconnect(_ context.Context, _ mcp.CallToolRequest, a disconnectArgs) (*mcp.CallToolResult, error) {
|
func (d *deps) handleDisconnect(_ context.Context, _ mcp.CallToolRequest, a disconnectArgs) (*mcp.CallToolResult, error) {
|
||||||
port := portOr(a.Port)
|
port := portOr(a.Port)
|
||||||
|
// Leer el log de diálogos ANTES de drop (drop lo limpia).
|
||||||
|
count, lastType, lastMsg := d.pool.dialogSnapshot(port)
|
||||||
d.pool.drop(port)
|
d.pool.drop(port)
|
||||||
return mcp.NewToolResultText(fmt.Sprintf("disconnected port=%d", port)), nil
|
msg := fmt.Sprintf("disconnected port=%d", port)
|
||||||
|
if count > 0 {
|
||||||
|
msg += fmt.Sprintf(" (dialogs auto-handled: %d, last %s: %q)", count, lastType, lastMsg)
|
||||||
|
}
|
||||||
|
return mcp.NewToolResultText(msg), nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user