diff --git a/moveit_plugins/moveit_ros_control_interface/CMakeLists.txt b/moveit_plugins/moveit_ros_control_interface/CMakeLists.txt
index 38a203fe27..a23dac9f96 100644
--- a/moveit_plugins/moveit_ros_control_interface/CMakeLists.txt
+++ b/moveit_plugins/moveit_ros_control_interface/CMakeLists.txt
@@ -38,7 +38,7 @@ ament_target_dependencies(moveit_ros_control_interface_trajectory_plugin
                           ${THIS_PACKAGE_INCLUDE_DEPENDS} Boost)
 
 add_library(moveit_ros_control_interface_gripper_plugin SHARED
-            src/gripper_controller_plugin.cpp)
+            src/gripper_command_controller_plugin.cpp)
 set_target_properties(
   moveit_ros_control_interface_gripper_plugin
   PROPERTIES VERSION "${moveit_ros_control_interface_VERSION}")
@@ -47,6 +47,16 @@ target_include_directories(moveit_ros_control_interface_gripper_plugin
 ament_target_dependencies(moveit_ros_control_interface_gripper_plugin
                           ${THIS_PACKAGE_INCLUDE_DEPENDS} Boost)
 
+add_library(moveit_ros_control_interface_parallel_gripper_plugin SHARED
+            src/parallel_gripper_command_controller_plugin.cpp)
+set_target_properties(
+  moveit_ros_control_interface_parallel_gripper_plugin
+  PROPERTIES VERSION "${moveit_ros_control_interface_VERSION}")
+target_include_directories(moveit_ros_control_interface_parallel_gripper_plugin
+                           PRIVATE include)
+ament_target_dependencies(moveit_ros_control_interface_parallel_gripper_plugin
+                          ${THIS_PACKAGE_INCLUDE_DEPENDS} Boost)
+
 add_library(moveit_ros_control_interface_empty_plugin SHARED
             src/empty_controller_plugin.cpp)
 set_target_properties(
@@ -65,6 +75,7 @@ set(TARGET_LIBRARIES
     moveit_ros_control_interface_plugin
     moveit_ros_control_interface_trajectory_plugin
     moveit_ros_control_interface_gripper_plugin
+    moveit_ros_control_interface_parallel_gripper_plugin
     moveit_ros_control_interface_empty_plugin)
 
 # Mark executables and/or libraries for installation
diff --git a/moveit_plugins/moveit_ros_control_interface/moveit_ros_control_interface_plugins.xml b/moveit_plugins/moveit_ros_control_interface/moveit_ros_control_interface_plugins.xml
index fc6777c13c..928be71cc3 100644
--- a/moveit_plugins/moveit_ros_control_interface/moveit_ros_control_interface_plugins.xml
+++ b/moveit_plugins/moveit_ros_control_interface/moveit_ros_control_interface_plugins.xml
@@ -10,10 +10,15 @@
     </class>
   </library>
   <library path="moveit_ros_control_interface_gripper_plugin">
-    <class name="position_controllers/GripperActionController" type="moveit_ros_control_interface::GripperControllerAllocator" base_class_type="moveit_ros_control_interface::ControllerHandleAllocator">
+    <class name="position_controllers/GripperActionController" type="moveit_ros_control_interface::GripperCommandControllerAllocator" base_class_type="moveit_ros_control_interface::ControllerHandleAllocator">
         <description></description>
     </class>
-    <class name="effort_controllers/GripperActionController" type="moveit_ros_control_interface::GripperControllerAllocator" base_class_type="moveit_ros_control_interface::ControllerHandleAllocator">
+    <class name="effort_controllers/GripperActionController" type="moveit_ros_control_interface::GripperCommandControllerAllocator" base_class_type="moveit_ros_control_interface::ControllerHandleAllocator">
+        <description></description>
+    </class>
+  </library>
+  <library path="moveit_ros_control_interface_parallel_gripper_plugin">
+    <class name="parallel_gripper_action_controller/GripperActionController" type="moveit_ros_control_interface::ParallelGripperCommandControllerAllocator" base_class_type="moveit_ros_control_interface::ControllerHandleAllocator">
         <description></description>
     </class>
   </library>
diff --git a/moveit_plugins/moveit_ros_control_interface/src/gripper_controller_plugin.cpp b/moveit_plugins/moveit_ros_control_interface/src/gripper_command_controller_plugin.cpp
similarity index 88%
rename from moveit_plugins/moveit_ros_control_interface/src/gripper_controller_plugin.cpp
rename to moveit_plugins/moveit_ros_control_interface/src/gripper_command_controller_plugin.cpp
index 8d6e9fafad..ba0e0cefd9 100644
--- a/moveit_plugins/moveit_ros_control_interface/src/gripper_controller_plugin.cpp
+++ b/moveit_plugins/moveit_ros_control_interface/src/gripper_command_controller_plugin.cpp
@@ -36,27 +36,27 @@
 
 #include <moveit_ros_control_interface/ControllerHandle.hpp>
 #include <pluginlib/class_list_macros.hpp>
-#include <moveit_simple_controller_manager/gripper_controller_handle.hpp>
+#include <moveit_simple_controller_manager/gripper_command_controller_handle.hpp>
 #include <rclcpp/node.hpp>
 #include <memory>
 
 namespace moveit_ros_control_interface
 {
 /**
- * \brief Simple allocator for moveit_simple_controller_manager::FollowJointTrajectoryControllerHandle instances.
+ * \brief Simple allocator for moveit_simple_controller_manager::GripperCommandControllerHandle instances.
  */
-class GripperControllerAllocator : public ControllerHandleAllocator
+class GripperCommandControllerAllocator : public ControllerHandleAllocator
 {
 public:
   moveit_controller_manager::MoveItControllerHandlePtr alloc(const rclcpp::Node::SharedPtr& node,
                                                              const std::string& name,
                                                              const std::vector<std::string>& /* resources */) override
   {
-    return std::make_shared<moveit_simple_controller_manager::GripperControllerHandle>(node, name, "gripper_cmd");
+    return std::make_shared<moveit_simple_controller_manager::GripperCommandControllerHandle>(node, name, "gripper_cmd");
   }
 };
 
 }  // namespace moveit_ros_control_interface
 
-PLUGINLIB_EXPORT_CLASS(moveit_ros_control_interface::GripperControllerAllocator,
+PLUGINLIB_EXPORT_CLASS(moveit_ros_control_interface::GripperCommandControllerAllocator,
                        moveit_ros_control_interface::ControllerHandleAllocator);
diff --git a/moveit_plugins/moveit_ros_control_interface/src/parallel_gripper_command_controller_plugin.cpp b/moveit_plugins/moveit_ros_control_interface/src/parallel_gripper_command_controller_plugin.cpp
new file mode 100644
index 0000000000..9666cb11dc
--- /dev/null
+++ b/moveit_plugins/moveit_ros_control_interface/src/parallel_gripper_command_controller_plugin.cpp
@@ -0,0 +1,63 @@
+/*********************************************************************
+ * Software License Agreement (BSD License)
+ *
+ *  Copyright (c) 2025, Marq Rasmussen
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Marq Rasmussen nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ *********************************************************************/
+
+/* Author: Marq Rasmussen */
+
+#include <moveit_ros_control_interface/ControllerHandle.hpp>
+#include <pluginlib/class_list_macros.hpp>
+#include <moveit_simple_controller_manager/parallel_gripper_command_controller_handle.hpp>
+#include <rclcpp/node.hpp>
+#include <memory>
+
+namespace moveit_ros_control_interface
+{
+/**
+ * \brief Simple allocator for moveit_simple_controller_manager::ParallelGripperCommandControllerHandle instances.
+ */
+class ParallelGripperCommandControllerAllocator : public ControllerHandleAllocator
+{
+public:
+  moveit_controller_manager::MoveItControllerHandlePtr alloc(const rclcpp::Node::SharedPtr& node,
+                                                             const std::string& name,
+                                                             const std::vector<std::string>& /* resources */) override
+  {
+    return std::make_shared<moveit_simple_controller_manager::ParallelGripperCommandControllerHandle>(node, name,
+                                                                                                      "gripper_cmd");
+  }
+};
+
+}  // namespace moveit_ros_control_interface
+
+PLUGINLIB_EXPORT_CLASS(moveit_ros_control_interface::ParallelGripperCommandControllerAllocator,
+                       moveit_ros_control_interface::ControllerHandleAllocator);
diff --git a/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.hpp b/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_command_controller_handle.hpp
similarity index 92%
rename from moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.hpp
rename to moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_command_controller_handle.hpp
index 6f2dc9a21b..2284e81e3d 100644
--- a/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.hpp
+++ b/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_command_controller_handle.hpp
@@ -47,12 +47,12 @@ namespace moveit_simple_controller_manager
  * This is an interface for a gripper using control_msgs/GripperCommandAction
  * action interface (single DOF).
  */
-class GripperControllerHandle : public ActionBasedControllerHandle<control_msgs::action::GripperCommand>
+class GripperCommandControllerHandle : public ActionBasedControllerHandle<control_msgs::action::GripperCommand>
 {
 public:
   /* Topics will map to name/ns/goal, name/ns/result, etc */
-  GripperControllerHandle(const rclcpp::Node::SharedPtr& node, const std::string& name, const std::string& ns,
-                          const double max_effort = 0.0)
+  GripperCommandControllerHandle(const rclcpp::Node::SharedPtr& node, const std::string& name, const std::string& ns,
+                                 const double max_effort = 0.0)
     : ActionBasedControllerHandle<control_msgs::action::GripperCommand>(
           node, name, ns, "moveit.simple_controller_manager.gripper_controller_handle")
     , allow_failure_(false)
@@ -98,23 +98,23 @@ class GripperControllerHandle : public ActionBasedControllerHandle<control_msgs:
       return false;
     }
 
-    std::vector<std::size_t> gripper_joint_indexes;
+    std::vector<std::size_t> gripper_joint_indices;
     for (std::size_t i = 0; i < trajectory.joint_trajectory.joint_names.size(); ++i)
     {
       if (command_joints_.find(trajectory.joint_trajectory.joint_names[i]) != command_joints_.end())
       {
-        gripper_joint_indexes.push_back(i);
+        gripper_joint_indices.push_back(i);
         if (!parallel_jaw_gripper_)
           break;
       }
     }
 
-    if (gripper_joint_indexes.empty())
+    if (gripper_joint_indices.empty())
     {
       RCLCPP_WARN(logger_, "No command_joint was specified for the MoveIt controller gripper handle. \
-                      Please see GripperControllerHandle::addCommandJoint() and \
-                      GripperControllerHandle::setCommandJoint(). Assuming index 0.");
-      gripper_joint_indexes.push_back(0);
+                      Please see GripperCommandControllerHandle::addCommandJoint() and \
+                      GripperCommandControllerHandle::setCommandJoint(). Assuming index 0.");
+      gripper_joint_indices.push_back(0);
     }
 
     // goal to be sent
@@ -126,7 +126,7 @@ class GripperControllerHandle : public ActionBasedControllerHandle<control_msgs:
     RCLCPP_DEBUG(logger_, "Sending command from trajectory point %d", tpoint);
 
     // fill in goal from last point
-    for (std::size_t idx : gripper_joint_indexes)
+    for (std::size_t idx : gripper_joint_indices)
     {
       if (trajectory.joint_trajectory.points[tpoint].positions.size() <= idx)
       {
diff --git a/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.h b/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.h
index 761eaf3ef9..da78a57dfc 100644
--- a/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.h
+++ b/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/gripper_controller_handle.h
@@ -48,5 +48,5 @@
 /* Author: Michael Ferguson, Ioan Sucan, E. Gil Jones */
 
 #pragma once
-#pragma message(".h header is obsolete. Please use the .hpp header instead.")
-#include <moveit_simple_controller_manager/gripper_controller_handle.hpp>
+#pragma message(".h header is obsolete. Please use the gripper_command_controller_handle.hpp header instead.")
+#include <moveit_simple_controller_manager/gripper_command_controller_handle.hpp>
diff --git a/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/parallel_gripper_command_controller_handle.hpp b/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/parallel_gripper_command_controller_handle.hpp
new file mode 100644
index 0000000000..4df57ecacc
--- /dev/null
+++ b/moveit_plugins/moveit_simple_controller_manager/include/moveit_simple_controller_manager/parallel_gripper_command_controller_handle.hpp
@@ -0,0 +1,178 @@
+/*********************************************************************
+ * Software License Agreement (BSD License)
+ *
+ *  Copyright (c) 2025, Marq Rasmussen
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of the Marq Rasmussen nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ *********************************************************************/
+/* Design inspired by gripper_command_controller_handle.hpp */
+/* Author: Marq Rasmussen */
+
+#pragma once
+
+#include <moveit_simple_controller_manager/action_based_controller_handle.hpp>
+#include <control_msgs/action/parallel_gripper_command.hpp>
+#include <set>
+
+namespace moveit_simple_controller_manager
+{
+/*
+ * This is an interface for a gripper using the control_msgs/ParallelGripperCommand action interface.
+ */
+class ParallelGripperCommandControllerHandle
+  : public ActionBasedControllerHandle<control_msgs::action::ParallelGripperCommand>
+{
+public:
+  /* Topics will map to name/ns/goal, name/ns/result, etc */
+  ParallelGripperCommandControllerHandle(const rclcpp::Node::SharedPtr& node, const std::string& name,
+                                         const std::string& ns, const double max_effort = 0.0,
+                                         const double max_velocity = 0.0)
+    : ActionBasedControllerHandle<control_msgs::action::ParallelGripperCommand>(
+          node, name, ns, "moveit.simple_controller_manager.parallel_gripper_controller_handle")
+    , max_effort_(max_effort)
+    , max_velocity_(max_velocity)
+  {
+  }
+
+  bool sendTrajectory(const moveit_msgs::msg::RobotTrajectory& trajectory) override
+  {
+    RCLCPP_DEBUG_STREAM(logger_, "Received new trajectory for " << name_);
+
+    if (!controller_action_client_)
+      return false;
+
+    if (!isConnected())
+    {
+      RCLCPP_ERROR_STREAM(logger_, "Action client not connected to action server: " << getActionName());
+      return false;
+    }
+
+    if (!trajectory.multi_dof_joint_trajectory.points.empty())
+    {
+      RCLCPP_ERROR(logger_, "%s cannot execute multi-dof trajectories.", name_.c_str());
+      return false;
+    }
+
+    if (trajectory.joint_trajectory.points.empty())
+    {
+      RCLCPP_ERROR(logger_, "%s requires at least one joint trajectory point, but none received.", name_.c_str());
+      return false;
+    }
+
+    if (trajectory.joint_trajectory.joint_names.empty())
+    {
+      RCLCPP_ERROR(logger_, "%s received a trajectory with no joint names specified.", name_.c_str());
+      return false;
+    }
+
+    // goal to be sent
+    control_msgs::action::ParallelGripperCommand::Goal goal;
+    auto& cmd_state = goal.command;
+
+    auto& joint_names = trajectory.joint_trajectory.joint_names;
+    auto it = std::find(joint_names.begin(), joint_names.end(), command_joint_);
+    if (it != joint_names.end())
+    {
+      cmd_state.name.push_back(command_joint_);
+      std::size_t gripper_joint_index = std::distance(joint_names.begin(), it);
+      // send last trajectory point
+      if (trajectory.joint_trajectory.points.back().positions.size() <= gripper_joint_index)
+      {
+        RCLCPP_ERROR(logger_, "ParallelGripperCommand expects a joint trajectory with one \
+                               point that specifies at least the position of joint \
+                               '%s', but insufficient positions provided.",
+                     trajectory.joint_trajectory.joint_names[gripper_joint_index].c_str());
+        return false;
+      }
+      cmd_state.position.push_back(trajectory.joint_trajectory.points.back().positions[gripper_joint_index]);
+      // only set the velocity or effort if the user has specified a positive non-zero max value
+      if (max_velocity_ > 0.0)
+      {
+        cmd_state.velocity.push_back(max_velocity_);
+      }
+      if (max_effort_ > 0.0)
+      {
+        cmd_state.effort.push_back(max_effort_);
+      }
+    }
+    else
+    {
+      RCLCPP_ERROR(logger_, "Received trajectory does not include a command for joint name %s.", command_joint_.c_str());
+      return false;
+    }
+
+    rclcpp_action::Client<control_msgs::action::ParallelGripperCommand>::SendGoalOptions send_goal_options;
+    // Active callback
+    send_goal_options.goal_response_callback =
+        [this](const rclcpp_action::Client<control_msgs::action::ParallelGripperCommand>::GoalHandle::SharedPtr&
+               /* unused-arg */) { RCLCPP_DEBUG_STREAM(logger_, name_ << " started execution."); };
+    // Send goal
+    auto current_goal_future = controller_action_client_->async_send_goal(goal, send_goal_options);
+    current_goal_ = current_goal_future.get();
+    if (!current_goal_)
+    {
+      RCLCPP_ERROR(logger_, "%s goal was rejected by server.", name_.c_str());
+      return false;
+    }
+
+    done_ = false;
+    last_exec_ = moveit_controller_manager::ExecutionStatus::RUNNING;
+    return true;
+  }
+
+  void setCommandJoint(const std::string& name)
+  {
+    command_joint_ = name;
+  }
+
+private:
+  void controllerDoneCallback(
+      const rclcpp_action::ClientGoalHandle<control_msgs::action::ParallelGripperCommand>::WrappedResult& wrapped_result)
+      override
+  {
+    finishControllerExecution(wrapped_result.code);
+  }
+
+  /*
+   * The ``max_effort`` used in the ParallelGripperCommand message.
+   */
+  double max_effort_ = 0.0;
+
+  /*
+   * The ``max_velocity_`` used in the ParallelGripperCommand message.
+   */
+  double max_velocity_ = 0.0;
+
+  /*
+   * The joint to command in the ParallelGripperCommand message
+   */
+  std::string command_joint_;
+};  // namespace moveit_simple_controller_manager
+
+}  // end namespace moveit_simple_controller_manager
diff --git a/moveit_plugins/moveit_simple_controller_manager/src/moveit_simple_controller_manager.cpp b/moveit_plugins/moveit_simple_controller_manager/src/moveit_simple_controller_manager.cpp
index 394765c8cb..b38c84a596 100644
--- a/moveit_plugins/moveit_simple_controller_manager/src/moveit_simple_controller_manager.cpp
+++ b/moveit_plugins/moveit_simple_controller_manager/src/moveit_simple_controller_manager.cpp
@@ -36,7 +36,8 @@
 /* Author: Michael Ferguson, Ioan Sucan, E. Gil Jones */
 
 #include <moveit_simple_controller_manager/action_based_controller_handle.hpp>
-#include <moveit_simple_controller_manager/gripper_controller_handle.hpp>
+#include <moveit_simple_controller_manager/gripper_command_controller_handle.hpp>
+#include <moveit_simple_controller_manager/parallel_gripper_command_controller_handle.hpp>
 #include <moveit_simple_controller_manager/follow_joint_trajectory_controller_handle.hpp>
 #include <boost/algorithm/string/join.hpp>
 #include <pluginlib/class_list_macros.hpp>
@@ -147,14 +148,10 @@ class MoveItSimpleControllerManager : public moveit_controller_manager::MoveItCo
         if (type == "GripperCommand")
         {
           double max_effort;
-          const std::string& max_effort_param = makeParameterName(PARAM_BASE_NAME, controller_name, "max_effort");
-          if (!node->get_parameter(max_effort_param, max_effort))
-          {
-            RCLCPP_INFO_STREAM(getLogger(), "Max effort set to 0.0");
-            max_effort = 0.0;
-          }
+          node_->get_parameter_or(makeParameterName(PARAM_BASE_NAME, controller_name, "max_effort"), max_effort, 0.0);
+          RCLCPP_INFO_STREAM(getLogger(), controller_name << " will command a max effort of: " << max_effort);
 
-          new_handle = std::make_shared<GripperControllerHandle>(node_, controller_name, action_ns, max_effort);
+          new_handle = std::make_shared<GripperCommandControllerHandle>(node_, controller_name, action_ns, max_effort);
           bool parallel_gripper = false;
           if (node_->get_parameter(makeParameterName(PARAM_BASE_NAME, controller_name, "parallel"), parallel_gripper) &&
               parallel_gripper)
@@ -165,27 +162,57 @@ class MoveItSimpleControllerManager : public moveit_controller_manager::MoveItCo
                                                    << controller_joints.size() << " are specified");
               continue;
             }
-            static_cast<GripperControllerHandle*>(new_handle.get())
+            static_cast<GripperCommandControllerHandle*>(new_handle.get())
                 ->setParallelJawGripper(controller_joints[0], controller_joints[1]);
           }
           else
           {
             std::string command_joint;
-            if (!node_->get_parameter(makeParameterName(PARAM_BASE_NAME, controller_name, "command_joint"),
-                                      command_joint))
-              command_joint = controller_joints[0];
+            node_->get_parameter_or(makeParameterName(PARAM_BASE_NAME, controller_name, "command_joint"), command_joint,
+                                    controller_joints[0]);
 
-            static_cast<GripperControllerHandle*>(new_handle.get())->setCommandJoint(command_joint);
+            static_cast<GripperCommandControllerHandle*>(new_handle.get())->setCommandJoint(command_joint);
           }
 
           bool allow_failure;
           node_->get_parameter_or(makeParameterName(PARAM_BASE_NAME, controller_name, "allow_failure"), allow_failure,
                                   false);
-          static_cast<GripperControllerHandle*>(new_handle.get())->allowFailure(allow_failure);
+          static_cast<GripperCommandControllerHandle*>(new_handle.get())->allowFailure(allow_failure);
 
           RCLCPP_INFO_STREAM(getLogger(), "Added GripperCommand controller for " << controller_name);
           controllers_[controller_name] = new_handle;
         }
+        else if (type == "ParallelGripperCommand")
+        {
+          double max_effort;
+          node_->get_parameter_or(makeParameterName(PARAM_BASE_NAME, controller_name, "max_effort"), max_effort, 0.0);
+          if (max_effort > 0.0)
+          {
+            RCLCPP_INFO_STREAM(getLogger(), controller_name << " will command a max effort of: " << max_effort);
+          }
+
+          double max_velocity;
+          node_->get_parameter_or(makeParameterName(PARAM_BASE_NAME, controller_name, "max_velocity"), max_velocity,
+                                  0.0);
+          if (max_effort > 0.0)
+          {
+            RCLCPP_INFO_STREAM(getLogger(), controller_name << " will command a max velocity of: " << max_velocity);
+          }
+
+          new_handle = std::make_shared<ParallelGripperCommandControllerHandle>(node_, controller_name, action_ns,
+                                                                                max_effort, max_velocity);
+
+          if (controller_joints.size() > 1)
+          {
+            RCLCPP_WARN_STREAM(getLogger(), "ParallelGripperCommand controller only supports commanding a single joint "
+                                            "and multiple joint names were specified. Assuming control of joint: "
+                                                << controller_joints[0]);
+          }
+          static_cast<ParallelGripperCommandControllerHandle*>(new_handle.get())->setCommandJoint(controller_joints[0]);
+
+          RCLCPP_INFO_STREAM(getLogger(), "Added ParallelGripperCommand controller for " << controller_name);
+          controllers_[controller_name] = new_handle;
+        }
         else if (type == "FollowJointTrajectory")
         {
           new_handle = std::make_shared<FollowJointTrajectoryControllerHandle>(node_, controller_name, action_ns);