Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user