Files
omnichannel-configserver-mcp/main.go
T
2026-10-07 20:13:23 +07:00

105 lines
3.4 KiB
Go

// Command omnichannel-mcp — MCP-сервер управления платформой Omnichannel через
// HTTP API config_server v1.1.0+ (деплой и эксплуатация).
//
// Следует общим соглашениям MCP-серверов: stdio-only, один потокобезопасный
// Manager, ручные JSON-схемы, отдельный домен, конфиг с live-reload,
// probe-паттерны разрешений на изменяющих инструментах.
//
// Логи пишутся ТОЛЬКО в stderr: stdout занят JSON-RPC протоколом.
package main
import (
"encoding/json"
"flag"
"fmt"
"log/slog"
"os"
"strings"
"omnichannel-mcp/internal/configserver"
"omnichannel-mcp/internal/tools"
"git.totmin.ru/en2zmax/forge-toolkit"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// serverName — имя MCP-сервера; часть хостов добавляет префикс serverName+"__"
// к именам инструментов.
const serverName = "omnichannel-mcp"
func main() {
// --health должен быть дешёвым: без чтения конфига и подключений.
if toolkit.Health() {
return
}
configPath := flag.String("config", "", "путь к config.json (иначе ищется OMNI_CONFIG_DIR/omnichannel-mcp.json)")
checkConfig := flag.Bool("check-config", false, "проверить конфиг, напечатать (секреты маскируются) и выйти")
logLevel := flag.String("log-level", envOr("OMNI_LOG_LEVEL", "info"), "уровень логов: debug|info|warn|error")
flag.Parse()
setupLogger(*logLevel)
if *checkConfig {
os.Exit(runCheckConfig(*configPath))
}
mgr := configserver.NewManager(*configPath)
defer mgr.Close()
if err := toolkit.Run(serverName, func(s *mcp.Server) {
tools.RegisterAll(s, mgr)
}); err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", serverName, err)
os.Exit(1)
}
}
// runCheckConfig печатает разрешённый конфиг (с маской секретов) — для проверки
// настройки до подключения клиента и в CI. Возвращает код выхода.
func runCheckConfig(cliPath string) int {
mgr := configserver.NewManager(cliPath)
path := mgr.ResolvePath("")
if path == "" {
fmt.Fprintln(os.Stderr, "config: путь не задан (нужен -config или OMNI_CONFIG_DIR)")
return 1
}
cfg, _, err := mgr.Config("")
if err != nil {
fmt.Fprintf(os.Stderr, "config: %v\n", err)
return 1
}
out, err := json.MarshalIndent(cfg.Redacted(), "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "config: %v\n", err)
return 1
}
fmt.Printf("config: %s\n%s\n", path, out)
fmt.Printf("servers: %s (default: %s)\n", strings.Join(cfg.ServerNames(), ", "), cfg.Default)
return 0
}
// setupLogger направляет логи в stderr с выбранным уровнем.
func setupLogger(level string) {
var lvl slog.Level
switch strings.ToLower(strings.TrimSpace(level)) {
case "debug":
lvl = slog.LevelDebug
case "warn", "warning":
lvl = slog.LevelWarn
case "error":
lvl = slog.LevelError
default:
lvl = slog.LevelInfo
}
handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: lvl})
slog.SetDefault(slog.New(handler))
}
func envOr(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}