package mcpserver import ( "fmt" "os" "strings" "github.com/modelcontextprotocol/go-sdk/mcp" "isphere-ai-bridge/internal/helperclient" "isphere-ai-bridge/internal/isphere" "isphere-ai-bridge/internal/msglib" "isphere-ai-bridge/internal/tools" ) const ( ServerName = "isphere-ai-bridge" ServerTitle = "iSphere AI Bridge" ServerVersion = "0.1.0" EnvPacketLogFileKey = "ISPHERE_PACKET_LOG_FILE" EnvPacketLogDirKey = "ISPHERE_PACKET_LOG_DIR" EnvMsgLibSidecarExeKey = "ISPHERE_MSGLIB_SIDECAR_EXE" EnvMsgLibSQLiteDLLKey = "ISPHERE_MSGLIB_SQLITE_DLL" EnvMsgLibDBKey = "ISPHERE_MSGLIB_DB" ) type msgLibClient interface { tools.DisplayEntitySource isphere.MsgLibMessageLister } var newMsgLibClient = func(cfg msglib.Config) msgLibClient { return msglib.NewClient(cfg) } func NewServer() *mcp.Server { return NewServerWithReceiveSource(isphere.EncryptedPacketLogSource{}) } func NewServerFromEnv() (*mcp.Server, error) { var source tools.ReceiveMessagesSource = isphere.EncryptedPacketLogSource{} packetLogFile := strings.TrimSpace(os.Getenv(EnvPacketLogFileKey)) packetLogDir := strings.TrimSpace(os.Getenv(EnvPacketLogDirKey)) if packetLogFile != "" && packetLogDir != "" { return nil, fmt.Errorf("configure packet log source: set only one of %s or %s", EnvPacketLogFileKey, EnvPacketLogDirKey) } if packetLogFile != "" { loaded, err := isphere.LoadEncryptedPacketLogSourceFromFile(packetLogFile) if err != nil { return nil, fmt.Errorf("configure %s: %w", EnvPacketLogFileKey, err) } source = loaded } else if packetLogDir != "" { loaded, err := isphere.LoadEncryptedPacketLogSourceFromDirectory(packetLogDir) if err != nil { return nil, fmt.Errorf("configure %s: %w", EnvPacketLogDirKey, err) } source = loaded } displaySource, msglibReceiveSource, err := msgLibSourcesFromEnv() if err != nil { return nil, err } return NewServerWithSourcesAndMsgLibReceive(source, displaySource, msglibReceiveSource), nil } func msgLibSourcesFromEnv() (tools.DisplayEntitySource, tools.ReceiveMessagesSource, error) { sidecarExe := strings.TrimSpace(os.Getenv(EnvMsgLibSidecarExeKey)) sqliteDLL := strings.TrimSpace(os.Getenv(EnvMsgLibSQLiteDLLKey)) dbPath := strings.TrimSpace(os.Getenv(EnvMsgLibDBKey)) configured := 0 for _, value := range []string{sidecarExe, sqliteDLL, dbPath} { if value != "" { configured++ } } if configured == 0 { return nil, nil, nil } if configured != 3 { return nil, nil, fmt.Errorf("configure MsgLib source: set all of %s, %s, and %s, or set none", EnvMsgLibSidecarExeKey, EnvMsgLibSQLiteDLLKey, EnvMsgLibDBKey) } cfg, err := msglib.ConfigFromEnv() if err != nil { return nil, nil, fmt.Errorf("configure MsgLib source: %w", err) } client := newMsgLibClient(cfg) return client, isphere.MsgLibMessageSource{ Lister: client, IncludeBody: true, IncludeAttachmentMetadata: true, }, nil } func NewServerWithReceiveSource(source tools.ReceiveMessagesSource) *mcp.Server { return NewServerWithSources(source, nil) } func NewServerWithSources(source tools.ReceiveMessagesSource, displaySource tools.DisplayEntitySource) *mcp.Server { return NewServerWithSourcesAndMsgLibReceive(source, displaySource, nil) } func NewServerWithSourcesAndMsgLibReceive(source tools.ReceiveMessagesSource, displaySource tools.DisplayEntitySource, msglibReceiveSource tools.ReceiveMessagesSource) *mcp.Server { server := mcp.NewServer(&mcp.Implementation{ Name: ServerName, Title: ServerTitle, Version: ServerVersion, }, nil) tools.RegisterWinHelperTools(server, helperclient.Client{}) tools.RegisterISphereReadToolsWithReceiveSources(server, source, msglibReceiveSource, displaySource) tools.RegisterISphereContactToolsWithDisplayEntities(server, source, displaySource) tools.RegisterISphereGroupToolsWithDisplayEntities(server, source, displaySource) tools.RegisterISphereFileTools(server, source) tools.RegisterISphereSendMessageTool(server, nil) return server }