package toolkit import ( "context" "github.com/modelcontextprotocol/go-sdk/mcp" ) // Ключи forge-специфичных метаданных в стандартном поле `_meta` (SEP-2575). // MCP-хост forge, вызвав инструмент с флагом probe, ожидает, что сервер НЕ // выполнит действие, а вернёт эмитированные permission-паттерны. Константы // дублируются на стороне хоста: хост не импортирует этот пакет. const ( metaProbeKey = "forge.permission_patterns" metaPatternsKey = "forge.patterns" metaAlwaysKey = "forge.always" // metaCapabilityKey — флаг в Tool.Meta: сервер объявляет поддержку // forge-протокола эмитированных паттернов. Хост проверяет его перед // probe, чтобы не слать probe серверам без поддержки. metaCapabilityKey = "forge.supports_permission_patterns" ) // PatternsFn вычисляет эмитированные permission-паттерны и безопасные // префиксы "always" по аргументам инструмента. nil — инструмент паттернов // не эмитирует (хост применит политику по умолчанию). type PatternsFn func(args map[string]any) (patterns, always []string) // WrapProbe оборачивает handler, перехватывая probe-запрос хоста: вместо // выполнения действия возвращает `_meta` с паттернами. func WrapProbe(h mcp.ToolHandler, fn PatternsFn) mcp.ToolHandler { return func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { if IsProbe(req) { var patterns, always []string if fn != nil { patterns, always = fn(RequestArgs(req)) } return &mcp.CallToolResult{ Meta: mcp.Meta{metaPatternsKey: patterns, metaAlwaysKey: always}, }, nil } return h(ctx, req) } } // RegisterPatternTool регистрирует инструмент с поддержкой forge-probe: // помечает Tool.Meta флагом поддержки и оборачивает handler. Без флага хост // не стал бы слать probe-вызов. func RegisterPatternTool(s *mcp.Server, tool *mcp.Tool, fn PatternsFn, h mcp.ToolHandler) { if tool.Meta == nil { tool.Meta = mcp.Meta{} } tool.Meta[metaCapabilityKey] = true s.AddTool(tool, WrapProbe(h, fn)) } // IsProbe сообщает, является ли вызов probe-запросом паттернов. func IsProbe(req *mcp.CallToolRequest) bool { if req == nil || req.Params == nil { return false } v, ok := req.Params.GetMeta()[metaProbeKey] if !ok { return false } b, _ := v.(bool) return b }