2026-03-13 16:08:18 +00:00
// pkg/mcp/tools_webview.go
package mcp
import (
2026-04-15 13:58:56 +01:00
"bytes"
2026-03-13 16:08:18 +00:00
"context"
2026-04-15 13:58:56 +01:00
"encoding/base64"
"image"
"image/draw"
"image/png"
"math"
2026-03-13 16:08:18 +00:00
2026-03-31 16:14:19 +01:00
core "dappco.re/go/core"
coreerr "dappco.re/go/core/log"
2026-03-13 16:08:18 +00:00
"forge.lthn.ai/core/gui/pkg/webview"
2026-04-15 13:58:56 +01:00
"forge.lthn.ai/core/gui/pkg/window"
2026-03-13 16:08:18 +00:00
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// --- webview_eval ---
type WebviewEvalInput struct {
Window string ` json:"window" `
Script string ` json:"script" `
}
type WebviewEvalOutput struct {
Result any ` json:"result" `
Window string ` json:"window" `
}
func ( s * Subsystem ) webviewEval ( _ context . Context , _ * mcp . CallToolRequest , input WebviewEvalInput ) ( * mcp . CallToolResult , WebviewEvalOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.evaluate" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskEvaluate { Window : input . Window , Script : input . Script } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewEvalOutput { } , e
}
return nil , WebviewEvalOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
return nil , WebviewEvalOutput { Result : r . Value , Window : input . Window } , nil
2026-03-13 16:08:18 +00:00
}
// --- webview_click ---
type WebviewClickInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewClickOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewClick ( _ context . Context , _ * mcp . CallToolRequest , input WebviewClickInput ) ( * mcp . CallToolResult , WebviewClickOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.click" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskClick { Window : input . Window , Selector : input . Selector } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewClickOutput { } , e
}
return nil , WebviewClickOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewClickOutput { Success : true } , nil
}
// --- webview_type ---
type WebviewTypeInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
Text string ` json:"text" `
}
type WebviewTypeOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewType ( _ context . Context , _ * mcp . CallToolRequest , input WebviewTypeInput ) ( * mcp . CallToolResult , WebviewTypeOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.type" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskType { Window : input . Window , Selector : input . Selector , Text : input . Text } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewTypeOutput { } , e
}
return nil , WebviewTypeOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewTypeOutput { Success : true } , nil
}
// --- webview_navigate ---
type WebviewNavigateInput struct {
Window string ` json:"window" `
URL string ` json:"url" `
}
type WebviewNavigateOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewNavigate ( _ context . Context , _ * mcp . CallToolRequest , input WebviewNavigateInput ) ( * mcp . CallToolResult , WebviewNavigateOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.navigate" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskNavigate { Window : input . Window , URL : input . URL } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewNavigateOutput { } , e
}
return nil , WebviewNavigateOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewNavigateOutput { Success : true } , nil
}
// --- webview_screenshot ---
type WebviewScreenshotInput struct {
Window string ` json:"window" `
}
type WebviewScreenshotOutput struct {
Base64 string ` json:"base64" `
MimeType string ` json:"mimeType" `
}
func ( s * Subsystem ) webviewScreenshot ( _ context . Context , _ * mcp . CallToolRequest , input WebviewScreenshotInput ) ( * mcp . CallToolResult , WebviewScreenshotOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.screenshot" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskScreenshot { Window : input . Window } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewScreenshotOutput { } , e
}
return nil , WebviewScreenshotOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
sr , ok := r . Value . ( webview . ScreenshotResult )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewScreenshotOutput { } , coreerr . E ( "mcp.webviewScreenshot" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewScreenshotOutput { Base64 : sr . Base64 , MimeType : sr . MimeType } , nil
}
// --- webview_scroll ---
type WebviewScrollInput struct {
Window string ` json:"window" `
X int ` json:"x" `
Y int ` json:"y" `
}
type WebviewScrollOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewScroll ( _ context . Context , _ * mcp . CallToolRequest , input WebviewScrollInput ) ( * mcp . CallToolResult , WebviewScrollOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.scroll" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskScroll { Window : input . Window , X : input . X , Y : input . Y } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewScrollOutput { } , e
}
return nil , WebviewScrollOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewScrollOutput { Success : true } , nil
}
// --- webview_hover ---
type WebviewHoverInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewHoverOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewHover ( _ context . Context , _ * mcp . CallToolRequest , input WebviewHoverInput ) ( * mcp . CallToolResult , WebviewHoverOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.hover" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskHover { Window : input . Window , Selector : input . Selector } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewHoverOutput { } , e
}
return nil , WebviewHoverOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewHoverOutput { Success : true } , nil
}
// --- webview_select ---
type WebviewSelectInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
Value string ` json:"value" `
}
type WebviewSelectOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewSelect ( _ context . Context , _ * mcp . CallToolRequest , input WebviewSelectInput ) ( * mcp . CallToolResult , WebviewSelectOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.select" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskSelect { Window : input . Window , Selector : input . Selector , Value : input . Value } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewSelectOutput { } , e
}
return nil , WebviewSelectOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewSelectOutput { Success : true } , nil
}
// --- webview_check ---
type WebviewCheckInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
Checked bool ` json:"checked" `
}
type WebviewCheckOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewCheck ( _ context . Context , _ * mcp . CallToolRequest , input WebviewCheckInput ) ( * mcp . CallToolResult , WebviewCheckOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.check" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskCheck { Window : input . Window , Selector : input . Selector , Checked : input . Checked } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewCheckOutput { } , e
}
return nil , WebviewCheckOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewCheckOutput { Success : true } , nil
}
// --- webview_upload ---
type WebviewUploadInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
Paths [ ] string ` json:"paths" `
}
type WebviewUploadOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewUpload ( _ context . Context , _ * mcp . CallToolRequest , input WebviewUploadInput ) ( * mcp . CallToolResult , WebviewUploadOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.uploadFile" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskUploadFile { Window : input . Window , Selector : input . Selector , Paths : input . Paths } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewUploadOutput { } , e
}
return nil , WebviewUploadOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewUploadOutput { Success : true } , nil
}
// --- webview_viewport ---
type WebviewViewportInput struct {
Window string ` json:"window" `
Width int ` json:"width" `
Height int ` json:"height" `
}
type WebviewViewportOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewViewport ( _ context . Context , _ * mcp . CallToolRequest , input WebviewViewportInput ) ( * mcp . CallToolResult , WebviewViewportOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.setViewport" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskSetViewport { Window : input . Window , Width : input . Width , Height : input . Height } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewViewportOutput { } , e
}
return nil , WebviewViewportOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewViewportOutput { Success : true } , nil
}
// --- webview_console ---
type WebviewConsoleInput struct {
Window string ` json:"window" `
Level string ` json:"level,omitempty" `
Limit int ` json:"limit,omitempty" `
}
type WebviewConsoleOutput struct {
Messages [ ] webview . ConsoleMessage ` json:"messages" `
}
func ( s * Subsystem ) webviewConsole ( _ context . Context , _ * mcp . CallToolRequest , input WebviewConsoleInput ) ( * mcp . CallToolResult , WebviewConsoleOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . QUERY ( webview . QueryConsole { Window : input . Window , Level : input . Level , Limit : input . Limit } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewConsoleOutput { } , e
}
return nil , WebviewConsoleOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
msgs , ok := r . Value . ( [ ] webview . ConsoleMessage )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewConsoleOutput { } , coreerr . E ( "mcp.webviewConsole" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewConsoleOutput { Messages : msgs } , nil
}
// --- webview_console_clear ---
type WebviewConsoleClearInput struct {
Window string ` json:"window" `
}
type WebviewConsoleClearOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewConsoleClear ( _ context . Context , _ * mcp . CallToolRequest , input WebviewConsoleClearInput ) ( * mcp . CallToolResult , WebviewConsoleClearOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . Action ( "webview.clearConsole" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskClearConsole { Window : input . Window } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewConsoleClearOutput { } , e
}
return nil , WebviewConsoleClearOutput { } , nil
2026-03-13 16:08:18 +00:00
}
return nil , WebviewConsoleClearOutput { Success : true } , nil
}
// --- webview_query ---
type WebviewQueryInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewQueryOutput struct {
Element * webview . ElementInfo ` json:"element" `
}
func ( s * Subsystem ) webviewQuery ( _ context . Context , _ * mcp . CallToolRequest , input WebviewQueryInput ) ( * mcp . CallToolResult , WebviewQueryOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . QUERY ( webview . QuerySelector { Window : input . Window , Selector : input . Selector } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewQueryOutput { } , e
}
return nil , WebviewQueryOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
el , ok := r . Value . ( * webview . ElementInfo )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewQueryOutput { } , coreerr . E ( "mcp.webviewQuery" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewQueryOutput { Element : el } , nil
}
// --- webview_query_all ---
type WebviewQueryAllInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewQueryAllOutput struct {
Elements [ ] * webview . ElementInfo ` json:"elements" `
}
func ( s * Subsystem ) webviewQueryAll ( _ context . Context , _ * mcp . CallToolRequest , input WebviewQueryAllInput ) ( * mcp . CallToolResult , WebviewQueryAllOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . QUERY ( webview . QuerySelectorAll { Window : input . Window , Selector : input . Selector } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewQueryAllOutput { } , e
}
return nil , WebviewQueryAllOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
els , ok := r . Value . ( [ ] * webview . ElementInfo )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewQueryAllOutput { } , coreerr . E ( "mcp.webviewQueryAll" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewQueryAllOutput { Elements : els } , nil
}
// --- webview_dom_tree ---
type WebviewDOMTreeInput struct {
Window string ` json:"window" `
Selector string ` json:"selector,omitempty" `
}
type WebviewDOMTreeOutput struct {
HTML string ` json:"html" `
}
func ( s * Subsystem ) webviewDOMTree ( _ context . Context , _ * mcp . CallToolRequest , input WebviewDOMTreeInput ) ( * mcp . CallToolResult , WebviewDOMTreeOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . QUERY ( webview . QueryDOMTree { Window : input . Window , Selector : input . Selector } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewDOMTreeOutput { } , e
}
return nil , WebviewDOMTreeOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
html , ok := r . Value . ( string )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewDOMTreeOutput { } , coreerr . E ( "mcp.webviewDOMTree" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewDOMTreeOutput { HTML : html } , nil
}
// --- webview_url ---
type WebviewURLInput struct {
Window string ` json:"window" `
}
type WebviewURLOutput struct {
URL string ` json:"url" `
}
func ( s * Subsystem ) webviewURL ( _ context . Context , _ * mcp . CallToolRequest , input WebviewURLInput ) ( * mcp . CallToolResult , WebviewURLOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . QUERY ( webview . QueryURL { Window : input . Window } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewURLOutput { } , e
}
return nil , WebviewURLOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
url , ok := r . Value . ( string )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewURLOutput { } , coreerr . E ( "mcp.webviewURL" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewURLOutput { URL : url } , nil
}
// --- webview_title ---
type WebviewTitleInput struct {
Window string ` json:"window" `
}
type WebviewTitleOutput struct {
Title string ` json:"title" `
}
func ( s * Subsystem ) webviewTitle ( _ context . Context , _ * mcp . CallToolRequest , input WebviewTitleInput ) ( * mcp . CallToolResult , WebviewTitleOutput , error ) {
2026-03-31 16:14:19 +01:00
r := s . core . QUERY ( webview . QueryTitle { Window : input . Window } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewTitleOutput { } , e
}
return nil , WebviewTitleOutput { } , nil
2026-03-13 16:08:18 +00:00
}
2026-03-31 16:14:19 +01:00
title , ok := r . Value . ( string )
2026-03-13 16:28:15 +00:00
if ! ok {
fix(dx): use coreerr.E() and go-io, update CLAUDE.md, add tests
- Replace 90+ fmt.Errorf calls with coreerr.E() from go-log across
display, window, systray, keybinding, contextmenu, and mcp packages
- Replace os.ReadFile/WriteFile/MkdirAll with coreio.Local in
window/layout.go and window/state.go
- Update CLAUDE.md: fix key files table for new package structure,
document error handling and file I/O conventions, add missing deps
- Add 37 tests for window package (task handlers, persistence,
tiling modes, snap positions, workflow layouts)
- Window coverage: 47.1% → 69.8%
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-17 09:05:35 +00:00
return nil , WebviewTitleOutput { } , coreerr . E ( "mcp.webviewTitle" , "unexpected result type" , nil )
2026-03-13 16:28:15 +00:00
}
2026-03-13 16:08:18 +00:00
return nil , WebviewTitleOutput { Title : title } , nil
}
2026-04-15 13:58:56 +01:00
// --- webview_list ---
type WebviewListInput struct { }
type WebviewListOutput struct {
Windows [ ] window . WindowInfo ` json:"windows" `
}
func ( s * Subsystem ) webviewList ( _ context . Context , _ * mcp . CallToolRequest , _ WebviewListInput ) ( * mcp . CallToolResult , WebviewListOutput , error ) {
r := s . core . QUERY ( window . QueryWindowList { } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewListOutput { } , e
}
return nil , WebviewListOutput { } , nil
}
windows , ok := r . Value . ( [ ] window . WindowInfo )
if ! ok {
return nil , WebviewListOutput { } , coreerr . E ( "mcp.webviewList" , "unexpected result type" , nil )
}
return nil , WebviewListOutput { Windows : windows } , nil
}
// --- webview_errors ---
type WebviewErrorsInput struct {
Window string ` json:"window" `
Limit int ` json:"limit,omitempty" `
}
type WebviewErrorsOutput struct {
Errors [ ] webview . ExceptionInfo ` json:"errors" `
}
func ( s * Subsystem ) webviewErrors ( _ context . Context , _ * mcp . CallToolRequest , input WebviewErrorsInput ) ( * mcp . CallToolResult , WebviewErrorsOutput , error ) {
r := s . core . QUERY ( webview . QueryExceptions { Window : input . Window , Limit : input . Limit } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewErrorsOutput { } , e
}
return nil , WebviewErrorsOutput { } , nil
}
errors , ok := r . Value . ( [ ] webview . ExceptionInfo )
if ! ok {
return nil , WebviewErrorsOutput { } , coreerr . E ( "mcp.webviewErrors" , "unexpected result type" , nil )
}
return nil , WebviewErrorsOutput { Errors : errors } , nil
}
// --- webview_clear_console ---
type WebviewClearConsoleInput struct {
Window string ` json:"window" `
}
type WebviewClearConsoleOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewClearConsole ( _ context . Context , _ * mcp . CallToolRequest , input WebviewClearConsoleInput ) ( * mcp . CallToolResult , WebviewClearConsoleOutput , error ) {
_ , out , err := s . webviewConsoleClear ( context . Background ( ) , nil , WebviewConsoleClearInput { Window : input . Window } )
return nil , WebviewClearConsoleOutput { Success : out . Success } , err
}
// --- webview_element_info ---
type WebviewElementInfoInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewElementInfoOutput struct {
Element * webview . ElementInfo ` json:"element" `
}
func ( s * Subsystem ) webviewElementInfo ( _ context . Context , _ * mcp . CallToolRequest , input WebviewElementInfoInput ) ( * mcp . CallToolResult , WebviewElementInfoOutput , error ) {
_ , out , err := s . webviewQuery ( context . Background ( ) , nil , WebviewQueryInput { Window : input . Window , Selector : input . Selector } )
return nil , WebviewElementInfoOutput { Element : out . Element } , err
}
// --- webview_highlight ---
type WebviewHighlightInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
Colour string ` json:"colour,omitempty" `
}
type WebviewHighlightOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewHighlight ( _ context . Context , _ * mcp . CallToolRequest , input WebviewHighlightInput ) ( * mcp . CallToolResult , WebviewHighlightOutput , error ) {
result , err := s . evaluateWebview ( input . Window , webview . HighlightScript ( input . Selector , input . Colour ) )
if err != nil {
return nil , WebviewHighlightOutput { } , err
}
success , _ := result . ( bool )
return nil , WebviewHighlightOutput { Success : success } , nil
}
// --- webview_computed_style ---
type WebviewComputedStyleInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewComputedStyleOutput struct {
Styles map [ string ] any ` json:"styles" `
}
func ( s * Subsystem ) webviewComputedStyle ( _ context . Context , _ * mcp . CallToolRequest , input WebviewComputedStyleInput ) ( * mcp . CallToolResult , WebviewComputedStyleOutput , error ) {
result , err := s . evaluateWebview ( input . Window , webview . ComputedStyleScript ( input . Selector ) )
if err != nil {
return nil , WebviewComputedStyleOutput { } , err
}
styles , err := decodeJSONLike [ map [ string ] any ] ( result )
if err != nil {
return nil , WebviewComputedStyleOutput { } , err
}
return nil , WebviewComputedStyleOutput { Styles : styles } , nil
}
// --- webview_source ---
type WebviewSourceInput struct {
Window string ` json:"window" `
}
type WebviewSourceOutput struct {
HTML string ` json:"html" `
}
func ( s * Subsystem ) webviewSource ( _ context . Context , _ * mcp . CallToolRequest , input WebviewSourceInput ) ( * mcp . CallToolResult , WebviewSourceOutput , error ) {
r := s . core . QUERY ( webview . QueryDOMTree { Window : input . Window } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewSourceOutput { } , e
}
return nil , WebviewSourceOutput { } , nil
}
html , ok := r . Value . ( string )
if ! ok {
return nil , WebviewSourceOutput { } , coreerr . E ( "mcp.webviewSource" , "unexpected result type" , nil )
}
return nil , WebviewSourceOutput { HTML : html } , nil
}
// --- webview_screenshot_element ---
type WebviewScreenshotElementInput struct {
Window string ` json:"window" `
Selector string ` json:"selector" `
}
type WebviewScreenshotElementOutput struct {
Base64 string ` json:"base64" `
MimeType string ` json:"mimeType" `
}
func ( s * Subsystem ) webviewScreenshotElement ( _ context . Context , _ * mcp . CallToolRequest , input WebviewScreenshotElementInput ) ( * mcp . CallToolResult , WebviewScreenshotElementOutput , error ) {
r := s . core . QUERY ( webview . QuerySelector { Window : input . Window , Selector : input . Selector } )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewScreenshotElementOutput { } , e
}
return nil , WebviewScreenshotElementOutput { } , nil
}
element , ok := r . Value . ( * webview . ElementInfo )
if ! ok {
return nil , WebviewScreenshotElementOutput { } , coreerr . E ( "mcp.webviewScreenshotElement" , "unexpected result type" , nil )
}
if element == nil || element . BoundingBox == nil {
return nil , WebviewScreenshotElementOutput { } , coreerr . E ( "mcp.webviewScreenshotElement" , "element not found or has no bounding box" , nil )
}
r = s . core . Action ( "webview.screenshot" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskScreenshot { Window : input . Window } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewScreenshotElementOutput { } , e
}
return nil , WebviewScreenshotElementOutput { } , nil
}
screenshotResult , ok := r . Value . ( webview . ScreenshotResult )
if ! ok {
return nil , WebviewScreenshotElementOutput { } , coreerr . E ( "mcp.webviewScreenshotElement" , "unexpected screenshot result type" , nil )
}
imageBytes , err := base64 . StdEncoding . DecodeString ( screenshotResult . Base64 )
if err != nil {
return nil , WebviewScreenshotElementOutput { } , err
}
cropped , err := cropPNGToBoundingBox ( imageBytes , element . BoundingBox )
if err != nil {
return nil , WebviewScreenshotElementOutput { } , err
}
return nil , WebviewScreenshotElementOutput {
Base64 : base64 . StdEncoding . EncodeToString ( cropped ) ,
MimeType : "image/png" ,
} , nil
}
// --- webview_pdf ---
type WebviewPDFInput struct {
Window string ` json:"window" `
}
type WebviewPDFOutput struct {
Base64 string ` json:"base64" `
MimeType string ` json:"mimeType" `
}
func ( s * Subsystem ) webviewPDF ( _ context . Context , _ * mcp . CallToolRequest , input WebviewPDFInput ) ( * mcp . CallToolResult , WebviewPDFOutput , error ) {
r := s . core . Action ( "webview.print" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskPrint { Window : input . Window , ToPDF : true } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewPDFOutput { } , e
}
return nil , WebviewPDFOutput { } , nil
}
result , ok := r . Value . ( webview . PrintResult )
if ! ok {
return nil , WebviewPDFOutput { } , coreerr . E ( "mcp.webviewPDF" , "unexpected result type" , nil )
}
return nil , WebviewPDFOutput { Base64 : result . Base64 , MimeType : result . MimeType } , nil
}
// --- webview_print ---
type WebviewPrintInput struct {
Window string ` json:"window" `
}
type WebviewPrintOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewPrint ( _ context . Context , _ * mcp . CallToolRequest , input WebviewPrintInput ) ( * mcp . CallToolResult , WebviewPrintOutput , error ) {
r := s . core . Action ( "webview.print" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskPrint { Window : input . Window } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewPrintOutput { } , e
}
return nil , WebviewPrintOutput { } , nil
}
return nil , WebviewPrintOutput { Success : true } , nil
}
// --- webview_network ---
type WebviewNetworkInput struct {
Window string ` json:"window" `
Limit int ` json:"limit,omitempty" `
}
type WebviewNetworkOutput struct {
Requests [ ] map [ string ] any ` json:"requests" `
}
func ( s * Subsystem ) webviewNetwork ( _ context . Context , _ * mcp . CallToolRequest , input WebviewNetworkInput ) ( * mcp . CallToolResult , WebviewNetworkOutput , error ) {
result , err := s . evaluateWebview ( input . Window , webview . NetworkLogScript ( input . Limit ) )
if err != nil {
return nil , WebviewNetworkOutput { } , err
}
requests , err := decodeJSONLike [ [ ] map [ string ] any ] ( result )
if err != nil {
return nil , WebviewNetworkOutput { } , err
}
return nil , WebviewNetworkOutput { Requests : requests } , nil
}
// --- webview_network_clear ---
type WebviewNetworkClearInput struct {
Window string ` json:"window" `
}
type WebviewNetworkClearOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewNetworkClear ( _ context . Context , _ * mcp . CallToolRequest , input WebviewNetworkClearInput ) ( * mcp . CallToolResult , WebviewNetworkClearOutput , error ) {
_ , err := s . evaluateWebview ( input . Window , webview . NetworkClearScript ( ) )
if err != nil {
return nil , WebviewNetworkClearOutput { } , err
}
return nil , WebviewNetworkClearOutput { Success : true } , nil
}
// --- webview_network_inject ---
type WebviewNetworkInjectInput struct {
Window string ` json:"window" `
}
type WebviewNetworkInjectOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewNetworkInject ( _ context . Context , _ * mcp . CallToolRequest , input WebviewNetworkInjectInput ) ( * mcp . CallToolResult , WebviewNetworkInjectOutput , error ) {
_ , err := s . evaluateWebview ( input . Window , webview . NetworkInitScript ( ) )
if err != nil {
return nil , WebviewNetworkInjectOutput { } , err
}
return nil , WebviewNetworkInjectOutput { Success : true } , nil
}
// --- webview_performance ---
type WebviewPerformanceInput struct {
Window string ` json:"window" `
}
type WebviewPerformanceOutput struct {
Metrics map [ string ] any ` json:"metrics" `
}
func ( s * Subsystem ) webviewPerformance ( _ context . Context , _ * mcp . CallToolRequest , input WebviewPerformanceInput ) ( * mcp . CallToolResult , WebviewPerformanceOutput , error ) {
result , err := s . evaluateWebview ( input . Window , webview . PerformanceScript ( ) )
if err != nil {
return nil , WebviewPerformanceOutput { } , err
}
metrics , err := decodeJSONLike [ map [ string ] any ] ( result )
if err != nil {
return nil , WebviewPerformanceOutput { } , err
}
return nil , WebviewPerformanceOutput { Metrics : metrics } , nil
}
// --- webview_resources ---
type WebviewResourcesInput struct {
Window string ` json:"window" `
}
type WebviewResourcesOutput struct {
Resources [ ] map [ string ] any ` json:"resources" `
}
func ( s * Subsystem ) webviewResources ( _ context . Context , _ * mcp . CallToolRequest , input WebviewResourcesInput ) ( * mcp . CallToolResult , WebviewResourcesOutput , error ) {
result , err := s . evaluateWebview ( input . Window , webview . ResourcesScript ( ) )
if err != nil {
return nil , WebviewResourcesOutput { } , err
}
resources , err := decodeJSONLike [ [ ] map [ string ] any ] ( result )
if err != nil {
return nil , WebviewResourcesOutput { } , err
}
return nil , WebviewResourcesOutput { Resources : resources } , nil
}
// --- webview_devtools_open ---
type WebviewDevToolsOpenInput struct {
Window string ` json:"window" `
}
type WebviewDevToolsOpenOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewDevToolsOpen ( _ context . Context , _ * mcp . CallToolRequest , input WebviewDevToolsOpenInput ) ( * mcp . CallToolResult , WebviewDevToolsOpenOutput , error ) {
r := s . core . Action ( "webview.devtoolsOpen" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskDevToolsOpen { Window : input . Window } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewDevToolsOpenOutput { } , e
}
return nil , WebviewDevToolsOpenOutput { } , nil
}
return nil , WebviewDevToolsOpenOutput { Success : true } , nil
}
// --- webview_devtools_close ---
type WebviewDevToolsCloseInput struct {
Window string ` json:"window" `
}
type WebviewDevToolsCloseOutput struct {
Success bool ` json:"success" `
}
func ( s * Subsystem ) webviewDevToolsClose ( _ context . Context , _ * mcp . CallToolRequest , input WebviewDevToolsCloseInput ) ( * mcp . CallToolResult , WebviewDevToolsCloseOutput , error ) {
r := s . core . Action ( "webview.devtoolsClose" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskDevToolsClose { Window : input . Window } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , WebviewDevToolsCloseOutput { } , e
}
return nil , WebviewDevToolsCloseOutput { } , nil
}
return nil , WebviewDevToolsCloseOutput { Success : true } , nil
}
func ( s * Subsystem ) evaluateWebview ( windowName , script string ) ( any , error ) {
r := s . core . Action ( "webview.evaluate" ) . Run ( context . Background ( ) , core . NewOptions (
core . Option { Key : "task" , Value : webview . TaskEvaluate { Window : windowName , Script : script } } ,
) )
if ! r . OK {
if e , ok := r . Value . ( error ) ; ok {
return nil , e
}
return nil , coreerr . E ( "mcp.evaluateWebview" , "webview evaluation failed" , nil )
}
return r . Value , nil
}
func decodeJSONLike [ T any ] ( value any ) ( T , error ) {
var out T
result := core . JSONUnmarshalString ( core . JSONMarshalString ( value ) , & out )
if ! result . OK {
if err , ok := result . Value . ( error ) ; ok {
return out , err
}
return out , coreerr . E ( "mcp.decodeJSONLike" , "failed to decode result" , nil )
}
return out , nil
}
func cropPNGToBoundingBox ( pngData [ ] byte , bbox * webview . BoundingBox ) ( [ ] byte , error ) {
img , err := png . Decode ( bytes . NewReader ( pngData ) )
if err != nil {
return nil , err
}
bounds := img . Bounds ( )
rect := image . Rect (
maxInt ( bounds . Min . X , int ( math . Floor ( bbox . X ) ) ) ,
maxInt ( bounds . Min . Y , int ( math . Floor ( bbox . Y ) ) ) ,
minInt ( bounds . Max . X , int ( math . Ceil ( bbox . X + bbox . Width ) ) ) ,
minInt ( bounds . Max . Y , int ( math . Ceil ( bbox . Y + bbox . Height ) ) ) ,
)
if rect . Empty ( ) {
return nil , coreerr . E ( "mcp.cropPNGToBoundingBox" , "element bounding box is empty" , nil )
}
var cropped image . Image
if subImager , ok := img . ( interface {
SubImage ( r image . Rectangle ) image . Image
} ) ; ok {
cropped = subImager . SubImage ( rect )
} else {
target := image . NewRGBA ( image . Rect ( 0 , 0 , rect . Dx ( ) , rect . Dy ( ) ) )
draw . Draw ( target , target . Bounds ( ) , img , rect . Min , draw . Src )
cropped = target
}
var out bytes . Buffer
if err := png . Encode ( & out , cropped ) ; err != nil {
return nil , err
}
return out . Bytes ( ) , nil
}
func minInt ( a , b int ) int {
if a < b {
return a
}
return b
}
func maxInt ( a , b int ) int {
if a > b {
return a
}
return b
}
2026-03-13 16:08:18 +00:00
// --- Registration ---
func ( s * Subsystem ) registerWebviewTools ( server * mcp . Server ) {
2026-04-15 13:39:13 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_eval" , Description : "Execute JavaScript in a webview" } , s . webviewEval )
2026-04-15 13:58:56 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_list" , Description : "List webview windows with geometry" } , s . webviewList )
2026-04-15 13:39:13 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_click" , Description : "Click an element in a webview" } , s . webviewClick )
addTool ( s , server , & mcp . Tool { Name : "webview_type" , Description : "Type text into an element in a webview" } , s . webviewType )
addTool ( s , server , & mcp . Tool { Name : "webview_navigate" , Description : "Navigate a webview to a URL" } , s . webviewNavigate )
addTool ( s , server , & mcp . Tool { Name : "webview_screenshot" , Description : "Capture a webview screenshot as base64 PNG" } , s . webviewScreenshot )
2026-04-15 13:58:56 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_screenshot_element" , Description : "Capture a specific DOM element as base64 PNG" } , s . webviewScreenshotElement )
2026-04-15 13:39:13 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_scroll" , Description : "Scroll a webview to an absolute position" } , s . webviewScroll )
addTool ( s , server , & mcp . Tool { Name : "webview_hover" , Description : "Hover over an element in a webview" } , s . webviewHover )
addTool ( s , server , & mcp . Tool { Name : "webview_select" , Description : "Select an option in a select element" } , s . webviewSelect )
addTool ( s , server , & mcp . Tool { Name : "webview_check" , Description : "Check or uncheck a checkbox" } , s . webviewCheck )
addTool ( s , server , & mcp . Tool { Name : "webview_upload" , Description : "Upload files to a file input element" } , s . webviewUpload )
addTool ( s , server , & mcp . Tool { Name : "webview_viewport" , Description : "Set the webview viewport dimensions" } , s . webviewViewport )
addTool ( s , server , & mcp . Tool { Name : "webview_console" , Description : "Get captured console messages from a webview" } , s . webviewConsole )
addTool ( s , server , & mcp . Tool { Name : "webview_console_clear" , Description : "Clear captured console messages" } , s . webviewConsoleClear )
2026-04-15 13:58:56 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_clear_console" , Description : "Clear captured console messages" } , s . webviewClearConsole )
addTool ( s , server , & mcp . Tool { Name : "webview_errors" , Description : "Get captured JavaScript errors and exceptions" } , s . webviewErrors )
2026-04-15 13:39:13 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_query" , Description : "Find a single DOM element by CSS selector" } , s . webviewQuery )
2026-04-15 13:58:56 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_element_info" , Description : "Get detailed information about a DOM element" } , s . webviewElementInfo )
2026-04-15 13:39:13 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_query_all" , Description : "Find all DOM elements matching a CSS selector" } , s . webviewQueryAll )
addTool ( s , server , & mcp . Tool { Name : "webview_dom_tree" , Description : "Get HTML content of a webview" } , s . webviewDOMTree )
2026-04-15 13:58:56 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_source" , Description : "Get the full HTML source of a webview" } , s . webviewSource )
addTool ( s , server , & mcp . Tool { Name : "webview_highlight" , Description : "Highlight a DOM element inside the webview" } , s . webviewHighlight )
addTool ( s , server , & mcp . Tool { Name : "webview_computed_style" , Description : "Get computed CSS styles for a DOM element" } , s . webviewComputedStyle )
2026-04-15 13:39:13 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_url" , Description : "Get the current URL of a webview" } , s . webviewURL )
addTool ( s , server , & mcp . Tool { Name : "webview_title" , Description : "Get the current page title of a webview" } , s . webviewTitle )
2026-04-15 13:58:56 +01:00
addTool ( s , server , & mcp . Tool { Name : "webview_pdf" , Description : "Export the current page as PDF" } , s . webviewPDF )
addTool ( s , server , & mcp . Tool { Name : "webview_print" , Description : "Open the native print dialog for the current page" } , s . webviewPrint )
addTool ( s , server , & mcp . Tool { Name : "webview_network" , Description : "Get recent network activity for the page" } , s . webviewNetwork )
addTool ( s , server , & mcp . Tool { Name : "webview_network_clear" , Description : "Clear the injected webview network log" } , s . webviewNetworkClear )
addTool ( s , server , & mcp . Tool { Name : "webview_network_inject" , Description : "Inject fetch and XHR interception for detailed network logging" } , s . webviewNetworkInject )
addTool ( s , server , & mcp . Tool { Name : "webview_performance" , Description : "Get page performance metrics" } , s . webviewPerformance )
addTool ( s , server , & mcp . Tool { Name : "webview_resources" , Description : "List loaded page resources" } , s . webviewResources )
addTool ( s , server , & mcp . Tool { Name : "webview_devtools_open" , Description : "Open native developer tools for the window" } , s . webviewDevToolsOpen )
addTool ( s , server , & mcp . Tool { Name : "webview_devtools_close" , Description : "Close native developer tools for the window when supported" } , s . webviewDevToolsClose )
2026-03-13 16:08:18 +00:00
}