Why: models need a capped way to call several tools from one script, and a slow MCP server must not block the first prompt. Evidence: GOWORK=off go test ./runtime/codeorch/ ./runtime/tools/ ./runtime/mcp/ ./runtime/policy/ ./adapters/native/ Impact: run_tool_script stays unregistered until the caller turns it on. Deferred tools stay out of the model list. An unready MCP server connects in the background. Refs: feature/code-orchestration Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
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")
|
|
}
|
|
}
|
|
}
|