diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts/MTConnectDriverOptions.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts/MTConnectDriverOptions.cs
index 8b9a9031..0b1dce3b 100644
--- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts/MTConnectDriverOptions.cs
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Contracts/MTConnectDriverOptions.cs
@@ -54,6 +54,21 @@ public sealed class MTConnectDriverOptions
///
public int HeartbeatMs { get; init; } = 10000;
+ ///
+ /// The authored tags this driver instance serves — one
+ /// per tag, keyed by the DataItem id it binds
+ /// (). The factory config DTO carries these and
+ /// builds them into the options; the driver indexes them to know each tag's target
+ /// when coercing an Agent observation
+ /// (whose wire form is always text) into a published value.
+ ///
+ /// Defaults to empty, never null: a driver instance may legitimately be
+ /// authored with no tags yet, and a null collection here would surface as a
+ /// NullReferenceException at deploy time from operator-authored config.
+ ///
+ ///
+ public IReadOnlyList Tags { get; init; } = [];
+
///
/// Background connectivity-probe settings. When
/// is true the driver periodically issues a cheap /probe request and raises
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverOptionsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverOptionsTests.cs
index 0b77db18..91d1c4ca 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverOptionsTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect.Tests/MTConnectDriverOptionsTests.cs
@@ -56,6 +56,47 @@ public sealed class MTConnectDriverOptionsTests
reconnect.BackoffMultiplier.ShouldBeGreaterThan(1.0);
}
+ ///
+ /// defaults to an empty collection, never
+ /// null — a null collection here is an operator-authorable NullReferenceException at
+ /// deploy, since a driver instance authored with no tags binds this default.
+ ///
+ [Fact]
+ public void Options_tags_default_to_empty_and_are_never_null()
+ {
+ var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000" };
+
+ o.Tags.ShouldNotBeNull();
+ o.Tags.ShouldBeEmpty();
+ }
+
+ ///
+ /// A populated options object carries the authored tag definitions through unchanged —
+ /// the observation index (Task 8) coerces each present observation to its tag's
+ /// DriverDataType, so the definitions must reach the driver from the factory config.
+ ///
+ [Fact]
+ public void Options_round_trip_the_authored_tag_definitions()
+ {
+ var pos = new MTConnectTagDefinition(
+ FullName: "dev1_pos",
+ DriverDataType: ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Float64,
+ MtCategory: "SAMPLE",
+ MtType: "POSITION",
+ Units: "MILLIMETER");
+ var partCount = new MTConnectTagDefinition(
+ FullName: "dev1_partcount",
+ DriverDataType: ZB.MOM.WW.OtOpcUa.Core.Abstractions.DriverDataType.Int64,
+ MtCategory: "EVENT",
+ MtType: "PART_COUNT");
+
+ var o = new MTConnectDriverOptions { AgentUri = "http://agent:5000", Tags = [pos, partCount] };
+
+ o.Tags.Count.ShouldBe(2);
+ o.Tags[0].ShouldBe(pos);
+ o.Tags[1].ShouldBe(partCount);
+ }
+
/// MTConnectTagDefinition round-trips its positional fields, including MTConnect metadata.
[Fact]
public void TagDefinition_carries_dataItemId_and_mtconnect_metadata()