Revert "[Camera] Migrate WebRTC Provider Cluster to Server Cluster Interface (#40432)" (#40490)

This reverts commit 75a6da17358225f0d0770956b1a1fcad3b0d36f2.
diff --git a/examples/camera-app/camera-common/BUILD.gn b/examples/camera-app/camera-common/BUILD.gn
index 058fec1..cadeabb 100644
--- a/examples/camera-app/camera-common/BUILD.gn
+++ b/examples/camera-app/camera-common/BUILD.gn
@@ -45,7 +45,6 @@
 
   deps = [
     "${chip_root}/examples/camera-app/camera-common",
-    "${chip_root}/src/app/clusters/webrtc-transport-provider-server",
     "${chip_root}/src/lib",
   ]
 }
diff --git a/examples/camera-app/camera-common/include/camera-app.h b/examples/camera-app/camera-common/include/camera-app.h
index 7465635..19a0978 100644
--- a/examples/camera-app/camera-common/include/camera-app.h
+++ b/examples/camera-app/camera-common/include/camera-app.h
@@ -23,7 +23,6 @@
 #include "camera-device-interface.h"
 #include <app/util/config.h>
 #include <cstring>
-#include <data-model-providers/codegen/CodegenDataModelProvider.h>
 #include <protocols/interaction_model/StatusCode.h>
 #include <utility>
 
@@ -38,15 +37,12 @@
     // Initialize all the camera device clusters.
     void InitCameraDeviceClusters();
 
-    void Shutdown();
-
 private:
     chip::EndpointId mEndpoint;
     CameraDeviceInterface * mCameraDevice;
 
     // SDK cluster servers
-    chip::app::LazyRegisteredServerCluster<chip::app::Clusters::WebRTCTransportProvider::WebRTCTransportProviderServer>
-        mWebRTCTransportProviderServer;
+    std::unique_ptr<chip::app::Clusters::WebRTCTransportProvider::WebRTCTransportProviderServer> mWebRTCTransportProviderPtr;
     std::unique_ptr<chip::app::Clusters::ChimeServer> mChimeServerPtr;
     std::unique_ptr<chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamMgmtServer> mAVStreamMgmtServerPtr;
     std::unique_ptr<chip::app::Clusters::CameraAvSettingsUserLevelManagement::CameraAvSettingsUserLevelMgmtServer>
diff --git a/examples/camera-app/camera-common/include/camera-device-interface.h b/examples/camera-app/camera-common/include/camera-device-interface.h
index 48ad7bf..5daccf5 100644
--- a/examples/camera-app/camera-common/include/camera-device-interface.h
+++ b/examples/camera-app/camera-common/include/camera-device-interface.h
@@ -22,7 +22,7 @@
 #include <app/clusters/camera-av-settings-user-level-management-server/camera-av-settings-user-level-management-server.h>
 #include <app/clusters/camera-av-stream-management-server/camera-av-stream-management-server.h>
 #include <app/clusters/chime-server/chime-server.h>
-#include <app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.h>
+#include <app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.h>
 #include <app/clusters/zone-management-server/zone-management-server.h>
 
 using chip::app::Clusters::CameraAvStreamManagement::AudioCapabilitiesStruct;
diff --git a/examples/camera-app/camera-common/src/camera-app.cpp b/examples/camera-app/camera-common/src/camera-app.cpp
index f8d3ffa..1f817e1 100644
--- a/examples/camera-app/camera-common/src/camera-app.cpp
+++ b/examples/camera-app/camera-common/src/camera-app.cpp
@@ -38,6 +38,10 @@
     // Instantiate Chime Server
     mChimeServerPtr = std::make_unique<ChimeServer>(mEndpoint, mCameraDevice->GetChimeDelegate());
 
+    // Instantiate WebRTCTransport Provider
+    mWebRTCTransportProviderPtr =
+        std::make_unique<WebRTCTransportProviderServer>(mCameraDevice->GetWebRTCProviderDelegate(), mEndpoint);
+
     // Fetch all initialization parameters for CameraAVStreamMgmt Server
     BitFlags<CameraAvStreamManagement::Feature> avsmFeatures;
     BitFlags<CameraAvStreamManagement::OptionalAttribute> avsmOptionalAttrs;
@@ -250,13 +254,7 @@
 void CameraApp::InitCameraDeviceClusters()
 {
     // Initialize Cluster Servers
-    mWebRTCTransportProviderServer.Create(mEndpoint, mCameraDevice->GetWebRTCProviderDelegate());
-    CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Register(mWebRTCTransportProviderServer.Registration());
-    if (err != CHIP_NO_ERROR)
-    {
-        ChipLogError(Camera, "Failed to register WebRTCTransportProvider on endpoint %u: %" CHIP_ERROR_FORMAT, mEndpoint,
-                     err.Format());
-    }
+    mWebRTCTransportProviderPtr->Init();
 
     mChimeServerPtr->Init();
 
@@ -267,16 +265,6 @@
     mZoneMgmtServerPtr->Init();
 }
 
-void CameraApp::Shutdown()
-{
-    CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Unregister(&mWebRTCTransportProviderServer.Cluster());
-    if (err != CHIP_NO_ERROR)
-    {
-        ChipLogError(Camera, "WebRTCTransportProvider unregister error: %" CHIP_ERROR_FORMAT, err.Format());
-    }
-    mWebRTCTransportProviderServer.Destroy();
-}
-
 static constexpr EndpointId kCameraEndpointId = 1;
 
 Platform::UniquePtr<CameraApp> gCameraApp;
@@ -292,8 +280,5 @@
 void CameraAppShutdown()
 {
     ChipLogDetail(Camera, "CameraAppShutdown: Shutting down Camera app");
-
-    gCameraApp.get()->Shutdown();
-
     gCameraApp = nullptr;
 }
diff --git a/examples/camera-app/linux/include/clusters/webrtc-provider/webrtc-provider-manager.h b/examples/camera-app/linux/include/clusters/webrtc-provider/webrtc-provider-manager.h
index a79d33e..91dec82 100644
--- a/examples/camera-app/linux/include/clusters/webrtc-provider/webrtc-provider-manager.h
+++ b/examples/camera-app/linux/include/clusters/webrtc-provider/webrtc-provider-manager.h
@@ -22,7 +22,7 @@
 #include "webrtc-abstract.h"
 #include <app-common/zap-generated/cluster-enums.h>
 #include <app/CASESessionManager.h>
-#include <app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.h>
+#include <app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.h>
 #include <media-controller.h>
 #include <webrtc-transport.h>
 
diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py
index 9f3c6f3..e4fec0c 100644
--- a/scripts/tools/check_includes_config.py
+++ b/scripts/tools/check_includes_config.py
@@ -138,7 +138,7 @@
     'src/app/clusters/media-playback-server/media-playback-delegate.h': {'list'},
     'src/app/clusters/target-navigator-server/target-navigator-delegate.h': {'list'},
     # WebRTCTransportProvider is for Camera and is intended to run on devices that are capable of handling these types.
-    'src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.h': {'string', 'vector'},
+    'src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.h': {'string', 'vector'},
     # Camera AV Stream Management and Camera AV Settings User Level Management clusters are expected to run on resource-capable devices
     'src/app/clusters/camera-av-stream-management-server/camera-av-stream-management-server.h': {'vector'},
     'src/app/clusters/camera-av-stream-management-server/camera-av-stream-management-server.cpp': {'set'},
diff --git a/src/app/clusters/webrtc-transport-provider-server/BUILD.gn b/src/app/clusters/webrtc-transport-provider-server/BUILD.gn
index 9cf08c7..1156f54 100644
--- a/src/app/clusters/webrtc-transport-provider-server/BUILD.gn
+++ b/src/app/clusters/webrtc-transport-provider-server/BUILD.gn
@@ -11,20 +11,5 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-import("//build_overrides/build.gni")
-import("//build_overrides/chip.gni")
-
-source_set("webrtc-transport-provider-server") {
-  sources = [
-    "webrtc-transport-provider-cluster.cpp",
-    "webrtc-transport-provider-cluster.h",
-  ]
-
-  public_deps = [
-    "${chip_root}/src/app:attribute-access",
-    "${chip_root}/src/app/data-model-provider:metadata",
-    "${chip_root}/src/app/server-cluster",
-    "${chip_root}/src/lib/core:error",
-    "${chip_root}/zzz_generated/app-common/clusters/WebRTCTransportProvider",
-  ]
+group("webrtc-transport-provider-server") {
 }
diff --git a/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.cmake b/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.cmake
index 2b7e3f9..f909053 100644
--- a/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.cmake
+++ b/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.cmake
@@ -12,10 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# These are the things that BUILD.gn dependencies would pull
+# This is the equivalent to app_config_dependent_sources.gni
 TARGET_SOURCES(
   ${APP_TARGET}
   PRIVATE
-    "${CLUSTER_DIR}/webrtc-transport-provider-cluster.cpp"
-    "${CLUSTER_DIR}/webrtc-transport-provider-cluster.h"
-)
+    "${CLUSTER_DIR}/webrtc-transport-provider-server.cpp"
+    "${CLUSTER_DIR}/webrtc-transport-provider-server.h"
+)
\ No newline at end of file
diff --git a/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.gni b/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.gni
index ef59f84..c2e0719 100644
--- a/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.gni
+++ b/src/app/clusters/webrtc-transport-provider-server/app_config_dependent_sources.gni
@@ -11,4 +11,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-app_config_dependent_sources = []
+app_config_dependent_sources = [
+  "webrtc-transport-provider-server.cpp",
+  "webrtc-transport-provider-server.h",
+]
diff --git a/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.cpp b/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.cpp
similarity index 65%
rename from src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.cpp
rename to src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.cpp
index f09fd97..3635f9b 100644
--- a/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.cpp
+++ b/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.cpp
@@ -15,38 +15,37 @@
  *    See the License for the specific language governing permissions and
  *    limitations under the License.
  */
-#include "webrtc-transport-provider-cluster.h"
+#include "webrtc-transport-provider-server.h"
+
+#include <protocols/interaction_model/StatusCode.h>
 
 #include <app-common/zap-generated/attributes/Accessors.h>
 #include <app-common/zap-generated/cluster-enums.h>
 #include <app-common/zap-generated/cluster-objects.h>
-#include <app/server-cluster/AttributeListBuilder.h>
+#include <app/AttributeAccessInterfaceRegistry.h>
+#include <app/CommandHandler.h>
+#include <app/CommandHandlerInterfaceRegistry.h>
+#include <app/EventLogging.h>
+#include <app/reporting/reporting.h>
 #include <platform/CHIPDeviceLayer.h>
 #include <platform/PlatformManager.h>
-#include <protocols/interaction_model/StatusCode.h>
 
 #include <iterator>
 #include <memory>
 
 using namespace chip;
 using namespace chip::app;
-using namespace chip::app::Clusters;
-using namespace chip::app::Clusters::WebRTCTransportProvider;
-using namespace chip::app::Clusters::WebRTCTransportProvider::Attributes;
-using namespace chip::Protocols::InteractionModel;
+using chip::Protocols::InteractionModel::Status;
+
+using ICEServerDecodableStruct = chip::app::Clusters::Globals::Structs::ICEServerStruct::DecodableType;
+using WebRTCSessionStruct      = chip::app::Clusters::Globals::Structs::WebRTCSessionStruct::Type;
+using ICECandidateStruct       = chip::app::Clusters::Globals::Structs::ICECandidateStruct::Type;
+using StreamUsageEnum          = chip::app::Clusters::Globals::StreamUsageEnum;
+using WebRTCEndReasonEnum      = chip::app::Clusters::Globals::WebRTCEndReasonEnum;
 
 namespace {
 
-constexpr uint16_t kMaxSessionId = 65534;
-
-constexpr DataModel::AcceptedCommandEntry kAcceptedCommands[] = {
-    Commands::SolicitOffer::kMetadataEntry,         Commands::ProvideOffer::kMetadataEntry, Commands::ProvideAnswer::kMetadataEntry,
-    Commands::ProvideICECandidates::kMetadataEntry, Commands::EndSession::kMetadataEntry,
-};
-
-constexpr DataModel::AttributeEntry kMandatoryAttributes[] = {
-    CurrentSessions::kMetadataEntry,
-};
+static constexpr uint16_t kMaxSessionId = 65534;
 
 NodeId GetNodeIdFromCtx(const CommandHandler & commandHandler)
 {
@@ -66,86 +65,90 @@
 namespace Clusters {
 namespace WebRTCTransportProvider {
 
-WebRTCTransportProviderServer::WebRTCTransportProviderServer(EndpointId endpointId, Delegate & delegate) :
-    DefaultServerCluster({ endpointId, Id }), mDelegate(delegate)
+WebRTCTransportProviderServer::WebRTCTransportProviderServer(Delegate & delegate, EndpointId endpointId) :
+    AttributeAccessInterface(MakeOptional(endpointId), WebRTCTransportProvider::Id),
+    CommandHandlerInterface(MakeOptional(endpointId), WebRTCTransportProvider::Id), mDelegate(delegate)
 {}
 
-DataModel::ActionReturnStatus WebRTCTransportProviderServer::ReadAttribute(const DataModel::ReadAttributeRequest & request,
-                                                                           AttributeValueEncoder & encoder)
+WebRTCTransportProviderServer::~WebRTCTransportProviderServer()
 {
-    switch (request.path.mAttributeId)
+    Shutdown();
+}
+
+CHIP_ERROR WebRTCTransportProviderServer::Init()
+{
+    ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this));
+    VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INCORRECT_STATE);
+
+    return CHIP_NO_ERROR;
+}
+
+void WebRTCTransportProviderServer::Shutdown()
+{
+    CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this);
+    AttributeAccessInterfaceRegistry::Instance().Unregister(this);
+}
+
+// AttributeAccessInterface
+CHIP_ERROR WebRTCTransportProviderServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
+{
+    // The only attribute from the spec is "CurrentSessions" (attribute ID 0x0000),
+    // which is a list[WebRTCSessionStruct].
+    if (aPath.mClusterId == Id && aPath.mAttributeId == Attributes::CurrentSessions::Id)
     {
-    case CurrentSessions::Id:
-        return encoder.EncodeList([this](const auto & listEncoder) -> CHIP_ERROR {
+        // We encode mCurrentSessions as a list of WebRTCSessionStruct
+        return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR {
             for (auto & session : mCurrentSessions)
             {
-                ReturnErrorOnFailure(listEncoder.Encode(session));
+                ReturnErrorOnFailure(encoder.Encode(session));
             }
             return CHIP_NO_ERROR;
         });
-    case ClusterRevision::Id:
-        return encoder.Encode(kRevision);
-    case FeatureMap::Id:
-        // TODO: Allow delegate to specify supported features
-        // Currently hardcoded to 0 (no features supported)
-        // METADATA feature (bit 0) should be configurable based on delegate capabilities
-        return encoder.Encode<uint32_t>(0);
-    default:
-        return Status::UnsupportedAttribute;
     }
+
+    // If not our attribute, let default logic handle
+    return CHIP_NO_ERROR;
 }
 
-std::optional<DataModel::ActionReturnStatus> WebRTCTransportProviderServer::InvokeCommand(const DataModel::InvokeRequest & request,
-                                                                                          TLV::TLVReader & input_arguments,
-                                                                                          CommandHandler * handler)
+// CommandHandlerInterface
+void WebRTCTransportProviderServer::InvokeCommand(HandlerContext & ctx)
 {
-    FabricIndex accessingFabricIndex = handler->GetAccessingFabricIndex();
+    ChipLogDetail(Zcl, "WebRTCTransportProvider: InvokeCommand called with CommandId=0x%08" PRIx32, ctx.mRequestPath.mCommandId);
 
-    switch (request.path.mCommandId)
+    switch (ctx.mRequestPath.mCommandId)
     {
-    case Commands::SolicitOffer::Id: {
-        Commands::SolicitOffer::DecodableType req;
-        ReturnErrorOnFailure(req.Decode(input_arguments, accessingFabricIndex));
-        return HandleSolicitOffer(*handler, req);
-    }
-    case Commands::ProvideOffer::Id: {
-        Commands::ProvideOffer::DecodableType req;
-        ReturnErrorOnFailure(req.Decode(input_arguments, accessingFabricIndex));
-        return HandleProvideOffer(*handler, req);
-    }
-    case Commands::ProvideAnswer::Id: {
-        Commands::ProvideAnswer::DecodableType req;
-        ReturnErrorOnFailure(req.Decode(input_arguments, accessingFabricIndex));
-        return HandleProvideAnswer(*handler, req);
-    }
-    case Commands::ProvideICECandidates::Id: {
-        Commands::ProvideICECandidates::DecodableType req;
-        ReturnErrorOnFailure(req.Decode(input_arguments, accessingFabricIndex));
-        return HandleProvideICECandidates(*handler, req);
-    }
-    case Commands::EndSession::Id: {
-        Commands::EndSession::DecodableType req;
-        ReturnErrorOnFailure(req.Decode(input_arguments, accessingFabricIndex));
-        return HandleEndSession(*handler, req);
-    }
+    case Commands::SolicitOffer::Id:
+        CommandHandlerInterface::HandleCommand<Commands::SolicitOffer::DecodableType>(
+            ctx, [this](HandlerContext & subCtx, const auto & req) { HandleSolicitOffer(subCtx, req); });
+        break;
+
+    case Commands::ProvideOffer::Id:
+        CommandHandlerInterface::HandleCommand<Commands::ProvideOffer::DecodableType>(
+            ctx, [this](HandlerContext & subCtx, const auto & req) { HandleProvideOffer(subCtx, req); });
+        break;
+
+    case Commands::ProvideAnswer::Id:
+        CommandHandlerInterface::HandleCommand<Commands::ProvideAnswer::DecodableType>(
+            ctx, [this](HandlerContext & subCtx, const auto & req) { HandleProvideAnswer(subCtx, req); });
+        break;
+
+    case Commands::ProvideICECandidates::Id:
+        CommandHandlerInterface::HandleCommand<Commands::ProvideICECandidates::DecodableType>(
+            ctx, [this](HandlerContext & subCtx, const auto & req) { HandleProvideICECandidates(subCtx, req); });
+        break;
+
+    case Commands::EndSession::Id:
+        CommandHandlerInterface::HandleCommand<Commands::EndSession::DecodableType>(
+            ctx, [this](HandlerContext & subCtx, const auto & req) { HandleEndSession(subCtx, req); });
+        break;
+
     default:
-        return Status::UnsupportedCommand;
+        // Mark unrecognized command as UnsupportedCommand
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::UnsupportedCommand);
+        break;
     }
 }
 
-CHIP_ERROR WebRTCTransportProviderServer::AcceptedCommands(const ConcreteClusterPath & path,
-                                                           ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder)
-{
-    return builder.ReferenceExisting(kAcceptedCommands);
-}
-
-CHIP_ERROR WebRTCTransportProviderServer::Attributes(const ConcreteClusterPath & path,
-                                                     ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder)
-{
-    AttributeListBuilder listBuilder(builder);
-    return listBuilder.Append(Span(kMandatoryAttributes), Span<AttributeListBuilder::OptionalAttributeEntry>());
-}
-
 // Helper functions
 WebRTCSessionStruct * WebRTCTransportProviderServer::FindSession(uint16_t sessionId)
 {
@@ -177,7 +180,8 @@
         result = UpsertResultEnum::kInserted;
     }
 
-    NotifyAttributeChanged(Attributes::CurrentSessions::Id);
+    MatterReportingAttributeChangeCallback(AttributeAccessInterface::GetEndpointId().Value(), WebRTCTransportProvider::Id,
+                                           WebRTCTransportProvider::Attributes::CurrentSessions::Id);
 
     return result;
 }
@@ -194,12 +198,13 @@
     // If a session was removed, the size will be smaller.
     if (mCurrentSessions.size() < originalSize)
     {
-        NotifyAttributeChanged(Attributes::CurrentSessions::Id);
+        // Notify the stack that the CurrentSessions attribute has changed.
+        MatterReportingAttributeChangeCallback(AttributeAccessInterface::GetEndpointId().Value(), WebRTCTransportProvider::Id,
+                                               WebRTCTransportProvider::Attributes::CurrentSessions::Id);
     }
 }
 
-WebRTCSessionStruct * WebRTCTransportProviderServer::CheckForMatchingSession(const CommandHandler & commandHandler,
-                                                                             uint16_t sessionId)
+WebRTCSessionStruct * WebRTCTransportProviderServer::CheckForMatchingSession(HandlerContext & ctx, uint16_t sessionId)
 {
     WebRTCSessionStruct * session = FindSession(sessionId);
     if (session == nullptr)
@@ -207,8 +212,8 @@
         return nullptr;
     }
 
-    NodeId peerNodeId           = GetNodeIdFromCtx(commandHandler);
-    FabricIndex peerFabricIndex = commandHandler.GetAccessingFabricIndex();
+    NodeId peerNodeId           = GetNodeIdFromCtx(ctx.mCommandHandler);
+    FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex();
 
     // Ensure the session’s peer matches the current command invoker
     if (peerNodeId != session->peerNodeID || peerFabricIndex != session->GetFabricIndex())
@@ -221,62 +226,57 @@
 
 uint16_t WebRTCTransportProviderServer::GenerateSessionId()
 {
-    static uint16_t lastSessionId = 0;
-    uint16_t candidateId          = 0;
+    static uint16_t lastSessionId = 1;
 
-    // Try at most kMaxSessionId+1 attempts to find a free ID
-    // This ensures we never loop infinitely even if all IDs are somehow in use
-    for (uint16_t attempts = 0; attempts <= kMaxSessionId; attempts++)
+    do
     {
-        candidateId = lastSessionId++;
+        uint16_t candidateId = lastSessionId++;
 
         // Handle wrap-around per spec
         if (lastSessionId > kMaxSessionId)
         {
-            lastSessionId = 0;
+            lastSessionId = 1;
         }
 
         if (FindSession(candidateId) == nullptr)
         {
             return candidateId;
         }
-    }
-
-    // This should never happen in practice since we support 65534 sessions
-    // and typical applications will have far fewer active sessions
-    ChipLogError(Zcl, "All session IDs are in use!");
-    chipDie();
+    } while (true);
 }
 
 // Command Handlers
-DataModel::ActionReturnStatus WebRTCTransportProviderServer::HandleSolicitOffer(CommandHandler & commandHandler,
-                                                                                const Commands::SolicitOffer::DecodableType & req)
+void WebRTCTransportProviderServer::HandleSolicitOffer(HandlerContext & ctx, const Commands::SolicitOffer::DecodableType & req)
 {
     // Validate the streamUsage field against the allowed enum values.
     if (req.streamUsage == StreamUsageEnum::kUnknownEnumValue)
     {
         ChipLogError(Zcl, "HandleSolicitOffer: Invalid streamUsage value %u.", to_underlying(req.streamUsage));
-        return Status::ConstraintError;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError);
+        return;
     }
 
     bool privacyModeActive = false;
     if (mDelegate.IsPrivacyModeActive(privacyModeActive) != CHIP_NO_ERROR)
     {
         ChipLogError(Zcl, "HandleSolicitOffer: Cannot determine privacy mode state");
-        return Status::InvalidInState;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+        return;
     }
 
     if (privacyModeActive)
     {
         ChipLogError(Zcl, "HandleSolicitOffer: Privacy mode is enabled");
-        return Status::InvalidInState;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+        return;
     }
 
     // At least one of Video Stream ID and Audio Stream ID has to be present
     if (!req.videoStreamID.HasValue() && !req.audioStreamID.HasValue())
     {
         ChipLogError(Zcl, "HandleSolicitOffer: one of VideoStreamID or AudioStreamID must be present");
-        return Status::InvalidCommand;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand);
+        return;
     }
 
     // Validate VideoStreamID against AllocatedVideoStreams.
@@ -290,7 +290,8 @@
             if (!mDelegate.HasAllocatedVideoStreams())
             {
                 ChipLogError(Zcl, "HandleSolicitOffer: video requested when there are no AllocatedVideoStreams");
-                return Status::InvalidInState;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+                return;
             }
         }
         else
@@ -300,7 +301,8 @@
             {
                 ChipLogError(Zcl, "HandleSolicitOffer: VideoStreamID %u does not match AllocatedVideoStreams",
                              req.videoStreamID.Value().Value());
-                return Status::DynamicConstraintError;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::DynamicConstraintError);
+                return;
             }
         }
     }
@@ -316,7 +318,8 @@
             if (!mDelegate.HasAllocatedAudioStreams())
             {
                 ChipLogError(Zcl, "HandleSolicitOffer: audio requested when there are no AllocatedAudioStreams");
-                return Status::InvalidInState;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+                return;
             }
         }
         else
@@ -326,7 +329,8 @@
             {
                 ChipLogError(Zcl, "HandleSolicitOffer: AudioStreamID %u does not match AllocatedAudioStreams",
                              req.audioStreamID.Value().Value());
-                return Status::DynamicConstraintError;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::DynamicConstraintError);
+                return;
             }
         }
     }
@@ -337,8 +341,8 @@
     args.streamUsage           = req.streamUsage;
     args.videoStreamId         = req.videoStreamID;
     args.audioStreamId         = req.audioStreamID;
-    args.peerNodeId            = GetNodeIdFromCtx(commandHandler);
-    args.fabricIndex           = commandHandler.GetAccessingFabricIndex();
+    args.peerNodeId            = GetNodeIdFromCtx(ctx.mCommandHandler);
+    args.fabricIndex           = ctx.mCommandHandler.GetAccessingFabricIndex();
     args.originatingEndpointId = req.originatingEndpointID;
 
     if (req.ICEServers.HasValue())
@@ -357,7 +361,8 @@
         if (listErr != CHIP_NO_ERROR)
         {
             ChipLogError(Zcl, "HandleSolicitOffer: ICECandidates list error: %" CHIP_ERROR_FORMAT, listErr.Format());
-            return Status::InvalidCommand;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand);
+            return;
         }
 
         args.iceServers.SetValue(std::move(localIceServers));
@@ -378,10 +383,11 @@
     WebRTCSessionStruct outSession;
     bool deferredOffer = false;
 
-    auto status = ClusterStatusCode(mDelegate.HandleSolicitOffer(args, outSession, deferredOffer));
+    auto status = Protocols::InteractionModel::ClusterStatusCode(mDelegate.HandleSolicitOffer(args, outSession, deferredOffer));
     if (!status.IsSuccess())
     {
-        return status;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status);
+        return;
     }
 
     // Store or update the session.
@@ -411,21 +417,17 @@
         resp.audioStreamID.SetValue(outSession.audioStreamID);
     }
 
-    ConcreteCommandPath requestPath(mPath.mEndpointId, Id, Commands::SolicitOffer::Id);
-    commandHandler.AddResponse(requestPath, resp);
-
-    return Status::Success;
+    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, resp);
 }
 
-DataModel::ActionReturnStatus WebRTCTransportProviderServer::HandleProvideOffer(CommandHandler & commandHandler,
-                                                                                const Commands::ProvideOffer::DecodableType & req)
+void WebRTCTransportProviderServer::HandleProvideOffer(HandlerContext & ctx, const Commands::ProvideOffer::DecodableType & req)
 {
     auto webRTCSessionID = req.webRTCSessionID;
     auto videoStreamID   = req.videoStreamID;
     auto audioStreamID   = req.audioStreamID;
 
-    NodeId peerNodeId           = GetNodeIdFromCtx(commandHandler);
-    FabricIndex peerFabricIndex = commandHandler.GetAccessingFabricIndex();
+    NodeId peerNodeId           = GetNodeIdFromCtx(ctx.mCommandHandler);
+    FabricIndex peerFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex();
 
     WebRTCSessionStruct outSession;
 
@@ -436,17 +438,19 @@
     if (req.streamUsage == StreamUsageEnum::kUnknownEnumValue)
     {
         ChipLogError(Zcl, "HandleProvideOffer: Invalid streamUsage value %u.", to_underlying(req.streamUsage));
-        return Status::ConstraintError;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError);
+        return;
     }
 
     // If WebRTCSessionID is not null and does not match a value in CurrentSessions: Respond with NOT_FOUND.
     if (!webRTCSessionID.IsNull())
     {
         uint16_t sessionId                    = webRTCSessionID.Value();
-        WebRTCSessionStruct * existingSession = CheckForMatchingSession(commandHandler, sessionId);
+        WebRTCSessionStruct * existingSession = CheckForMatchingSession(ctx, sessionId);
         if (existingSession == nullptr)
         {
-            return Status::NotFound;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound);
+            return;
         }
 
         // Use the existing session for further processing (re-offer case).
@@ -464,20 +468,23 @@
         if (mDelegate.IsPrivacyModeActive(privacyModeActive) != CHIP_NO_ERROR)
         {
             ChipLogError(Zcl, "HandleProvideOffer: Cannot determine privacy mode state");
-            return Status::InvalidInState;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+            return;
         }
 
         if (privacyModeActive)
         {
             ChipLogError(Zcl, "HandleProvideOffer: Privacy mode is enabled");
-            return Status::InvalidInState;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+            return;
         }
 
         // At least one of Video Stream ID and Audio Stream ID has to be present
         if (!req.videoStreamID.HasValue() && !req.audioStreamID.HasValue())
         {
             ChipLogError(Zcl, "HandleProvideOffer: one of VideoStreamID or AudioStreamID must be present");
-            return Status::InvalidCommand;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand);
+            return;
         }
 
         // Validate VideoStreamID against AllocatedVideoStreams.
@@ -489,7 +496,8 @@
             {
                 ChipLogError(Zcl, "HandleProvideOffer: VideoStreamID %u does not match AllocatedVideoStreams",
                              videoStreamID.Value().Value());
-                return Status::DynamicConstraintError;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::DynamicConstraintError);
+                return;
             }
         }
         else if (videoStreamID.HasValue() && videoStreamID.Value().IsNull())
@@ -499,7 +507,8 @@
             if (!mDelegate.HasAllocatedVideoStreams())
             {
                 ChipLogError(Zcl, "HandleProvideOffer: No video streams currently allocated");
-                return Status::InvalidInState;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+                return;
             }
             // Automatic selection will be handled by the delegate in HandleProvideOffer.
         }
@@ -511,7 +520,8 @@
             {
                 ChipLogError(Zcl, "HandleProvideOffer: AudioStreamID %u does not match AllocatedAudioStreams",
                              audioStreamID.Value().Value());
-                return Status::DynamicConstraintError;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::DynamicConstraintError);
+                return;
             }
         }
         else if (audioStreamID.HasValue() && audioStreamID.Value().IsNull())
@@ -521,7 +531,8 @@
             if (!mDelegate.HasAllocatedAudioStreams())
             {
                 ChipLogError(Zcl, "HandleProvideOffer: No audio streams currently allocated");
-                return Status::InvalidInState;
+                ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidInState);
+                return;
             }
             // Automatic selection will be handled by the delegate in HandleProvideOffer.
         }
@@ -531,7 +542,8 @@
         if (err != CHIP_NO_ERROR)
         {
             ChipLogError(Zcl, "HandleProvideOffer: Cannot meet resource management conditions");
-            return Status::ResourceExhausted;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ResourceExhausted);
+            return;
         }
 
         // Generate new sessiond id
@@ -561,7 +573,8 @@
         if (listErr != CHIP_NO_ERROR)
         {
             ChipLogError(Zcl, "HandleProvideOffer: ICEServers list error: %" CHIP_ERROR_FORMAT, listErr.Format());
-            return Status::InvalidCommand;
+            ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand);
+            return;
         }
 
         args.iceServers.SetValue(std::move(localIceServers));
@@ -574,10 +587,11 @@
     }
 
     // Delegate processing: process the SDP offer, create session, increment reference counts.
-    auto status = Protocols::InteractionModel::ClusterStatusCode(mDelegate.HandleProvideOffer(args, outSession));
-    if (!status.IsSuccess())
+    auto delegateStatus = Protocols::InteractionModel::ClusterStatusCode(mDelegate.HandleProvideOffer(args, outSession));
+    if (!delegateStatus.IsSuccess())
     {
-        return status;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, delegateStatus);
+        return;
     }
 
     // Update/Insert the WebRTCSessionStruct in CurrentSessions.
@@ -599,32 +613,31 @@
         resp.audioStreamID.SetValue(outSession.audioStreamID);
     }
 
-    ConcreteCommandPath requestPath(mPath.mEndpointId, Id, Commands::ProvideOffer::Id);
-    commandHandler.AddResponse(requestPath, resp);
-
-    return Status::Success;
+    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, resp);
 }
 
-DataModel::ActionReturnStatus WebRTCTransportProviderServer::HandleProvideAnswer(CommandHandler & commandHandler,
-                                                                                 const Commands::ProvideAnswer::DecodableType & req)
+void WebRTCTransportProviderServer::HandleProvideAnswer(HandlerContext & ctx, const Commands::ProvideAnswer::DecodableType & req)
 {
+    // Extract command fields from the request.
     uint16_t sessionId = req.webRTCSessionID;
     auto sdpSpan       = req.sdp;
 
-    WebRTCSessionStruct * existingSession = CheckForMatchingSession(commandHandler, sessionId);
+    WebRTCSessionStruct * existingSession = CheckForMatchingSession(ctx, sessionId);
     if (existingSession == nullptr)
     {
-        return Status::NotFound;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound);
+        return;
     }
 
     std::string sdpAnswer(sdpSpan.data(), sdpSpan.size());
-    return mDelegate.HandleProvideAnswer(sessionId, sdpAnswer);
+    ctx.mCommandHandler.AddStatus(
+        ctx.mRequestPath, Protocols::InteractionModel::ClusterStatusCode(mDelegate.HandleProvideAnswer(sessionId, sdpAnswer)));
 }
 
-DataModel::ActionReturnStatus
-WebRTCTransportProviderServer::HandleProvideICECandidates(CommandHandler & commandHandler,
-                                                          const Commands::ProvideICECandidates::DecodableType & req)
+void WebRTCTransportProviderServer::HandleProvideICECandidates(HandlerContext & ctx,
+                                                               const Commands::ProvideICECandidates::DecodableType & req)
 {
+    // Extract command fields from the request.
     uint16_t sessionId = req.webRTCSessionID;
 
     std::vector<ICECandidateStruct> candidates;
@@ -641,21 +654,24 @@
     if (listErr != CHIP_NO_ERROR)
     {
         ChipLogError(Zcl, "HandleProvideICECandidates: ICECandidates list error: %" CHIP_ERROR_FORMAT, listErr.Format());
-        return Status::InvalidCommand;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand);
+        return;
     }
 
-    WebRTCSessionStruct * existingSession = CheckForMatchingSession(commandHandler, sessionId);
+    WebRTCSessionStruct * existingSession = CheckForMatchingSession(ctx, sessionId);
     if (existingSession == nullptr)
     {
-        return Status::NotFound;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound);
+        return;
     }
 
     // Delegate the handling of ICE candidates.
-    return mDelegate.HandleProvideICECandidates(sessionId, candidates);
+    ctx.mCommandHandler.AddStatus(
+        ctx.mRequestPath,
+        Protocols::InteractionModel::ClusterStatusCode(mDelegate.HandleProvideICECandidates(sessionId, candidates)));
 }
 
-DataModel::ActionReturnStatus WebRTCTransportProviderServer::HandleEndSession(CommandHandler & commandHandler,
-                                                                              const Commands::EndSession::DecodableType & req)
+void WebRTCTransportProviderServer::HandleEndSession(HandlerContext & ctx, const Commands::EndSession::DecodableType & req)
 {
     // Extract command fields from the request.
     uint16_t sessionId = req.webRTCSessionID;
@@ -665,22 +681,24 @@
     if (reason == WebRTCEndReasonEnum::kUnknownEnumValue)
     {
         ChipLogError(Zcl, "HandleEndSession: Invalid reason value %u.", to_underlying(reason));
-        return Status::ConstraintError;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::ConstraintError);
+        return;
     }
 
-    WebRTCSessionStruct * existingSession = CheckForMatchingSession(commandHandler, sessionId);
+    WebRTCSessionStruct * existingSession = CheckForMatchingSession(ctx, sessionId);
     if (existingSession == nullptr)
     {
-        return Status::NotFound;
+        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::NotFound);
+        return;
     }
 
     // Delegate handles decrementing reference counts on video/audio streams if applicable.
     CHIP_ERROR err = mDelegate.HandleEndSession(sessionId, reason, existingSession->videoStreamID, existingSession->audioStreamID);
 
+    ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::ClusterStatusCode(err));
+
     // Remove the session entry from CurrentSessions.
     RemoveSession(sessionId);
-
-    return err;
 }
 
 } // namespace WebRTCTransportProvider
diff --git a/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.h b/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.h
similarity index 86%
rename from src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.h
rename to src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.h
index 9502e4d..e9f549f 100644
--- a/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-cluster.h
+++ b/src/app/clusters/webrtc-transport-provider-server/webrtc-transport-provider-server.h
@@ -17,11 +17,9 @@
  */
 #pragma once
 
-#include <app/server-cluster/DefaultServerCluster.h>
-#include <clusters/WebRTCTransportProvider/ClusterId.h>
-#include <clusters/WebRTCTransportProvider/Commands.h>
-#include <clusters/WebRTCTransportProvider/Enums.h>
-#include <clusters/WebRTCTransportProvider/Metadata.h>
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/AttributeAccessInterface.h>
+#include <app/CommandHandlerInterface.h>
 #include <string>
 #include <vector>
 
@@ -276,29 +274,39 @@
     virtual bool HasAllocatedAudioStreams() = 0;
 };
 
-class WebRTCTransportProviderServer : public DefaultServerCluster
+class WebRTCTransportProviderServer : public AttributeAccessInterface, public CommandHandlerInterface
 {
 public:
     /**
      * @brief
      *   Constructs the WebRTCTransportProviderServer with the specified delegate and endpoint.
      *
-     * @param[in] endpointId The Endpoint where the WebRTC Transport Provider cluster is published.
      * @param[in] delegate   A reference to an implementation of the Delegate interface. Must remain
      *                       valid for the lifetime of this object.
+     * @param[in] endpointId The Endpoint where the WebRTC Transport Provider cluster is published.
      */
-    WebRTCTransportProviderServer(EndpointId endpointId, Delegate & delegate);
+    WebRTCTransportProviderServer(Delegate & delegate, EndpointId endpointId);
 
-    DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
-                                                AttributeValueEncoder & encoder) override;
+    /**
+     * @brief
+     *   Destructor. Cleans up any internal data, but does not destroy the delegate.
+     */
+    ~WebRTCTransportProviderServer() override;
 
-    std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
-                                                               TLV::TLVReader & input_arguments, CommandHandler * handler) override;
+    /**
+     * @brief
+     *   Registers the command handler and attribute interface with the Matter Stack.
+     * @return CHIP_ERROR
+     *   - CHIP_NO_ERROR on successful registration.
+     *   - Other CHIP_ERROR codes if registration fails.
+     */
+    CHIP_ERROR Init();
 
-    CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
-                                ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder) override;
-
-    CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
+    /**
+     * @brief
+     *   Unregisters the command handler and attribute interface, releasing resources.
+     */
+    void Shutdown();
 
     /**
      * @brief Get a reference to the current WebRTC sessions.
@@ -317,27 +325,25 @@
         kUpdated  = 0x01,
     };
 
-    Delegate & mDelegate;
-    std::vector<WebRTCSessionStruct> mCurrentSessions;
+    CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
+    void InvokeCommand(HandlerContext & ctx) override;
 
     // Helper functions
     WebRTCSessionStruct * FindSession(uint16_t sessionId);
-    WebRTCSessionStruct * CheckForMatchingSession(const CommandHandler & commandHandler, uint16_t sessionId);
+    WebRTCSessionStruct * CheckForMatchingSession(HandlerContext & ctx, uint16_t sessionId);
     UpsertResultEnum UpsertSession(const WebRTCSessionStruct & session);
     void RemoveSession(uint16_t sessionId);
     uint16_t GenerateSessionId();
 
     // Command Handlers
-    DataModel::ActionReturnStatus HandleSolicitOffer(CommandHandler & commandHandler,
-                                                     const Commands::SolicitOffer::DecodableType & req);
-    DataModel::ActionReturnStatus HandleProvideOffer(CommandHandler & commandHandler,
-                                                     const Commands::ProvideOffer::DecodableType & req);
-    DataModel::ActionReturnStatus HandleProvideAnswer(CommandHandler & commandHandler,
-                                                      const Commands::ProvideAnswer::DecodableType & req);
-    DataModel::ActionReturnStatus HandleProvideICECandidates(CommandHandler & commandHandler,
-                                                             const Commands::ProvideICECandidates::DecodableType & req);
-    DataModel::ActionReturnStatus HandleEndSession(CommandHandler & commandHandler,
-                                                   const Commands::EndSession::DecodableType & req);
+    void HandleSolicitOffer(HandlerContext & ctx, const Commands::SolicitOffer::DecodableType & req);
+    void HandleProvideOffer(HandlerContext & ctx, const Commands::ProvideOffer::DecodableType & req);
+    void HandleProvideAnswer(HandlerContext & ctx, const Commands::ProvideAnswer::DecodableType & req);
+    void HandleProvideICECandidates(HandlerContext & ctx, const Commands::ProvideICECandidates::DecodableType & req);
+    void HandleEndSession(HandlerContext & ctx, const Commands::EndSession::DecodableType & req);
+
+    Delegate & mDelegate;
+    std::vector<WebRTCSessionStruct> mCurrentSessions;
 };
 
 } // namespace WebRTCTransportProvider
diff --git a/src/python_testing/TC_WEBRTCP_2_1.py b/src/python_testing/TC_WEBRTCP_2_1.py
index 5e945cd..89067f1 100644
--- a/src/python_testing/TC_WEBRTCP_2_1.py
+++ b/src/python_testing/TC_WEBRTCP_2_1.py
@@ -162,7 +162,7 @@
         resp = await self.send_single_cmd(cmd=cmd, endpoint=endpoint, payloadCapability=ChipDeviceCtrl.TransportPayloadCapability.LARGE_PAYLOAD)
         asserts.assert_equal(type(resp), Clusters.WebRTCTransportProvider.Commands.SolicitOfferResponse,
                              "Incorrect response type")
-        asserts.assert_equal(resp.webRTCSessionID, 0, "webrtcSessionID in SolicitOfferResponse should be 0.")
+        asserts.assert_not_equal(resp.webRTCSessionID, 0, "webrtcSessionID in SolicitOfferResponse should not be 0.")
         asserts.assert_true(resp.deferredOffer, "Expected 'deferredOffer' to be True.")
 
         self.step(7)
diff --git a/src/python_testing/TC_WEBRTCP_2_2.py b/src/python_testing/TC_WEBRTCP_2_2.py
index 517c5dd..5457c8c 100644
--- a/src/python_testing/TC_WEBRTCP_2_2.py
+++ b/src/python_testing/TC_WEBRTCP_2_2.py
@@ -108,7 +108,7 @@
         resp = await self.send_single_cmd(cmd=cmd, endpoint=endpoint, payloadCapability=ChipDeviceCtrl.TransportPayloadCapability.LARGE_PAYLOAD)
         asserts.assert_equal(type(resp), Clusters.WebRTCTransportProvider.Commands.SolicitOfferResponse,
                              "Incorrect response type")
-        asserts.assert_equal(resp.webRTCSessionID, 0, "webrtcSessionID in SolicitOfferResponse should be 0.")
+        asserts.assert_not_equal(resp.webRTCSessionID, 0, "webrtcSessionID in SolicitOfferResponse should not be 0.")
         asserts.assert_false(resp.deferredOffer, "Expected 'deferredOffer' to be False.")
 
         # TODO: Enable this check after integrating with Camera AvStreamManager
diff --git a/src/python_testing/TC_WEBRTCP_2_3.py b/src/python_testing/TC_WEBRTCP_2_3.py
index 76b7d9a..346d701 100644
--- a/src/python_testing/TC_WEBRTCP_2_3.py
+++ b/src/python_testing/TC_WEBRTCP_2_3.py
@@ -213,7 +213,7 @@
         resp = await self.send_single_cmd(cmd=cmd, endpoint=endpoint, payloadCapability=ChipDeviceCtrl.TransportPayloadCapability.LARGE_PAYLOAD)
         asserts.assert_equal(type(resp), Clusters.WebRTCTransportProvider.Commands.ProvideOfferResponse,
                              "Incorrect response type")
-        asserts.assert_equal(resp.webRTCSessionID, 0, "webRTCSessionID in ProvideOfferResponse should be 0.")
+        asserts.assert_not_equal(resp.webRTCSessionID, 0, "webRTCSessionID in ProvideOfferResponse should not be 0.")
 
         self.step(9)
         # Verify CurrentSessions contains the new session
diff --git a/src/python_testing/TC_WEBRTCP_2_4.py b/src/python_testing/TC_WEBRTCP_2_4.py
index 65c5a75..1a71305 100644
--- a/src/python_testing/TC_WEBRTCP_2_4.py
+++ b/src/python_testing/TC_WEBRTCP_2_4.py
@@ -158,8 +158,8 @@
         saved_session_id = resp.webRTCSessionID
         asserts.assert_equal(videoStreamID, resp.videoStreamID, "Video stream ID does not match that in the command")
         asserts.assert_equal(audioStreamID, resp.audioStreamID, "Audio stream ID does not match that in the command")
-        asserts.assert_equal(
-            saved_session_id, 0, "First allocated WebRTCSessionID must be zero"
+        asserts.assert_not_equal(
+            saved_session_id, 0, "Allocated WebRTCSessionID must be non‑zero"
         )
 
         self.step(5)
diff --git a/src/python_testing/TC_WEBRTCP_2_5.py b/src/python_testing/TC_WEBRTCP_2_5.py
index b12f539..510df6c 100644
--- a/src/python_testing/TC_WEBRTCP_2_5.py
+++ b/src/python_testing/TC_WEBRTCP_2_5.py
@@ -146,7 +146,7 @@
         resp = await self.send_single_cmd(cmd=cmd, endpoint=endpoint, payloadCapability=ChipDeviceCtrl.TransportPayloadCapability.LARGE_PAYLOAD)
         asserts.assert_equal(type(resp), Clusters.WebRTCTransportProvider.Commands.SolicitOfferResponse,
                              "Incorrect response type")
-        asserts.assert_equal(resp.webRTCSessionID, 0, "webrtcSessionID in SolicitOfferResponse should be 0.")
+        asserts.assert_not_equal(resp.webRTCSessionID, 0, "webrtcSessionID in SolicitOfferResponse should not be 0.")
         asserts.assert_false(resp.deferredOffer, "Expected 'deferredOffer' to be False.")
 
         # TODO: Enable this check after integrating with Camera AvStreamManager