diff --git a/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/MuonCondAlg/MmCTPCondDbAlg.h b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/MuonCondAlg/MmCTPCondDbAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..5fa10fa3bb2a71d6b8aae83da4d290345409d5e7
--- /dev/null
+++ b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/MuonCondAlg/MmCTPCondDbAlg.h
@@ -0,0 +1,43 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef MUONCONDALG_MmCTPCondDbAlg_H
+#define MUONCONDALG_MmCTPCondDbAlg_H
+
+// Athena includes
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "AthenaPoolUtilities/CondAttrListCollection.h"
+#include "MuonIdHelpers/IMuonIdHelperSvc.h"
+#include "StoreGate/CondHandleKeyArray.h"
+#include "StoreGate/WriteCondHandleKey.h"
+#include <nlohmann/json.hpp>
+#include "MuonCondData/mmCTPClusterCalibData.h"
+
+class MmCTPCondDbAlg : public AthReentrantAlgorithm {
+public:
+    //No need for individual constructor
+    using AthReentrantAlgorithm::AthReentrantAlgorithm ;
+    virtual ~MmCTPCondDbAlg() = default;
+    virtual StatusCode initialize() override final;
+    virtual StatusCode execute(const EventContext& ctx) const override final;
+    virtual bool isReEntrant() const override final{ return false; }
+
+private:
+    /// Parse data from COOL
+    Gaudi::Property<std::string> m_readFromJSON{this, "readFromJSON", "" };
+    StatusCode parseDataFromJSON(const nlohmann::json& lines,
+                                 Muon::mmCTPClusterCalibData& ctpClusterCondData) const;
+
+    ServiceHandle<Muon::IMuonIdHelperSvc> m_idHelperSvc{this, "MuonIdHelperSvc", "Muon::MuonIdHelperSvc/MuonIdHelperSvc"};
+
+
+
+    SG::WriteCondHandleKey<Muon::mmCTPClusterCalibData> m_writeKey{this, "WriteKey", "mmCTPClusterCalibData", "Key of the CTP slope data in the CondStore"};
+    SG::ReadCondHandleKey<CondAttrListCollection> m_readKeyDb{this, "ReadKey", "", "Folder of the MM CTP corrections as they are stored in COOL"};
+
+
+};
+
+#endif
+
diff --git a/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/MmCTPCondDbAlg.cxx b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/MmCTPCondDbAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..33d88826abd6a7125ef1300068d7ddb5b4c8f7b6
--- /dev/null
+++ b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/MmCTPCondDbAlg.cxx
@@ -0,0 +1,125 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "MuonCondAlg/MmCTPCondDbAlg.h"
+#include <StoreGate/WriteCondHandle.h>
+#include <StoreGate/ReadCondHandle.h>
+#include <AthenaKernel/IOVInfiniteRange.h>
+#include <PathResolver/PathResolver.h>
+#include <CoralBase/Blob.h>
+#include <CoralUtilities/blobaccess.h>
+#include <fstream>
+
+
+
+// Initialize
+StatusCode MmCTPCondDbAlg::initialize() {
+
+    ATH_CHECK(m_idHelperSvc.retrieve());
+    if( !m_idHelperSvc->hasMM() ){
+         ATH_MSG_ERROR("MuonIdHelperSvc does not have MM, the CTP calibration should not be run!");
+         return StatusCode::FAILURE;
+    }
+
+    ATH_CHECK(m_readKeyDb.initialize(m_readFromJSON.value().empty()));
+    if (m_readFromJSON.value().size()) {
+        ATH_MSG_INFO("Read the uncertainty data from a JSON file "<<m_readFromJSON);
+    } else if (!m_readKeyDb.empty()) {
+        ATH_MSG_INFO("Read the uncertainty data from a COOL databse: " << m_readKeyDb.fullKey() );
+    } else {
+        ATH_MSG_FATAL("Neither an extrenal JSON nor a COOL folder were defined. Please check");
+        return StatusCode::FAILURE;
+    }
+    ATH_CHECK(m_writeKey.initialize());
+
+    
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode MmCTPCondDbAlg::execute(const EventContext& ctx) const {
+    ATH_MSG_DEBUG("execute " << name());
+    // launching Write Cond Handle
+    SG::WriteCondHandle writeHandle{m_writeKey, ctx};
+    if (writeHandle.isValid()) {
+        ATH_MSG_DEBUG("CondHandle " << writeHandle.fullKey() << " is already valid."
+                                    << " In theory this should not be called, but may happen"
+                                    << " if multiple concurrent events are being processed out of order.");
+        return StatusCode::SUCCESS;
+    }
+
+    std::unique_ptr<Muon::mmCTPClusterCalibData> writeCdo{std::make_unique<Muon::mmCTPClusterCalibData>(m_idHelperSvc.get())};
+    writeHandle.addDependency(EventIDRange(IOVInfiniteRange::infiniteTime()));
+
+    if (!m_readFromJSON.value().empty()) {
+       std::ifstream inStream{PathResolverFindCalibFile(m_readFromJSON)};
+       if (!inStream.good()) {
+          ATH_MSG_FATAL("No such file or directory");
+          return StatusCode::FAILURE;
+       }
+       nlohmann::json lines;
+       inStream >> lines;
+       ATH_CHECK(parseDataFromJSON(lines, *writeCdo));          
+    } else{
+
+       SG::ReadCondHandle readHandle{m_readKeyDb, ctx};
+       if (!readHandle.isValid()) {
+          ATH_MSG_FATAL("Failed to load NSW error calibration folder from "<<m_readKeyDb.fullKey());
+          return StatusCode::FAILURE;
+       }
+
+       for (CondAttrListCollection::const_iterator itr = readHandle->begin(); 
+             itr != readHandle->end(); ++itr) {
+          const coral::AttributeList& atr = itr->second;
+          std::string data = *(static_cast<const std::string*>((atr["data"]).addressOfData()));
+          nlohmann::json lines = nlohmann::json::parse(data);
+          ATH_CHECK(parseDataFromJSON(lines, *writeCdo));
+       }
+    }
+    
+    ATH_CHECK(writeHandle.record(std::move(writeCdo)));    
+    return StatusCode::SUCCESS;
+}
+
+StatusCode MmCTPCondDbAlg::parseDataFromJSON(const nlohmann::json& lines,
+                                                 Muon::mmCTPClusterCalibData& ctpClusterCondData) const {
+    for (auto& corr : lines.items()) {
+        nlohmann::json line = corr.value();    
+
+        //Check entry keys, shall we make it to just check the first entry and then assume the rest is okay? maybe it doesn't take that much time to do it on the whole thing...
+        if (!line.contains("station") || !line.contains("phi") || !line.contains("eta") || !line.contains("multilayer") || !line.contains("gasGap")) {
+            ATH_MSG_ERROR("Missing expected keys in the JSON file");
+            return StatusCode::FAILURE; // Handle error appropriately
+        }
+
+        // Ensure the JSON array exists and has exactly 2 elements
+        if (!line.contains("P1Parameters") || !line["P1Parameters"].is_array() || line["P1Parameters"].size() != 2) {
+            ATH_MSG_ERROR("Get unexpected size of the P1Parameters array");
+            return StatusCode::FAILURE; // Handle error appropriately
+        }
+
+         /// Station Component identification
+        const std::string stationType = line["station"];
+        const int stationPhi          = line["phi"];
+        const int stationEta          = line["eta"];
+        const int multiLayer          = line["multilayer"];
+        const int gasGap              = line["gasGap"];
+
+        // Convert JSON array to std::array<double, 2>
+        std::array<double, 2> modelPars;
+        std::copy_n(line["P1Parameters"].begin(), 2, modelPars.begin());
+
+        Identifier errorCalibId{};
+
+        //Using PCB 1 as default
+        errorCalibId = m_idHelperSvc->mmIdHelper().channelID(stationType, stationEta, stationPhi, multiLayer, gasGap, 1 );
+
+        Muon::mmCTPClusterCalibData::CTPParameters constants{std::move(modelPars)};
+
+        ATH_CHECK(ctpClusterCondData.storeConstants(errorCalibId, std::move(constants)));
+
+
+    }
+    return StatusCode::SUCCESS;
+}
diff --git a/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/components/MuonCondAlg_entries.cxx b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/components/MuonCondAlg_entries.cxx
index 477576bd32fcc2094122cbfad8d1b7608483a414..33ab0bebe095864475d3b7c2748c3efb77ec595b 100644
--- a/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/components/MuonCondAlg_entries.cxx
+++ b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondAlg/src/components/MuonCondAlg_entries.cxx
@@ -21,6 +21,7 @@
 #include "MuonCondAlg/MdtAsBuiltCondAlg.h"
 #include "MuonCondAlg/CscILinesCondAlg.h"
 #include "MuonCondAlg/sTGCAsBuiltCondAlg2.h"
+#include "MuonCondAlg/MmCTPCondDbAlg.h"
 
 
 DECLARE_COMPONENT(CscCondDbAlg)
@@ -43,3 +44,4 @@ DECLARE_COMPONENT(MdtAsBuiltCondAlg)
 DECLARE_COMPONENT(CscILinesCondAlg)
 DECLARE_COMPONENT(NswUncertDbAlg)
 DECLARE_COMPONENT(sTGCAsBuiltCondAlg2)
+DECLARE_COMPONENT(MmCTPCondDbAlg)
\ No newline at end of file
diff --git a/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondData/MuonCondData/mmCTPClusterCalibData.h b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondData/MuonCondData/mmCTPClusterCalibData.h
new file mode 100644
index 0000000000000000000000000000000000000000..74db8b46ff987c26fe3e5deb5fd1f12688ed7f6d
--- /dev/null
+++ b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondData/MuonCondData/mmCTPClusterCalibData.h
@@ -0,0 +1,53 @@
+/*
+  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef MUONCONDDATA_MMCTPCLUSTERCALIBDATA_H
+#define MUONCONDDATA_MMCTPCLUSTERCALIBDATA_H
+
+
+// Athena includes
+#include "AthenaKernel/CondCont.h" 
+#include "AthenaKernel/BaseInfo.h" 
+#include "AthenaBaseComps/AthMessaging.h"
+#include "MuonIdHelpers/IMuonIdHelperSvc.h"
+#include "GeoPrimitives/GeoPrimitives.h"
+#include "GeoModelUtilities/TransientConstSharedPtr.h"
+
+namespace Muon{
+class mmCTPClusterCalibData: public AthMessaging {
+    public:
+
+    class CTPParameters{
+        public:
+            CTPParameters() = default;
+            CTPParameters(std::array<double, 2>&& pars);
+            CTPParameters(CTPParameters&& other) noexcept : m_pars(std::move(other.m_pars)) {}
+            CTPParameters& operator=(CTPParameters&& other) = default;
+
+            const std::array<double, 2>& pars() const { return m_pars; }
+
+        private:       
+            std::array<double, 2> m_pars{0., 0.};
+
+    };
+
+        mmCTPClusterCalibData(const Muon::IMuonIdHelperSvc* idHelperSvc);
+         ~mmCTPClusterCalibData() = default;
+
+        StatusCode storeConstants(const Identifier& gasGapId, CTPParameters&& newConstants);
+        double getCTPCorrectedDriftVelocity(const Identifier& identifier, const double theta) const;
+    
+    private:
+        const Muon::IMuonIdHelperSvc* m_idHelperSvc{nullptr};
+        using parameterMap = std::unordered_map<Identifier, CTPParameters>; 
+        parameterMap m_database{};
+};
+
+} // end for Muon namespace
+
+
+CLASS_DEF( Muon::mmCTPClusterCalibData , 211195114 , 1 );
+CONDCONT_DEF( Muon::mmCTPClusterCalibData , 242286916 );
+
+#endif
diff --git a/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondData/src/mmCTPClusterCalibData.cxx b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondData/src/mmCTPClusterCalibData.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7bb93fbf51c3997da3880aec59fd6a333266132c
--- /dev/null
+++ b/MuonSpectrometer/MuonConditions/MuonCondGeneral/MuonCondData/src/mmCTPClusterCalibData.cxx
@@ -0,0 +1,75 @@
+#include "MuonCondData/mmCTPClusterCalibData.h"
+
+#include "GaudiKernel/SystemOfUnits.h"
+
+
+
+namespace Muon{
+
+mmCTPClusterCalibData::mmCTPClusterCalibData(const Muon::IMuonIdHelperSvc* idHelperSvc):
+    AthMessaging{"mmCTPClusterCalibData"},
+    m_idHelperSvc{idHelperSvc} {}
+
+mmCTPClusterCalibData::CTPParameters::CTPParameters(std::array<double, 2>&& pars): m_pars (std::move(pars)){}
+
+StatusCode mmCTPClusterCalibData::storeConstants(const Identifier& gasGapIdentifier,
+                                             CTPParameters&& newConstants) {    
+
+    if (m_database.find(gasGapIdentifier) != m_database.end()) {
+        ATH_MSG_ERROR("The drift velocity calibration constants for gas gap "<<m_idHelperSvc->toStringGasGap(gasGapIdentifier) <<" already exist. Overwriting is not allowed");
+        return StatusCode::FAILURE;
+    }
+
+    //Retrieve the calibration constants, need at least 2 parameters for currently implemented parametrisation
+    if (newConstants.pars().size() < 2) {
+        ATH_MSG_ERROR("The error calibration constants for stName:" << m_idHelperSvc->mmIdHelper().stationName(gasGapIdentifier) << 
+                                                           " stEta: " << m_idHelperSvc->mmIdHelper().stationEta(gasGapIdentifier)  << 
+                                                           " stPhi: " << m_idHelperSvc->mmIdHelper().stationPhi(gasGapIdentifier) << 
+                                                           " ml: "    << m_idHelperSvc->mmIdHelper().multilayer(gasGapIdentifier) << 
+                                                           " gg: "    << m_idHelperSvc->mmIdHelper().gasGap(gasGapIdentifier)  <<
+                                                           " are incomplete. 2 parameters are required!");
+
+        return StatusCode::FAILURE;
+    }
+
+
+    m_database[gasGapIdentifier] = std::move(newConstants);
+
+    return StatusCode::SUCCESS;
+}
+
+double mmCTPClusterCalibData::getCTPCorrectedDriftVelocity(const Identifier& gasGapIdentifier, const double theta) const {
+    //Identifier: There is no pcb segmentation for these corrections, stored with pcb = 1 as default! Therefore expects pcb = 1 
+    //Theta: Expected in degrees
+    //Drift velocity: will be fully taken from the parametrisation from calibration file 
+
+    //If not present in the map return original value
+    if( m_database.find(gasGapIdentifier) == m_database.end()) {
+        ATH_MSG_ERROR("There's no drift velocity calibration available for gasGap " << m_idHelperSvc->toStringGasGap(gasGapIdentifier)<< " size of the calib map is: " << m_database.size() );
+    }
+
+    ATH_MSG_VERBOSE( "Retriving drift velocity for stName" << m_idHelperSvc->toStringGasGap(gasGapIdentifier) );
+
+    //Conversions of incident angle
+    double trf_theta_in_degrees = (theta > 90) ? (180 - theta) : theta;
+    double trf_theta_in_radians = trf_theta_in_degrees * Gaudi::Units::deg;
+    double tan_theta            = std::tan(trf_theta_in_radians);
+
+    //Parametrisation was derived as delta_residual = x_true - x_charge_weighted = (time_true - time_charge_weighted) * v_drift * tan(theta)
+    //The fit used is a linear fit from data obtained for VMM 17 to VMM 95. Data outside this fit range were excluted due to problems with the magnetic field at the inner and outer PCBs, see figure 11.8 on page 135 of https://cds.cern.ch/record/2839930/files/CERN-THESIS-2021-354.pdf. Therefore, it is expected, that x^2 and x^3 are 0.
+    //Parametrisation from Stefanie Gotz and Fabian Vogel, for more details see:
+    //https://indico.cern.ch/event/1501492/contributions/6321472/attachments/3013528/5314031/SpatialResolution14.pdf#page=5
+    //https://indico.cern.ch/event/1458120/contributions/6138857/attachments/2942828/5170893/MuonWeekOctober24FV1.pdf
+    double vDrift = m_database.at(gasGapIdentifier).pars()[0] + ((m_database.at(gasGapIdentifier).pars()[1])*trf_theta_in_degrees); 
+    
+    //Remove tantheta and always return positive drift velocity!
+
+    vDrift =  (tan_theta != 0 ) ?   std::fabs(vDrift/tan_theta) : std::fabs(vDrift);
+
+
+    ATH_MSG_VERBOSE( "New drift velocity: " << vDrift  << " for theta: " << trf_theta_in_degrees << " degrees" );
+
+    return vDrift;
+}
+
+}//end for Muon namespace
diff --git a/MuonSpectrometer/MuonConfig/python/MuonCalibrationConfig.py b/MuonSpectrometer/MuonConfig/python/MuonCalibrationConfig.py
index f223b515396a58640af82545a0669d982d4b2670..567dc383d43bc099bf34cd6a3a66265c334e556a 100644
--- a/MuonSpectrometer/MuonConfig/python/MuonCalibrationConfig.py
+++ b/MuonSpectrometer/MuonConfig/python/MuonCalibrationConfig.py
@@ -198,3 +198,16 @@ def NswErrorCalibDbAlgCfg(flags, name = "NswErrorCalibDbAlg", **kwargs):
     the_alg = CompFactory.NswUncertDbAlg(name = name, **kwargs)
     result.addCondAlgo(the_alg, primary = True)
     return result
+
+def MmCTPCondDbAlgCfg(flags, name = "MmCTPCondDbAlg", **kwargs):
+    result = ComponentAccumulator()
+    if "readFromJSON" in kwargs:
+      kwargs.setdefault("ReadKey", "")
+    else:
+      from IOVDbSvc.IOVDbSvcConfig import addFolders
+      kwargs.setdefault("ReadKey", "/MDT/MM/CTPSLOPE")
+      result.merge(addFolders(flags, kwargs["ReadKey"], className='CondAttrListCollection', detDb="MDT_OFL", tag="MMCTPCorrections_toroidOn_v1" )) 
+
+    the_alg = CompFactory.MmCTPCondDbAlg(name = name, **kwargs)
+    result.addCondAlgo(the_alg, primary = True)
+    return result