diff --git a/rclrs/src/action/server.rs b/rclrs/src/action/server.rs
index dfb4e9659..0b0357224 100644
--- a/rclrs/src/action/server.rs
+++ b/rclrs/src/action/server.rs
@@ -197,9 +197,7 @@ where
         mut request_id: rmw_request_id_t,
         accepted: bool,
     ) -> Result<(), RclrsError> {
-        type RmwResponse<T> = <<<T as ActionImpl>::SendGoalService as Service>::Response as Message>::RmwMsg;
-        let mut response_rmw = RmwResponse::<T>::default();
-        <T as ActionImpl>::set_goal_response_accepted(&mut response_rmw, accepted);
+        let mut response_rmw = <T as ActionImpl>::create_goal_response(accepted, (0, 0));
         let handle = &*self.handle.lock();
         let result = unsafe {
             // SAFETY: The action server handle is locked and so synchronized with other
@@ -210,7 +208,7 @@ where
             rcl_action_send_goal_response(
                 handle,
                 &mut request_id,
-                &mut response_rmw as *mut RmwResponse<T> as *mut _,
+                &mut response_rmw as *mut _ as *mut _,
             )
         }
         .ok();
@@ -242,7 +240,7 @@ where
             Err(err) => return Err(err),
         };
 
-        let uuid = GoalUuid(<T as ActionImpl>::get_goal_request_uuid(&request));
+        let uuid = GoalUuid(*<T as ActionImpl>::get_goal_request_uuid(&request));
 
         let response: GoalResponse = {
             todo!("Optionally convert request to an idiomatic type for the user's callback.");
@@ -522,7 +520,7 @@ where
             Err(err) => return Err(err),
         };
 
-        let uuid = GoalUuid(<T as ActionImpl>::get_result_request_uuid(&request));
+        let uuid = GoalUuid(*<T as ActionImpl>::get_result_request_uuid(&request));
 
         let goal_exists = unsafe {
             // SAFETY: No preconditions
@@ -640,7 +638,7 @@ where
 
     pub(crate) fn publish_feedback(&self, goal_id: &GoalUuid, feedback: &<T as rosidl_runtime_rs::Action>::Feedback) -> Result<(), RclrsError> {
         let feedback_rmw = <<T as rosidl_runtime_rs::Action>::Feedback as Message>::into_rmw_message(std::borrow::Cow::Borrowed(feedback));
-        let mut feedback_msg = <T as rosidl_runtime_rs::ActionImpl>::create_feedback_message(&goal_id.0, &*feedback_rmw);
+        let mut feedback_msg = <T as rosidl_runtime_rs::ActionImpl>::create_feedback_message(&goal_id.0, feedback_rmw.into_owned());
         unsafe {
             // SAFETY: The action server is locked through the handle, meaning that no other
             // non-thread-safe functions can be called on it at the same time. The feedback_msg is
diff --git a/rosidl_generator_rs/resource/action.rs.em b/rosidl_generator_rs/resource/action.rs.em
index 3257d44b5..4c72b90b8 100644
--- a/rosidl_generator_rs/resource/action.rs.em
+++ b/rosidl_generator_rs/resource/action.rs.em
@@ -6,6 +6,8 @@ from rosidl_parser.definition import (
     ACTION_GOAL_SUFFIX,
     ACTION_RESULT_SERVICE_SUFFIX,
     ACTION_RESULT_SUFFIX,
+    SERVICE_REQUEST_MESSAGE_SUFFIX,
+    SERVICE_RESPONSE_MESSAGE_SUFFIX,
 )
 
 action_msg_specs = []
@@ -100,31 +102,74 @@ impl rosidl_runtime_rs::ActionImpl for @(type_name) {
   type CancelGoalService = action_msgs::srv::rmw::CancelGoal;
   type GetResultService = crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX);
 
-  fn get_goal_request_uuid(request: &<<Self::SendGoalService as rosidl_runtime_rs::Service>::Request as rosidl_runtime_rs::Message>::RmwMsg) -> [u8; 16] {
-    request.goal_id.uuid
+  fn create_goal_request(goal_id: &[u8; 16], goal: crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SUFFIX)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
+    crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
+      goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id },
+      goal,
+    }
+  }
+
+  fn get_goal_request_uuid(request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX)) -> &[u8; 16] {
+    &request.goal_id.uuid
+  }
+
+  fn create_goal_response(accepted: bool, stamp: (i32, u32)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
+    crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
+      accepted,
+      stamp: builtin_interfaces::msg::rmw::Time {
+        sec: stamp.0,
+        nanosec: stamp.1,
+      },
+    }
+  }
+
+  fn get_goal_response_accepted(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> bool {
+    response.accepted
   }
 
-  fn set_goal_response_accepted(response: &mut <<Self::SendGoalService as rosidl_runtime_rs::Service>::Response as rosidl_runtime_rs::Message>::RmwMsg, accepted: bool) {
-    response.accepted = accepted;
+  fn get_goal_response_stamp(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> (i32, u32) {
+    (response.stamp.sec, response.stamp.nanosec)
   }
 
-  fn create_feedback_message(goal_id: &[u8; 16], feedback: &<<Self as rosidl_runtime_rs::Action>::Feedback as rosidl_runtime_rs::Message>::RmwMsg) -> <Self::FeedbackMessage as rosidl_runtime_rs::Message>::RmwMsg {
-    let mut message = <Self::FeedbackMessage as rosidl_runtime_rs::Message>::RmwMsg::default();
+  fn create_feedback_message(goal_id: &[u8; 16], feedback: crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX) {
+    let mut message = crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)::default();
     message.goal_id.uuid = *goal_id;
-    message.feedback = feedback.clone();
+    message.feedback = feedback;
     message
   }
 
-  fn get_result_request_uuid(request: &<<Self::GetResultService as rosidl_runtime_rs::Service>::Request as rosidl_runtime_rs::Message>::RmwMsg) -> [u8; 16] {
-    request.goal_id.uuid
+  fn get_feedback_message_uuid(feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)) -> &[u8; 16] {
+    &feedback.goal_id.uuid
+  }
+
+  fn get_feedback_message_feedback(feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX) {
+    &feedback.feedback
   }
 
-  fn create_result_response(status: i8, result: <<Self as Action>::Result as Message>::RmwMsg) -> <<Self::GetResultService as Service>::Response as Message>::RmwMsg {
-    <<Self::GetResultService as rosidl_runtime_rs::Service>::Response as rosidl_runtime_rs::Message>::RmwMsg {
+  fn create_result_request(goal_id: &[u8; 16]) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
+    crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) {
+      goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id },
+    }
+  }
+
+  fn get_result_request_uuid(request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX)) -> &[u8; 16] {
+    &request.goal_id.uuid
+  }
+
+  fn create_result_response(status: i8, result: crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX)) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
+    crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) {
       status,
       result,
     }
   }
+
+  fn get_result_response_result(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX) {
+    &response.result
+  }
+
+  fn get_result_response_status(response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX)) -> i8 {
+    response.status
+  }
 }
 
 @[end for]
diff --git a/rosidl_runtime_rs/src/traits.rs b/rosidl_runtime_rs/src/traits.rs
index 4ac60e515..d0402dd5f 100644
--- a/rosidl_runtime_rs/src/traits.rs
+++ b/rosidl_runtime_rs/src/traits.rs
@@ -196,18 +196,42 @@ pub trait ActionImpl: 'static + Action {
     /// The get_result service associated with this action.
     type GetResultService: Service;
 
+    /// Create a goal request message with the given UUID and goal.
+    fn create_goal_request(goal_id: &[u8; 16], goal: <<Self as Action>::Goal as Message>::RmwMsg) -> <<Self::SendGoalService as Service>::Request as Message>::RmwMsg;
+
     /// Get the UUID of a goal request.
-    fn get_goal_request_uuid(request: &<<Self::SendGoalService as Service>::Request as Message>::RmwMsg) -> [u8; 16];
+    fn get_goal_request_uuid(request: &<<Self::SendGoalService as Service>::Request as Message>::RmwMsg) -> &[u8; 16];
+
+    /// Create a goal response message with the given acceptance and timestamp.
+    fn create_goal_response(accepted: bool, stamp: (i32, u32)) -> <<Self::SendGoalService as Service>::Response as Message>::RmwMsg;
 
-    /// Sets the `accepted` field of a goal response.
-    fn set_goal_response_accepted(response: &mut <<Self::SendGoalService as Service>::Response as Message>::RmwMsg, accepted: bool);
+    /// Get the `accepted` field of a goal response.
+    fn get_goal_response_accepted(response: &<<Self::SendGoalService as Service>::Response as Message>::RmwMsg) -> bool;
+
+    /// Get the `stamp` field of a goal response.
+    fn get_goal_response_stamp(response: &<<Self::SendGoalService as Service>::Response as Message>::RmwMsg) -> (i32, u32);
 
     /// Create a feedback message with the given goal ID and contents.
-    fn create_feedback_message(goal_id: &[u8; 16], feedback: &<<Self as Action>::Feedback as Message>::RmwMsg) -> <Self::FeedbackMessage as Message>::RmwMsg;
+    fn create_feedback_message(goal_id: &[u8; 16], feedback: <<Self as Action>::Feedback as Message>::RmwMsg) -> <Self::FeedbackMessage as Message>::RmwMsg;
+
+    /// Get the UUID of a feedback message.
+    fn get_feedback_message_uuid(feedback: &<Self::FeedbackMessage as Message>::RmwMsg) -> &[u8; 16];
+
+    /// Get the feedback of a feedback message.
+    fn get_feedback_message_feedback(feedback: &<Self::FeedbackMessage as Message>::RmwMsg) -> &<<Self as Action>::Feedback as Message>::RmwMsg;
+
+    /// Create a result request message with the given goal ID.
+    fn create_result_request(goal_id: &[u8; 16]) -> <<Self::GetResultService as Service>::Request as Message>::RmwMsg;
 
     /// Get the UUID of a result request.
-    fn get_result_request_uuid(request: &<<Self::GetResultService as Service>::Request as Message>::RmwMsg) -> [u8; 16];
+    fn get_result_request_uuid(request: &<<Self::GetResultService as Service>::Request as Message>::RmwMsg) -> &[u8; 16];
 
     /// Create a result response message with the given status and contents.
     fn create_result_response(status: i8, result: <<Self as Action>::Result as Message>::RmwMsg) -> <<Self::GetResultService as Service>::Response as Message>::RmwMsg;
+
+    /// Get the result of a result response.
+    fn get_result_response_result(response: &<<Self::GetResultService as Service>::Response as Message>::RmwMsg) -> &<<Self as Action>::Result as Message>::RmwMsg;
+
+    /// Get the status of a result response.
+    fn get_result_response_status(response: &<<Self::GetResultService as Service>::Response as Message>::RmwMsg) -> i8;
 }