mctp: filter endpoints by message type

Add a filter to MctpUtil to only care about endpoints that support a
specific message type (e.g., type 4 for NVMe MI).

We assume the mctpd service name is au.com.codeconstruct.MCTP1 and use
it directly to query the supported message types of an endpoint.

A new unit test is added to validate that unsupported endpoints are
correctly filtered.

Misc:
- Define MctpMessageType enum for readable message type passing.
- Update mock server to support D-Bus properties and custom message
types.

Tested: All 10 tests passed in Docker environment.

Change-Id: I452cab692640d55efa71c2ee81058c7a51fb33f3
Google-Bug-Id: 490106522
Signed-off-by: Hao Jiang <jianghao@google.com>
diff --git a/src/MctpUtil.cpp b/src/MctpUtil.cpp
index 8d1e82c..b02f844 100644
--- a/src/MctpUtil.cpp
+++ b/src/MctpUtil.cpp
@@ -2,21 +2,18 @@
 
 #include "absl/strings/match.h"
 
-#include "Utils.hpp"
-#include "VariantVisitors.hpp"
-
 #include <boost/asio/steady_timer.hpp>
 #include <boost/container/flat_map.hpp>
 #include <phosphor-logging/lg2.hpp>
 #include <sdbusplus/bus/match.hpp>
 
 #include <chrono>
+#include <cstdint>
 #include <filesystem>
-#include <string>
 
 std::map<std::string, SensorData> mctpEndpointConfigMap;
 
-enum class State : uint8_t
+enum class State : std::uint8_t
 {
     None,
     Add,
@@ -33,6 +30,7 @@
 };
 
 static std::map<std::string, EndpointState> endpointStates;
+static uint8_t filterMsgType = 0;
 
 static std::unique_ptr<sdbusplus::bus::match_t> associationMatch = nullptr;
 static std::unique_ptr<sdbusplus::bus::match_t> associationRemoveMatch =
@@ -41,7 +39,7 @@
 static void performEmConfigQuery(
     const std::shared_ptr<sdbusplus::asio::connection>& conn,
     const std::string& endpointPath, const std::string& emConfigPath,
-    bool bypassCooldown = false)
+    bool bypassCooldown = false, std::function<void()> cleanup = nullptr)
 {
     auto& state = endpointStates[endpointPath];
     auto now = std::chrono::steady_clock::now();
@@ -63,77 +61,159 @@
         timer->expires_after(delay);
 
         timer->async_wait([conn, endpointPath, emConfigPath,
-                           timer](const boost::system::error_code& ec) {
+                           cleanup = std::move(cleanup),
+                           timer](const boost::system::error_code& ec) mutable {
             if (ec)
             {
                 return;
             }
-            performEmConfigQuery(conn, endpointPath, emConfigPath);
+            performEmConfigQuery(conn, endpointPath, emConfigPath, false,
+                                 std::move(cleanup));
         });
         return;
     }
-
+    // Step 1: Query SupportedMessageTypes directly from mctpd
     conn->async_method_call(
-        [conn, endpointPath, emConfigPath](const boost::system::error_code& ec,
-                                           const ManagedObjectType& objects) {
+        [conn, endpointPath, emConfigPath, cleanup = std::move(cleanup)](
+            const boost::system::error_code& ec,
+            const std::variant<std::vector<uint8_t>>& value) mutable {
         auto& state = endpointStates[endpointPath];
-        state.querying = false;
-
-        state.lastQueryTime = std::chrono::steady_clock::now(); // Update time!
-
-        State lastState = state.state;
-        state.state = State::None; // Reset for next query
-
-        if (lastState == State::Remove)
+        if (ec)
         {
-            lg2::info("Ignoring query reply for removed endpoint {ENDPOINT}",
-                      "ENDPOINT", endpointPath);
-            auto it = mctpEndpointConfigMap.find(endpointPath);
-            if (it != mctpEndpointConfigMap.end())
+            lg2::error(
+                "Failed to get SupportedMessageTypes for {PATH}: {ERROR}",
+                "PATH", endpointPath, "ERROR", ec.message());
+            if (cleanup)
             {
-                mctpEndpointConfigMap.erase(it);
+                cleanup();
+            }
+            else
+            {
+                state.querying = false;
             }
             return;
         }
 
-        if (ec)
+        const auto* typesPtr = std::get_if<std::vector<uint8_t>>(&value);
+        if (!typesPtr)
         {
-            lg2::error("Failed to get managed objects for {PATH}: {ERROR}",
-                       "PATH", emConfigPath, "ERROR", ec.message());
+            lg2::error("Invalid SupportedMessageTypes type for {PATH}", "PATH",
+                       endpointPath);
+            if (cleanup)
+            {
+                cleanup();
+            }
+            else
+            {
+                state.querying = false;
+            }
             return;
         }
 
-        auto objIt =
-            objects.find(sdbusplus::message::object_path(emConfigPath));
-
-        if (objIt == objects.end())
+        // Check if it contains the requested message type
+        if (std::find(typesPtr->begin(), typesPtr->end(), filterMsgType) ==
+            typesPtr->end())
         {
-            lg2::warning("EM config path {PATH} not found in managed objects",
-                         "PATH", emConfigPath);
+            lg2::info(
+                "Endpoint {PATH} does not support message type {TYPE}, ignoring",
+                "PATH", endpointPath, "TYPE", filterMsgType);
+            if (cleanup)
+            {
+                cleanup();
+            }
+            else
+            {
+                state.querying = false; // Clear protection!
+            }
             return;
         }
 
-        if (!objIt->second.empty())
-        {
-            lg2::info(
-                "Recorded MCTP endpoint {ENDPOINT} with config from {PATH}",
-                "ENDPOINT", endpointPath, "PATH", emConfigPath);
-            mctpEndpointConfigMap[endpointPath] = objIt->second;
-            lg2::info("DEBUG: Map populated, new size: {SIZE}", "SIZE",
-                      mctpEndpointConfigMap.size());
-        }
+        // Step 2: Proceed to EM query
+        conn->async_method_call(
+            [conn, endpointPath, emConfigPath, cleanup = std::move(cleanup)](
+                const boost::system::error_code& ec,
+                const ManagedObjectType& objects) mutable {
+            auto& state = endpointStates[endpointPath];
+            state.querying = false;
+            state.lastQueryTime =
+                std::chrono::steady_clock::now(); // Update time!
 
-        if (lastState == State::Add)
-        {
-            lg2::info(
-                "DEBUG: Triggering deferred query for endpoint {ENDPOINT}",
-                "ENDPOINT", endpointPath);
-            state.querying = true;
-            performEmConfigQuery(conn, endpointPath, emConfigPath);
-        }
+            State lastState = state.state;
+            state.state = State::None; // Reset for next query
+
+            if (lastState == State::Remove)
+            {
+                lg2::info(
+                    "Ignoring query reply for removed endpoint {ENDPOINT}",
+                    "ENDPOINT", endpointPath);
+                auto it = mctpEndpointConfigMap.find(endpointPath);
+                if (it != mctpEndpointConfigMap.end())
+                {
+                    mctpEndpointConfigMap.erase(it);
+                }
+                if (cleanup)
+                {
+                    cleanup();
+                }
+                return;
+            }
+
+            if (ec)
+            {
+                lg2::error("Failed to get managed objects for {PATH}: {ERROR}",
+                           "PATH", emConfigPath, "ERROR", ec.message());
+                if (cleanup)
+                {
+                    cleanup();
+                }
+                return;
+            }
+
+            auto objIt =
+                objects.find(sdbusplus::message::object_path(emConfigPath));
+
+            if (objIt == objects.end())
+            {
+                lg2::warning(
+                    "EM config path {PATH} not found in managed objects",
+                    "PATH", emConfigPath);
+                if (cleanup)
+                {
+                    cleanup();
+                }
+                return;
+            }
+
+            if (!objIt->second.empty())
+            {
+                lg2::info(
+                    "Recorded MCTP endpoint {ENDPOINT} with config from {PATH}",
+                    "ENDPOINT", endpointPath, "PATH", emConfigPath);
+                mctpEndpointConfigMap[endpointPath] = objIt->second;
+                lg2::info("DEBUG: Map populated, new size: {SIZE}", "SIZE",
+                          mctpEndpointConfigMap.size());
+            }
+
+            if (lastState == State::Add)
+            {
+                lg2::info(
+                    "DEBUG: Triggering deferred query for endpoint {ENDPOINT}",
+                    "ENDPOINT", endpointPath);
+                performEmConfigQuery(conn, endpointPath, emConfigPath);
+            }
+
+            if (cleanup)
+            {
+                cleanup();
+            }
+        },
+            "xyz.openbmc_project.EntityManager",
+            "/xyz/openbmc_project/inventory",
+            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
     },
-        "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory",
-        "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+        "au.com.codeconstruct.MCTP1", endpointPath,
+        "org.freedesktop.DBus.Properties", "Get",
+        "xyz.openbmc_project.MCTP.Endpoint", "SupportedMessageTypes");
 }
 
 static void triggerDeferredQueries(
@@ -165,8 +245,10 @@
 }
 
 void setupMctpEndpointListener(
-    const std::shared_ptr<sdbusplus::asio::connection>& conn)
+    const std::shared_ptr<sdbusplus::asio::connection>& conn,
+    MctpMessageType msgType)
 {
+    filterMsgType = static_cast<uint8_t>(msgType);
     // Match 1: Listen for Associations from mctp-reactor
     const std::string associationMatchSpec =
         "type='signal',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded',arg0path='/au/com/codeconstruct/mctp1/'";
@@ -195,14 +277,14 @@
             return;
         }
 
-        const auto* endpoints =
+        const auto* endpointsPtr =
             std::get_if<std::vector<std::string>>(&propIt->second);
-        if (!endpoints || endpoints->empty())
+        if (!endpointsPtr)
         {
             return;
         }
 
-        std::string emConfigPath = endpoints->front();
+        std::string emConfigPath = endpointsPtr->front();
 
         std::string endpointPath = path.str;
         constexpr std::string_view suffix = "/configured_by";
@@ -215,14 +297,19 @@
         auto& state = endpointStates[endpointPath];
         state.emConfigPath = emConfigPath;
 
-        if (endpointStates["__global__"].querying || state.querying)
-        {
-            state.state = State::Add;
-            return;
-        }
+        bool wasQuerying = endpointStates["__global__"].querying ||
+                           state.querying;
 
-        state.querying = true;
-        performEmConfigQuery(conn, endpointPath, emConfigPath);
+        if (wasQuerying)
+        {
+            lg2::info("Deferring Add for endpoint {PATH}", "PATH",
+                      endpointPath);
+            state.state = State::Add;
+        }
+        else
+        {
+            performEmConfigQuery(conn, endpointPath, emConfigPath);
+        }
     });
 
     const std::string associationRemoveMatchSpec =
@@ -313,7 +400,13 @@
                     [conn, barrier, endpointPath](
                         const boost::system::error_code& ec,
                         const std::variant<std::vector<std::string>>& value) {
-                    auto cleanup = [&]() {
+                    std::weak_ptr<sdbusplus::asio::connection> weakConn = conn;
+                    auto cleanup = [barrier, weakConn]() {
+                        auto conn = weakConn.lock();
+                        if (!conn)
+                        {
+                            return;
+                        }
                         (*barrier)--;
                         if (*barrier == 0)
                         {
@@ -344,10 +437,8 @@
                     auto& state = endpointStates[endpointPath];
                     state.emConfigPath = emConfigPath;
 
-                    state.querying = true;
-                    performEmConfigQuery(conn, endpointPath, emConfigPath);
-
-                    cleanup();
+                    performEmConfigQuery(conn, endpointPath, emConfigPath, true,
+                                         std::move(cleanup));
                 },
                     "xyz.openbmc_project.ObjectMapper", path,
                     "org.freedesktop.DBus.Properties", "Get",
diff --git a/src/MctpUtil.hpp b/src/MctpUtil.hpp
index 282e7e2..8f6d25c 100644
--- a/src/MctpUtil.hpp
+++ b/src/MctpUtil.hpp
@@ -10,6 +10,14 @@
 
 using BusInfo = std::map<std::string, std::string>;
 
+enum class MctpMessageType : uint8_t
+{
+    CONTROL = 0,
+    PLDM = 2,
+    NVME_MI = 4,
+    SPDM = 5
+};
+
 // Global maps (declared extern)
 
 #include "Utils.hpp"
@@ -17,7 +25,8 @@
 extern std::map<std::string, SensorData> mctpEndpointConfigMap;
 
 void setupMctpEndpointListener(
-    const std::shared_ptr<sdbusplus::asio::connection>& conn);
+    const std::shared_ptr<sdbusplus::asio::connection>& conn,
+    MctpMessageType msgType);
 void cleanupMctpEndpointListener();
 
 BusInfo extractBusInfo(const SensorData& configData);
diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
index 584fd89..76a584c 100644
--- a/src/NVMeSensorMain.cpp
+++ b/src/NVMeSensorMain.cpp
@@ -580,7 +580,7 @@
     objectServer.add_manager("/xyz/openbmc_project/sensors");
     objectServer.add_manager("/xyz/openbmc_project/inventory");
 
-    setupMctpEndpointListener(systemBus);
+    setupMctpEndpointListener(systemBus, MctpMessageType::NVME_MI);
 
     boost::asio::post(
         io, [&]() { createNVMeSubsystems(io, objectServer, systemBus); });
diff --git a/tests/mock_mctpd.py b/tests/mock_mctpd.py
index 1d99a03..65801e7 100644
--- a/tests/mock_mctpd.py
+++ b/tests/mock_mctpd.py
@@ -29,22 +29,30 @@
 
 
 class MockEndpoint(dbus.service.Object):
-    def __init__(self, bus, path, em_config_path):
+    def __init__(self, bus, path, em_config_path, msg_types=None):
         dbus.service.Object.__init__(self, bus, path)
-        self.associations = dbus.Array([
+        self._associations = dbus.Array([
             dbus.Struct(('configured_by', 'endpoint', em_config_path), signature='(sss)')
         ], signature='(sss)')
+        if msg_types is not None:
+            self._message_types = dbus.ByteArray(msg_types)
+        else:
+            self._message_types = dbus.ByteArray(b'\x02\x00\x04')
 
     @dbus.service.method('org.freedesktop.DBus.Properties', in_signature='ss', out_signature='v')
     def Get(self, interface_name, property_name):
         if interface_name == 'xyz.openbmc_project.Association.Definitions' and property_name == 'Associations':
-            return dbus.LowLevel.Variant(self.associations, 'a(sss)')
+            return self._associations
+        if interface_name == 'xyz.openbmc_project.MCTP.Endpoint' and property_name == 'SupportedMessageTypes':
+            return self._message_types
         raise dbus.exceptions.DBusException('org.freedesktop.DBus.Error.InvalidArgs')
 
     @dbus.service.method('org.freedesktop.DBus.Properties', in_signature='s', out_signature='a{sv}')
     def GetAll(self, interface_name):
         if interface_name == 'xyz.openbmc_project.Association.Definitions':
-            return {'Associations': dbus.LowLevel.Variant(self.associations, 'a(sss)')}
+            return {'Associations': self._associations}
+        if interface_name == 'xyz.openbmc_project.MCTP.Endpoint':
+            return {'SupportedMessageTypes': self._message_types}
         return {}
 
 
@@ -85,9 +93,25 @@
 
         self.InterfacesAdded(dbus.ObjectPath(endpoint_path), {
             'xyz.openbmc_project.Association.Definitions': {
-                'Associations': dbus.Array([
-                    dbus.Struct(('configured_by', 'endpoint', em_config_path), signature='(sss)')
-                ], signature='(sss)')
+                'Associations': self.endpoints[endpoint_path]._associations
+            },
+            'xyz.openbmc_project.MCTP.Endpoint': {
+                'SupportedMessageTypes': self.endpoints[endpoint_path]._message_types
+            }
+        })
+        return "OK"
+
+    @dbus.service.method('com.example.Control', in_signature='ssay')
+    def TriggerAddCustom(self, endpoint_path, em_config_path, msg_types):
+        if endpoint_path not in self.endpoints:
+            self.endpoints[endpoint_path] = MockEndpoint(self.bus, endpoint_path, em_config_path, msg_types)
+
+        self.InterfacesAdded(dbus.ObjectPath(endpoint_path), {
+            'xyz.openbmc_project.Association.Definitions': {
+                'Associations': self.endpoints[endpoint_path]._associations
+            },
+            'xyz.openbmc_project.MCTP.Endpoint': {
+                'SupportedMessageTypes': self.endpoints[endpoint_path]._message_types
             }
         })
         return "OK"
@@ -154,7 +178,7 @@
     _em = MockEM(bus)  # noqa: F841
 
     _name_em = dbus.service.BusName("xyz.openbmc_project.EntityManager", bus)  # noqa: F841
-    _name_mctp = dbus.service.BusName("xyz.openbmc_project.Mctp", bus)  # noqa: F841
+    _name_mctp = dbus.service.BusName("au.com.codeconstruct.MCTP1", bus)  # noqa: F841
     print("Mock mctpd and EM running...")
     sys.stdout.flush()
     loop.run()
diff --git a/tests/test_MctpUtil.cpp b/tests/test_MctpUtil.cpp
index e939779..a743d07 100644
--- a/tests/test_MctpUtil.cpp
+++ b/tests/test_MctpUtil.cpp
@@ -135,7 +135,7 @@
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
     // 2.a setupMctpEndpointListener() first in the test.
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     // 2.b setup the busctl event listener to all O-M event.
     // We use a C++ match listener for all InterfacesAdded signals!
@@ -154,7 +154,7 @@
 
     // Trigger Add event via D-Bus!
     std::string addCmd =
-        "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" +
+        "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" +
         endpointPath + "\" \"" + emConfigPath + "\"";
     runCmd(addCmd);
 
@@ -192,7 +192,7 @@
     std::string emConfigPath =
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -216,7 +216,7 @@
 
     // Trigger Add event
     std::string addCmd =
-        "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" +
+        "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" +
         endpointPath + "\" \"" + emConfigPath + "\"";
     runCmd(addCmd);
 
@@ -229,7 +229,7 @@
 
     // IMMEDIATELY trigger Remove event (while query is in progress!)
     std::string removeCmd =
-        "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" +
+        "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" +
         endpointPath + "\"";
     runCmd(removeCmd);
 
@@ -263,7 +263,7 @@
     std::string emConfigPath =
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -287,7 +287,7 @@
 
     // Trigger Add event
     std::string addCmd =
-        "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" +
+        "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerAdd ss \"" +
         endpointPath + "\" \"" + emConfigPath + "\"";
     runCmd(addCmd);
 
@@ -300,7 +300,7 @@
 
     // IMMEDIATELY trigger Remove event
     std::string removeCmd =
-        "busctl call xyz.openbmc_project.Mctp /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" +
+        "busctl call au.com.codeconstruct.MCTP1 /au/com/codeconstruct/mctp1 com.example.Control TriggerRemove s \"" +
         endpointPath + "\"";
     runCmd(removeCmd);
 
@@ -339,7 +339,7 @@
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
     // Pre-populate association by triggering it BEFORE listener starts!
-    auto m = conn->new_method_call("xyz.openbmc_project.Mctp",
+    auto m = conn->new_method_call("au.com.codeconstruct.MCTP1",
                                    "/au/com/codeconstruct/mctp1",
                                    "com.example.Control", "TriggerAdd");
     m.append(endpointPath, emConfigPath);
@@ -359,7 +359,7 @@
     ASSERT_TRUE(found);
 
     // Now setup listener (this starts priming!)
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -400,7 +400,7 @@
         {
             std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath5, emConfigPath);
 
     // Wait for queries to complete (we expect 2!)
@@ -442,7 +442,7 @@
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
     // Pre-populate association by triggering it BEFORE listener starts!
-    auto m = conn->new_method_call("xyz.openbmc_project.Mctp",
+    auto m = conn->new_method_call("au.com.codeconstruct.MCTP1",
                                    "/au/com/codeconstruct/mctp1",
                                    "com.example.Control", "TriggerAdd");
     m.append(endpointPath, emConfigPath);
@@ -462,7 +462,7 @@
     ASSERT_TRUE(found);
 
     // Now setup listener (this starts priming!)
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -501,7 +501,7 @@
             std::cout << "[TEST] TriggerRemove failed: " << ec.message()
                       << "\n";
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerRemove", endpointPath);
 
     // Wait for queries to complete (we expect 1!)
@@ -534,7 +534,7 @@
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
     // Pre-populate association by triggering it BEFORE listener starts!
-    auto m = conn->new_method_call("xyz.openbmc_project.Mctp",
+    auto m = conn->new_method_call("au.com.codeconstruct.MCTP1",
                                    "/au/com/codeconstruct/mctp1",
                                    "com.example.Control", "TriggerAdd");
     m.append(endpointPath, emConfigPath);
@@ -554,7 +554,7 @@
     ASSERT_TRUE(found);
 
     // Now setup listener (this starts priming!)
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -593,7 +593,7 @@
             std::cout << "[TEST] TriggerRemove failed: " << ec.message()
                       << "\n";
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerRemove", endpointPath);
 
     std::cout << "[DEBUG] Triggering Add again (racing!)\n";
@@ -604,7 +604,7 @@
         {
             std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath, emConfigPath);
 
     // Wait for queries to complete (we expect 2!)
@@ -638,7 +638,7 @@
     std::string emConfigPath =
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -648,7 +648,7 @@
     auto queryStartedMatch = std::make_unique<sdbusplus::bus::match_t>(
         static_cast<sdbusplus::bus_t&>(*conn), queryStartedSpec,
         [&](sdbusplus::message_t&) {
-        std::cout << "[TEST] QueryStarted received\n";
+        std::cout << "[TEST] QueryStarted received" << '\n';
         queryStarted = true;
     });
 
@@ -657,7 +657,7 @@
     auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>(
         static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec,
         [&](sdbusplus::message_t&) {
-        std::cout << "[TEST] QueryCompleted received\n";
+        std::cout << "[TEST] QueryCompleted received" << '\n';
         queryCompletedCount++;
     });
 
@@ -666,9 +666,9 @@
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
-            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
+            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath, emConfigPath);
 
     // Wait for FIRST query to complete!
@@ -684,16 +684,16 @@
     }
     ASSERT_EQ(queryCompletedCount, 1);
 
-    std::cout << "[DEBUG] Triggering Remove and Add (racing!)\n";
+    std::cout << "[DEBUG] Triggering Remove and Add (racing!)" << '\n';
     // Trigger Remove!
     conn->async_method_call(
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
             std::cout << "[TEST] TriggerRemove failed: " << ec.message()
-                      << "\n";
+                      << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerRemove", endpointPath);
 
     // Trigger SECOND Add! (Immediately after!).
@@ -701,9 +701,9 @@
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
-            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
+            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath, emConfigPath);
 
     // Wait for SECOND query to complete!
@@ -742,7 +742,7 @@
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
     // Pre-populate association by triggering it BEFORE listener starts!
-    auto m = conn->new_method_call("xyz.openbmc_project.Mctp",
+    auto m = conn->new_method_call("au.com.codeconstruct.MCTP1",
                                    "/au/com/codeconstruct/mctp1",
                                    "com.example.Control", "TriggerAdd");
     m.append(endpointPath, emConfigPath);
@@ -762,7 +762,7 @@
     ASSERT_TRUE(found);
 
     // Now setup listener (this starts priming!)
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -772,7 +772,7 @@
     auto queryStartedMatch = std::make_unique<sdbusplus::bus::match_t>(
         static_cast<sdbusplus::bus_t&>(*conn), queryStartedSpec,
         [&](sdbusplus::message_t&) {
-        std::cout << "[TEST] QueryStarted received\n";
+        std::cout << "[TEST] QueryStarted received" << '\n';
         queryStarted = true;
     });
 
@@ -781,7 +781,7 @@
     auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>(
         static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec,
         [&](sdbusplus::message_t&) {
-        std::cout << "[TEST] QueryCompleted received\n";
+        std::cout << "[TEST] QueryCompleted received" << '\n';
         queryCompletedCount++;
     });
 
@@ -792,16 +792,17 @@
         usleep(10000); // 10ms
     }
 
-    std::cout << "[DEBUG] Triggering Remove and Add (racing with priming!)\n";
+    std::cout << "[DEBUG] Triggering Remove and Add (racing with priming!)"
+              << '\n';
     // Trigger Remove event!
     conn->async_method_call(
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
             std::cout << "[TEST] TriggerRemove failed: " << ec.message()
-                      << "\n";
+                      << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerRemove", endpointPath);
 
     // Trigger Add event!
@@ -809,9 +810,9 @@
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
-            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
+            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath, emConfigPath);
 
     // Wait for queries to complete!
@@ -848,7 +849,7 @@
     std::string emConfigPath =
         "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
 
-    setupMctpEndpointListener(conn);
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
 
     bool queryStarted = false;
     int queryCompletedCount = 0;
@@ -858,7 +859,7 @@
     auto queryStartedMatch = std::make_unique<sdbusplus::bus::match_t>(
         static_cast<sdbusplus::bus_t&>(*conn), queryStartedSpec,
         [&](sdbusplus::message_t&) {
-        std::cout << "[TEST] QueryStarted received\n";
+        std::cout << "[TEST] QueryStarted received" << '\n';
         queryStarted = true;
     });
 
@@ -867,7 +868,7 @@
     auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>(
         static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec,
         [&](sdbusplus::message_t&) {
-        std::cout << "[TEST] QueryCompleted received\n";
+        std::cout << "[TEST] QueryCompleted received" << '\n';
         queryCompletedCount++;
     });
 
@@ -876,9 +877,9 @@
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
-            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
+            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath, emConfigPath);
 
     // Wait for FIRST query to complete!
@@ -894,16 +895,16 @@
     }
     ASSERT_EQ(queryCompletedCount, 1);
 
-    std::cout << "[DEBUG] Triggering Remove -> Add -> Remove\n";
+    std::cout << "[DEBUG] Triggering Remove -> Add -> Remove" << '\n';
     // Trigger Remove!
     conn->async_method_call(
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
             std::cout << "[TEST] TriggerRemove failed: " << ec.message()
-                      << "\n";
+                      << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerRemove", endpointPath);
 
     // Trigger Add!
@@ -911,9 +912,9 @@
         [](const boost::system::error_code& ec, const std::string&) {
         if (ec)
         {
-            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << "\n";
+            std::cout << "[TEST] TriggerAdd failed: " << ec.message() << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerAdd", endpointPath, emConfigPath);
 
     // Trigger Remove!
@@ -922,9 +923,9 @@
         if (ec)
         {
             std::cout << "[TEST] TriggerRemove failed: " << ec.message()
-                      << "\n";
+                      << '\n';
         }
-    }, "xyz.openbmc_project.Mctp", "/au/com/codeconstruct/mctp1",
+    }, "au.com.codeconstruct.MCTP1", "/au/com/codeconstruct/mctp1",
         "com.example.Control", "TriggerRemove", endpointPath);
 
     // Wait for queries to complete!
@@ -933,17 +934,75 @@
         while (io.poll() > 0)
         {}              // Drain all ready handlers!
         usleep(100000); // 100ms
-        if (queryCompletedCount >= 2)
+    }
+
+    EXPECT_EQ(queryCompletedCount, 1);
+
+    // Verify map is EMPTY!
+    auto it = mctpEndpointConfigMap.find(endpointPath);
+    EXPECT_EQ(it, mctpEndpointConfigMap.end());
+}
+
+// 10. Filter unsupported message types
+TEST_F(MctpUtilTest, Filter_UnsupportedMessageType_IsIgnored)
+{
+    std::string endpointPathSupported =
+        "/au/com/codeconstruct/mctp1/networks/1/endpoints/test10_supported";
+    std::string endpointPathUnsupported =
+        "/au/com/codeconstruct/mctp1/networks/1/endpoints/"
+        "test10_unsupported";
+    std::string emConfigPath =
+        "/xyz/openbmc_project/inventory/system/board/MockBoard/mctp_device";
+
+    setupMctpEndpointListener(conn, MctpMessageType::NVME_MI);
+
+    int queryCompletedCount = 0;
+    const std::string queryCompletedSpec =
+        "type='signal',interface='com.example.Control',member='QueryCompleted'";
+    auto queryCompletedMatch = std::make_unique<sdbusplus::bus::match_t>(
+        static_cast<sdbusplus::bus_t&>(*conn), queryCompletedSpec,
+        [&](sdbusplus::message_t&) { queryCompletedCount++; });
+
+    // 1. Trigger Add for SUPPORTED endpoint (type 4 included)
+    std::vector<uint8_t> msgTypesSupported = {2, 0, 4};
+    auto m1 = conn->new_method_call("au.com.codeconstruct.MCTP1",
+                                    "/au/com/codeconstruct/mctp1",
+                                    "com.example.Control", "TriggerAddCustom");
+    m1.append(endpointPathSupported, emConfigPath, msgTypesSupported);
+    conn->call(m1);
+
+    // 2. Trigger Add for UNSUPPORTED endpoint (no type 4)
+    std::vector<uint8_t> msgTypesUnsupported = {2};
+    auto m2 = conn->new_method_call("au.com.codeconstruct.MCTP1",
+                                    "/au/com/codeconstruct/mctp1",
+                                    "com.example.Control", "TriggerAddCustom");
+    m2.append(endpointPathUnsupported, emConfigPath, msgTypesUnsupported);
+    conn->call(m2);
+
+    // Wait for queries to complete (we expect exactly 1 to complete!)
+    for (int i = 0; i < 50; i++)
+    {
+        while (io.poll() > 0)
+        {}              // Drain all ready handlers!
+        usleep(100000); // 100ms
+        if (queryCompletedCount >= 1)
         {
             break;
         }
     }
 
-    EXPECT_EQ(queryCompletedCount, 2);
+    EXPECT_EQ(queryCompletedCount, 1);
 
-    // Verify map is EMPTY!
-    auto it = mctpEndpointConfigMap.find(endpointPath);
-    EXPECT_EQ(it, mctpEndpointConfigMap.end());
+    // Verify map size is 1
+    EXPECT_EQ(mctpEndpointConfigMap.size(), 1);
+
+    // Verify supported endpoint is present
+    auto itSupported = mctpEndpointConfigMap.find(endpointPathSupported);
+    EXPECT_NE(itSupported, mctpEndpointConfigMap.end());
+
+    // Verify unsupported endpoint is NOT present
+    auto itUnsupported = mctpEndpointConfigMap.find(endpointPathUnsupported);
+    EXPECT_EQ(itUnsupported, mctpEndpointConfigMap.end());
 }
 
 int main(int argc, char** argv)