// Package tools реализует MCP-инструменты модуля omnichannel-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-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 }