diff --git a/docs/pages/Calculation-Configuration.md b/docs/pages/Calculation-Configuration.md
index 7f8bf3d8a1e10796ac3f577cf72b7e77e9d2edba..097e8d5920e0c6c435c45b261826c3fcde7822b4 100644
--- a/docs/pages/Calculation-Configuration.md
+++ b/docs/pages/Calculation-Configuration.md
@@ -14,38 +14,43 @@ may be overridden. See [building and running](./Building-&-Running.md) and the
 Parameter | Type | Default | Notes |
 --------- | ---- | ------- | ----- |
 __`hazard`__
-   `.exceedanceModel`       |`String`   | `TRUNCATION_3SIGMA_UPPER`  | [`ExceedanceModel`][url-exceedance]
+   `.exceedanceModel`       |`String`   | `"TRUNCATION_3SIGMA_UPPER"`| [`ExceedanceModel`][url-exceedance]
    `.truncationLevel`       |`Double`   | `3.0`                      | [1](#notes)
-   `.imts`                  |`String[]` | `[ PGV, PGA, SA0P01, SA0P02, SA0P03, SA0P05, SA0P075, SA0P1, SA0P15, SA0P2, SA0P25, SA0P3, SA0P4, SA0P5, SA0P75, SA1P0, SA1P5, SA2P0, SA3P0, SA4P0, SA5P0, SA7P5, SA10P0 ]` | [`Imt`][url-imt]
-   `.tectonicSettings`      |`String[]` | `[]`                       | Tectonic setting filter
-   `.sourceTypes`           |`String[]` | `[]`                       | Source type filter
+&nbsp;&nbsp;&nbsp;`.imts`                  |`String[]` | `["PGV","PGA","SA0P01","SA0P02",`<br>`"SA0P03","SA0P05","SA0P075",`<br>`"SA0P1","SA0P15","SA0P2","SA0P25",`<br>`"SA0P3","SA0P4","SA0P5","SA0P75",`<br>`"SA1P0","SA1P5","SA2P0","SA3P0",`<br>`"SA4P0","SA5P0","SA7P5","SA10P0"]` | [`Imt`][url-imt]
+&nbsp;&nbsp;&nbsp;`.tectonicSettings`      |`String[]` | `[]`                       | [`TectonicSetting`][url-tectonicsetting] filter
+&nbsp;&nbsp;&nbsp;`.sourceTypes`           |`String[]` | `[]`                       | [`SourceType`][url-sourcetype] filter
 &nbsp;&nbsp;&nbsp;`.vs30s`                 |`Double[]` | `[]`                       | Vs30s to use for batch jobs
+&nbsp;&nbsp;&nbsp;`.useSiteData`           |`Boolean`  | `true`                     | Enable site data (e.g. basin depths)
 &nbsp;&nbsp;&nbsp;`.customImls`            |`Map<String, Double[]>`  | `{}` (empty object)     | [2](#notes)
-&nbsp;&nbsp;&nbsp;`.gmmDampingRatio`       |`Double`   | `0.05` (5%)                | [3](#notes)
-&nbsp;&nbsp;&nbsp;`.gmmSigmaScale`         |`Double`   | `1.0` (100%, no scaling)   |
-&nbsp;&nbsp;&nbsp;`.valueFormat`           |`String`   | `ANNUAL_RATE`              | [`ValueFormat`][url-valueformat]
+&nbsp;&nbsp;&nbsp;`.valueFormat`           |`String`   | `"ANNUAL_RATE"`            | [`ValueFormat`][url-valueformat]
+__`gmm`__
+&nbsp;&nbsp;&nbsp;`.dampingRatio`          |`Double`   | `0.05` (5%)                | Limited to range [0.005..0.3] 0.5% to 30%
+&nbsp;&nbsp;&nbsp;`.sigmaScale`            |`Double`   | `1.0` (100%, no scaling)   | Limited to range [0.5..1.0] 50% to 100%
+&nbsp;&nbsp;&nbsp;`.vertical`              |`Boolean`  | `false`                    | Compute vertical ground motions
 __`disagg`__
-&nbsp;&nbsp;&nbsp;`.retrunPeriod`          |`Double`   | `2475`                     |
+&nbsp;&nbsp;&nbsp;`.returnPeriod`          |`Double`   | `2475`                     |
 &nbsp;&nbsp;&nbsp;`.bins`                  |`Object`   |                            | [4](#notes)
 &nbsp;&nbsp;&nbsp;`.contributorLimit`      |`Double`   | `0.1`                      | [5](#notes)
 __`rate`__
 &nbsp;&nbsp;&nbsp;`.bins`                  |`Object`   |                            | [6](#notes)
 &nbsp;&nbsp;&nbsp;`.distance`              |`Double`   | `20` km
-&nbsp;&nbsp;&nbsp;`.distributionFormat`    |`String`   | `INCREMENTAL`              | [`DistributionFormat`][url-distribution]
+&nbsp;&nbsp;&nbsp;`.distributionFormat`    |`String`   | `"INCREMENTAL"`            | [`DistributionFormat`][url-distribution]
 &nbsp;&nbsp;&nbsp;`.timespan`              |`Double`   | `30` years
-&nbsp;&nbsp;&nbsp;`.valueFormat`           |`String`   | `ANNUAL_RATE`              | [`ValueFormat`][url-valueformat]
+&nbsp;&nbsp;&nbsp;`.valueFormat`           |`String`   | `"ANNUAL_RATE"`            | [`ValueFormat`][url-valueformat]
 __`output`__                               |
 &nbsp;&nbsp;&nbsp;`.directory`             |`String`   | `hazout`
-&nbsp;&nbsp;&nbsp;`.dataTypes`             |`String[]` | `[ TOTAL, MAP ]`           | [`DataType`][url-datatype]
-&nbsp;&nbsp;&nbsp;`.returnPeriods`         |`Double[]` | `[ 475, 975, 2475, 10000]` | [`ReturnPeriods`][url-returnperiods]
+&nbsp;&nbsp;&nbsp;`.dataTypes`             |`String[]` | `["TOTAL","MAP"]`          | [`DataType`][url-datatype]
+&nbsp;&nbsp;&nbsp;`.returnPeriods`         |`Double[]` | `[475,975,2475,10000]`     | [`ReturnPeriods`][url-returnperiods]
 __`performance`__
 &nbsp;&nbsp;&nbsp;`.optimizeGrids`         |`Boolean`  | `true`                     | [7](#notes)
 &nbsp;&nbsp;&nbsp;`.smoothGrids`           |`Boolean`  | `true`                     | [8](#notes)
 &nbsp;&nbsp;&nbsp;`.systemPartition`       |`Integer`  | `1000`                     | [9](#notes)
-&nbsp;&nbsp;&nbsp;`.threadCount`           |`String`   | `ALL`                      | [`ThreadCount`][url-sheets]
+&nbsp;&nbsp;&nbsp;`.threadCount`           |`String`   | `"ALL"`                    | [`ThreadCount`][url-sheets]
 
 [url-exceedance]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/calc/ExceedanceModel.html
 [url-imt]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/gmm/Imt.html
+[url-tectonicsetting]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/model/TectonicSetting.html
+[url-sourcetype]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/model/SourceType.html
 [url-valueformat]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/calc/ValueFormat.html
 [url-distribution]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/calc/DistributionFormat.html
 [url-datatype]: https://earthquake.usgs.gov/nshmp/docs/nshmp-lib/gov/usgs/earthquake/nshmp/calc/DataType.html
diff --git a/gradle.properties b/gradle.properties
index c97c988f51647e4f24ca18df5a54d66ee1f6cb5d..04ae04e2d10f92e45bc0d81598dffd799e627f7d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,7 +11,7 @@ micronautVersion = 3.2.3
 micronautRxVersion = 2.1.1
 nodePluginVersion = 3.0.1
 nodeVersion = 16.3.0
-nshmpLibVersion = 1.2.8
+nshmpLibVersion = 1.2.10
 nshmpWsUtilsVersion = 0.3.11
 openApiVersion = 4.0.0
 shadowVersion = 7.1.2
diff --git a/gradle/nshm.gradle b/gradle/nshm.gradle
index 5eecb87f60f13bc56d519bbdc4b64f494cd659df..0e1a39a17c9477faf6afe3985adf691099b5e3bc 100644
--- a/gradle/nshm.gradle
+++ b/gradle/nshm.gradle
@@ -43,7 +43,7 @@ clean.dependsOn cleanNshm
 task nshms() {
   dependsOn cleanNshm
   def yaml = new Yaml()
-  def nshmConfig = new Yaml().load(new File("nshms.yml").newInputStream())
+  def nshmConfig = new Yaml().load(new File("${projectDir}/nshms.yml").newInputStream())
 
   doLast {
     for (nshm in nshmConfig.nshms) {
diff --git a/nshms.yml b/nshms.yml
index 2a4f21e7658609ea651b2be297e2b01640164ab8..a3a2c9975aad5e793312fe004d113aeed02dedf4 100644
--- a/nshms.yml
+++ b/nshms.yml
@@ -5,7 +5,7 @@ nshms:
   # Alaska 2023 NSHM
   -
     repo: nshm-alaska
-    tag: 3.a.0
+    tag: 3.a.1
     year: 2023
     deployments:
       development:
@@ -32,7 +32,7 @@ nshms:
   # CONUS 2023 NSHM
   -
     repo: nshm-conus
-    tag: 6.a.3
+    tag: 6.a.4
     year: 2023
     deployments:
       development:
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/source/LogicTreesController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/source/LogicTreesController.java
index 1c6b2396ffb5f88780752728a34d06fbdc555631..2f941ff14789f3fe25383dea32c0bb8b150c654b 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/www/source/LogicTreesController.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/source/LogicTreesController.java
@@ -70,7 +70,7 @@ public class LogicTreesController {
       description = "Returns the logic tree of features for the supplied ID",
       operationId = "source-tree-features")
   @ApiResponse(
-      description = "NSHM source logic tree",
+      description = "NSHM source logic tree of features",
       responseCode = "200",
       content = @Content(
           schema = @Schema(implementation = TreeResponse.class)))
@@ -94,7 +94,7 @@ public class LogicTreesController {
       description = "Returns the logic tree of MFDs for the supplied ID",
       operationId = "source-tree-mfds")
   @ApiResponse(
-      description = "NSHM source logic tree",
+      description = "NSHM source logic tree of MFDs",
       responseCode = "200",
       content = @Content(
           schema = @Schema(implementation = TreeResponse.class)))
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/source/MfdsController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/source/MfdsController.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d87c87bb4dd583ed65f3528671a0d2511829333
--- /dev/null
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/source/MfdsController.java
@@ -0,0 +1,131 @@
+package gov.usgs.earthquake.nshmp.www.source;
+
+import java.util.Optional;
+
+import gov.usgs.earthquake.nshmp.model.SourceType;
+import gov.usgs.earthquake.nshmp.model.TectonicSetting;
+import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet;
+import gov.usgs.earthquake.nshmp.www.ResponseBody;
+import gov.usgs.earthquake.nshmp.www.ServletUtil;
+import gov.usgs.earthquake.nshmp.www.source.MfdsService.Metadata;
+import gov.usgs.earthquake.nshmp.www.source.MfdsService.RequestData;
+
+import io.micronaut.http.HttpRequest;
+import io.micronaut.http.HttpResponse;
+import io.micronaut.http.annotation.Controller;
+import io.micronaut.http.annotation.Get;
+import io.micronaut.http.annotation.PathVariable;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.inject.Inject;
+
+/**
+ * Micronaut web service controller for summed MFDs in the installed model.
+ * These endpoints return the magnitude frequency distributions (MFDs)
+ * associated with the different tectonic settings and source types present in
+ * the model. MFDs for requested tetonic settings and source types are scaled by
+ * their branch weights and then summed.
+ *
+ * <p>See src/main/resources/application.yml nshmp-haz.model-path for installed
+ * model.
+ *
+ * <p>To run the Micronaut jar file with a model: java -jar
+ * path/to/nshmp-haz.jar --model=<path/to/model>
+ *
+ * @author U.S. Geological Survey
+ */
+@Tag(
+    name = MfdsService.NAME,
+    description = "USGS NSHM source model MFD service")
+@Controller("/mfds")
+public class MfdsController {
+
+  @Inject
+  private NshmpMicronautServlet servlet;
+
+  @Operation(
+      summary = "Source model tectonic settings and source types",
+      description = "Returns the tectonic settings and source types in the model",
+      operationId = "source-mfd-metadata")
+  @ApiResponse(
+      description = "Source MFD metadata",
+      responseCode = "200",
+      content = @Content(
+          schema = @Schema(
+              implementation = MetadataResponse.class)))
+  @Get
+  public HttpResponse<String> doGetMetadata(HttpRequest<?> http) {
+    try {
+      return LogicTreesService.getMetadata(http);
+    } catch (Exception e) {
+      return ServletUtil.error(
+          LogicTreesService.LOG, e,
+          LogicTreesService.NAME,
+          http.getUri().toString());
+    }
+  }
+
+  /**
+   * @param setting model tectonic setting
+   */
+  @Operation(
+      summary = "Get a source model MFD for a tectonic setting",
+      description = "Returns the total MFD for the supplied tectonic setting",
+      operationId = "source-setting-mfd")
+  @ApiResponse(
+      description = "NSHM source MFD",
+      responseCode = "200",
+      content = @Content(
+          schema = @Schema(implementation = Response.class)))
+  @Get(uri = "/{setting}")
+  public HttpResponse<String> doGetMfdSettingGroup(
+      HttpRequest<?> http,
+      @PathVariable TectonicSetting setting) {
+    try {
+      return MfdsService.getMfdGroup(http, setting, Optional.empty());
+    } catch (Exception e) {
+      return ServletUtil.error(
+          LogicTreesService.LOG, e,
+          LogicTreesService.NAME,
+          http.getUri().toString());
+    }
+  }
+
+  /**
+   * @param setting model tectonic setting
+   * @param type model source type}
+   */
+  @Operation(
+      summary = "Get a source model MFD for a tectonic setting and source type",
+      description = "Returns the total MFD for the supplied tectonic setting and source type",
+      operationId = "source-setting-type-mfd")
+  @ApiResponse(
+      description = "NSHM source MFD",
+      responseCode = "200",
+      content = @Content(
+          schema = @Schema(implementation = Response.class)))
+  @Get(uri = "/{setting}/{type}")
+  public HttpResponse<String> doGetMfdTypeGroup(
+      HttpRequest<?> http,
+      @PathVariable TectonicSetting setting,
+      @PathVariable SourceType type) {
+    try {
+      return MfdsService.getMfdGroup(http, setting, Optional.of(type));
+    } catch (Exception e) {
+      return ServletUtil.error(
+          LogicTreesService.LOG, e,
+          LogicTreesService.NAME,
+          http.getUri().toString());
+    }
+  }
+
+  // Swagger schema
+  private static class MetadataResponse extends ResponseBody<String, Metadata> {}
+
+  // Swagger schema
+  private static class Response extends ResponseBody<RequestData, Object> {}
+
+}
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/source/MfdsService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/source/MfdsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..4062fd449bc564e643924d10a62246d07410fe06
--- /dev/null
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/source/MfdsService.java
@@ -0,0 +1,107 @@
+package gov.usgs.earthquake.nshmp.www.source;
+
+import java.util.Optional;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gov.usgs.earthquake.nshmp.model.HazardModel;
+import gov.usgs.earthquake.nshmp.model.Models;
+import gov.usgs.earthquake.nshmp.model.SourceType;
+import gov.usgs.earthquake.nshmp.model.TectonicSetting;
+import gov.usgs.earthquake.nshmp.www.HazVersion;
+import gov.usgs.earthquake.nshmp.www.ResponseBody;
+import gov.usgs.earthquake.nshmp.www.ResponseMetadata;
+import gov.usgs.earthquake.nshmp.www.ServletUtil;
+import gov.usgs.earthquake.nshmp.www.source.SourceService.SourceModel;
+
+import io.micronaut.http.HttpRequest;
+import io.micronaut.http.HttpResponse;
+import jakarta.inject.Singleton;
+
+/**
+ * Source model MFD handler {@link MfdsController}
+ *
+ * @author U.S. Geological Survey
+ */
+@Singleton
+public class MfdsService {
+
+  static final String NAME = "Model MFDs";
+  static final Logger LOG = LoggerFactory.getLogger(LogicTreesService.class);
+
+  public static HttpResponse<String> getMetadata(HttpRequest<?> request) {
+    var url = request.getUri().toString();
+    var metadata = new Metadata(ServletUtil.model());
+    var response = ResponseBody.usage()
+        .name(NAME)
+        .url(url)
+        .metadata(new ResponseMetadata(HazVersion.appVersions()))
+        .request(url)
+        .response(metadata)
+        .build();
+    return HttpResponse.ok(ServletUtil.GSON2.toJson(response));
+  }
+
+  public static HttpResponse<String> getMfdGroup(
+      HttpRequest<?> request,
+      TectonicSetting setting,
+      Optional<SourceType> type) {
+
+    var url = request.getUri().toString();
+    var mfd = Models.mfd(ServletUtil.model(), setting, type);
+    var requestData = new RequestData(setting, type.orElse(null));
+    var response = ResponseBody.success()
+        .name(NAME)
+        .url(url)
+        .metadata(new ResponseMetadata(HazVersion.appVersions()))
+        .request(requestData)
+        .response(mfd)
+        .build();
+    return HttpResponse.ok(ServletUtil.GSON2.toJson(response));
+  }
+
+  static class Metadata {
+    final SourceModel model;
+    final Set<TectonicSetting> settings;
+    final Set<SourceType> types;
+
+    Metadata(HazardModel model) {
+      this.model = new SourceModel(model);
+      this.settings = model.settings();
+      this.types = model.types();
+    }
+
+    public SourceModel getModel() {
+      return model;
+    }
+
+    public Set<TectonicSetting> getSettings() {
+      return settings;
+    }
+
+    public Set<SourceType> getTypes() {
+      return types;
+    }
+  }
+
+  static class RequestData {
+    final TectonicSetting setting;
+    final SourceType type;
+
+    RequestData(TectonicSetting setting, SourceType type) {
+      this.setting = setting;
+      this.type = type;
+    }
+
+    public TectonicSetting getSetting() {
+      return setting;
+    }
+
+    public SourceType getType() {
+      return type;
+    }
+  }
+
+}