// pkg/mcp/tools_dialog.go package mcp import ( "context" core "dappco.re/go/core" coreerr "dappco.re/go/core/log" "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"` CanChooseDirectories bool `json:"canChooseDirectories,omitempty"` CanChooseFiles bool `json:"canChooseFiles,omitempty"` ShowHiddenFiles bool `json:"showHiddenFiles,omitempty"` } type DialogOpenFileOutput struct { Paths []string `json:"paths"` } func (s *Subsystem) dialogOpenFile(_ context.Context, _ *mcp.CallToolRequest, input DialogOpenFileInput) (*mcp.CallToolResult, DialogOpenFileOutput, error) { r := s.core.Action("dialog.openFile").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskOpenFile{Options: dialog.OpenFileOptions{ Title: input.Title, Directory: input.Directory, Filters: input.Filters, AllowMultiple: input.AllowMultiple, CanChooseDirectories: input.CanChooseDirectories, CanChooseFiles: input.CanChooseFiles, ShowHiddenFiles: input.ShowHiddenFiles, }}}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogOpenFileOutput{}, e } return nil, DialogOpenFileOutput{}, nil } paths, ok := r.Value.([]string) if !ok { return nil, DialogOpenFileOutput{}, coreerr.E("mcp.dialogOpenFile", "unexpected result type", nil) } 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"` ShowHiddenFiles bool `json:"showHiddenFiles,omitempty"` } type DialogSaveFileOutput struct { Path string `json:"path"` } func (s *Subsystem) dialogSaveFile(_ context.Context, _ *mcp.CallToolRequest, input DialogSaveFileInput) (*mcp.CallToolResult, DialogSaveFileOutput, error) { r := s.core.Action("dialog.saveFile").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskSaveFile{Options: dialog.SaveFileOptions{ Title: input.Title, Directory: input.Directory, Filename: input.Filename, Filters: input.Filters, ShowHiddenFiles: input.ShowHiddenFiles, }}}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogSaveFileOutput{}, e } return nil, DialogSaveFileOutput{}, nil } path, ok := r.Value.(string) if !ok { return nil, DialogSaveFileOutput{}, coreerr.E("mcp.dialogSaveFile", "unexpected result type", nil) } return nil, DialogSaveFileOutput{Path: path}, nil } // --- dialog_open_directory --- type DialogOpenDirectoryInput struct { Title string `json:"title,omitempty"` Directory string `json:"directory,omitempty"` ShowHiddenFiles bool `json:"showHiddenFiles,omitempty"` } type DialogOpenDirectoryOutput struct { Path string `json:"path"` } func (s *Subsystem) dialogOpenDirectory(_ context.Context, _ *mcp.CallToolRequest, input DialogOpenDirectoryInput) (*mcp.CallToolResult, DialogOpenDirectoryOutput, error) { r := s.core.Action("dialog.openDirectory").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskOpenDirectory{Options: dialog.OpenDirectoryOptions{ Title: input.Title, Directory: input.Directory, ShowHiddenFiles: input.ShowHiddenFiles, }}}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogOpenDirectoryOutput{}, e } return nil, DialogOpenDirectoryOutput{}, nil } path, ok := r.Value.(string) if !ok { return nil, DialogOpenDirectoryOutput{}, coreerr.E("mcp.dialogOpenDirectory", "unexpected result type", nil) } 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) { r := s.core.Action("dialog.question").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskQuestion{ Title: input.Title, Message: input.Message, Buttons: input.Buttons, }}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogConfirmOutput{}, e } return nil, DialogConfirmOutput{}, nil } button, ok := r.Value.(string) if !ok { return nil, DialogConfirmOutput{}, coreerr.E("mcp.dialogConfirm", "unexpected result type", nil) } return nil, DialogConfirmOutput{Button: button}, nil } // --- dialog_message --- type DialogMessageInput struct { Type string `json:"type,omitempty"` Title string `json:"title"` Message string `json:"message"` Buttons []string `json:"buttons,omitempty"` } type DialogMessageOutput struct { Button string `json:"button"` } func (s *Subsystem) dialogMessage(_ context.Context, _ *mcp.CallToolRequest, input DialogMessageInput) (*mcp.CallToolResult, DialogMessageOutput, error) { dialogType := dialog.DialogInfo switch input.Type { case "", "info": dialogType = dialog.DialogInfo case "question": dialogType = dialog.DialogQuestion case "warning": dialogType = dialog.DialogWarning case "error": dialogType = dialog.DialogError default: return nil, DialogMessageOutput{}, coreerr.E("mcp.dialogMessage", "invalid dialog type: "+input.Type, nil) } r := s.core.Action("dialog.message").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskMessageDialog{ Options: dialog.MessageDialogOptions{ Type: dialogType, Title: input.Title, Message: input.Message, Buttons: input.Buttons, }, }}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogMessageOutput{}, e } return nil, DialogMessageOutput{}, nil } button, ok := r.Value.(string) if !ok { return nil, DialogMessageOutput{}, coreerr.E("mcp.dialogMessage", "unexpected result type", nil) } return nil, DialogMessageOutput{Button: button}, nil } // --- dialog_prompt --- type DialogPromptInput struct { Title string `json:"title"` Message string `json:"message"` DefaultValue string `json:"defaultValue,omitempty"` } type DialogPromptOutput struct { Value string `json:"value"` Confirmed bool `json:"confirmed"` } func (s *Subsystem) dialogPrompt(_ context.Context, _ *mcp.CallToolRequest, input DialogPromptInput) (*mcp.CallToolResult, DialogPromptOutput, error) { r := s.core.Action("dialog.prompt").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskPrompt{ Title: input.Title, Message: input.Message, DefaultValue: input.DefaultValue, }}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogPromptOutput{}, e } return nil, DialogPromptOutput{}, nil } result, ok := r.Value.(dialog.PromptResult) if !ok { return nil, DialogPromptOutput{}, coreerr.E("mcp.dialogPrompt", "unexpected result type", nil) } return nil, DialogPromptOutput{Value: result.Value, Confirmed: result.Confirmed}, nil } // --- dialog_info --- type DialogInfoInput struct { Title string `json:"title"` Message string `json:"message"` Buttons []string `json:"buttons,omitempty"` } type DialogInfoOutput struct { Button string `json:"button"` } func (s *Subsystem) dialogInfo(_ context.Context, _ *mcp.CallToolRequest, input DialogInfoInput) (*mcp.CallToolResult, DialogInfoOutput, error) { r := s.core.Action("dialog.info").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskInfo{ Title: input.Title, Message: input.Message, Buttons: input.Buttons, }}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogInfoOutput{}, e } return nil, DialogInfoOutput{}, nil } button, ok := r.Value.(string) if !ok { return nil, DialogInfoOutput{}, coreerr.E("mcp.dialogInfo", "unexpected result type", nil) } return nil, DialogInfoOutput{Button: button}, nil } // --- dialog_warning --- type DialogWarningInput struct { Title string `json:"title"` Message string `json:"message"` Buttons []string `json:"buttons,omitempty"` } type DialogWarningOutput struct { Button string `json:"button"` } func (s *Subsystem) dialogWarning(_ context.Context, _ *mcp.CallToolRequest, input DialogWarningInput) (*mcp.CallToolResult, DialogWarningOutput, error) { r := s.core.Action("dialog.warning").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskWarning{ Title: input.Title, Message: input.Message, Buttons: input.Buttons, }}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogWarningOutput{}, e } return nil, DialogWarningOutput{}, nil } button, ok := r.Value.(string) if !ok { return nil, DialogWarningOutput{}, coreerr.E("mcp.dialogWarning", "unexpected result type", nil) } return nil, DialogWarningOutput{Button: button}, nil } // --- dialog_error --- type DialogErrorInput struct { Title string `json:"title"` Message string `json:"message"` Buttons []string `json:"buttons,omitempty"` } type DialogErrorOutput struct { Button string `json:"button"` } func (s *Subsystem) dialogError(_ context.Context, _ *mcp.CallToolRequest, input DialogErrorInput) (*mcp.CallToolResult, DialogErrorOutput, error) { r := s.core.Action("dialog.error").Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: dialog.TaskError{ Title: input.Title, Message: input.Message, Buttons: input.Buttons, }}, )) if !r.OK { if e, ok := r.Value.(error); ok { return nil, DialogErrorOutput{}, e } return nil, DialogErrorOutput{}, nil } button, ok := r.Value.(string) if !ok { return nil, DialogErrorOutput{}, coreerr.E("mcp.dialogError", "unexpected result type", nil) } return nil, DialogErrorOutput{Button: button}, nil } // --- Registration --- func (s *Subsystem) registerDialogTools(server *mcp.Server) { addTool(s, server, &mcp.Tool{Name: "dialog_open_file", Description: "Show an open file dialog"}, s.dialogOpenFile) addTool(s, server, &mcp.Tool{Name: "dialog_save_file", Description: "Show a save file dialog"}, s.dialogSaveFile) addTool(s, server, &mcp.Tool{Name: "dialog_open_directory", Description: "Show a directory picker dialog"}, s.dialogOpenDirectory) addTool(s, server, &mcp.Tool{Name: "dialog_confirm", Description: "Show a question/confirmation dialog"}, s.dialogConfirm) addTool(s, server, &mcp.Tool{Name: "dialog_message", Description: "Show a message dialog"}, s.dialogMessage) addTool(s, server, &mcp.Tool{ Name: "dialog_prompt", Description: `Show an input prompt dialog in the active window. Example: {"title":"Rename","message":"New name","defaultValue":"project-a"}`, }, s.dialogPrompt) addTool(s, server, &mcp.Tool{Name: "dialog_info", Description: "Show an information message dialog"}, s.dialogInfo) addTool(s, server, &mcp.Tool{Name: "dialog_warning", Description: "Show a warning message dialog"}, s.dialogWarning) addTool(s, server, &mcp.Tool{Name: "dialog_error", Description: "Show an error message dialog"}, s.dialogError) }