package cli import ( "context" "errors" "fmt" "io" "os" "email-mcp/internal/secretstore" "email-mcp/internal/secretstore/kwallet" ) type MCPRunner interface { Run(ctx context.Context) error } type App struct { prompter SetupPrompter store secretstore.Store runner MCPRunner stderr io.Writer } func NewApp() *App { return NewAppWithDependencies(nil, nil, nil, os.Stderr) } func NewAppWithDependencies(prompter SetupPrompter, store secretstore.Store, runner MCPRunner, stderr io.Writer) *App { if stderr == nil { stderr = io.Discard } return &App{ prompter: prompter, store: store, runner: runner, stderr: stderr, } } func (a *App) Run(args []string) error { if len(args) == 0 { return fmt.Errorf("usage: email-mcp ") } switch args[0] { case "setup": return a.runSetup(context.Background()) case "mcp": return a.runMCP(context.Background()) default: return fmt.Errorf("unknown command: %s", args[0]) } } func (a *App) runSetup(ctx context.Context) error { if a.prompter == nil { return fmt.Errorf("setup prompter is not configured") } if a.store == nil { return fmt.Errorf("secret store is not configured") } cred, err := a.prompter.PromptSetup(ctx) if err != nil { return err } if err := cred.Validate(); err != nil { return err } if err := a.store.Save(ctx, secretstore.DefaultAccountKey, cred); err != nil { return mapAppError(err) } return nil } func (a *App) runMCP(ctx context.Context) error { if a.runner == nil { return fmt.Errorf("mcp runner is not configured") } return mapAppError(a.runner.Run(ctx)) } func mapAppError(err error) error { if err == nil { return nil } switch { case errors.Is(err, kwallet.ErrKWalletUnavailable): return fmt.Errorf("kwallet is not available; make sure KDE Wallet is installed and your session D-Bus is running") case errors.Is(err, kwallet.ErrKWalletDisabled): return fmt.Errorf("kwallet is disabled in this KDE session") case errors.Is(err, kwallet.ErrKWalletOpenFailed): return fmt.Errorf("kwallet could not be opened; unlock the wallet and try again") case errors.Is(err, kwallet.ErrCredentialNotFound): return fmt.Errorf("credentials not configured; run `email-mcp setup`") default: return err } }