102 lines
4.2 KiB
Go
102 lines
4.2 KiB
Go
// Package tools реализует MCP-инструменты модуля omnichannel-configserver-mcp поверх
|
|
// доменного пакета configserver. Слой тонкий: разобрать аргументы, вызвать
|
|
// домен, вернуть текст/ошибку. Бизнес-логики здесь нет.
|
|
package tools
|
|
|
|
import (
|
|
"context"
|
|
|
|
"omnichannel-configserver-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-configserver-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
|
|
}
|