diff --git a/ZB.MOM.WW.SPHistorianClient/Directory.Packages.props b/ZB.MOM.WW.SPHistorianClient/Directory.Packages.props
index ab763ff..ead2913 100644
--- a/ZB.MOM.WW.SPHistorianClient/Directory.Packages.props
+++ b/ZB.MOM.WW.SPHistorianClient/Directory.Packages.props
@@ -20,6 +20,7 @@
+
diff --git a/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/DependencyInjection/ZbSpHistorianClientServiceCollectionExtensions.cs b/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/DependencyInjection/ZbSpHistorianClientServiceCollectionExtensions.cs
new file mode 100644
index 0000000..a8e231d
--- /dev/null
+++ b/ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/DependencyInjection/ZbSpHistorianClientServiceCollectionExtensions.cs
@@ -0,0 +1,31 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace ZB.MOM.WW.SPHistorianClient;
+
+///
+/// ZB.MOM.WW DI registration for . Mirrors the family's
+/// AddZb* convention. Because is required/
+/// init-only, callers pass a fully-built options instance (bind it from configuration in the
+/// consuming app, e.g. config.GetSection("Historian").Get<HistorianClientOptions>()).
+///
+public static class ZbSpHistorianClientServiceCollectionExtensions
+{
+ public static IServiceCollection AddZbSpHistorianClient(
+ this IServiceCollection services,
+ HistorianClientOptions options)
+ {
+ ArgumentNullException.ThrowIfNull(services);
+ ArgumentNullException.ThrowIfNull(options);
+ if (string.IsNullOrWhiteSpace(options.Host))
+ {
+ throw new ArgumentException(
+ "HistorianClientOptions.Host must be set.", nameof(options));
+ }
+
+ services.AddSingleton(options);
+ // HistorianClient opens a fresh channel per operation and has a no-op DisposeAsync,
+ // so transient is safe and avoids assuming the shared dialect is concurrency-safe.
+ services.AddTransient();
+ return services;
+ }
+}
diff --git a/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/DependencyInjectionTests.cs b/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/DependencyInjectionTests.cs
new file mode 100644
index 0000000..1b36707
--- /dev/null
+++ b/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/DependencyInjectionTests.cs
@@ -0,0 +1,38 @@
+using Microsoft.Extensions.DependencyInjection;
+using ZB.MOM.WW.SPHistorianClient;
+
+namespace ZB.MOM.WW.SPHistorianClient.Tests;
+
+public class DependencyInjectionTests
+{
+ [Fact]
+ public async Task AddZbSpHistorianClient_resolves_client_and_options()
+ {
+ var services = new ServiceCollection();
+ var options = new HistorianClientOptions { Host = "localhost" };
+
+ services.AddZbSpHistorianClient(options);
+
+ // HistorianClient is IAsyncDisposable-only, so the container must be disposed
+ // asynchronously (a synchronous `using` throws InvalidOperationException).
+ await using var sp = services.BuildServiceProvider();
+ Assert.Same(options, sp.GetRequiredService());
+ Assert.NotNull(sp.GetRequiredService());
+ }
+
+ [Fact]
+ public void AddZbSpHistorianClient_throws_when_host_missing()
+ {
+ var services = new ServiceCollection();
+ var options = new HistorianClientOptions { Host = "" };
+
+ Assert.Throws(() => services.AddZbSpHistorianClient(options));
+ }
+
+ [Fact]
+ public void AddZbSpHistorianClient_throws_on_null_options()
+ {
+ var services = new ServiceCollection();
+ Assert.Throws(() => services.AddZbSpHistorianClient(null!));
+ }
+}
diff --git a/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/ZB.MOM.WW.SPHistorianClient.Tests.csproj b/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/ZB.MOM.WW.SPHistorianClient.Tests.csproj
index e593f46..011e716 100644
--- a/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/ZB.MOM.WW.SPHistorianClient.Tests.csproj
+++ b/ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/ZB.MOM.WW.SPHistorianClient.Tests.csproj
@@ -6,6 +6,7 @@
+