Initial commit: omnichannel-mcp — MCP-сервер управления платформой Omnichannel
ci / test (push) Successful in 2m22s
ci / lint (push) Successful in 4m31s

This commit is contained in:
Maksim Totmin
2026-10-07 20:13:23 +07:00
commit fdbd4a4fb6
54 changed files with 5672 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
// Package tools реализует MCP-инструменты модуля omnichannel-mcp поверх
// доменного пакета configserver. Слой тонкий: разобрать аргументы, вызвать
// домен, вернуть текст/ошибку. Бизнес-логики здесь нет.
package tools
import (
"context"
"omnichannel-mcp/internal/configserver"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// mgr — одиночный Manager процесса (см. ARCHITECTURE.md §2). Устанавливается
// один раз в RegisterAll; Manager потокобезопасен.
var mgr *configserver.Manager
func manager() *configserver.Manager { return mgr }
// RegisterAll — единая точка регистрации инструментов, сгруппированных по
// фазам жизненного цикла: observe (чтение) → configure → operate → release →
// maintain. Имена инструментов — без префикса (префикс может добавить MCP-хост).
func RegisterAll(s *mcp.Server, m *configserver.Manager) {
mgr = m
registerObserveTools(s)
registerConfigureTools(s)
registerOperateTools(s)
registerReleaseTools(s)
registerMaintainTools(s)
registerOverviewTools(s)
}
// openAPI резолвит конфиг/сервер и открывает фасад API. Любая ошибка на этом
// шаге (нет конфига, неизвестный алиас, read_only) — доменная: отдаём модели.
func openAPI(ctx context.Context, args map[string]any, write bool) (*configserver.API, *mcp.CallToolResult) {
m := manager()
if m == nil {
return nil, errorResult("omnichannel-mcp: manager не инициализирован")
}
alias := getString(args, "server", "")
api, err := m.Open(ctx, configPathArg(args), alias, write)
if err != nil {
return nil, errorResult(err.Error())
}
return api, nil
}
// configPathArg возвращает опциональный путь к конфигу из аргументов вызова.
// Основной способ задания конфига — флаг `-config`; per-call переопределение
// (`config_path`) поддержано для многотенантных обёрток. `_tenant_config` —
// устаревший алиас, принимается для совместимости.
func configPathArg(args map[string]any) string {
if v := getString(args, "config_path", ""); v != "" {
return v
}
return getString(args, "_tenant_config", "")
}
// result приводит (значение, ошибка) домена к результату инструмента:
// доменные ошибки — recoverable (текст видит модель), инфраструктурные — Go-ошибка.
func result(v any, err error) (*mcp.CallToolResult, error) {
if err != nil {
if configserver.IsDomainError(err) {
return errorResult(err.Error()), nil
}
return nil, err
}
return textResult(jsonText(v)), nil
}
// confirmGate требует confirm="true" для разрушительных операций. Это второй
// слой поверх require_approval в agent.yaml.
func confirmGate(args map[string]any, what string) *mcp.CallToolResult {
if !getBool(args, "confirm", false) {
return errorResult("операция \"" + what + "\" требует confirm=\"true\"")
}
return nil
}
// serverProp — общее описание аргумента выбора сервера.
func serverProp() map[string]any {
return strProps("Алиас сервера из config.json (по умолчанию — default)", false)
}
// appIDArg читает обязательный app_id.
func appIDArg(args map[string]any) (int, *mcp.CallToolResult) {
id := getInt(args, "app_id", 0)
if id <= 0 {
return 0, errorResult("нужен положительный app_id")
}
return id, nil
}
// versionIDArg читает обязательный version_id.
func versionIDArg(args map[string]any) (int, *mcp.CallToolResult) {
id := getInt(args, "version_id", 0)
if id <= 0 {
return 0, errorResult("нужен положительный version_id")
}
return id, nil
}