email-mcp/internal/cli/wire.go

118 lines
3.5 KiB
Go
Raw Normal View History

package cli
import (
2026-04-13 16:01:28 +00:00
"context"
"io"
"os"
"strings"
2026-05-11 09:16:37 +00:00
"email-mcp/mcpgen"
frameworkconfig "forge.lclr.dev/AI/mcp-framework/config"
frameworksecretstore "forge.lclr.dev/AI/mcp-framework/secretstore"
2026-04-13 16:01:28 +00:00
2026-04-10 10:21:58 +00:00
"email-mcp/internal/imapclient"
"email-mcp/internal/mcpserver"
"email-mcp/internal/secretstore"
)
2026-04-10 10:21:58 +00:00
type runtimeFactories struct {
2026-04-13 16:01:28 +00:00
newPrompter func(io.Reader, io.Writer) ConfigPrompter
newConfigStore func() profileConfigStore
openSecretStore func() (secretStore, error)
newMailService func() mcpserver.MailService
newRunner func(secretstore.Credential, mcpserver.MailService, io.Reader, io.Writer, io.Writer) MCPRunner
loadManifest manifestLoader
resolveExecutable executableResolver
}
2026-04-13 16:01:28 +00:00
func BuildApp(version string) *App {
return buildApp(os.Stdin, os.Stdout, os.Stderr, version, runtimeFactories{})
}
2026-04-13 16:01:28 +00:00
func buildApp(stdin io.Reader, stdout io.Writer, stderr io.Writer, version string, factories runtimeFactories) *App {
2026-04-10 10:21:58 +00:00
factories = factories.withDefaults()
2026-04-13 16:01:28 +00:00
return NewAppWithDependencies(
factories.newPrompter(stdin, stderr),
factories.newConfigStore(),
factories.openSecretStore,
factories.newMailService,
factories.newRunner,
factories.loadManifest,
factories.resolveExecutable,
stdin,
stdout,
stderr,
version,
)
}
2026-04-10 10:21:58 +00:00
func (f runtimeFactories) withDefaults() runtimeFactories {
2026-05-11 09:16:37 +00:00
useGeneratedManifest := f.loadManifest == nil
2026-04-10 10:21:58 +00:00
if f.newPrompter == nil {
2026-04-13 16:01:28 +00:00
f.newPrompter = func(input io.Reader, output io.Writer) ConfigPrompter {
return NewInteractiveConfigPrompter(input, output)
2026-04-10 10:21:58 +00:00
}
}
2026-04-13 16:01:28 +00:00
if f.newConfigStore == nil {
f.newConfigStore = func() profileConfigStore {
2026-05-11 09:16:37 +00:00
return frameworkconfig.NewStore[ProfileConfig](mcpgen.BinaryName)
2026-04-13 16:01:28 +00:00
}
2026-04-10 10:21:58 +00:00
}
if f.loadManifest == nil {
2026-05-11 09:16:37 +00:00
f.loadManifest = mcpgen.LoadManifest
}
if f.resolveExecutable == nil {
f.resolveExecutable = os.Executable
}
2026-04-13 16:01:28 +00:00
if f.openSecretStore == nil {
f.openSecretStore = func() (secretStore, error) {
2026-05-11 09:16:37 +00:00
if useGeneratedManifest {
return mcpgen.OpenSecretStore(mcpgen.SecretStoreOptions{
ExecutableResolver: frameworksecretstore.ExecutableResolver(f.resolveExecutable),
LookupEnv: profilePasswordLookupEnv,
})
}
return frameworksecretstore.OpenFromManifest(frameworksecretstore.OpenFromManifestOptions{
2026-05-11 09:16:37 +00:00
ServiceName: mcpgen.BinaryName,
ManifestLoader: frameworksecretstore.ManifestLoader(f.loadManifest),
ExecutableResolver: frameworksecretstore.ExecutableResolver(f.resolveExecutable),
2026-05-11 09:16:37 +00:00
LookupEnv: profilePasswordLookupEnv,
2026-04-13 16:01:28 +00:00
})
2026-04-10 10:21:58 +00:00
}
}
if f.newMailService == nil {
f.newMailService = func() mcpserver.MailService {
return imapclient.NewService(imapclient.NewDefaultBackend())
}
}
if f.newRunner == nil {
2026-04-13 16:01:28 +00:00
f.newRunner = func(cred secretstore.Credential, mail mcpserver.MailService, input io.Reader, output io.Writer, errOut io.Writer) MCPRunner {
return mcpserver.NewRunner(mcpserver.New(staticCredentialStore{credential: cred}, mail), input, output, errOut)
2026-04-10 10:21:58 +00:00
}
}
return f
}
2026-04-13 16:01:28 +00:00
2026-05-11 09:16:37 +00:00
func profilePasswordLookupEnv(name string) (string, bool) {
trimmedName := strings.TrimSpace(name)
if strings.HasPrefix(trimmedName, "imap-password/") {
return os.LookupEnv(passwordEnv)
}
return os.LookupEnv(trimmedName)
}
2026-04-13 16:01:28 +00:00
type staticCredentialStore struct {
credential secretstore.Credential
}
func (s staticCredentialStore) Save(_ context.Context, _ string, _ secretstore.Credential) error {
return nil
}
func (s staticCredentialStore) Load(_ context.Context, _ string) (secretstore.Credential, error) {
return s.credential, nil
}