forge-toolkit: хелперы для разработки MCP-серверов на Go. - toolkit: args/schema/result/env/probe/server - toolkit/validate: валидаторы синтаксиса - configreload: live-reload конфига по контент-хэшу - docs: ARCHITECTURE, quickstart, mcp-contract - template/ и examples/hello-tool/ - CI (Gitea Actions), лицензия Apache-2.0
151 lines
3.8 KiB
Go
151 lines
3.8 KiB
Go
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
|
|
}
|