diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index d84a18e8b6..04a42b8906 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -3508,10 +3508,11 @@ class DevtoolIdeSdkTests(DevtoolBase):
 
         # Start gdbserver on target using the task command (keep the ssh connection open while debugging)
         ssh_gdbserver_cmd = [task_command] + task_args
-        # Fix shell command escaping - remove extra quotes from the last argument
-        # The task_args likely contains a quoted shell command that needs to be unquoted
+        # The tasks.json argument is formatted for an intermediate shell. Strip
+        # its quotes and restore dollar expansions before passing it directly to
+        # SSH via subprocess.
         if len(ssh_gdbserver_cmd) > 0 and ssh_gdbserver_cmd[-1].startswith('"') and ssh_gdbserver_cmd[-1].endswith('"'):
-            ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1]  # Remove surrounding quotes
+            ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1].replace('\\$', '$')
         self.logger.debug(f"Starting gdbserver with command: {' '.join(ssh_gdbserver_cmd)}")
         with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger):
             # Give gdbserver a moment to start
diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py
index deb6050907..6c88e0e34e 100644
--- a/scripts/lib/devtool/ide_plugins/__init__.py
+++ b/scripts/lib/devtool/ide_plugins/__init__.py
@@ -101,6 +101,12 @@ class DebuggerCrossConfig:
         hex_port = "%04X" % self.debug_server_port
         return "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null" % hex_port
 
+    def get_debug_server_ready_marker(self, port):
+        return "%s ready on port %s" % (self.DEBUG_SERVER_NAME, port)
+
+    def get_debug_server_ready_marker_pattern(self):
+        return "^%s$" % self.get_debug_server_ready_marker("[0-9]+")
+
     def _target_wait_for_tcp_port_cmd(self, pid_var=None):
         cleanup = ""
         if pid_var:
@@ -130,6 +136,7 @@ class GdbCrossConfig(DebuggerCrossConfig):
     gdbinit / gdb wrapper scripts used by ide=none as well as the
     target-side tmp/pid/log paths consumed by the gdbserver start command.
     """
+    DEBUG_SERVER_NAME = "gdbserver"
 
     def __init__(self, image_recipe, modified_recipe, binary,
                  default_mode=DebuggerServerModes.MULTI):
@@ -172,26 +179,35 @@ class GdbCrossConfig(DebuggerCrossConfig):
           "\"/bin/sh -c '/usr/bin/gdbserver --once :1234 /usr/bin/cmake-example'\""
         """
         if server_mode == DebuggerServerModes.ONCE:
-            gdbserver_cmd_start = "%s --once :%s %s" % (
+            gdbserver_cmd_start = "%s --once :%s %s & " % (
                 self.debugger_cross.debug_server_path, self.debug_server_port, self.binary.binary_path)
+            gdbserver_cmd_start += "_gdbserver_pid=\\$!; "
+            gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd(
+                "gdbserver_pid") + " "
+            gdbserver_cmd_start += "echo %s; wait \\$_gdbserver_pid" % (
+                self.get_debug_server_ready_marker(self.debug_server_port))
         elif server_mode == DebuggerServerModes.ATTACH:
             pid_command = self.binary.pid_command
             if pid_command:
-                gdbserver_cmd_start = "%s --attach :%s \\$(%s)" % (
+                gdbserver_cmd_start = "%s --attach :%s \\$(%s) & " % (
                     self.debugger_cross.debug_server_path,
                     self.debug_server_port,
                     pid_command)
+                gdbserver_cmd_start += "_gdbserver_pid=\\$!; "
+                gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd(
+                    "gdbserver_pid") + " "
+                gdbserver_cmd_start += "echo %s; wait \\$_gdbserver_pid" % (
+                    self.get_debug_server_ready_marker(self.debug_server_port))
             else:
                 raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path)
         elif server_mode == DebuggerServerModes.MULTI:
-            hex_port = "%04X" % self.debug_server_port
-            gdbserver_cmd_start = "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null && exit 0; " % hex_port
+            gdbserver_cmd_start = self._target_tcp_port_check_cmd() + " && exit 0; "
             gdbserver_cmd_start += "mkdir -p %s; " % self._gdbserver_tmp_dir(server_mode)
-            gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & " % (
+            gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & _gdbserver_pid=\\$!; " % (
                 self.debugger_cross.debug_server_path, self.debug_server_port, self._gdbserver_log_file(server_mode))
-            gdbserver_cmd_start += "echo \\$! > %s; " % self._gdbserver_pid_file(server_mode)
-            gdbserver_cmd_start += "_w=0; while ! grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null; " % hex_port
-            gdbserver_cmd_start += "do _w=\\$((_w+1)); [ \\$_w -lt 100 ] || exit 1; sleep 0.1; done;"
+            gdbserver_cmd_start += "echo \\$_gdbserver_pid > %s; " % self._gdbserver_pid_file(server_mode)
+            gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd(
+                "gdbserver_pid")
         else:
             raise DevtoolError("Unsupported gdbserver mode: %s" % server_mode)
         return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\""
@@ -235,10 +251,7 @@ class LldbServerConfig(DebuggerCrossConfig):
         # lldb-server 21.x and the remote lldb client connects from the host.
         # Start from /tmp because lldb-server creates temp files in its cwd and
         # the SSH default cwd (/home/root) may not exist on a minimal image.
-        if mode == DebuggerServerModes.ONCE:
-            cmd = "cd /tmp && %s platform --one-shot --server --listen *:%s" % (
-                lldb_server, self.debug_server_port)
-        elif mode == DebuggerServerModes.MULTI:
+        if mode == DebuggerServerModes.MULTI:
             pid_file = self._lldb_server_pid_file(mode)
             tmp_dir = self._lldb_server_tmp_dir(mode)
             log_file = self._lldb_server_log_file(mode)
@@ -252,8 +265,8 @@ class LldbServerConfig(DebuggerCrossConfig):
                 "lldb_server_pid")
         else:
             raise DevtoolError(
-                "lldb-server does not support mode %s "
-                "(ATTACH is handled client-side with 'process attach')" % mode)
+                "lldb-server only supports MULTI mode; "
+                "ATTACH is handled client-side with 'process attach': %s" % mode)
         return "\"/bin/sh -c '" + cmd + "'\""
 
     def _target_kill_cmd(self):
diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py
index 9faba3f2d1..5b66c56e35 100644
--- a/scripts/lib/devtool/ide_plugins/ide_code.py
+++ b/scripts/lib/devtool/ide_plugins/ide_code.py
@@ -609,6 +609,11 @@ class IdeVSCode(IdeBase):
                     # ONCE / ATTACH: gdbserver runs in the foreground for the
                     # whole session, so VSCode needs isBackground + a pattern
                     # matcher to avoid waiting for the task to exit.
+                    if server_mode in (DebuggerServerModes.ONCE,
+                                       DebuggerServerModes.ATTACH):
+                        ends_pattern = cross_debug_config.get_debug_server_ready_marker_pattern()
+                    else:
+                        ends_pattern = "."
                     new_task = {
                         "label": cross_debug_config.id_pretty_mode(server_mode),
                         "type": "shell",
@@ -628,7 +633,7 @@ class IdeVSCode(IdeBase):
                                 "background": {
                                     "activeOnStart": True,
                                     "beginsPattern": ".",
-                                    "endsPattern": ".",
+                                    "endsPattern": ends_pattern,
                                 }
                             }
                         ]
