diff --git a/event/description.go b/event/description.go
index d605b9707d..95bfedb607 100644
--- a/event/description.go
+++ b/event/description.go
@@ -11,7 +11,7 @@ import (
 
 	"go.mongodb.org/mongo-driver/v2/bson"
 	"go.mongodb.org/mongo-driver/v2/mongo/address"
-	"go.mongodb.org/mongo-driver/v2/tag"
+	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 )
 
 // ServerDescription contains information about a node in a cluster. This is
@@ -43,7 +43,7 @@ type ServerDescription struct {
 	SessionTimeoutMinutes    *int64
 	SetName                  string
 	SetVersion               uint32
-	Tags                     tag.Set
+	Tags                     readpref.TagSet
 	TopologyVersionProcessID bson.ObjectID
 	TopologyVersionCounter   int64
 }
diff --git a/internal/driverutil/description.go b/internal/driverutil/description.go
index f0ab6ab846..f323f9f049 100644
--- a/internal/driverutil/description.go
+++ b/internal/driverutil/description.go
@@ -16,7 +16,7 @@ import (
 	"go.mongodb.org/mongo-driver/v2/internal/handshake"
 	"go.mongodb.org/mongo-driver/v2/internal/ptrutil"
 	"go.mongodb.org/mongo-driver/v2/mongo/address"
-	"go.mongodb.org/mongo-driver/v2/tag"
+	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description"
 )
 
@@ -421,7 +421,7 @@ func NewServerDescription(addr address.Address, response bson.Raw) description.S
 				desc.LastError = err
 				return desc
 			}
-			desc.Tags = tag.NewTagSetFromMap(m)
+			desc.Tags = readpref.NewTagSetFromMap(m)
 		case "topologyVersion":
 			doc, ok := element.Value().DocumentOK()
 			if !ok {
diff --git a/internal/integration/database_test.go b/internal/integration/database_test.go
index 89d9d87f9f..ba03b8a8d4 100644
--- a/internal/integration/database_test.go
+++ b/internal/integration/database_test.go
@@ -75,7 +75,7 @@ func TestDatabase(t *testing.T) {
 			// layer, which should add a top-level $readPreference field to the command.
 
 			runCmdOpts := options.RunCmd().
-				SetReadPreference(readpref.SecondaryPreferred())
+				SetReadPreference(&readpref.ReadPref{Mode: readpref.SecondaryPreferredMode})
 			err := mt.DB.RunCommand(context.Background(), bson.D{{handshake.LegacyHello, 1}}, runCmdOpts).Err()
 			assert.Nil(mt, err, "RunCommand error: %v", err)
 
diff --git a/internal/integration/initial_dns_seedlist_discovery_test.go b/internal/integration/initial_dns_seedlist_discovery_test.go
index de9d44a058..a00f4709b8 100644
--- a/internal/integration/initial_dns_seedlist_discovery_test.go
+++ b/internal/integration/initial_dns_seedlist_discovery_test.go
@@ -87,7 +87,7 @@ func runSeedlistDiscoveryPingTest(mt *mtest.T, clientOpts *options.ClientOptions
 	defer cancel()
 
 	// Ping the server.
-	err = client.Ping(pingCtx, readpref.Nearest())
+	err = client.Ping(pingCtx, &readpref.ReadPref{Mode: readpref.NearestMode})
 	assert.Nil(mt, err, "Ping error: %v", err)
 }
 
diff --git a/internal/integration/json_helpers_test.go b/internal/integration/json_helpers_test.go
index acdebce53d..b986b49802 100644
--- a/internal/integration/json_helpers_test.go
+++ b/internal/integration/json_helpers_test.go
@@ -420,13 +420,13 @@ func readPrefFromString(s string) *readpref.ReadPref {
 	case "primary":
 		return readpref.Primary()
 	case "primarypreferred":
-		return readpref.PrimaryPreferred()
+		return &readpref.ReadPref{Mode: readpref.PrimaryPreferredMode}
 	case "secondary":
-		return readpref.Secondary()
+		return &readpref.ReadPref{Mode: readpref.SecondaryMode}
 	case "secondarypreferred":
-		return readpref.SecondaryPreferred()
+		return &readpref.ReadPref{Mode: readpref.SecondaryPreferredMode}
 	case "nearest":
-		return readpref.Nearest()
+		return &readpref.ReadPref{Mode: readpref.NearestMode}
 	}
 	return readpref.Primary()
 }
diff --git a/internal/integration/mtest/mongotest.go b/internal/integration/mtest/mongotest.go
index ed3d65a004..b3da9235fe 100644
--- a/internal/integration/mtest/mongotest.go
+++ b/internal/integration/mtest/mongotest.go
@@ -36,7 +36,7 @@ var (
 	// PrimaryRp is the primary read preference.
 	PrimaryRp = readpref.Primary()
 	// SecondaryRp is the secondary read preference.
-	SecondaryRp = readpref.Secondary()
+	SecondaryRp = &readpref.ReadPref{Mode: readpref.SecondaryMode}
 	// LocalRc is the local read concern
 	LocalRc = readconcern.Local()
 	// MajorityRc is the majority read concern
diff --git a/internal/integration/unified/common_options.go b/internal/integration/unified/common_options.go
index b0f3e84b57..cae3642860 100644
--- a/internal/integration/unified/common_options.go
+++ b/internal/integration/unified/common_options.go
@@ -14,7 +14,6 @@ import (
 	"go.mongodb.org/mongo-driver/v2/mongo/readconcern"
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 	"go.mongodb.org/mongo-driver/v2/mongo/writeconcern"
-	"go.mongodb.org/mongo-driver/v2/tag"
 )
 
 // This file defines helper types to convert BSON documents to ReadConcern, WriteConcern, and ReadPref objects.
@@ -70,32 +69,34 @@ func (rp *ReadPreference) ToReadPrefOption() (*readpref.ReadPref, error) {
 		return nil, fmt.Errorf("invalid read preference mode %q", rp.Mode)
 	}
 
-	var rpOptions []readpref.Option
+	rpOpts := readpref.Options()
+
 	if rp.TagSets != nil {
 		// Each item in the TagSets slice is a document that represents one set.
-		sets := make([]tag.Set, 0, len(rp.TagSets))
+		sets := make([]readpref.TagSet, 0, len(rp.TagSets))
 		for _, rawSet := range rp.TagSets {
-			parsed := make(tag.Set, 0, len(rawSet))
+			parsed := make(readpref.TagSet, 0, len(rawSet))
 			for k, v := range rawSet {
-				parsed = append(parsed, tag.Tag{Name: k, Value: v})
+				parsed = append(parsed, readpref.Tag{Name: k, Value: v})
 			}
 			sets = append(sets, parsed)
 		}
 
-		rpOptions = append(rpOptions, readpref.WithTagSets(sets...))
+		rpOpts.SetTagSets(sets)
 	}
 	if rp.MaxStalenessSeconds != nil {
 		maxStaleness := time.Duration(*rp.MaxStalenessSeconds) * time.Second
-		rpOptions = append(rpOptions, readpref.WithMaxStaleness(maxStaleness))
+		rpOpts.SetMaxStaleness(maxStaleness)
 	}
 	if rp.Hedge != nil {
 		if len(rp.Hedge) > 1 {
 			return nil, fmt.Errorf("invalid read preference hedge document: length cannot be greater than 1")
 		}
 		if enabled, ok := rp.Hedge["enabled"]; ok {
-			rpOptions = append(rpOptions, readpref.WithHedgeEnabled(enabled.(bool)))
+			hedgeEnabled := enabled.(bool)
+			rpOpts.SetHedgeEnabled(hedgeEnabled)
 		}
 	}
 
-	return readpref.New(mode, rpOptions...)
+	return readpref.New(mode, rpOpts)
 }
diff --git a/internal/serverselector/server_selector.go b/internal/serverselector/server_selector.go
index 86b33733ba..cd2d273047 100644
--- a/internal/serverselector/server_selector.go
+++ b/internal/serverselector/server_selector.go
@@ -12,7 +12,6 @@ import (
 	"time"
 
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
-	"go.mongodb.org/mongo-driver/v2/tag"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description"
 )
 
@@ -190,12 +189,12 @@ func (ssf Func) SelectServer(
 }
 
 func verifyMaxStaleness(rp *readpref.ReadPref, topo description.Topology) error {
-	maxStaleness, set := rp.MaxStaleness()
-	if !set {
+	maxStaleness := rp.MaxStaleness()
+	if maxStaleness == nil {
 		return nil
 	}
 
-	if maxStaleness < 90*time.Second {
+	if *maxStaleness < 90*time.Second {
 		return fmt.Errorf("max staleness (%s) must be greater than or equal to 90s", maxStaleness)
 	}
 
@@ -208,7 +207,7 @@ func verifyMaxStaleness(rp *readpref.ReadPref, topo description.Topology) error
 	s := topo.Servers[0]
 	idleWritePeriod := 10 * time.Second
 
-	if maxStaleness < s.HeartbeatInterval+idleWritePeriod {
+	if *maxStaleness < s.HeartbeatInterval+idleWritePeriod {
 		return fmt.Errorf(
 			"max staleness (%s) must be greater than or equal to the heartbeat interval (%s) plus idle write period (%s)",
 			maxStaleness, s.HeartbeatInterval, idleWritePeriod,
@@ -242,7 +241,7 @@ func selectSecondaries(rp *readpref.ReadPref, candidates []description.Server) [
 	if len(secondaries) == 0 {
 		return secondaries
 	}
-	if maxStaleness, set := rp.MaxStaleness(); set {
+	if maxStaleness := rp.MaxStaleness(); maxStaleness != nil {
 		primaries := selectByKind(candidates, description.ServerKindRSPrimary)
 		if len(primaries) == 0 {
 			baseTime := secondaries[0].LastWriteTime
@@ -255,7 +254,7 @@ func selectSecondaries(rp *readpref.ReadPref, candidates []description.Server) [
 			var selected []description.Server
 			for _, secondary := range secondaries {
 				estimatedStaleness := baseTime.Sub(secondary.LastWriteTime) + secondary.HeartbeatInterval
-				if estimatedStaleness <= maxStaleness {
+				if estimatedStaleness <= *maxStaleness {
 					selected = append(selected, secondary)
 				}
 			}
@@ -269,7 +268,7 @@ func selectSecondaries(rp *readpref.ReadPref, candidates []description.Server) [
 		for _, secondary := range secondaries {
 			estimatedStaleness := secondary.LastUpdateTime.Sub(secondary.LastWriteTime) -
 				primary.LastUpdateTime.Sub(primary.LastWriteTime) + secondary.HeartbeatInterval
-			if estimatedStaleness <= maxStaleness {
+			if estimatedStaleness <= *maxStaleness {
 				selected = append(selected, secondary)
 			}
 		}
@@ -279,7 +278,7 @@ func selectSecondaries(rp *readpref.ReadPref, candidates []description.Server) [
 	return secondaries
 }
 
-func selectByTagSet(candidates []description.Server, tagSets []tag.Set) []description.Server {
+func selectByTagSet(candidates []description.Server, tagSets []readpref.TagSet) []description.Server {
 	if len(tagSets) == 0 {
 		return candidates
 	}
@@ -327,7 +326,7 @@ func selectForReplicaSet(
 		}
 	}
 
-	switch rp.Mode() {
+	switch rp.Mode {
 	case readpref.PrimaryMode:
 		return selectByKind(candidates, description.ServerKindRSPrimary), nil
 	case readpref.PrimaryPreferredMode:
@@ -355,5 +354,5 @@ func selectForReplicaSet(
 		return selectByTagSet(selected, rp.TagSets()), nil
 	}
 
-	return nil, fmt.Errorf("unsupported mode: %d", rp.Mode())
+	return nil, fmt.Errorf("unsupported mode: %d", rp.Mode)
 }
diff --git a/internal/serverselector/server_selector_test.go b/internal/serverselector/server_selector_test.go
index bc330e27c7..59acecba20 100644
--- a/internal/serverselector/server_selector_test.go
+++ b/internal/serverselector/server_selector_test.go
@@ -21,7 +21,6 @@ import (
 	"go.mongodb.org/mongo-driver/v2/internal/spectest"
 	"go.mongodb.org/mongo-driver/v2/mongo/address"
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
-	"go.mongodb.org/mongo-driver/v2/tag"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description"
 )
 
@@ -115,7 +114,7 @@ func topologyKindFromString(t *testing.T, s string) description.TopologyKind {
 	return description.Unknown
 }
 
-func anyTagsInSets(sets []tag.Set) bool {
+func anyTagsInSets(sets []readpref.TagSet) bool {
 	for _, set := range sets {
 		if len(set) > 0 {
 			return true
@@ -219,7 +218,7 @@ func selectServers(t *testing.T, test *testCase) error {
 		}
 
 		if serverDescription.Tags != nil {
-			server.Tags = tag.NewTagSetFromMap(serverDescription.Tags)
+			server.Tags = readpref.NewTagSetFromMap(serverDescription.Tags)
 		}
 
 		if test.ReadPreference.MaxStaleness != nil && server.WireVersion == nil {
@@ -243,19 +242,19 @@ func selectServers(t *testing.T, test *testCase) error {
 		return err
 	}
 
-	options := make([]readpref.Option, 0, 1)
+	rpOpts := readpref.Options()
 
-	tagSets := tag.NewTagSetsFromMaps(test.ReadPreference.TagSets)
+	tagSets := readpref.NewTagSetsFromMaps(test.ReadPreference.TagSets)
 	if anyTagsInSets(tagSets) {
-		options = append(options, readpref.WithTagSets(tagSets...))
+		rpOpts.SetTagSets(tagSets)
 	}
 
 	if test.ReadPreference.MaxStaleness != nil {
 		s := time.Duration(*test.ReadPreference.MaxStaleness) * time.Second
-		options = append(options, readpref.WithMaxStaleness(s))
+		rpOpts.SetMaxStaleness(s)
 	}
 
-	rp, err := readpref.New(readprefMode, options...)
+	rp, err := readpref.New(readprefMode, rpOpts)
 	if err != nil {
 		return err
 	}
@@ -496,7 +495,7 @@ var readPrefTestPrimary = description.Server{
 	LastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),
 	LastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),
 	Kind:              description.ServerKindRSPrimary,
-	Tags:              tag.Set{tag.Tag{Name: "a", Value: "1"}},
+	Tags:              readpref.TagSet{readpref.Tag{Name: "a", Value: "1"}},
 	WireVersion:       &description.VersionRange{Min: 6, Max: 21},
 }
 var readPrefTestSecondary1 = description.Server{
@@ -505,7 +504,7 @@ var readPrefTestSecondary1 = description.Server{
 	LastWriteTime:     time.Date(2017, 2, 11, 13, 58, 0, 0, time.UTC),
 	LastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),
 	Kind:              description.ServerKindRSSecondary,
-	Tags:              tag.Set{tag.Tag{Name: "a", Value: "1"}},
+	Tags:              readpref.TagSet{readpref.Tag{Name: "a", Value: "1"}},
 	WireVersion:       &description.VersionRange{Min: 6, Max: 21},
 }
 var readPrefTestSecondary2 = description.Server{
@@ -514,7 +513,7 @@ var readPrefTestSecondary2 = description.Server{
 	LastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),
 	LastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),
 	Kind:              description.ServerKindRSSecondary,
-	Tags:              tag.Set{tag.Tag{Name: "a", Value: "2"}},
+	Tags:              readpref.TagSet{readpref.Tag{Name: "a", Value: "2"}},
 	WireVersion:       &description.VersionRange{Min: 6, Max: 21},
 }
 var readPrefTestTopology = description.Topology{
@@ -755,7 +754,7 @@ func TestSelector_Primary_with_no_primary(t *testing.T) {
 func TestSelector_PrimaryPreferred(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.PrimaryPreferred()
+	subject := &readpref.ReadPref{Mode: readpref.PrimaryPreferredMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).
 		SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
@@ -768,9 +767,15 @@ func TestSelector_PrimaryPreferred(t *testing.T) {
 func TestSelector_PrimaryPreferred_ignores_tags(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.PrimaryPreferred(
-		readpref.WithTags("a", "2"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "2")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, err := readpref.New(readpref.PrimaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).
 		SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
@@ -783,7 +788,8 @@ func TestSelector_PrimaryPreferred_ignores_tags(t *testing.T) {
 func TestSelector_PrimaryPreferred_with_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.PrimaryPreferred()
+	subject, err := readpref.New(readpref.PrimaryPreferredMode, nil)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).
 		SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
@@ -796,9 +802,15 @@ func TestSelector_PrimaryPreferred_with_no_primary(t *testing.T) {
 func TestSelector_PrimaryPreferred_with_no_primary_and_tags(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.PrimaryPreferred(
-		readpref.WithTags("a", "2"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "2")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, err := readpref.New(readpref.PrimaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).
 		SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
@@ -811,9 +823,13 @@ func TestSelector_PrimaryPreferred_with_no_primary_and_tags(t *testing.T) {
 func TestSelector_PrimaryPreferred_with_maxStaleness(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.PrimaryPreferred(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, err := readpref.New(readpref.PrimaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).
 		SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
@@ -826,9 +842,13 @@ func TestSelector_PrimaryPreferred_with_maxStaleness(t *testing.T) {
 func TestSelector_PrimaryPreferred_with_maxStaleness_and_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.PrimaryPreferred(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, err := readpref.New(readpref.PrimaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).
 		SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
@@ -841,7 +861,8 @@ func TestSelector_PrimaryPreferred_with_maxStaleness_and_no_primary(t *testing.T
 func TestSelector_SecondaryPreferred(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred()
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, nil)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -853,9 +874,15 @@ func TestSelector_SecondaryPreferred(t *testing.T) {
 func TestSelector_SecondaryPreferred_with_tags(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred(
-		readpref.WithTags("a", "2"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "2")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -867,9 +894,15 @@ func TestSelector_SecondaryPreferred_with_tags(t *testing.T) {
 func TestSelector_SecondaryPreferred_with_tags_that_do_not_match(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred(
-		readpref.WithTags("a", "3"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "3")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -881,9 +914,15 @@ func TestSelector_SecondaryPreferred_with_tags_that_do_not_match(t *testing.T) {
 func TestSelector_SecondaryPreferred_with_tags_that_do_not_match_and_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred(
-		readpref.WithTags("a", "3"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "3")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
 
@@ -894,7 +933,8 @@ func TestSelector_SecondaryPreferred_with_tags_that_do_not_match_and_no_primary(
 func TestSelector_SecondaryPreferred_with_no_secondaries(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred()
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, nil)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestPrimary})
 
@@ -906,7 +946,7 @@ func TestSelector_SecondaryPreferred_with_no_secondaries(t *testing.T) {
 func TestSelector_SecondaryPreferred_with_no_secondaries_or_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred()
+	subject := &readpref.ReadPref{Mode: readpref.SecondaryPreferredMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{})
 
@@ -917,9 +957,13 @@ func TestSelector_SecondaryPreferred_with_no_secondaries_or_primary(t *testing.T
 func TestSelector_SecondaryPreferred_with_maxStaleness(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -931,9 +975,13 @@ func TestSelector_SecondaryPreferred_with_maxStaleness(t *testing.T) {
 func TestSelector_SecondaryPreferred_with_maxStaleness_and_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.SecondaryPreferred(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, err := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+	assert.NoError(t, err)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
 
@@ -945,7 +993,7 @@ func TestSelector_SecondaryPreferred_with_maxStaleness_and_no_primary(t *testing
 func TestSelector_Secondary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Secondary()
+	subject := &readpref.ReadPref{Mode: readpref.SecondaryPreferredMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -957,9 +1005,12 @@ func TestSelector_Secondary(t *testing.T) {
 func TestSelector_Secondary_with_tags(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Secondary(
-		readpref.WithTags("a", "2"),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.SecondaryMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -991,13 +1042,15 @@ func TestSelector_Secondary_with_empty_tag_set(t *testing.T) {
 		Servers: []description.Server{primaryNoTags, firstSecondaryNoTags, secondSecondaryNoTags},
 	}
 
-	nonMatchingSet := tag.Set{
+	nonMatchingSet := readpref.TagSet{
 		{Name: "foo", Value: "bar"},
 	}
-	emptyTagSet := tag.Set{}
-	rp := readpref.Secondary(
-		readpref.WithTagSets(nonMatchingSet, emptyTagSet),
-	)
+	emptyTagSet := readpref.TagSet{}
+
+	rpOpts := readpref.Options()
+	rpOpts.SetTagSets([]readpref.TagSet{nonMatchingSet, emptyTagSet})
+
+	rp, _ := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: rp}).SelectServer(topologyNoTags, topologyNoTags.Servers)
 	assert.Nil(t, err, "SelectServer error: %v", err)
@@ -1008,9 +1061,13 @@ func TestSelector_Secondary_with_empty_tag_set(t *testing.T) {
 func TestSelector_Secondary_with_tags_that_do_not_match(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Secondary(
-		readpref.WithTags("a", "3"),
-	)
+	tagSet, err := readpref.NewTagSet("a", "3")
+	assert.NoError(t, err)
+
+	rpOpts := readpref.Options()
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, _ := readpref.New(readpref.SecondaryMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -1021,7 +1078,7 @@ func TestSelector_Secondary_with_tags_that_do_not_match(t *testing.T) {
 func TestSelector_Secondary_with_no_secondaries(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Secondary()
+	subject := &readpref.ReadPref{Mode: readpref.SecondaryMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestPrimary})
 
@@ -1032,9 +1089,12 @@ func TestSelector_Secondary_with_no_secondaries(t *testing.T) {
 func TestSelector_Secondary_with_maxStaleness(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Secondary(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.SecondaryMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -1046,9 +1106,12 @@ func TestSelector_Secondary_with_maxStaleness(t *testing.T) {
 func TestSelector_Secondary_with_maxStaleness_and_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Secondary(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.SecondaryMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
 
@@ -1060,7 +1123,7 @@ func TestSelector_Secondary_with_maxStaleness_and_no_primary(t *testing.T) {
 func TestSelector_Nearest(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest()
+	subject := &readpref.ReadPref{Mode: readpref.NearestMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -1072,9 +1135,14 @@ func TestSelector_Nearest(t *testing.T) {
 func TestSelector_Nearest_with_tags(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest(
-		readpref.WithTags("a", "1"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "1")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, _ := readpref.New(readpref.NearestMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -1086,9 +1154,14 @@ func TestSelector_Nearest_with_tags(t *testing.T) {
 func TestSelector_Nearest_with_tags_that_do_not_match(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest(
-		readpref.WithTags("a", "3"),
-	)
+	rpOpts := readpref.Options()
+
+	tagSet, err := readpref.NewTagSet("a", "3")
+	assert.NoError(t, err)
+
+	rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+	subject, _ := readpref.New(readpref.NearestMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -1099,7 +1172,7 @@ func TestSelector_Nearest_with_tags_that_do_not_match(t *testing.T) {
 func TestSelector_Nearest_with_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest()
+	subject := &readpref.ReadPref{Mode: readpref.NearestMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
 
@@ -1111,7 +1184,7 @@ func TestSelector_Nearest_with_no_primary(t *testing.T) {
 func TestSelector_Nearest_with_no_secondaries(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest()
+	subject := &readpref.ReadPref{Mode: readpref.NearestMode}
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestPrimary})
 
@@ -1123,9 +1196,12 @@ func TestSelector_Nearest_with_no_secondaries(t *testing.T) {
 func TestSelector_Nearest_with_maxStaleness(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.NearestMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)
 
@@ -1137,9 +1213,12 @@ func TestSelector_Nearest_with_maxStaleness(t *testing.T) {
 func TestSelector_Nearest_with_maxStaleness_and_no_primary(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest(
-		readpref.WithMaxStaleness(time.Duration(90) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 90 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.NearestMode, rpOpts)
 
 	result, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})
 
@@ -1151,9 +1230,12 @@ func TestSelector_Nearest_with_maxStaleness_and_no_primary(t *testing.T) {
 func TestSelector_Max_staleness_is_less_than_90_seconds(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest(
-		readpref.WithMaxStaleness(time.Duration(50) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 50 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.NearestMode, rpOpts)
 
 	s := description.Server{
 		Addr:              address.Address("localhost:27017"),
@@ -1176,9 +1258,12 @@ func TestSelector_Max_staleness_is_less_than_90_seconds(t *testing.T) {
 func TestSelector_Max_staleness_is_too_low(t *testing.T) {
 	t.Parallel()
 
-	subject := readpref.Nearest(
-		readpref.WithMaxStaleness(time.Duration(100) * time.Second),
-	)
+	rpOpts := readpref.Options()
+
+	maxStaleness := 100 * time.Second
+	rpOpts.SetMaxStaleness(maxStaleness)
+
+	subject, _ := readpref.New(readpref.NearestMode, rpOpts)
 
 	s := description.Server{
 		Addr:              address.Address("localhost:27017"),
@@ -1238,7 +1323,7 @@ func TestEqualServers(t *testing.T) {
 			},
 			{"setName", description.Server{SetName: "foo"}, false},
 			{"setVersion", description.Server{SetVersion: 1}, false},
-			{"tags", description.Server{Tags: tag.Set{tag.Tag{"foo", "bar"}}}, false},
+			{"tags", description.Server{Tags: readpref.TagSet{readpref.Tag{"foo", "bar"}}}, false},
 			{"topologyVersion", description.Server{TopologyVersion: &description.TopologyVersion{bson.NewObjectID(), 0}}, false},
 			{"kind", description.Server{Kind: description.ServerKindStandalone}, false},
 			{"wireVersion", description.Server{WireVersion: &description.VersionRange{1, 2}}, false},
diff --git a/mongo/client.go b/mongo/client.go
index 04ebcb4eb2..d2154367c9 100644
--- a/mongo/client.go
+++ b/mongo/client.go
@@ -492,9 +492,11 @@ func (c *Client) endSessions(ctx context.Context) {
 		return
 	}
 
+	rpOpts, _ := readpref.New(readpref.PrimaryPreferredMode, nil)
 	sessionIDs := c.sessionPool.IDSlice()
+
 	op := operation.NewEndSessions(nil).ClusterClock(c.clock).Deployment(c.deployment).
-		ServerSelector(&serverselector.ReadPref{ReadPref: readpref.PrimaryPreferred()}).
+		ServerSelector(&serverselector.ReadPref{ReadPref: rpOpts}).
 		CommandMonitor(c.monitor).Database("admin").Crypt(c.cryptFLE).ServerAPI(c.serverAPI)
 
 	totalNumIDs := len(sessionIDs)
diff --git a/mongo/client_test.go b/mongo/client_test.go
index ee56449ce6..bbd1873572 100644
--- a/mongo/client_test.go
+++ b/mongo/client_test.go
@@ -25,7 +25,6 @@ import (
 	"go.mongodb.org/mongo-driver/v2/mongo/readconcern"
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 	"go.mongodb.org/mongo-driver/v2/mongo/writeconcern"
-	"go.mongodb.org/mongo-driver/v2/tag"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session"
@@ -92,22 +91,22 @@ func TestClient(t *testing.T) {
 	t.Run("read preference", func(t *testing.T) {
 		t.Run("absent", func(t *testing.T) {
 			client := setupClient()
-			gotMode := client.readPreference.Mode()
+			gotMode := client.readPreference.Mode
 			wantMode := readpref.PrimaryMode
 			assert.Equal(t, gotMode, wantMode, "expected mode %v, got %v", wantMode, gotMode)
-			_, flag := client.readPreference.MaxStaleness()
-			assert.False(t, flag, "expected max staleness to not be set but was")
+			gotMaxStaleness := client.readPreference.MaxStaleness()
+			assert.Nil(t, gotMaxStaleness, "expected max staleness to not be set but was")
 		})
 		t.Run("specified", func(t *testing.T) {
-			tags := []tag.Set{
+			tags := []readpref.TagSet{
 				{
-					tag.Tag{
+					readpref.Tag{
 						Name:  "one",
 						Value: "1",
 					},
 				},
 				{
-					tag.Tag{
+					readpref.Tag{
 						Name:  "two",
 						Value: "2",
 					},
@@ -117,14 +116,14 @@ func TestClient(t *testing.T) {
 			cs += "?readpreference=secondary&readPreferenceTags=one:1&readPreferenceTags=two:2&maxStaleness=5"
 
 			client := setupClient(options.Client().ApplyURI(cs))
-			gotMode := client.readPreference.Mode()
+			gotMode := client.readPreference.Mode
 			assert.Equal(t, gotMode, readpref.SecondaryMode, "expected mode %v, got %v", readpref.SecondaryMode, gotMode)
 			gotTags := client.readPreference.TagSets()
 			assert.Equal(t, gotTags, tags, "expected tags %v, got %v", tags, gotTags)
-			gotStaleness, flag := client.readPreference.MaxStaleness()
-			assert.True(t, flag, "expected max staleness to be set but was not")
+			gotStaleness := client.readPreference.MaxStaleness()
+			require.NotNil(t, gotStaleness, "expected max staleness to be set but was not")
 			wantStaleness := time.Duration(5) * time.Second
-			assert.Equal(t, gotStaleness, wantStaleness, "expected staleness %v, got %v", wantStaleness, gotStaleness)
+			assert.Equal(t, *gotStaleness, wantStaleness, "expected staleness %v, got %v", wantStaleness, *gotStaleness)
 		})
 	})
 	t.Run("localThreshold", func(t *testing.T) {
diff --git a/mongo/collection_test.go b/mongo/collection_test.go
index 648f04a46f..eb0b64002c 100644
--- a/mongo/collection_test.go
+++ b/mongo/collection_test.go
@@ -48,7 +48,7 @@ func TestCollection(t *testing.T) {
 	})
 	t.Run("specified options", func(t *testing.T) {
 		rpPrimary := readpref.Primary()
-		rpSecondary := readpref.Secondary()
+		rpSecondary := &readpref.ReadPref{Mode: readpref.SecondaryMode}
 		wc1 := &writeconcern.WriteConcern{W: 5}
 		wc2 := &writeconcern.WriteConcern{W: 10}
 		rcLocal := readconcern.Local()
diff --git a/mongo/database.go b/mongo/database.go
index 2a80fbf238..931e219dd0 100644
--- a/mongo/database.go
+++ b/mongo/database.go
@@ -169,7 +169,7 @@ func (db *Database) processRunCommand(
 		return nil, sess, err
 	}
 
-	if sess != nil && sess.TransactionRunning() && args.ReadPreference != nil && args.ReadPreference.Mode() != readpref.PrimaryMode {
+	if sess != nil && sess.TransactionRunning() && args.ReadPreference != nil && args.ReadPreference.Mode != readpref.PrimaryMode {
 		return nil, sess, errors.New("read preference in a transaction must be primary")
 	}
 
diff --git a/mongo/database_test.go b/mongo/database_test.go
index 6b9b8df319..e01d6f70ad 100644
--- a/mongo/database_test.go
+++ b/mongo/database_test.go
@@ -48,7 +48,7 @@ func TestDatabase(t *testing.T) {
 	t.Run("options", func(t *testing.T) {
 		t.Run("custom", func(t *testing.T) {
 			rpPrimary := readpref.Primary()
-			rpSecondary := readpref.Secondary()
+			rpSecondary := &readpref.ReadPref{Mode: readpref.SecondaryMode}
 			wc1 := &writeconcern.WriteConcern{W: 5}
 			wc2 := &writeconcern.WriteConcern{W: 10}
 			rcLocal := readconcern.Local()
diff --git a/mongo/options/clientoptions.go b/mongo/options/clientoptions.go
index 90b503104d..91607996f8 100644
--- a/mongo/options/clientoptions.go
+++ b/mongo/options/clientoptions.go
@@ -28,7 +28,6 @@ import (
 	"go.mongodb.org/mongo-driver/v2/mongo/readconcern"
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 	"go.mongodb.org/mongo-driver/v2/mongo/writeconcern"
-	"go.mongodb.org/mongo-driver/v2/tag"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage"
@@ -392,15 +391,15 @@ func setURIOpts(uri string, opts *ClientOptions) error {
 	}
 
 	if connString.ReadPreference != "" || len(connString.ReadPreferenceTagSets) > 0 || connString.MaxStalenessSet {
-		readPrefOpts := make([]readpref.Option, 0, 1)
+		readPrefOpts := readpref.Options()
 
-		tagSets := tag.NewTagSetsFromMaps(connString.ReadPreferenceTagSets)
+		tagSets := readpref.NewTagSetsFromMaps(connString.ReadPreferenceTagSets)
 		if len(tagSets) > 0 {
-			readPrefOpts = append(readPrefOpts, readpref.WithTagSets(tagSets...))
+			readPrefOpts.SetTagSets(tagSets)
 		}
 
 		if connString.MaxStaleness != 0 {
-			readPrefOpts = append(readPrefOpts, readpref.WithMaxStaleness(connString.MaxStaleness))
+			readPrefOpts.SetMaxStaleness(connString.MaxStaleness)
 		}
 
 		mode, err := readpref.ModeFromString(connString.ReadPreference)
@@ -408,7 +407,7 @@ func setURIOpts(uri string, opts *ClientOptions) error {
 			return err
 		}
 
-		opts.ReadPreference, err = readpref.New(mode, readPrefOpts...)
+		opts.ReadPreference, err = readpref.New(mode, readPrefOpts)
 		if err != nil {
 			return err
 		}
diff --git a/mongo/options/clientoptions_test.go b/mongo/options/clientoptions_test.go
index c50a4dfb32..48d643d2ee 100644
--- a/mongo/options/clientoptions_test.go
+++ b/mongo/options/clientoptions_test.go
@@ -767,8 +767,11 @@ func TestSetURIopts(t *testing.T) {
 			name: "ReadPreferenceTagSets",
 			uri:  "mongodb://localhost/?readPreference=secondaryPreferred&readPreferenceTags=foo:bar",
 			wantopts: &ClientOptions{
-				Hosts:          []string{"localhost"},
-				ReadPreference: readpref.SecondaryPreferred(readpref.WithTags("foo", "bar")),
+				Hosts: []string{"localhost"},
+				ReadPreference: func() *readpref.ReadPref {
+					tagSet, _ := readpref.NewTagSet("foo", "bar")
+					return readpref.SecondaryPreferred(readpref.Options().SetTagSets([]readpref.TagSet{tagSet}))
+				}(),
 			},
 			wantErrs: nil,
 		},
@@ -777,7 +780,7 @@ func TestSetURIopts(t *testing.T) {
 			uri:  "mongodb://localhost/?readPreference=secondaryPreferred&maxStaleness=250",
 			wantopts: &ClientOptions{
 				Hosts:          []string{"localhost"},
-				ReadPreference: readpref.SecondaryPreferred(readpref.WithMaxStaleness(250 * time.Second)),
+				ReadPreference: readpref.SecondaryPreferred(readpref.Options().SetMaxStaleness(250 * time.Second)),
 			},
 			wantErrs: nil,
 		},
diff --git a/mongo/readpref/mode.go b/mongo/readpref/mode.go
index ce036504cb..aa2c25099c 100644
--- a/mongo/readpref/mode.go
+++ b/mongo/readpref/mode.go
@@ -12,29 +12,28 @@ import (
 )
 
 // Mode indicates the user's preference on reads.
-type Mode uint8
+type Mode string
 
 // Mode constants
 const (
-	_ Mode = iota
 	// PrimaryMode indicates that only a primary is
 	// considered for reading. This is the default
 	// mode.
-	PrimaryMode
+	PrimaryMode = "primary"
 	// PrimaryPreferredMode indicates that if a primary
 	// is available, use it; otherwise, eligible
 	// secondaries will be considered.
-	PrimaryPreferredMode
+	PrimaryPreferredMode = "primaryPreferred"
 	// SecondaryMode indicates that only secondaries
 	// should be considered.
-	SecondaryMode
+	SecondaryMode = "secondary"
 	// SecondaryPreferredMode indicates that only secondaries
 	// should be considered when one is available. If none
 	// are available, then a primary will be considered.
-	SecondaryPreferredMode
+	SecondaryPreferredMode = "secondaryPreferred"
 	// NearestMode indicates that all primaries and secondaries
 	// will be considered.
-	NearestMode
+	NearestMode = "nearest"
 )
 
 // ModeFromString returns a mode corresponding to
@@ -52,37 +51,5 @@ func ModeFromString(mode string) (Mode, error) {
 	case "nearest":
 		return NearestMode, nil
 	}
-	return Mode(0), fmt.Errorf("unknown read preference %v", mode)
-}
-
-// String returns the string representation of mode.
-func (mode Mode) String() string {
-	switch mode {
-	case PrimaryMode:
-		return "primary"
-	case PrimaryPreferredMode:
-		return "primaryPreferred"
-	case SecondaryMode:
-		return "secondary"
-	case SecondaryPreferredMode:
-		return "secondaryPreferred"
-	case NearestMode:
-		return "nearest"
-	default:
-		return "unknown"
-	}
-}
-
-// IsValid checks whether the mode is valid.
-func (mode Mode) IsValid() bool {
-	switch mode {
-	case PrimaryMode,
-		PrimaryPreferredMode,
-		SecondaryMode,
-		SecondaryPreferredMode,
-		NearestMode:
-		return true
-	default:
-		return false
-	}
+	return "", fmt.Errorf("unknown read preference %v", mode)
 }
diff --git a/mongo/readpref/mode_test.go b/mongo/readpref/mode_test.go
deleted file mode 100644
index e92d4a35fb..0000000000
--- a/mongo/readpref/mode_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) MongoDB, Inc. 2020-present.
-
-// Licensed under the Apache License, Version 2.0 (the "License"); you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-
-package readpref
-
-import (
-	"testing"
-
-	"go.mongodb.org/mongo-driver/v2/internal/assert"
-)
-
-func TestMode_String(t *testing.T) {
-	t.Parallel()
-
-	testCases := []struct {
-		name string
-		mode Mode
-	}{
-		{"primary", PrimaryMode},
-		{"primaryPreferred", PrimaryPreferredMode},
-		{"secondary", SecondaryMode},
-		{"secondaryPreferred", SecondaryPreferredMode},
-		{"nearest", NearestMode},
-		{"unknown", Mode(42)},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			assert.Equal(t, tc.name, tc.mode.String(), "expected %q, got %q", tc.name, tc.mode.String())
-		})
-	}
-}
diff --git a/mongo/readpref/options.go b/mongo/readpref/options.go
deleted file mode 100644
index f4671d5d36..0000000000
--- a/mongo/readpref/options.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) MongoDB, Inc. 2017-present.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-
-package readpref
-
-import (
-	"errors"
-	"time"
-
-	"go.mongodb.org/mongo-driver/v2/tag"
-)
-
-// ErrInvalidTagSet indicates that an invalid set of tags was specified.
-var ErrInvalidTagSet = errors.New("an even number of tags must be specified")
-
-// Option configures a read preference
-type Option func(*ReadPref) error
-
-// WithMaxStaleness sets the maximum staleness a
-// server is allowed.
-func WithMaxStaleness(ms time.Duration) Option {
-	return func(rp *ReadPref) error {
-		rp.maxStaleness = ms
-		rp.maxStalenessSet = true
-		return nil
-	}
-}
-
-// WithTags specifies a single tag set used to match replica set members. If no members match the
-// tag set, read operations will return an error. To avoid errors if no members match the tag set, use
-// [WithTagSets] and include an empty tag set as the last tag set in the list.
-//
-// The last call to [WithTags] or [WithTagSets] overrides all previous calls to either method.
-//
-// For more information about read preference tags, see
-// https://www.mongodb.com/docs/manual/core/read-preference-tags/
-func WithTags(tags ...string) Option {
-	return func(rp *ReadPref) error {
-		length := len(tags)
-		if length < 2 || length%2 != 0 {
-			return ErrInvalidTagSet
-		}
-
-		tagset := make(tag.Set, 0, length/2)
-
-		for i := 1; i < length; i += 2 {
-			tagset = append(tagset, tag.Tag{Name: tags[i-1], Value: tags[i]})
-		}
-
-		return WithTagSets(tagset)(rp)
-	}
-}
-
-// WithTagSets specifies a list of tag sets used to match replica set members. If the list contains
-// multiple tag sets, members are matched against each tag set in succession until a match is found.
-// Once a match is found, the remaining tag sets are ignored. If no members match any of the tag
-// sets, the read operation returns with an error. To avoid an error if no members match any of the
-// tag sets, include an empty tag set as the last tag set in the list.
-//
-// The last call to [WithTags] or [WithTagSets] overrides all previous calls to either method.
-//
-// For more information about read preference tags, see
-// https://www.mongodb.com/docs/manual/core/read-preference-tags/
-func WithTagSets(tagSets ...tag.Set) Option {
-	return func(rp *ReadPref) error {
-		rp.tagSets = tagSets
-		return nil
-	}
-}
-
-// WithHedgeEnabled specifies whether or not hedged reads should be enabled in the server. This feature requires MongoDB
-// server version 4.4 or higher. For more information about hedged reads, see
-// https://www.mongodb.com/docs/manual/core/sharded-cluster-query-router/#mongos-hedged-reads. If not specified, the default
-// is to not send a value to the server, which will result in the server defaults being used.
-func WithHedgeEnabled(hedgeEnabled bool) Option {
-	return func(rp *ReadPref) error {
-		rp.hedgeEnabled = &hedgeEnabled
-		return nil
-	}
-}
diff --git a/mongo/readpref/options_example_test.go b/mongo/readpref/options_example_test.go
deleted file mode 100644
index ff66de621f..0000000000
--- a/mongo/readpref/options_example_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) MongoDB, Inc. 2023-present.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-
-package readpref_test
-
-import (
-	"go.mongodb.org/mongo-driver/v2/mongo"
-	"go.mongodb.org/mongo-driver/v2/mongo/options"
-	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
-	"go.mongodb.org/mongo-driver/v2/tag"
-)
-
-// Configure a Client with a read preference that selects the nearest replica
-// set member that includes tags "region: South" and "datacenter: A".
-func ExampleWithTags() {
-	rp := readpref.Nearest(
-		readpref.WithTags(
-			"region", "South",
-			"datacenter", "A"))
-
-	opts := options.Client().
-		ApplyURI("mongodb://localhost:27017").
-		SetReadPreference(rp)
-
-	_, err := mongo.Connect(opts)
-	if err != nil {
-		panic(err)
-	}
-}
-
-// Configure a Client with a read preference that selects the nearest replica
-// set member matching a set of tags. Try to match members in 3 stages:
-//
-//  1. Match replica set members that include tags "region: South" and
-//     "datacenter: A".
-//  2. Match replica set members that includes tag "region: South".
-//  3. Match any replica set member.
-//
-// Stage 3 is used to avoid errors when no members match the previous 2 stages.
-func ExampleWithTagSets() {
-	tagSetList := tag.NewTagSetsFromMaps([]map[string]string{
-		{"region": "South", "datacenter": "A"},
-		{"region": "South"},
-		{},
-	})
-
-	rp := readpref.Nearest(readpref.WithTagSets(tagSetList...))
-
-	opts := options.Client().
-		ApplyURI("mongodb://localhost").
-		SetReadPreference(rp)
-
-	_, err := mongo.Connect(opts)
-	if err != nil {
-		panic(err)
-	}
-}
diff --git a/mongo/readpref/readpref.go b/mongo/readpref/readpref.go
index 47e5b0cb8a..7abb9df44c 100644
--- a/mongo/readpref/readpref.go
+++ b/mongo/readpref/readpref.go
@@ -12,94 +12,145 @@ import (
 	"errors"
 	"fmt"
 	"time"
-
-	"go.mongodb.org/mongo-driver/v2/tag"
 )
 
-var (
-	errInvalidReadPreference = errors.New("can not specify tags, max staleness, or hedge with mode primary")
-)
+var errInvalidReadPreference = errors.New("can not specify tags, max staleness, or hedge with mode primary")
+
+// ReadPref determines which servers are considered suitable for read operations.
+type ReadPref struct {
+	Mode Mode
+
+	maxStaleness *time.Duration
+	tagSets      []TagSet
+	hedgeEnabled *bool
+}
+
+// Builder contains options to configure a ReadPref object. Each option
+// can be set through setter functions. See documentation for each setter
+// function for an explanation of the option.
+type Builder struct {
+	opts []func(*ReadPref)
+}
+
+// Options creates a new Builder instance.
+func Options() *Builder {
+	return &Builder{}
+}
+
+// List returns a list of ReadPref setter functions.
+func (bldr *Builder) List() []func(*ReadPref) {
+	return bldr.opts
+}
+
+// SetMaxStaleness sets the value for the MaxStaleness field which is the
+// maximum amount of time to allow a server to be considered eligible for
+// selection.
+func (bldr *Builder) SetMaxStaleness(dur time.Duration) *Builder {
+	bldr.opts = append(bldr.opts, func(opts *ReadPref) {
+		opts.maxStaleness = &dur
+	})
+
+	return bldr
+}
+
+// SetTagSets sets the multiple tag sets indicating which servers should be
+// considered.
+func (bldr *Builder) SetTagSets(sets []TagSet) *Builder {
+	bldr.opts = append(bldr.opts, func(opts *ReadPref) {
+		opts.tagSets = sets
+	})
+
+	return bldr
+}
+
+// SetHedgeEnabled sets whether or not hedged reads are enabled for this read
+// preference.
+func (bldr *Builder) SetHedgeEnabled(hedgeEnabled bool) *Builder {
+	bldr.opts = append(bldr.opts, func(opts *ReadPref) {
+		opts.hedgeEnabled = &hedgeEnabled
+	})
+
+	return bldr
+}
+
+func validOpts(mode Mode, opts *ReadPref) bool {
+	if opts == nil || mode != PrimaryMode {
+		return true
+	}
+
+	return opts.maxStaleness == nil && len(opts.tagSets) == 0 && opts.hedgeEnabled == nil
+}
+
+func mergeBuilders(builders ...*Builder) *ReadPref {
+	opts := new(ReadPref)
+	for _, bldr := range builders {
+		if bldr == nil {
+			continue
+		}
+
+		for _, setterFn := range bldr.List() {
+			setterFn(opts)
+		}
+	}
+
+	return opts
+}
 
 // Primary constructs a read preference with a PrimaryMode.
 func Primary() *ReadPref {
-	return &ReadPref{mode: PrimaryMode}
+	return &ReadPref{Mode: PrimaryMode}
 }
 
 // PrimaryPreferred constructs a read preference with a PrimaryPreferredMode.
-func PrimaryPreferred(opts ...Option) *ReadPref {
+func PrimaryPreferred(opts ...*Builder) *ReadPref {
 	// New only returns an error with a mode of Primary
 	rp, _ := New(PrimaryPreferredMode, opts...)
 	return rp
 }
 
 // SecondaryPreferred constructs a read preference with a SecondaryPreferredMode.
-func SecondaryPreferred(opts ...Option) *ReadPref {
+func SecondaryPreferred(opts ...*Builder) *ReadPref {
 	// New only returns an error with a mode of Primary
 	rp, _ := New(SecondaryPreferredMode, opts...)
 	return rp
 }
 
 // Secondary constructs a read preference with a SecondaryMode.
-func Secondary(opts ...Option) *ReadPref {
+func Secondary(opts ...*Builder) *ReadPref {
 	// New only returns an error with a mode of Primary
 	rp, _ := New(SecondaryMode, opts...)
 	return rp
 }
 
 // Nearest constructs a read preference with a NearestMode.
-func Nearest(opts ...Option) *ReadPref {
+func Nearest(opts ...*Builder) *ReadPref {
 	// New only returns an error with a mode of Primary
 	rp, _ := New(NearestMode, opts...)
 	return rp
 }
 
 // New creates a new ReadPref.
-func New(mode Mode, opts ...Option) (*ReadPref, error) {
-	rp := &ReadPref{
-		mode: mode,
-	}
+func New(mode Mode, builders ...*Builder) (*ReadPref, error) {
+	rp := mergeBuilders(builders...)
+	rp.Mode = mode
 
-	if mode == PrimaryMode && len(opts) != 0 {
+	if !validOpts(mode, rp) {
 		return nil, errInvalidReadPreference
 	}
 
-	for _, opt := range opts {
-		if opt == nil {
-			continue
-		}
-		err := opt(rp)
-		if err != nil {
-			return nil, err
-		}
-	}
-
 	return rp, nil
 }
 
-// ReadPref determines which servers are considered suitable for read operations.
-type ReadPref struct {
-	maxStaleness    time.Duration
-	maxStalenessSet bool
-	mode            Mode
-	tagSets         []tag.Set
-	hedgeEnabled    *bool
-}
-
 // MaxStaleness is the maximum amount of time to allow
 // a server to be considered eligible for selection. The
 // second return value indicates if this value has been set.
-func (r *ReadPref) MaxStaleness() (time.Duration, bool) {
-	return r.maxStaleness, r.maxStalenessSet
-}
-
-// Mode indicates the mode of the read preference.
-func (r *ReadPref) Mode() Mode {
-	return r.mode
+func (r *ReadPref) MaxStaleness() *time.Duration {
+	return r.maxStaleness
 }
 
 // TagSets are multiple tag sets indicating
 // which servers should be considered.
-func (r *ReadPref) TagSets() []tag.Set {
+func (r *ReadPref) TagSets() []TagSet {
 	return r.tagSets
 }
 
@@ -112,18 +163,18 @@ func (r *ReadPref) HedgeEnabled() *bool {
 // String returns a human-readable description of the read preference.
 func (r *ReadPref) String() string {
 	var b bytes.Buffer
-	b.WriteString(r.mode.String())
+	b.WriteString(string(r.Mode))
 	delim := "("
-	if r.maxStalenessSet {
-		fmt.Fprintf(&b, "%smaxStaleness=%v", delim, r.maxStaleness)
+	if r.MaxStaleness() != nil {
+		fmt.Fprintf(&b, "%smaxStaleness=%v", delim, *r.MaxStaleness())
 		delim = " "
 	}
-	for _, tagSet := range r.tagSets {
+	for _, tagSet := range r.TagSets() {
 		fmt.Fprintf(&b, "%stagSet=%s", delim, tagSet.String())
 		delim = " "
 	}
-	if r.hedgeEnabled != nil {
-		fmt.Fprintf(&b, "%shedgeEnabled=%v", delim, *r.hedgeEnabled)
+	if r.HedgeEnabled() != nil {
+		fmt.Fprintf(&b, "%shedgeEnabled=%v", delim, *r.HedgeEnabled())
 		delim = " "
 	}
 	if delim != "(" {
diff --git a/mongo/readpref/readpref_test.go b/mongo/readpref/readpref_test.go
index e0c7bf7d4b..27bdbebe42 100644
--- a/mongo/readpref/readpref_test.go
+++ b/mongo/readpref/readpref_test.go
@@ -11,133 +11,153 @@ import (
 	"time"
 
 	"go.mongodb.org/mongo-driver/v2/internal/assert"
-	"go.mongodb.org/mongo-driver/v2/internal/require"
-	"go.mongodb.org/mongo-driver/v2/tag"
+	"go.mongodb.org/mongo-driver/v2/internal/ptrutil"
 )
 
-func TestPrimary(t *testing.T) {
-	subject := Primary()
-
-	require.Equal(t, PrimaryMode, subject.Mode())
-	_, set := subject.MaxStaleness()
-	require.False(t, set)
-	require.Len(t, subject.TagSets(), 0)
-}
-
-func TestPrimaryPreferred(t *testing.T) {
-	subject := PrimaryPreferred()
-
-	require.Equal(t, PrimaryPreferredMode, subject.Mode())
-	_, set := subject.MaxStaleness()
-	require.False(t, set)
-	require.Len(t, subject.TagSets(), 0)
-}
-
-func TestPrimaryPreferred_with_options(t *testing.T) {
-	subject := PrimaryPreferred(
-		WithMaxStaleness(time.Duration(10)),
-		WithTags("a", "1", "b", "2"),
-	)
-
-	require.Equal(t, PrimaryPreferredMode, subject.Mode())
-	ms, set := subject.MaxStaleness()
-	require.True(t, set)
-	require.Equal(t, time.Duration(10), ms)
-	require.Equal(t, []tag.Set{{tag.Tag{Name: "a", Value: "1"}, tag.Tag{Name: "b", Value: "2"}}}, subject.TagSets())
-}
-
-func TestSecondaryPreferred(t *testing.T) {
-	subject := SecondaryPreferred()
-
-	require.Equal(t, SecondaryPreferredMode, subject.Mode())
-	_, set := subject.MaxStaleness()
-	require.False(t, set)
-	require.Len(t, subject.TagSets(), 0)
-}
-
-func TestSecondaryPreferred_with_options(t *testing.T) {
-	subject := SecondaryPreferred(
-		WithMaxStaleness(time.Duration(10)),
-		WithTags("a", "1", "b", "2"),
-	)
-
-	require.Equal(t, SecondaryPreferredMode, subject.Mode())
-	ms, set := subject.MaxStaleness()
-	require.True(t, set)
-	require.Equal(t, time.Duration(10), ms)
-	require.Equal(t, []tag.Set{{tag.Tag{Name: "a", Value: "1"}, tag.Tag{Name: "b", Value: "2"}}}, subject.TagSets())
-}
-
-func TestSecondary(t *testing.T) {
-	subject := Secondary()
-
-	require.Equal(t, SecondaryMode, subject.Mode())
-	_, set := subject.MaxStaleness()
-	require.False(t, set)
-	require.Len(t, subject.TagSets(), 0)
-}
-
-func TestSecondary_with_options(t *testing.T) {
-	subject := Secondary(
-		WithMaxStaleness(time.Duration(10)),
-		WithTags("a", "1", "b", "2"),
-	)
-
-	require.Equal(t, SecondaryMode, subject.Mode())
-	ms, set := subject.MaxStaleness()
-	require.True(t, set)
-	require.Equal(t, time.Duration(10), ms)
-	require.Equal(t, []tag.Set{{tag.Tag{Name: "a", Value: "1"}, tag.Tag{Name: "b", Value: "2"}}}, subject.TagSets())
-}
-
-func TestNearest(t *testing.T) {
-	subject := Nearest()
-
-	require.Equal(t, NearestMode, subject.Mode())
-	_, set := subject.MaxStaleness()
-	require.False(t, set)
-	require.Len(t, subject.TagSets(), 0)
-}
-
-func TestNearest_with_options(t *testing.T) {
-	subject := Nearest(
-		WithMaxStaleness(time.Duration(10)),
-		WithTags("a", "1", "b", "2"),
-	)
-
-	require.Equal(t, NearestMode, subject.Mode())
-	ms, set := subject.MaxStaleness()
-	require.True(t, set)
-	require.Equal(t, time.Duration(10), ms)
-	require.Equal(t, []tag.Set{{tag.Tag{Name: "a", Value: "1"}, tag.Tag{Name: "b", Value: "2"}}}, subject.TagSets())
-}
-
-func TestHedge(t *testing.T) {
-	t.Run("hedge specified with primary mode errors", func(t *testing.T) {
-		_, err := New(PrimaryMode, WithHedgeEnabled(true))
-		assert.Equal(t, errInvalidReadPreference, err, "expected error %v, got %v", errInvalidReadPreference, err)
-	})
-	t.Run("valid hedge document and mode succeeds", func(t *testing.T) {
-		rp, err := New(SecondaryMode, WithHedgeEnabled(true))
-		assert.Nil(t, err, "expected no error, got %v", err)
-		enabled := rp.HedgeEnabled()
-		assert.NotNil(t, enabled, "expected HedgeEnabled to return a non-nil value, got nil")
-		assert.True(t, *enabled, "expected HedgeEnabled to return true, got false")
-	})
+func TestNew(t *testing.T) {
+	t.Parallel()
+
+	tagSets := []TagSet{
+		{
+			{Name: "a", Value: "1"},
+			{Name: "b", Value: "2"},
+		},
+	}
+
+	tests := []struct {
+		name    string
+		mode    Mode
+		opts    []*Builder
+		want    *ReadPref
+		wantErr error
+	}{
+		{
+			name:    "primary",
+			mode:    PrimaryMode,
+			opts:    nil,
+			want:    &ReadPref{Mode: PrimaryMode},
+			wantErr: nil,
+		},
+		{
+			name:    "primary with maxStaleness",
+			mode:    PrimaryMode,
+			opts:    []*Builder{Options().SetMaxStaleness(1)},
+			want:    nil,
+			wantErr: errInvalidReadPreference,
+		},
+		{
+			name:    "primary with tags",
+			mode:    PrimaryMode,
+			opts:    []*Builder{Options().SetTagSets([]TagSet{{}})},
+			want:    nil,
+			wantErr: errInvalidReadPreference,
+		},
+		{
+			name:    "primary with hedgeEnabled",
+			mode:    PrimaryMode,
+			opts:    []*Builder{Options().SetHedgeEnabled(false)},
+			want:    nil,
+			wantErr: errInvalidReadPreference,
+		},
+		{
+			name:    "primaryPreferred",
+			mode:    PrimaryPreferredMode,
+			opts:    nil,
+			want:    &ReadPref{Mode: PrimaryPreferredMode},
+			wantErr: nil,
+		},
+		{
+			name: "primaryPreferred with options",
+			mode: PrimaryPreferredMode,
+			opts: []*Builder{Options().SetMaxStaleness(1).SetTagSets(tagSets)},
+			want: &ReadPref{
+				Mode:         PrimaryPreferredMode,
+				maxStaleness: ptrutil.Ptr[time.Duration](1),
+				tagSets:      tagSets,
+			},
+			wantErr: nil,
+		},
+		{
+			name:    "secondary",
+			mode:    SecondaryMode,
+			opts:    nil,
+			want:    &ReadPref{Mode: SecondaryMode},
+			wantErr: nil,
+		},
+		{
+			name: "secondary with options",
+			mode: SecondaryMode,
+			opts: []*Builder{Options().SetMaxStaleness(1).SetTagSets(tagSets)},
+			want: &ReadPref{
+				Mode:         SecondaryMode,
+				maxStaleness: ptrutil.Ptr[time.Duration](1),
+				tagSets:      tagSets,
+			},
+			wantErr: nil,
+		},
+		{
+			name:    "nearest",
+			mode:    NearestMode,
+			opts:    nil,
+			want:    &ReadPref{Mode: NearestMode},
+			wantErr: nil,
+		},
+		{
+			name: "nearest with options",
+			mode: NearestMode,
+			opts: []*Builder{Options().SetMaxStaleness(1).SetTagSets(tagSets)},
+			want: &ReadPref{
+				Mode:         NearestMode,
+				maxStaleness: ptrutil.Ptr[time.Duration](1),
+				tagSets:      tagSets,
+			},
+			wantErr: nil,
+		},
+	}
+
+	for _, test := range tests {
+		test := test
+
+		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
+
+			readPref, err := New(test.mode, test.opts...)
+
+			if test.wantErr == nil {
+				assert.NoError(t, err)
+			} else {
+				assert.ErrorIs(t, err, test.wantErr)
+			}
+
+			if test.want == nil {
+				return
+			}
+
+			assert.Equal(t, test.mode, readPref.Mode)
+			assert.EqualValues(t, test.want, readPref)
+		})
+	}
 }
 
 func TestReadPref_String(t *testing.T) {
 	t.Run("ReadPref.String() with all options", func(t *testing.T) {
-		readPref := Nearest(
-			WithMaxStaleness(120*time.Second),
-			WithTagSets(tag.Set{{"a", "1"}, {"b", "2"}}, tag.Set{{"q", "5"}, {"r", "6"}}),
-			WithHedgeEnabled(true),
-		)
+		opts := Options().SetMaxStaleness(120 * time.Second).SetHedgeEnabled(true).SetTagSets([]TagSet{
+			{{"a", "1"}, {"b", "2"}},
+			{{"q", "5"}, {"r", "6"}},
+		})
+
+		readPref, err := New(NearestMode, opts)
+		assert.NoError(t, err)
+
 		expected := "nearest(maxStaleness=2m0s tagSet=a=1,b=2 tagSet=q=5,r=6 hedgeEnabled=true)"
 		assert.Equal(t, expected, readPref.String(), "expected %q, got %q", expected, readPref.String())
 	})
 	t.Run("ReadPref.String() with one option", func(t *testing.T) {
-		readPref := Secondary(WithTags("a", "1", "b", "2"))
+		opts := Options().SetTagSets([]TagSet{{{"a", "1"}, {"b", "2"}}})
+
+		readPref, err := New(SecondaryMode, opts)
+		assert.NoError(t, err)
+
 		expected := "secondary(tagSet=a=1,b=2)"
 		assert.Equal(t, expected, readPref.String(), "expected %q, got %q", expected, readPref.String())
 	})
diff --git a/tag/tag.go b/mongo/readpref/tag.go
similarity index 66%
rename from tag/tag.go
rename to mongo/readpref/tag.go
index 39c11e0460..37e188511c 100644
--- a/tag/tag.go
+++ b/mongo/readpref/tag.go
@@ -4,11 +4,7 @@
 // not use this file except in compliance with the License. You may obtain
 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 
-// Package tag provides types for filtering replica set members using tags in a read preference.
-//
-// For more information about read preference tags, see
-// https://www.mongodb.com/docs/manual/core/read-preference-tags/
-package tag
+package readpref
 
 import (
 	"bytes"
@@ -26,12 +22,36 @@ func (tag Tag) String() string {
 	return fmt.Sprintf("%s=%s", tag.Name, tag.Value)
 }
 
+// TagSet is an ordered list of Tags.
+type TagSet []Tag
+
+// NewTagSet is a convenience function to specify a single tag set used to match
+// replica set members. If no members match the tag set, read operations will
+// return an error.
+//
+// For more information about read preference tags, see
+// https://www.mongodb.com/docs/manual/core/read-preference-tags/
+func NewTagSet(tags ...string) (TagSet, error) {
+	length := len(tags)
+	if length < 2 || length%2 != 0 {
+		return nil, fmt.Errorf("an even number of tags must be specified")
+	}
+
+	tagset := make(TagSet, 0, length/2)
+
+	for i := 1; i < length; i += 2 {
+		tagset = append(tagset, Tag{Name: tags[i-1], Value: tags[i]})
+	}
+
+	return tagset, nil
+}
+
 // NewTagSetFromMap creates a tag set from a map.
 //
 // For more information about read preference tags, see
 // https://www.mongodb.com/docs/manual/core/read-preference-tags/
-func NewTagSetFromMap(m map[string]string) Set {
-	var set Set
+func NewTagSetFromMap(m map[string]string) TagSet {
+	set := make(TagSet, 0, len(m))
 	for k, v := range m {
 		set = append(set, Tag{Name: k, Value: v})
 	}
@@ -43,19 +63,16 @@ func NewTagSetFromMap(m map[string]string) Set {
 //
 // For more information about read preference tags, see
 // https://www.mongodb.com/docs/manual/core/read-preference-tags/
-func NewTagSetsFromMaps(maps []map[string]string) []Set {
-	sets := make([]Set, 0, len(maps))
+func NewTagSetsFromMaps(maps []map[string]string) []TagSet {
+	sets := make([]TagSet, 0, len(maps))
 	for _, m := range maps {
 		sets = append(sets, NewTagSetFromMap(m))
 	}
 	return sets
 }
 
-// Set is an ordered list of Tags.
-type Set []Tag
-
 // Contains indicates whether the name/value pair exists in the tagset.
-func (ts Set) Contains(name, value string) bool {
+func (ts TagSet) Contains(name, value string) bool {
 	for _, t := range ts {
 		if t.Name == name && t.Value == value {
 			return true
@@ -66,7 +83,7 @@ func (ts Set) Contains(name, value string) bool {
 }
 
 // ContainsAll indicates whether all the name/value pairs exist in the tagset.
-func (ts Set) ContainsAll(other []Tag) bool {
+func (ts TagSet) ContainsAll(other []Tag) bool {
 	for _, ot := range other {
 		if !ts.Contains(ot.Name, ot.Value) {
 			return false
@@ -77,7 +94,7 @@ func (ts Set) ContainsAll(other []Tag) bool {
 }
 
 // String returns a human-readable human-readable description of the tagset.
-func (ts Set) String() string {
+func (ts TagSet) String() string {
 	var b bytes.Buffer
 	for i, tag := range ts {
 		if i > 0 {
diff --git a/tag/tag_test.go b/mongo/readpref/tag_test.go
similarity index 81%
rename from tag/tag_test.go
rename to mongo/readpref/tag_test.go
index cdd6ca6efe..cd8b075b95 100644
--- a/tag/tag_test.go
+++ b/mongo/readpref/tag_test.go
@@ -4,7 +4,7 @@
 // not use this file except in compliance with the License. You may obtain
 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 
-package tag
+package readpref
 
 import (
 	"testing"
@@ -23,7 +23,7 @@ func TestTag_String(t *testing.T) {
 func TestTagSets_NewTagSet(t *testing.T) {
 	t.Parallel()
 
-	ts := Set{Tag{Name: "a", Value: "1"}}
+	ts := TagSet{Tag{Name: "a", Value: "1"}}
 
 	require.True(t, ts.Contains("a", "1"))
 	require.False(t, ts.Contains("1", "a"))
@@ -65,30 +65,30 @@ func TestTagSets_NewTagSetsFromMaps(t *testing.T) {
 func TestTagSets_ContainsAll(t *testing.T) {
 	t.Parallel()
 
-	ts := Set{
+	ts := TagSet{
 		Tag{Name: "a", Value: "1"},
 		Tag{Name: "b", Value: "2"},
 	}
 
-	test := Set{Tag{Name: "a", Value: "1"}}
+	test := TagSet{Tag{Name: "a", Value: "1"}}
 	require.True(t, ts.ContainsAll(test))
-	test = Set{Tag{Name: "a", Value: "1"}, Tag{Name: "b", Value: "2"}}
+	test = TagSet{Tag{Name: "a", Value: "1"}, Tag{Name: "b", Value: "2"}}
 	require.True(t, ts.ContainsAll(test))
-	test = Set{Tag{Name: "a", Value: "1"}, Tag{Name: "b", Value: "2"}}
+	test = TagSet{Tag{Name: "a", Value: "1"}, Tag{Name: "b", Value: "2"}}
 	require.True(t, ts.ContainsAll(test))
 
-	test = Set{Tag{Name: "a", Value: "2"}, Tag{Name: "b", Value: "1"}}
+	test = TagSet{Tag{Name: "a", Value: "2"}, Tag{Name: "b", Value: "1"}}
 	require.False(t, ts.ContainsAll(test))
-	test = Set{Tag{Name: "a", Value: "1"}, Tag{Name: "b", Value: "1"}}
+	test = TagSet{Tag{Name: "a", Value: "1"}, Tag{Name: "b", Value: "1"}}
 	require.False(t, ts.ContainsAll(test))
-	test = Set{Tag{Name: "a", Value: "2"}, Tag{Name: "b", Value: "2"}}
+	test = TagSet{Tag{Name: "a", Value: "2"}, Tag{Name: "b", Value: "2"}}
 	require.False(t, ts.ContainsAll(test))
 }
 
 func TestTagSets_String(t *testing.T) {
 	t.Parallel()
 
-	ts := Set{
+	ts := TagSet{
 		Tag{Name: "a", Value: "1"},
 		Tag{Name: "b", Value: "2"},
 	}
diff --git a/x/mongo/driver/description/server.go b/x/mongo/driver/description/server.go
index 7d2e679a27..b5c58a6369 100644
--- a/x/mongo/driver/description/server.go
+++ b/x/mongo/driver/description/server.go
@@ -12,7 +12,7 @@ import (
 
 	"go.mongodb.org/mongo-driver/v2/bson"
 	"go.mongodb.org/mongo-driver/v2/mongo/address"
-	"go.mongodb.org/mongo-driver/v2/tag"
+	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 )
 
 // ServerKind represents the type of a single server in a topology.
@@ -105,7 +105,7 @@ type Server struct {
 	SessionTimeoutMinutes *int64
 	SetName               string
 	SetVersion            uint32
-	Tags                  tag.Set
+	Tags                  readpref.TagSet
 	TopologyVersion       *TopologyVersion
 	Kind                  ServerKind
 	WireVersion           *VersionRange
diff --git a/x/mongo/driver/operation.go b/x/mongo/driver/operation.go
index 8c2e3e8e9a..20d4589b79 100644
--- a/x/mongo/driver/operation.go
+++ b/x/mongo/driver/operation.go
@@ -1744,7 +1744,7 @@ func (op Operation) getReadPrefBasedOnTransaction() (*readpref.ReadPref, error)
 		rp := op.Client.CurrentRp
 		// Reads in a transaction must have read preference primary
 		// This must not be checked in startTransaction
-		if rp != nil && !op.Client.TransactionStarting() && rp.Mode() != readpref.PrimaryMode {
+		if rp != nil && !op.Client.TransactionStarting() && rp.Mode != readpref.PrimaryMode {
 			return nil, ErrNonPrimaryReadPref
 		}
 		return rp, nil
@@ -1790,7 +1790,7 @@ func (op Operation) createReadPref(desc description.SelectedServer, isOpQuery bo
 		return nil, nil
 	}
 
-	switch rp.Mode() {
+	switch rp.Mode {
 	case readpref.PrimaryMode:
 		if desc.Server.Kind == description.ServerKindMongos {
 			return nil, nil
@@ -1811,7 +1811,7 @@ func (op Operation) createReadPref(desc description.SelectedServer, isOpQuery bo
 	case readpref.PrimaryPreferredMode:
 		doc = bsoncore.AppendStringElement(doc, "mode", "primaryPreferred")
 	case readpref.SecondaryPreferredMode:
-		_, ok := rp.MaxStaleness()
+		ok := rp.MaxStaleness() != nil
 		if desc.Server.Kind == description.ServerKindMongos && isOpQuery && !ok && len(rp.TagSets()) == 0 &&
 			rp.HedgeEnabled() == nil {
 
@@ -1842,8 +1842,8 @@ func (op Operation) createReadPref(desc description.SelectedServer, isOpQuery bo
 		doc, _ = bsoncore.AppendArrayEnd(doc, aidx)
 	}
 
-	if d, ok := rp.MaxStaleness(); ok {
-		doc = bsoncore.AppendInt32Element(doc, "maxStalenessSeconds", int32(d.Seconds()))
+	if maxStaleness := rp.MaxStaleness(); maxStaleness != nil {
+		doc = bsoncore.AppendInt32Element(doc, "maxStalenessSeconds", int32((*maxStaleness).Seconds()))
 	}
 
 	if hedgeEnabled := rp.HedgeEnabled(); hedgeEnabled != nil {
@@ -1865,7 +1865,7 @@ func (op Operation) secondaryOK(desc description.SelectedServer) wiremessage.Que
 		return wiremessage.SecondaryOK
 	}
 
-	if rp := op.ReadPreference; rp != nil && rp.Mode() != readpref.PrimaryMode {
+	if rp := op.ReadPreference; rp != nil && rp.Mode != readpref.PrimaryMode {
 		return wiremessage.SecondaryOK
 	}
 
diff --git a/x/mongo/driver/operation_test.go b/x/mongo/driver/operation_test.go
index 1b4a89b80a..09e7a6908c 100644
--- a/x/mongo/driver/operation_test.go
+++ b/x/mongo/driver/operation_test.go
@@ -24,7 +24,6 @@ import (
 	"go.mongodb.org/mongo-driver/v2/mongo/readconcern"
 	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
 	"go.mongodb.org/mongo-driver/v2/mongo/writeconcern"
-	"go.mongodb.org/mongo-driver/v2/tag"
 	"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description"
 	"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet"
@@ -438,14 +437,25 @@ func TestOperation(t *testing.T) {
 			{"primary/mongos", readpref.Primary(), description.ServerKindMongos, description.TopologyKindSharded, false, nil},
 			{"primary/single", readpref.Primary(), description.ServerKindRSPrimary, description.TopologyKindSingle, false, rpPrimaryPreferred},
 			{"primary/primary", readpref.Primary(), description.ServerKindRSPrimary, description.TopologyKindReplicaSet, false, nil},
-			{"primaryPreferred", readpref.PrimaryPreferred(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpPrimaryPreferred},
-			{"secondaryPreferred/mongos/opquery", readpref.SecondaryPreferred(), description.ServerKindMongos, description.TopologyKindSharded, true, nil},
-			{"secondaryPreferred", readpref.SecondaryPreferred(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpSecondaryPreferred},
-			{"secondary", readpref.Secondary(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpSecondary},
-			{"nearest", readpref.Nearest(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpNearest},
+			{"primaryPreferred", &readpref.ReadPref{Mode: readpref.PrimaryPreferredMode}, description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpPrimaryPreferred},
+			{"secondaryPreferred/mongos/opquery", &readpref.ReadPref{Mode: readpref.SecondaryPreferredMode}, description.ServerKindMongos, description.TopologyKindSharded, true, nil},
+			{"secondaryPreferred", &readpref.ReadPref{Mode: readpref.SecondaryPreferredMode}, description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpSecondaryPreferred},
+			{"secondary", &readpref.ReadPref{Mode: readpref.SecondaryMode}, description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpSecondary},
+			{"nearest", &readpref.ReadPref{Mode: readpref.NearestMode}, description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpNearest},
 			{
 				"secondaryPreferred/withTags",
-				readpref.SecondaryPreferred(readpref.WithTags("disk", "ssd", "use", "reporting")),
+				func() *readpref.ReadPref {
+					rpOpts := readpref.Options()
+
+					tagSet, err := readpref.NewTagSet("disk", "ssd", "use", "reporting")
+					assert.NoError(t, err)
+
+					rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+					rp, _ := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+
+					return rp
+				}(),
 				description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpWithTags,
 			},
 			// GODRIVER-2205: Ensure empty tag sets are written as an empty document in the read
@@ -453,9 +463,18 @@ func TestOperation(t *testing.T) {
 			// no other tag sets match any servers.
 			{
 				"secondaryPreferred/withTags/emptyTagSet",
-				readpref.SecondaryPreferred(readpref.WithTagSets(
-					tag.Set{{Name: "disk", Value: "ssd"}},
-					tag.Set{})),
+				func() *readpref.ReadPref {
+					rpOpts := readpref.Options()
+
+					rpOpts.SetTagSets([]readpref.TagSet{
+						readpref.TagSet{{Name: "disk", Value: "ssd"}},
+						readpref.TagSet{},
+					})
+
+					rp, _ := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+
+					return rp
+				}(),
 				description.ServerKindRSSecondary,
 				description.TopologyKindReplicaSet,
 				false,
@@ -469,13 +488,31 @@ func TestOperation(t *testing.T) {
 			},
 			{
 				"secondaryPreferred/withMaxStaleness",
-				readpref.SecondaryPreferred(readpref.WithMaxStaleness(25 * time.Second)),
+				func() *readpref.ReadPref {
+					rpOpts := readpref.Options()
+
+					maxStaleness := 25 * time.Second
+					rpOpts.SetMaxStaleness(maxStaleness)
+
+					rp, _ := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+
+					return rp
+				}(),
 				description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpWithMaxStaleness,
 			},
 			{
 				// A read preference document is generated for SecondaryPreferred if the hedge document is non-nil.
 				"secondaryPreferred with hedge to mongos using OP_QUERY",
-				readpref.SecondaryPreferred(readpref.WithHedgeEnabled(true)),
+				func() *readpref.ReadPref {
+					rpOpts := readpref.Options()
+
+					he := true
+					rpOpts.SetHedgeEnabled(he)
+
+					rp, _ := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+
+					return rp
+				}(),
 				description.ServerKindMongos,
 				description.TopologyKindSharded,
 				true,
@@ -483,11 +520,24 @@ func TestOperation(t *testing.T) {
 			},
 			{
 				"secondaryPreferred with all options",
-				readpref.SecondaryPreferred(
-					readpref.WithTags("disk", "ssd", "use", "reporting"),
-					readpref.WithMaxStaleness(25*time.Second),
-					readpref.WithHedgeEnabled(false),
-				),
+				func() *readpref.ReadPref {
+					tagSet, err := readpref.NewTagSet("disk", "ssd", "use", "reporting")
+					assert.NoError(t, err)
+
+					rpOpts := readpref.Options()
+
+					rpOpts.SetTagSets([]readpref.TagSet{tagSet})
+
+					maxStaleness := 25 * time.Second
+					rpOpts.SetMaxStaleness(maxStaleness)
+
+					he := false
+					rpOpts.SetHedgeEnabled(he)
+
+					rp, _ := readpref.New(readpref.SecondaryPreferredMode, rpOpts)
+
+					return rp
+				}(),
 				description.ServerKindRSSecondary,
 				description.TopologyKindReplicaSet,
 				false,
@@ -523,7 +573,7 @@ func TestOperation(t *testing.T) {
 		})
 		t.Run("readPreference", func(t *testing.T) {
 			want := wiremessage.SecondaryOK
-			got := Operation{ReadPreference: readpref.Secondary()}.secondaryOK(description.SelectedServer{})
+			got := Operation{ReadPreference: &readpref.ReadPref{Mode: readpref.SecondaryMode}}.secondaryOK(description.SelectedServer{})
 			if got != want {
 				t.Errorf("Did not receive expected query flags. got %v; want %v", got, want)
 			}
diff --git a/x/mongo/driver/topology/topology_test.go b/x/mongo/driver/topology/topology_test.go
index c55db8b778..4e1b310451 100644
--- a/x/mongo/driver/topology/topology_test.go
+++ b/x/mongo/driver/topology/topology_test.go
@@ -888,7 +888,7 @@ func runInWindowTest(t *testing.T, directory string, filename string) {
 	for i := 0; i < test.Iterations; i++ {
 		selected, err := topology.SelectServer(
 			context.Background(),
-			&serverselector.ReadPref{ReadPref: readpref.Nearest()})
+			&serverselector.ReadPref{ReadPref: &readpref.ReadPref{Mode: readpref.NearestMode}})
 		require.NoError(t, err, "error selecting server")
 		counts[string(selected.(*SelectedServer).address)]++
 	}