package toolkit import ( "encoding/json" "fmt" "strconv" "github.com/modelcontextprotocol/go-sdk/mcp" ) // RequestArgs распаковывает сырые аргументы tool-вызова в map[string]any. // Битый JSON / пустые аргументы → пустая карта (валидация — в хендлерах). func RequestArgs(req *mcp.CallToolRequest) map[string]any { if req == nil || req.Params == nil || len(req.Params.Arguments) == 0 { return map[string]any{} } var a map[string]any if err := json.Unmarshal(req.Params.Arguments, &a); err != nil { return map[string]any{} } return a } // GetString возвращает строковый аргумент или def. Не-строки приводятся к // строке через %v (терпимость к числам/булевым от моделей). func GetString(args map[string]any, key, def string) string { v, ok := args[key] if !ok || v == nil { return def } if s, ok := v.(string); ok { return s } return fmt.Sprintf("%v", v) } // RequireString возвращает непустой строковый аргумент или ошибку. func RequireString(args map[string]any, key string) (string, error) { v, ok := args[key] if !ok || v == nil { return "", fmt.Errorf("missing required argument '%s'", key) } s, ok := v.(string) if !ok || s == "" { return "", fmt.Errorf("argument '%s' must be a non-empty string", key) } return s, nil } // GetInt возвращает целочисленный аргумент или def. Принимает float64 // (обычный JSON-числовой тип), int и строку с числом. func GetInt(args map[string]any, key string, def int) int { v, ok := args[key] if !ok || v == nil { return def } switch n := v.(type) { case float64: return int(n) case int: return n case string: if i, err := strconv.Atoi(n); err == nil { return i } } return def } // GetBool возвращает булев аргумент или def. func GetBool(args map[string]any, key string, def bool) bool { v, ok := args[key] if !ok || v == nil { return def } if b, ok := v.(bool); ok { return b } return def } // GetStringArray возвращает непустой срез непустых строк. ok=false, если // аргумента нет, он не массив или после фильтрации пуст. func GetStringArray(args map[string]any, key string) ([]string, bool) { v, ok := args[key] if !ok || v == nil { return nil, false } arr, ok := v.([]any) if !ok || len(arr) == 0 { return nil, false } var out []string for _, item := range arr { if s, ok := item.(string); ok && s != "" { out = append(out, s) } } if len(out) == 0 { return nil, false } return out, true } // GetIntArray возвращает непустой срез int (float64/int элементы). func GetIntArray(args map[string]any, key string) ([]int, bool) { v, ok := args[key] if !ok || v == nil { return nil, false } arr, ok := v.([]any) if !ok || len(arr) == 0 { return nil, false } var out []int for _, item := range arr { switch n := item.(type) { case float64: out = append(out, int(n)) case int: out = append(out, n) } } if len(out) == 0 { return nil, false } return out, true } // GetStringMap разбирает объект со строковыми значениями (напр. заголовки). func GetStringMap(args map[string]any, key string) map[string]string { v, ok := args[key] if !ok || v == nil { return nil } m, ok := v.(map[string]any) if !ok || len(m) == 0 { return nil } out := make(map[string]string, len(m)) for k, val := range m { if s, ok := val.(string); ok { out[k] = s } } if len(out) == 0 { return nil } return out }