package mcpclient import ( "context" "errors" "net/http" "net/http/httptest" "sync/atomic" "testing" "time" "github.com/modelcontextprotocol/go-sdk/mcp" ) func TestAgentToolSessionsNamespacingDisableAndLeaseCancellation(t *testing.T) { var calls atomic.Int32 server := newTestMCPServer(&calls) remote := httptest.NewServer(mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server { return server }, nil)) defer remote.Close() service, _ := testService(t) var ids []string for _, name := range []string{"phone", "files"} { view, err := service.SaveServer(ServerInput{Config: ServerConfig{Name: name, Transport: "http", URL: remote.URL, Enabled: true, TimeoutSeconds: 3}}) if err != nil { t.Fatal(err) } ids = append(ids, view.Config.ID) } first, err := OpenTools(context.Background(), service, nil) if err != nil { t.Fatal(err) } defer first.Close() if len(first.Tools()) != 4 { t.Fatalf("tools: %d", len(first.Tools())) } unique := map[string]bool{} for _, tool := range first.Tools() { if unique[tool.Name] { t.Fatal("duplicate tool name") } unique[tool.Name] = true } name := first.Tools()[0].Name if _, err := first.Call(context.Background(), name, `{}`); err != nil { t.Fatal(err) } second, err := OpenTools(context.Background(), service, nil) if err != nil { t.Fatal(err) } defer second.Close() ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond) defer cancel() if _, err := second.Call(ctx, second.Tools()[0].Name, `{}`); !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("waiting lease did not cancel: %v", err) } if calls.Load() != 1 { t.Fatal("parallel action bypassed sequence lease") } if err := service.SetEnabled(ids[0], false); err != nil { t.Fatal(err) } if _, err := first.Call(context.Background(), name, `{}`); err == nil { t.Fatal("disabled service still executed") } if calls.Load() != 1 { t.Fatal("disabled service was invoked") } }