diff --git a/gradle.properties b/gradle.properties
index 16974b50fae880645864579e4917a5f250a21ca0..2812326520132211d8037e24b13cb7a04058feff 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -9,7 +9,7 @@ micronautAppVersion = 3.7.10
 micronautVersion = 3.10.1
 nodePluginVersion = 3.0.1
 nodeVersion = 16.3.0
-nshmpLibVersion = 1.5.6
+nshmpLibVersion = 1.5.8
 nshmpUtilsJavaVersion = 0.4.0
 openApiVersion = 4.0.0
 shadowVersion = 7.1.2
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateController.java
index 1ad8fa730622f2cf06cfbbceb64b6a522d5d2fcf..0a40d124a64588f58ee4488ffae6bf549b54d318 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateController.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateController.java
@@ -6,7 +6,6 @@ import gov.usgs.earthquake.nshmp.www.ServletUtil;
 import gov.usgs.earthquake.nshmp.www.source.RateService.Metadata;
 import gov.usgs.earthquake.nshmp.www.source.RateService.Request;
 import gov.usgs.earthquake.nshmp.www.source.RateService.Response;
-
 import io.micronaut.http.HttpRequest;
 import io.micronaut.http.HttpResponse;
 import io.micronaut.http.MediaType;
@@ -97,6 +96,42 @@ public class RateController {
     }
   }
 
+  /**
+   * @param longitude Longitude in decimal degrees in the range
+   * @param latitude Latitude in decimal degrees in the range
+   * @param distance Cutoff distance in the range [0.01, 1000] km.
+   */
+  @Operation(
+      summary = "Compute annual earthquake rates",
+      description = "Compute incremental annual earthquake rates at a location",
+      operationId = "rate-calc")
+  @ApiResponse(
+      description = "Earthquake annual rate calculation response",
+      responseCode = "200",
+      content = @Content(
+          schema = @Schema(implementation = RateResponse.class)))
+  @Get(
+      uri = "/dev/{longitude}/{latitude}/{distance}",
+      produces = MediaType.APPLICATION_JSON)
+  public HttpResponse<String> doGetRateDev(
+      HttpRequest<?> http,
+      @PathVariable double longitude,
+      @PathVariable double latitude,
+      @Schema(
+          minimum = "0.01",
+          maximum = "1000") @PathVariable double distance) {
+    try {
+      RateService.Request request = new RateService.Request(
+          http, longitude, latitude, distance);
+      return RateService.getRateDev(request);
+    } catch (Exception e) {
+      return ServletUtil.error(
+          RateService.LOG, e,
+          RateService.NAME_RATE,
+          http.getUri().getPath());
+    }
+  }
+
   // Swagger schema
   private static class RateResponse extends ResponseBody<Request, Response> {}
 
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateService.java
index 59246530d7358c50a78d24ac760eb1e7bc0a6424..6f8f5cacb7afc3c1174b422c4461135fd74bd727 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateService.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/www/source/RateService.java
@@ -7,7 +7,6 @@ import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLongitude;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.OptionalDouble;
-import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
@@ -15,13 +14,16 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.Range;
-import com.google.common.util.concurrent.ListenableFuture;
 
 import gov.usgs.earthquake.nshmp.calc.CalcConfig;
 import gov.usgs.earthquake.nshmp.calc.EqRate;
 import gov.usgs.earthquake.nshmp.calc.Site;
+import gov.usgs.earthquake.nshmp.data.MutableXySequence;
+import gov.usgs.earthquake.nshmp.data.XySequence;
 import gov.usgs.earthquake.nshmp.geo.Location;
 import gov.usgs.earthquake.nshmp.model.HazardModel;
+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;
@@ -29,7 +31,6 @@ import gov.usgs.earthquake.nshmp.www.ServletUtil;
 import gov.usgs.earthquake.nshmp.www.ServletUtil.Server;
 import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter;
 import gov.usgs.earthquake.nshmp.www.source.SourceService.SourceModel;
-
 import io.micronaut.http.HttpRequest;
 import io.micronaut.http.HttpResponse;
 import jakarta.inject.Singleton;
@@ -58,6 +59,25 @@ public final class RateService {
 
   private static final String TOTAL_KEY = "Total";
 
+  public static HttpResponse<String> getRateDev(Request request) {
+    var stopwatch = Stopwatch.createStarted();
+    var rates = calcRate(request);
+    var response = new Response.Builder()
+        .timer(stopwatch)
+        .request(request)
+        .ratesDev(rates)
+        .build();
+    var body = ResponseBody.success()
+        .name(NAME_RATE)
+        .url(request.http.getUri().getPath())
+        .metadata(new ResponseMetadata(HazVersion.appVersions(ServletUtil.model().root())))
+        .request(request)
+        .response(response)
+        .build();
+    String json = ServletUtil.GSON2.toJson(body);
+    return HttpResponse.ok(json);
+  }
+
   public static HttpResponse<String> getRate(Request request) {
     var stopwatch = Stopwatch.createStarted();
     var rates = calcRate(request);
@@ -107,59 +127,38 @@ public final class RateService {
   private static EqRate calc(Request request, OptionalDouble timespan) {
     var location = Location.create(request.longitude, request.latitude);
     var site = Site.builder().location(location).build();
-    var futureRates = new ArrayList<ListenableFuture<EqRate>>();
-
-    /*
-     * Because we need to combine model results, intially calculate incremental
-     * annual rates and only convert to cumulative probabilities at the end if
-     * probability service has been called.
-     */
-
-    var model = ServletUtil.model();
-    var rate = process(model, site, request.distance, timespan);
-    futureRates.add(rate);
-
-    var rates = futureRates.stream()
-        .map((future) -> {
-          try {
-            return future.get();
-          } catch (InterruptedException | ExecutionException e) {
-            throw new RuntimeException(e);
-          }
-        }).collect(Collectors.toList())
-        .toArray(new EqRate[] {});
-
-    var ratesCombined = EqRate.combine(rates);
+    HazardModel model = ServletUtil.model();
+    EqRate rate = process(model, site, request.distance, timespan);
 
     if (timespan.isPresent()) {
-      ratesCombined = EqRate.toCumulative(ratesCombined);
-      ratesCombined = EqRate.toPoissonProbability(ratesCombined, timespan.orElseThrow());
+      rate = EqRate.toCumulative(rate);
+      rate = EqRate.toPoissonProbability(rate, timespan.orElseThrow());
     }
 
-    return ratesCombined;
+    return rate;
   }
 
-  private static ListenableFuture<EqRate> process(
+  private static EqRate process(
       HazardModel model,
       Site site,
       double distance,
       OptionalDouble timespan) {
-    var configBuilder = CalcConfig.copyOf(model.config()).distance(distance);
+
+    CalcConfig.Builder configBuilder = CalcConfig.copyOf(model.config()).distance(distance);
     if (timespan.isPresent()) {
       /* Also sets value format to Poisson probability. */
       configBuilder.timespan(timespan.getAsDouble());
     }
-    var config = configBuilder.build();
-    var task = EqRate.callable(model, config, site);
-    return ServletUtil.CALC_EXECUTOR.submit(task);
+    CalcConfig config = configBuilder.build();
+    return EqRate.create(model, config, site);
   }
 
   static class Response {
 
     final Metadata metadata;
-    final List<Sequence> data;
+    final List<?> data;
 
-    Response(Metadata metadata, List<Sequence> data) {
+    Response(Metadata metadata, List<?> data) {
       this.metadata = metadata;
       this.data = data;
     }
@@ -168,7 +167,7 @@ public final class RateService {
       return metadata;
     }
 
-    public List<Sequence> getData() {
+    public List<?> getData() {
       return data;
     }
 
@@ -198,7 +197,7 @@ public final class RateService {
 
       Stopwatch timer;
       Request request;
-      List<Sequence> data;
+      List<?> data;
 
       Builder timer(Stopwatch timer) {
         this.timer = timer;
@@ -215,6 +214,11 @@ public final class RateService {
         return this;
       }
 
+      Builder ratesDev(EqRate rates) {
+        this.data = buildSequencesDev(rates);
+        return this;
+      }
+
       private List<Sequence> buildSequences(EqRate rates) {
         var sequences = new ArrayList<Sequence>();
 
@@ -243,6 +247,30 @@ public final class RateService {
         return List.copyOf(sequences);
       }
 
+      private List<SequenceDev> buildSequencesDev(EqRate rates) {
+        var sequences = new ArrayList<SequenceDev>();
+
+        /* Total mfd. */
+        var total = (!rates.totalMfd.isClear()) ? rates.totalMfd.trim() : rates.totalMfd;
+        var totalOut = new SequenceDev(TOTAL_KEY, null, null, null, total);
+        sequences.add(totalOut);
+
+        MutableXySequence totalMfdCheck = MutableXySequence.emptyCopyOf(rates.totalMfd);
+
+        /* Source type mfds. */
+        for (EqRate.Contributor contrib : rates.mfds) {
+          var contribOut = new SequenceDev(
+              contrib.component,
+              contrib.id,
+              contrib.setting,
+              contrib.type,
+              contrib.mfd.trim());
+          sequences.add(contribOut);
+          totalMfdCheck.add(contrib.mfd);
+        }
+        return sequences;
+      }
+
       Response build() {
         Server server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer);
         return new Response(new Response.Metadata(server), data);
@@ -255,6 +283,7 @@ public final class RateService {
    * HazardService.Curve
    */
   private static class Sequence {
+
     final String component;
     final List<Double> xvalues;
     final List<Double> yvalues;
@@ -278,6 +307,49 @@ public final class RateService {
     }
   }
 
+  private static class SequenceDev {
+
+    final String component;
+    final Integer id;
+    final TectonicSetting setting;
+    final SourceType type;
+    final XySequence values;
+
+    SequenceDev(
+        String component,
+        Integer id,
+        TectonicSetting setting,
+        SourceType type,
+        XySequence values) {
+
+      this.component = component;
+      this.id = id;
+      this.setting = setting;
+      this.type = type;
+      this.values = values;
+    }
+
+    public String getComponent() {
+      return component;
+    }
+
+    public Integer getId() {
+      return id;
+    }
+
+    public TectonicSetting getSetting() {
+      return setting;
+    }
+
+    public SourceType getSourceType() {
+      return type;
+    }
+
+    public XySequence getValues() {
+      return values;
+    }
+  }
+
   private static final Range<Double> DISTANCE_RANGE = Range.closed(0.01, 1000.0);
   private static final Range<Double> TIMESPAN_RANGE = Range.closed(1.0, 10000.0);