bcefbaa9c1
Adds `-O`/`-tools` CLI flag and `GITEA_TOOLS` environment variable accepting a comma-separated list of tool names. When set, only the listed tools are exposed to MCP clients, which lets AI agents trim their tool context. Composes with `--read-only`. Unknown names are logged at startup so typos surface instead of failing silently. Co-Authored-By: silverwind <me@silverwind.io> Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
package tool
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"gitea.com/gitea/gitea-mcp/pkg/flag"
|
|
"gitea.com/gitea/gitea-mcp/pkg/log"
|
|
|
|
"github.com/mark3labs/mcp-go/server"
|
|
)
|
|
|
|
type Tool struct {
|
|
write []server.ServerTool
|
|
read []server.ServerTool
|
|
}
|
|
|
|
func New() *Tool {
|
|
return &Tool{
|
|
write: make([]server.ServerTool, 0, 100),
|
|
read: make([]server.ServerTool, 0, 100),
|
|
}
|
|
}
|
|
|
|
func (t *Tool) RegisterWrite(s server.ServerTool) {
|
|
t.write = append(t.write, s)
|
|
}
|
|
|
|
func (t *Tool) RegisterRead(s server.ServerTool) {
|
|
t.read = append(t.read, s)
|
|
}
|
|
|
|
func (t *Tool) Tools() []server.ServerTool {
|
|
all := make([]server.ServerTool, 0, len(t.write)+len(t.read))
|
|
if !flag.ReadOnly {
|
|
all = append(all, t.write...)
|
|
}
|
|
all = append(all, t.read...)
|
|
if len(flag.AllowedTools) == 0 {
|
|
return all
|
|
}
|
|
filtered := make([]server.ServerTool, 0, len(all))
|
|
for _, st := range all {
|
|
if _, ok := flag.AllowedTools[st.Tool.Name]; ok {
|
|
filtered = append(filtered, st)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
// WarnUnmatchedAllowedTools logs any names in flag.AllowedTools that don't
|
|
// match a tool registered on any of the given domains. No-op if the allowlist
|
|
// is empty.
|
|
func WarnUnmatchedAllowedTools(domains ...*Tool) {
|
|
if len(flag.AllowedTools) == 0 {
|
|
return
|
|
}
|
|
known := map[string]struct{}{}
|
|
for _, d := range domains {
|
|
for _, st := range d.read {
|
|
known[st.Tool.Name] = struct{}{}
|
|
}
|
|
for _, st := range d.write {
|
|
known[st.Tool.Name] = struct{}{}
|
|
}
|
|
}
|
|
var unmatched []string
|
|
for name := range flag.AllowedTools {
|
|
if _, ok := known[name]; !ok {
|
|
unmatched = append(unmatched, name)
|
|
}
|
|
}
|
|
if len(unmatched) == 0 {
|
|
return
|
|
}
|
|
slices.Sort(unmatched)
|
|
log.Warnf("Unknown tools in --tools allowlist (ignored): %s", strings.Join(unmatched, ", "))
|
|
}
|