Add browser file MCP tool

This commit is contained in:
Snider 2026-04-15 14:16:35 +01:00
parent 2a1d785294
commit cd6a4c8d16
4 changed files with 68 additions and 0 deletions

View file

@ -65,6 +65,13 @@ The MCP service exposes numerous tools organized by category:
| `webview_navigate` | Navigate to URL |
| `webview_console` | Get console logs |
### Browser
| Tool | Description |
|------|-------------|
| `browser_open_url` | Open a URL in the default system browser |
| `browser_open_file` | Open a file with the system default application |
### Process Management
| Tool | Description |

View file

@ -76,6 +76,10 @@ This document tracks the implementation of display server features that enable A
- [x] `webview_eval` - Execute JavaScript and return result
- [x] `webview_list` - List all webview windows
### Browser
- [x] `browser_open_url` - Open a URL in the default system browser
- [x] `browser_open_file` - Open a file in the system default application
### Console & Errors
- [x] `webview_console` - Get console messages (log, warn, error, info)
- [x] `webview_errors` - Get structured JS errors with stack traces

View file

@ -7,6 +7,7 @@ import (
"testing"
core "dappco.re/go/core"
"forge.lthn.ai/core/gui/pkg/browser"
"forge.lthn.ai/core/gui/pkg/clipboard"
"forge.lthn.ai/core/gui/pkg/dialog"
"forge.lthn.ai/core/gui/pkg/events"
@ -72,6 +73,11 @@ func TestMCP_Bad_NoServices(t *testing.T) {
type manifestScreenPlatform struct{}
type manifestBrowserPlatform struct {
lastURL string
lastPath string
}
func (manifestScreenPlatform) GetAll() []screen.Screen {
return []screen.Screen{{
ID: "1", Name: "Primary", IsPrimary: true,
@ -106,6 +112,34 @@ func TestSubsystem_Good_CallTool_LayoutSuggest(t *testing.T) {
assert.Contains(t, result, "left-right")
}
func (m *manifestBrowserPlatform) OpenURL(url string) error {
m.lastURL = url
return nil
}
func (m *manifestBrowserPlatform) OpenFile(path string) error {
m.lastPath = path
return nil
}
func TestSubsystem_Good_CallTool_BrowserOpenFile(t *testing.T) {
browserPlatform := &manifestBrowserPlatform{}
c := core.New(
core.WithService(browser.Register(browserPlatform)),
core.WithServiceLock(),
)
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
sub := New(c)
server := mcp.NewServer(&mcp.Implementation{Name: "test", Version: "0.1.0"}, nil)
sub.RegisterTools(server)
result, err := sub.CallTool(context.Background(), "browser_open_file", map[string]any{"path": "/tmp/readme.txt"})
require.NoError(t, err)
assert.Contains(t, result, "success")
assert.Equal(t, "/tmp/readme.txt", browserPlatform.lastPath)
}
type aliasDialogPlatform struct {
last dialog.MessageDialogOptions
}

View file

@ -30,8 +30,31 @@ func (s *Subsystem) browserOpenURL(_ context.Context, _ *mcp.CallToolRequest, in
return nil, BrowserOpenURLOutput{Success: true}, nil
}
// --- browser_open_file ---
type BrowserOpenFileInput struct {
Path string `json:"path"`
}
type BrowserOpenFileOutput struct {
Success bool `json:"success"`
}
func (s *Subsystem) browserOpenFile(_ context.Context, _ *mcp.CallToolRequest, input BrowserOpenFileInput) (*mcp.CallToolResult, BrowserOpenFileOutput, error) {
r := s.core.Action("browser.openFile").Run(context.Background(), core.NewOptions(
core.Option{Key: "path", Value: input.Path},
))
if !r.OK {
if e, ok := r.Value.(error); ok {
return nil, BrowserOpenFileOutput{}, e
}
return nil, BrowserOpenFileOutput{}, nil
}
return nil, BrowserOpenFileOutput{Success: true}, nil
}
// --- Registration ---
func (s *Subsystem) registerBrowserTools(server *mcp.Server) {
addTool(s, server, &mcp.Tool{Name: "browser_open_url", Description: "Open a URL in the default system browser"}, s.browserOpenURL)
addTool(s, server, &mcp.Tool{Name: "browser_open_file", Description: "Open a file in the system default application"}, s.browserOpenFile)
}