package tools import ( "context" "encoding/json" "os" "path/filepath" "strings" "testing" ) type deferredEcho struct{} func (deferredEcho) Name() string { return "mcp__late__echo" } func (deferredEcho) Description() string { return "回声延迟工具" } func (deferredEcho) Parameters() json.RawMessage { return json.RawMessage(`{"type":"object","properties":{"text":{"type":"string"}}}`) } func (deferredEcho) Exposure() string { return ExposureDeferred } func (deferredEcho) Execute(_ context.Context, args json.RawMessage) (Result, error) { var a struct { Text string `json:"text"` } _ = json.Unmarshal(args, &a) return Result{Content: "echo " + a.Text}, nil } func TestDeferredToolStaysOutOfDeclsUntilCalledByName(t *testing.T) { reg := NewRegistry(t.TempDir()) reg.Register(deferredEcho{}) for _, d := range reg.Decls() { if d.Name == "mcp__late__echo" { t.Fatal("deferred tool must not be listed") } } found := reg.Search("回声") if !strings.Contains(found, "mcp__late__echo") || !strings.Contains(found, `"text"`) { t.Fatalf("search %q", found) } res := reg.Execute(context.Background(), "call_tool", json.RawMessage(`{"name":"mcp__late__echo","arguments":{"text":"hi"}}`)) if res.IsError || res.Content != "echo hi" { t.Fatalf("call %+v", res) } denied := reg.Execute(context.Background(), "call_tool", json.RawMessage(`{"name":"run_shell"}`)) if !denied.IsError { t.Fatalf("forbidden call should fail: %+v", denied) } } func TestActivatedNamesRestoreWithoutEnteringDecls(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "activated.json") reg := NewRegistry(dir) reg.SetActivationFile(path) reg.Register(deferredEcho{}) _ = reg.Execute(context.Background(), "call_tool", json.RawMessage(`{"name":"mcp__late__echo","arguments":{"text":"x"}}`)) if _, err := os.Stat(path); err != nil { t.Fatal(err) } next := NewRegistry(dir) next.SetActivationFile(path) if err := next.RestoreActivated(); err != nil { t.Fatal(err) } next.Register(deferredEcho{}) names := next.Activated() if len(names) != 1 || names[0] != "mcp__late__echo" { t.Fatalf("activated %+v", names) } for _, d := range next.Decls() { if d.Name == "mcp__late__echo" { t.Fatal("restored tool must stay out of the list") } } }