feat(mcp): add screen, clipboard, dialog tools (14)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
39d222d598
commit
985d3c737c
3 changed files with 330 additions and 0 deletions
84
pkg/mcp/tools_clipboard.go
Normal file
84
pkg/mcp/tools_clipboard.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// pkg/mcp/tools_clipboard.go
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"forge.lthn.ai/core/gui/pkg/clipboard"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// --- clipboard_read ---
|
||||
|
||||
type ClipboardReadInput struct{}
|
||||
type ClipboardReadOutput struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) clipboardRead(_ context.Context, _ *mcp.CallToolRequest, _ ClipboardReadInput) (*mcp.CallToolResult, ClipboardReadOutput, error) {
|
||||
result, _, err := s.core.QUERY(clipboard.QueryText{})
|
||||
if err != nil {
|
||||
return nil, ClipboardReadOutput{}, err
|
||||
}
|
||||
content, _ := result.(clipboard.ClipboardContent)
|
||||
return nil, ClipboardReadOutput{Content: content.Text}, nil
|
||||
}
|
||||
|
||||
// --- clipboard_write ---
|
||||
|
||||
type ClipboardWriteInput struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
type ClipboardWriteOutput struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) clipboardWrite(_ context.Context, _ *mcp.CallToolRequest, input ClipboardWriteInput) (*mcp.CallToolResult, ClipboardWriteOutput, error) {
|
||||
result, _, err := s.core.PERFORM(clipboard.TaskSetText{Text: input.Text})
|
||||
if err != nil {
|
||||
return nil, ClipboardWriteOutput{}, err
|
||||
}
|
||||
success, _ := result.(bool)
|
||||
return nil, ClipboardWriteOutput{Success: success}, nil
|
||||
}
|
||||
|
||||
// --- clipboard_has ---
|
||||
|
||||
type ClipboardHasInput struct{}
|
||||
type ClipboardHasOutput struct {
|
||||
HasContent bool `json:"hasContent"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) clipboardHas(_ context.Context, _ *mcp.CallToolRequest, _ ClipboardHasInput) (*mcp.CallToolResult, ClipboardHasOutput, error) {
|
||||
result, _, err := s.core.QUERY(clipboard.QueryText{})
|
||||
if err != nil {
|
||||
return nil, ClipboardHasOutput{}, err
|
||||
}
|
||||
content, _ := result.(clipboard.ClipboardContent)
|
||||
return nil, ClipboardHasOutput{HasContent: content.HasContent}, nil
|
||||
}
|
||||
|
||||
// --- clipboard_clear ---
|
||||
|
||||
type ClipboardClearInput struct{}
|
||||
type ClipboardClearOutput struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) clipboardClear(_ context.Context, _ *mcp.CallToolRequest, _ ClipboardClearInput) (*mcp.CallToolResult, ClipboardClearOutput, error) {
|
||||
result, _, err := s.core.PERFORM(clipboard.TaskClear{})
|
||||
if err != nil {
|
||||
return nil, ClipboardClearOutput{}, err
|
||||
}
|
||||
success, _ := result.(bool)
|
||||
return nil, ClipboardClearOutput{Success: success}, nil
|
||||
}
|
||||
|
||||
// --- Registration ---
|
||||
|
||||
func (s *Subsystem) registerClipboardTools(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "clipboard_read", Description: "Read the current clipboard content"}, s.clipboardRead)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "clipboard_write", Description: "Write text to the clipboard"}, s.clipboardWrite)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "clipboard_has", Description: "Check if the clipboard has content"}, s.clipboardHas)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "clipboard_clear", Description: "Clear the clipboard"}, s.clipboardClear)
|
||||
}
|
||||
142
pkg/mcp/tools_dialog.go
Normal file
142
pkg/mcp/tools_dialog.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// pkg/mcp/tools_dialog.go
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"forge.lthn.ai/core/gui/pkg/dialog"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// --- dialog_open_file ---
|
||||
|
||||
type DialogOpenFileInput struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Directory string `json:"directory,omitempty"`
|
||||
Filters []dialog.FileFilter `json:"filters,omitempty"`
|
||||
AllowMultiple bool `json:"allowMultiple,omitempty"`
|
||||
}
|
||||
type DialogOpenFileOutput struct {
|
||||
Paths []string `json:"paths"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) dialogOpenFile(_ context.Context, _ *mcp.CallToolRequest, input DialogOpenFileInput) (*mcp.CallToolResult, DialogOpenFileOutput, error) {
|
||||
result, _, err := s.core.PERFORM(dialog.TaskOpenFile{Opts: dialog.OpenFileOptions{
|
||||
Title: input.Title,
|
||||
Directory: input.Directory,
|
||||
Filters: input.Filters,
|
||||
AllowMultiple: input.AllowMultiple,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, DialogOpenFileOutput{}, err
|
||||
}
|
||||
paths, _ := result.([]string)
|
||||
return nil, DialogOpenFileOutput{Paths: paths}, nil
|
||||
}
|
||||
|
||||
// --- dialog_save_file ---
|
||||
|
||||
type DialogSaveFileInput struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Directory string `json:"directory,omitempty"`
|
||||
Filename string `json:"filename,omitempty"`
|
||||
Filters []dialog.FileFilter `json:"filters,omitempty"`
|
||||
}
|
||||
type DialogSaveFileOutput struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) dialogSaveFile(_ context.Context, _ *mcp.CallToolRequest, input DialogSaveFileInput) (*mcp.CallToolResult, DialogSaveFileOutput, error) {
|
||||
result, _, err := s.core.PERFORM(dialog.TaskSaveFile{Opts: dialog.SaveFileOptions{
|
||||
Title: input.Title,
|
||||
Directory: input.Directory,
|
||||
Filename: input.Filename,
|
||||
Filters: input.Filters,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, DialogSaveFileOutput{}, err
|
||||
}
|
||||
path, _ := result.(string)
|
||||
return nil, DialogSaveFileOutput{Path: path}, nil
|
||||
}
|
||||
|
||||
// --- dialog_open_directory ---
|
||||
|
||||
type DialogOpenDirectoryInput struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Directory string `json:"directory,omitempty"`
|
||||
}
|
||||
type DialogOpenDirectoryOutput struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) dialogOpenDirectory(_ context.Context, _ *mcp.CallToolRequest, input DialogOpenDirectoryInput) (*mcp.CallToolResult, DialogOpenDirectoryOutput, error) {
|
||||
result, _, err := s.core.PERFORM(dialog.TaskOpenDirectory{Opts: dialog.OpenDirectoryOptions{
|
||||
Title: input.Title,
|
||||
Directory: input.Directory,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, DialogOpenDirectoryOutput{}, err
|
||||
}
|
||||
path, _ := result.(string)
|
||||
return nil, DialogOpenDirectoryOutput{Path: path}, nil
|
||||
}
|
||||
|
||||
// --- dialog_confirm ---
|
||||
|
||||
type DialogConfirmInput struct {
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
Buttons []string `json:"buttons,omitempty"`
|
||||
}
|
||||
type DialogConfirmOutput struct {
|
||||
Button string `json:"button"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) dialogConfirm(_ context.Context, _ *mcp.CallToolRequest, input DialogConfirmInput) (*mcp.CallToolResult, DialogConfirmOutput, error) {
|
||||
result, _, err := s.core.PERFORM(dialog.TaskMessageDialog{Opts: dialog.MessageDialogOptions{
|
||||
Type: dialog.DialogQuestion,
|
||||
Title: input.Title,
|
||||
Message: input.Message,
|
||||
Buttons: input.Buttons,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, DialogConfirmOutput{}, err
|
||||
}
|
||||
button, _ := result.(string)
|
||||
return nil, DialogConfirmOutput{Button: button}, nil
|
||||
}
|
||||
|
||||
// --- dialog_prompt ---
|
||||
|
||||
type DialogPromptInput struct {
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
type DialogPromptOutput struct {
|
||||
Button string `json:"button"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) dialogPrompt(_ context.Context, _ *mcp.CallToolRequest, input DialogPromptInput) (*mcp.CallToolResult, DialogPromptOutput, error) {
|
||||
result, _, err := s.core.PERFORM(dialog.TaskMessageDialog{Opts: dialog.MessageDialogOptions{
|
||||
Type: dialog.DialogInfo,
|
||||
Title: input.Title,
|
||||
Message: input.Message,
|
||||
Buttons: []string{"OK", "Cancel"},
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, DialogPromptOutput{}, err
|
||||
}
|
||||
button, _ := result.(string)
|
||||
return nil, DialogPromptOutput{Button: button}, nil
|
||||
}
|
||||
|
||||
// --- Registration ---
|
||||
|
||||
func (s *Subsystem) registerDialogTools(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "dialog_open_file", Description: "Show an open file dialog"}, s.dialogOpenFile)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "dialog_save_file", Description: "Show a save file dialog"}, s.dialogSaveFile)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "dialog_open_directory", Description: "Show a directory picker dialog"}, s.dialogOpenDirectory)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "dialog_confirm", Description: "Show a confirmation dialog"}, s.dialogConfirm)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "dialog_prompt", Description: "Show a prompt dialog"}, s.dialogPrompt)
|
||||
}
|
||||
104
pkg/mcp/tools_screen.go
Normal file
104
pkg/mcp/tools_screen.go
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// pkg/mcp/tools_screen.go
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"forge.lthn.ai/core/gui/pkg/screen"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// --- screen_list ---
|
||||
|
||||
type ScreenListInput struct{}
|
||||
type ScreenListOutput struct {
|
||||
Screens []screen.Screen `json:"screens"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) screenList(_ context.Context, _ *mcp.CallToolRequest, _ ScreenListInput) (*mcp.CallToolResult, ScreenListOutput, error) {
|
||||
result, _, err := s.core.QUERY(screen.QueryAll{})
|
||||
if err != nil {
|
||||
return nil, ScreenListOutput{}, err
|
||||
}
|
||||
screens, _ := result.([]screen.Screen)
|
||||
return nil, ScreenListOutput{Screens: screens}, nil
|
||||
}
|
||||
|
||||
// --- screen_get ---
|
||||
|
||||
type ScreenGetInput struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
type ScreenGetOutput struct {
|
||||
Screen *screen.Screen `json:"screen"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) screenGet(_ context.Context, _ *mcp.CallToolRequest, input ScreenGetInput) (*mcp.CallToolResult, ScreenGetOutput, error) {
|
||||
result, _, err := s.core.QUERY(screen.QueryByID{ID: input.ID})
|
||||
if err != nil {
|
||||
return nil, ScreenGetOutput{}, err
|
||||
}
|
||||
scr, _ := result.(*screen.Screen)
|
||||
return nil, ScreenGetOutput{Screen: scr}, nil
|
||||
}
|
||||
|
||||
// --- screen_primary ---
|
||||
|
||||
type ScreenPrimaryInput struct{}
|
||||
type ScreenPrimaryOutput struct {
|
||||
Screen *screen.Screen `json:"screen"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) screenPrimary(_ context.Context, _ *mcp.CallToolRequest, _ ScreenPrimaryInput) (*mcp.CallToolResult, ScreenPrimaryOutput, error) {
|
||||
result, _, err := s.core.QUERY(screen.QueryPrimary{})
|
||||
if err != nil {
|
||||
return nil, ScreenPrimaryOutput{}, err
|
||||
}
|
||||
scr, _ := result.(*screen.Screen)
|
||||
return nil, ScreenPrimaryOutput{Screen: scr}, nil
|
||||
}
|
||||
|
||||
// --- screen_at_point ---
|
||||
|
||||
type ScreenAtPointInput struct {
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
}
|
||||
type ScreenAtPointOutput struct {
|
||||
Screen *screen.Screen `json:"screen"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) screenAtPoint(_ context.Context, _ *mcp.CallToolRequest, input ScreenAtPointInput) (*mcp.CallToolResult, ScreenAtPointOutput, error) {
|
||||
result, _, err := s.core.QUERY(screen.QueryAtPoint{X: input.X, Y: input.Y})
|
||||
if err != nil {
|
||||
return nil, ScreenAtPointOutput{}, err
|
||||
}
|
||||
scr, _ := result.(*screen.Screen)
|
||||
return nil, ScreenAtPointOutput{Screen: scr}, nil
|
||||
}
|
||||
|
||||
// --- screen_work_areas ---
|
||||
|
||||
type ScreenWorkAreasInput struct{}
|
||||
type ScreenWorkAreasOutput struct {
|
||||
WorkAreas []screen.Rect `json:"workAreas"`
|
||||
}
|
||||
|
||||
func (s *Subsystem) screenWorkAreas(_ context.Context, _ *mcp.CallToolRequest, _ ScreenWorkAreasInput) (*mcp.CallToolResult, ScreenWorkAreasOutput, error) {
|
||||
result, _, err := s.core.QUERY(screen.QueryWorkAreas{})
|
||||
if err != nil {
|
||||
return nil, ScreenWorkAreasOutput{}, err
|
||||
}
|
||||
areas, _ := result.([]screen.Rect)
|
||||
return nil, ScreenWorkAreasOutput{WorkAreas: areas}, nil
|
||||
}
|
||||
|
||||
// --- Registration ---
|
||||
|
||||
func (s *Subsystem) registerScreenTools(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "screen_list", Description: "List all connected displays/screens"}, s.screenList)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "screen_get", Description: "Get information about a specific screen"}, s.screenGet)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "screen_primary", Description: "Get the primary screen"}, s.screenPrimary)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "screen_at_point", Description: "Get the screen at a specific point"}, s.screenAtPoint)
|
||||
mcp.AddTool(server, &mcp.Tool{Name: "screen_work_areas", Description: "Get work areas for all screens"}, s.screenWorkAreas)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue