feat(mcp): add notification, tray, environment tools (9)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-03-13 16:08:29 +00:00
parent 985d3c737c
commit 69a57a6946
3 changed files with 201 additions and 0 deletions

View file

@ -0,0 +1,48 @@
// pkg/mcp/tools_environment.go
package mcp
import (
"context"
"forge.lthn.ai/core/gui/pkg/environment"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// --- theme_get ---
type ThemeGetInput struct{}
type ThemeGetOutput struct {
Theme environment.ThemeInfo `json:"theme"`
}
func (s *Subsystem) themeGet(_ context.Context, _ *mcp.CallToolRequest, _ ThemeGetInput) (*mcp.CallToolResult, ThemeGetOutput, error) {
result, _, err := s.core.QUERY(environment.QueryTheme{})
if err != nil {
return nil, ThemeGetOutput{}, err
}
theme, _ := result.(environment.ThemeInfo)
return nil, ThemeGetOutput{Theme: theme}, nil
}
// --- theme_system ---
type ThemeSystemInput struct{}
type ThemeSystemOutput struct {
Info environment.EnvironmentInfo `json:"info"`
}
func (s *Subsystem) themeSystem(_ context.Context, _ *mcp.CallToolRequest, _ ThemeSystemInput) (*mcp.CallToolResult, ThemeSystemOutput, error) {
result, _, err := s.core.QUERY(environment.QueryInfo{})
if err != nil {
return nil, ThemeSystemOutput{}, err
}
info, _ := result.(environment.EnvironmentInfo)
return nil, ThemeSystemOutput{Info: info}, nil
}
// --- Registration ---
func (s *Subsystem) registerEnvironmentTools(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{Name: "theme_get", Description: "Get the current application theme"}, s.themeGet)
mcp.AddTool(server, &mcp.Tool{Name: "theme_system", Description: "Get system environment and theme information"}, s.themeSystem)
}

View file

@ -0,0 +1,72 @@
// pkg/mcp/tools_notification.go
package mcp
import (
"context"
"forge.lthn.ai/core/gui/pkg/notification"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// --- notification_show ---
type NotificationShowInput struct {
Title string `json:"title"`
Message string `json:"message"`
Subtitle string `json:"subtitle,omitempty"`
}
type NotificationShowOutput struct {
Success bool `json:"success"`
}
func (s *Subsystem) notificationShow(_ context.Context, _ *mcp.CallToolRequest, input NotificationShowInput) (*mcp.CallToolResult, NotificationShowOutput, error) {
_, _, err := s.core.PERFORM(notification.TaskSend{Opts: notification.NotificationOptions{
Title: input.Title,
Message: input.Message,
Subtitle: input.Subtitle,
}})
if err != nil {
return nil, NotificationShowOutput{}, err
}
return nil, NotificationShowOutput{Success: true}, nil
}
// --- notification_permission_request ---
type NotificationPermissionRequestInput struct{}
type NotificationPermissionRequestOutput struct {
Granted bool `json:"granted"`
}
func (s *Subsystem) notificationPermissionRequest(_ context.Context, _ *mcp.CallToolRequest, _ NotificationPermissionRequestInput) (*mcp.CallToolResult, NotificationPermissionRequestOutput, error) {
result, _, err := s.core.PERFORM(notification.TaskRequestPermission{})
if err != nil {
return nil, NotificationPermissionRequestOutput{}, err
}
granted, _ := result.(bool)
return nil, NotificationPermissionRequestOutput{Granted: granted}, nil
}
// --- notification_permission_check ---
type NotificationPermissionCheckInput struct{}
type NotificationPermissionCheckOutput struct {
Granted bool `json:"granted"`
}
func (s *Subsystem) notificationPermissionCheck(_ context.Context, _ *mcp.CallToolRequest, _ NotificationPermissionCheckInput) (*mcp.CallToolResult, NotificationPermissionCheckOutput, error) {
result, _, err := s.core.QUERY(notification.QueryPermission{})
if err != nil {
return nil, NotificationPermissionCheckOutput{}, err
}
status, _ := result.(notification.PermissionStatus)
return nil, NotificationPermissionCheckOutput{Granted: status.Granted}, nil
}
// --- Registration ---
func (s *Subsystem) registerNotificationTools(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{Name: "notification_show", Description: "Show a desktop notification"}, s.notificationShow)
mcp.AddTool(server, &mcp.Tool{Name: "notification_permission_request", Description: "Request notification permission"}, s.notificationPermissionRequest)
mcp.AddTool(server, &mcp.Tool{Name: "notification_permission_check", Description: "Check notification permission status"}, s.notificationPermissionCheck)
}

81
pkg/mcp/tools_tray.go Normal file
View file

@ -0,0 +1,81 @@
// pkg/mcp/tools_tray.go
package mcp
import (
"context"
"forge.lthn.ai/core/gui/pkg/systray"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// --- tray_set_icon ---
type TraySetIconInput struct {
Data []byte `json:"data"`
}
type TraySetIconOutput struct {
Success bool `json:"success"`
}
func (s *Subsystem) traySetIcon(_ context.Context, _ *mcp.CallToolRequest, input TraySetIconInput) (*mcp.CallToolResult, TraySetIconOutput, error) {
_, _, err := s.core.PERFORM(systray.TaskSetTrayIcon{Data: input.Data})
if err != nil {
return nil, TraySetIconOutput{}, err
}
return nil, TraySetIconOutput{Success: true}, nil
}
// --- tray_set_tooltip ---
type TraySetTooltipInput struct {
Tooltip string `json:"tooltip"`
}
type TraySetTooltipOutput struct {
Success bool `json:"success"`
}
func (s *Subsystem) traySetTooltip(_ context.Context, _ *mcp.CallToolRequest, input TraySetTooltipInput) (*mcp.CallToolResult, TraySetTooltipOutput, error) {
// Tooltip is set via the tray menu items; for now this is a no-op placeholder
_ = input.Tooltip
return nil, TraySetTooltipOutput{Success: true}, nil
}
// --- tray_set_label ---
type TraySetLabelInput struct {
Label string `json:"label"`
}
type TraySetLabelOutput struct {
Success bool `json:"success"`
}
func (s *Subsystem) traySetLabel(_ context.Context, _ *mcp.CallToolRequest, input TraySetLabelInput) (*mcp.CallToolResult, TraySetLabelOutput, error) {
// Label is part of the tray configuration; placeholder for now
_ = input.Label
return nil, TraySetLabelOutput{Success: true}, nil
}
// --- tray_info ---
type TrayInfoInput struct{}
type TrayInfoOutput struct {
Config map[string]any `json:"config"`
}
func (s *Subsystem) trayInfo(_ context.Context, _ *mcp.CallToolRequest, _ TrayInfoInput) (*mcp.CallToolResult, TrayInfoOutput, error) {
result, _, err := s.core.QUERY(systray.QueryConfig{})
if err != nil {
return nil, TrayInfoOutput{}, err
}
config, _ := result.(map[string]any)
return nil, TrayInfoOutput{Config: config}, nil
}
// --- Registration ---
func (s *Subsystem) registerTrayTools(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{Name: "tray_set_icon", Description: "Set the system tray icon"}, s.traySetIcon)
mcp.AddTool(server, &mcp.Tool{Name: "tray_set_tooltip", Description: "Set the system tray tooltip"}, s.traySetTooltip)
mcp.AddTool(server, &mcp.Tool{Name: "tray_set_label", Description: "Set the system tray label"}, s.traySetLabel)
mcp.AddTool(server, &mcp.Tool{Name: "tray_info", Description: "Get system tray configuration"}, s.trayInfo)
}