diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/IMTConnectAgentClient.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/IMTConnectAgentClient.cs
new file mode 100644
index 00000000..6f54ef98
--- /dev/null
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/IMTConnectAgentClient.cs
@@ -0,0 +1,44 @@
+namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect;
+
+///
+/// The seam between the MTConnect driver and an MTConnect Agent's HTTP surface
+/// (/probe, /current, /sample). This interface is transport-neutral by
+/// design — every method returns/yields the plain DTOs in MTConnectDtos.cs, never a
+/// TrakHound type, an XElement, or any other wire/library artifact. That is what lets
+/// every driver behaviour above this seam (Tasks 6–13) be unit-tested against a fake serving
+/// canned probe/current/sample XML, with no sockets involved.
+///
+///
+/// The production implementation (Task 6/7) is built on the TrakHound MTConnect.NET client
+/// libraries (Task 0's decision); a fake for tests only needs to implement these three members.
+///
+public interface IMTConnectAgentClient
+{
+ ///
+ /// Issues an Agent /probe request and returns the parsed device hierarchy. Called
+ /// once at driver initialization; the result is cached and later walked to build the OPC UA
+ /// browse tree, where each becomes a tag's FullName.
+ ///
+ /// Cancellation token.
+ Task ProbeAsync(CancellationToken ct);
+
+ ///
+ /// Issues an Agent /current request and returns the latest observed value for every
+ /// data item. Used both to prime the observation index at startup and to re-baseline after
+ /// a detected sequence gap in .
+ ///
+ /// Cancellation token.
+ Task CurrentAsync(CancellationToken ct);
+
+ ///
+ /// Opens an Agent /sample long-poll stream starting at sequence
+ /// and yields one per multipart chunk the Agent sends,
+ /// indefinitely, until is cancelled. Each yielded result's
+ /// is load-bearing — the caller compares
+ /// it against the requested to detect a ring-buffer sequence gap and
+ /// re-baseline via .
+ ///
+ /// The sequence number to resume streaming from.
+ /// Cancellation token; cancelling ends the stream.
+ IAsyncEnumerable SampleAsync(long from, CancellationToken ct);
+}
diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDtos.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDtos.cs
new file mode 100644
index 00000000..76bd41c9
--- /dev/null
+++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.MTConnect/MTConnectDtos.cs
@@ -0,0 +1,170 @@
+namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect;
+
+///
+/// Result of an Agent /probe request: the full device hierarchy the Agent exposes.
+/// Produced by . This is the neutral shape both
+/// the TrakHound-backed implementation and a hand-rolled XML fallback must produce — nothing
+/// downstream of ever sees a TrakHound type or an
+/// XElement.
+///
+/// Every device the Agent's probe response declares.
+public sealed record MTConnectProbeModel(IReadOnlyList Devices);
+
+///
+/// Common shape shared by and :
+/// each may directly own data items and/or nest further components, to arbitrary depth. Backs
+/// the recursive walk.
+///
+public interface IMTConnectComponentContainer
+{
+ /// Components nested directly under this device/component (may be empty).
+ IReadOnlyList Components { get; }
+
+ /// Data items declared directly on this device/component (may be empty).
+ IReadOnlyList DataItems { get; }
+}
+
+///
+/// One MTConnect device from the Agent's probe response — the root of a nested
+/// component/data-item tree.
+///
+/// The device's id attribute.
+/// The device's name attribute, when the Agent supplies one.
+/// Child components declared directly under this device.
+/// Data items declared directly on this device (outside any component).
+public sealed record MTConnectDevice(
+ string Id,
+ string? Name,
+ IReadOnlyList Components,
+ IReadOnlyList DataItems) : IMTConnectComponentContainer;
+
+///
+/// One MTConnect component (e.g. Axes, Controller, Path) from the Agent's
+/// probe response. Components nest to arbitrary depth — a component may itself contain further
+/// child components as well as data items.
+///
+/// The component's id attribute.
+/// The component's name attribute, when the Agent supplies one.
+/// Child components nested directly under this component.
+/// Data items declared directly on this component.
+public sealed record MTConnectComponent(
+ string Id,
+ string? Name,
+ IReadOnlyList Components,
+ IReadOnlyList DataItems) : IMTConnectComponentContainer;
+
+///
+/// One MTConnect DataItem declaration from the Agent's probe response. This is pure metadata —
+/// it carries no observed value; values arrive separately via
+/// /
+/// and are correlated back to a data item by .
+///
+///
+/// The DataItem's id attribute — globally unique within the Agent's probe response and
+/// the correlation key used against .
+///
+/// The DataItem's name attribute, when the Agent supplies one.
+/// The DataItem's category attribute — SAMPLE, EVENT, or CONDITION.
+/// The DataItem's type attribute (e.g. POSITION, EXECUTION).
+/// The DataItem's subType attribute (e.g. ACTUAL, COMMANDED), when present.
+/// The DataItem's units attribute (e.g. MILLIMETER, REVOLUTION/MINUTE), when present.
+///
+/// The DataItem's representation attribute (e.g. TIME_SERIES, DISCRETE);
+/// absent means the MTConnect default (VALUE).
+///
+///
+/// The DataItem's sampleCount attribute — element count for a TIME_SERIES
+/// representation. Present only on data items that carry one.
+///
+public sealed record MTConnectDataItem(
+ string Id,
+ string? Name,
+ string Category,
+ string Type,
+ string? SubType,
+ string? Units,
+ string? Representation,
+ int? SampleCount);
+
+///
+/// Result of an Agent /current request, or of a single multipart chunk from a
+/// /sample stream. Produced by and
+/// .
+///
+///
+/// The Agent's current instance id (its MTConnectStreams header instanceId). Changes
+/// whenever the Agent restarts or its underlying device model changes; the driver watches this
+/// for change to raise rediscovery rather than trusting a stale observation snapshot.
+///
+///
+/// The header nextSequence — the sequence number to resume a subsequent
+/// call from.
+///
+///
+/// The header firstSequence — the oldest sequence number still held in the Agent's
+/// circular buffer. Load-bearing for sequence-gap detection: a caller that requested
+/// from a sequence older than this chunk's has fallen out of
+/// the Agent's buffer and must re-baseline via .
+///
+///
+/// The observations carried in this snapshot/chunk, in Agent-supplied order. Never null;
+/// empty when the chunk carries no new observations (e.g. a heartbeat).
+///
+public sealed record MTConnectStreamsResult(
+ long InstanceId,
+ long NextSequence,
+ long FirstSequence,
+ IReadOnlyList Observations);
+
+///
+/// One observed value for a single DataItem, as reported in a /current snapshot or a
+/// /sample stream chunk. Deliberately dumb: is carried as the raw
+/// string the Agent reported (including the literal UNAVAILABLE) with no interpretation
+/// applied here — coercing UNAVAILABLE to a bad-quality status and converting the string
+/// to the tag's DriverDataType are the observation index's job, not this layer's.
+///
+///
+/// The reporting DataItem's id — correlates back to
+/// from the probe model.
+///
+///
+/// The observed value exactly as the Agent reported it, or the literal string
+/// "UNAVAILABLE" when the Agent has no current value for the data item. Never
+/// pre-parsed into a nullable or an enum at this layer.
+///
+///
+/// The observation's Agent-reported timestamp, normalized to UTC (
+/// is always ). Becomes the OPC UA variable's SourceTimestamp.
+///
+public sealed record MTConnectObservation(
+ string DataItemId,
+ string Value,
+ DateTime TimestampUtc);
+
+///
+/// Recursive helpers over the device/component tree.
+///
+public static class MTConnectComponentTreeExtensions
+{
+ ///
+ /// Enumerates every data item owned by this device or component, plus every data item owned
+ /// by any component nested beneath it, to arbitrary depth. Order is depth-first: a node's
+ /// own data items first, then each child component's data items in turn.
+ ///
+ /// The device or component to walk.
+ public static IEnumerable AllDataItems(this IMTConnectComponentContainer container)
+ {
+ foreach (var dataItem in container.DataItems)
+ {
+ yield return dataItem;
+ }
+
+ foreach (var child in container.Components)
+ {
+ foreach (var dataItem in child.AllDataItems())
+ {
+ yield return dataItem;
+ }
+ }
+ }
+}