From c2e49c32e760fab90eebeac9aabeea273efeb874 Mon Sep 17 00:00:00 2001 From: Snider Date: Fri, 13 Mar 2026 15:53:48 +0000 Subject: [PATCH] docs: add MCP bridge implementation plan (13 tasks) Co-Authored-By: Claude Opus 4.6 --- .../plans/2026-03-13-gui-mcp-bridge.md | 1393 +++++++++++++++++ 1 file changed, 1393 insertions(+) create mode 100644 docs/superpowers/plans/2026-03-13-gui-mcp-bridge.md diff --git a/docs/superpowers/plans/2026-03-13-gui-mcp-bridge.md b/docs/superpowers/plans/2026-03-13-gui-mcp-bridge.md new file mode 100644 index 0000000..d418ec1 --- /dev/null +++ b/docs/superpowers/plans/2026-03-13-gui-mcp-bridge.md @@ -0,0 +1,1393 @@ +# MCP Bridge & WebView Service Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add `pkg/webview` (CDP-backed core.Service) and `pkg/mcp` (MCP display subsystem with ~74 tools) to core/gui + +**Architecture:** `pkg/webview` wraps go-webview as a core.Service with IPC messages. `pkg/mcp` implements the MCP Subsystem interface via structural typing, translating tool calls to IPC `PERFORM`/`QUERY` calls across all 15 GUI packages. + +**Tech Stack:** Go 1.25, core/go DI framework, go-webview CDP client, MCP SDK (`github.com/modelcontextprotocol/go-sdk/mcp`) + +**Spec:** `docs/superpowers/specs/2026-03-13-gui-mcp-bridge-design.md` + +--- + +## File Structure + +### New files + +| Package | File | Responsibility | +|---------|------|---------------| +| `pkg/webview/` | `messages.go` | IPC message types (6 Queries, 11 Tasks, 2 Actions) + own types (ConsoleMessage, ElementInfo, etc.) | +| | `service.go` | connector interface, Service struct, Register factory, IPC handlers | +| | `service_test.go` | Mock connector, all IPC round-trip tests | +| `pkg/mcp/` | `subsystem.go` | Subsystem struct, New(), Name(), RegisterTools() | +| | `tools_webview.go` | 18 webview tool handlers + Input/Output types | +| | `tools_window.go` | 15 window tool handlers | +| | `tools_layout.go` | 7 layout tool handlers | +| | `tools_screen.go` | 5 screen tool handlers | +| | `tools_clipboard.go` | 4 clipboard tool handlers | +| | `tools_dialog.go` | 5 dialog tool handlers | +| | `tools_notification.go` | 3 notification tool handlers | +| | `tools_tray.go` | 4 systray tool handlers | +| | `tools_environment.go` | 2 environment/theme tool handlers | +| | `tools_browser.go` | 1 browser tool handler | +| | `tools_contextmenu.go` | 4 contextmenu tool handlers | +| | `tools_keybinding.go` | 2 keybinding tool handlers | +| | `tools_dock.go` | 3 dock tool handlers | +| | `tools_lifecycle.go` | 1 lifecycle tool handler | +| | `mcp_test.go` | RegisterTools smoke test + IPC round-trip tests | + +### Modified files + +| File | Changes | +|------|---------| +| `pkg/display/display.go` | Add webview import, HandleIPCEvents cases, handleWSMessage cases, EventType constants | +| `go.mod` | Add `forge.lthn.ai/core/go-webview`, `github.com/modelcontextprotocol/go-sdk` | + +### Prerequisite (separate repo) + +| File | Changes | +|------|---------| +| `go-webview/cdp.go` | Export `targetInfo` → `TargetInfo` | + +--- + +## Task 1: Export TargetInfo in go-webview + +**Files:** +- Modify: `/Users/snider/Code/core/go-webview/cdp.go` + +- [ ] **Step 1: Rename targetInfo → TargetInfo** + +In `/Users/snider/Code/core/go-webview/cdp.go`, rename the struct and update all references: + +```go +// TargetInfo represents Chrome DevTools target information. +type TargetInfo struct { + ID string `json:"id"` + Type string `json:"type"` + Title string `json:"title"` + URL string `json:"url"` + WebSocketDebuggerURL string `json:"webSocketDebuggerUrl"` +} +``` + +Update `ListTargets` return type: `func ListTargets(debugURL string) ([]TargetInfo, error)` +Update `ListTargetsAll` return type: `func ListTargetsAll(debugURL string) iter.Seq[TargetInfo]` +Update all internal references (`var targets []targetInfo` → `var targets []TargetInfo`, etc.) + +- [ ] **Step 2: Run tests** + +Run: `cd /Users/snider/Code/core/go-webview && go build ./...` +Expected: Build succeeds (tests need Chrome, so just verify compilation) + +- [ ] **Step 3: Commit** + +```bash +cd /Users/snider/Code/core/go-webview +git add cdp.go +git commit -m "feat: export TargetInfo type for external CDP target enumeration" +``` + +--- + +## Task 2: pkg/webview — messages.go + +**Files:** +- Create: `pkg/webview/messages.go` + +- [ ] **Step 1: Write messages.go** + +```go +// pkg/webview/messages.go +package webview + +import "time" + +// --- Queries (read-only) --- + +// QueryURL gets the current page URL. Result: string +type QueryURL struct{ Window string `json:"window"` } + +// QueryTitle gets the current page title. Result: string +type QueryTitle struct{ Window string `json:"window"` } + +// QueryConsole gets captured console messages. Result: []ConsoleMessage +type QueryConsole struct { + Window string `json:"window"` + Level string `json:"level,omitempty"` // filter by type: "log", "warn", "error", "info", "debug" + Limit int `json:"limit,omitempty"` // max messages (0 = all) +} + +// QuerySelector finds a single element. Result: *ElementInfo (nil if not found) +type QuerySelector struct { + Window string `json:"window"` + Selector string `json:"selector"` +} + +// QuerySelectorAll finds all matching elements. Result: []*ElementInfo +type QuerySelectorAll struct { + Window string `json:"window"` + Selector string `json:"selector"` +} + +// QueryDOMTree gets HTML content. Result: string (outerHTML) +type QueryDOMTree struct { + Window string `json:"window"` + Selector string `json:"selector,omitempty"` // empty = full document +} + +// --- Tasks (side-effects) --- + +// TaskEvaluate executes JavaScript. Result: any (JS return value) +type TaskEvaluate struct { + Window string `json:"window"` + Script string `json:"script"` +} + +// TaskClick clicks an element. Result: nil +type TaskClick struct { + Window string `json:"window"` + Selector string `json:"selector"` +} + +// TaskType types text into an element. Result: nil +type TaskType struct { + Window string `json:"window"` + Selector string `json:"selector"` + Text string `json:"text"` +} + +// TaskNavigate navigates to a URL. Result: nil +type TaskNavigate struct { + Window string `json:"window"` + URL string `json:"url"` +} + +// TaskScreenshot captures the page as PNG. Result: ScreenshotResult +type TaskScreenshot struct{ Window string `json:"window"` } + +// TaskScroll scrolls to an absolute position (window.scrollTo). Result: nil +type TaskScroll struct { + Window string `json:"window"` + X int `json:"x"` + Y int `json:"y"` +} + +// TaskHover hovers over an element. Result: nil +type TaskHover struct { + Window string `json:"window"` + Selector string `json:"selector"` +} + +// TaskSelect selects an option in a