diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
index d38106e973..eb46917d00 100644
--- a/include/mcdstub/mcdstub.h
+++ b/include/mcdstub/mcdstub.h
@@ -36,6 +36,20 @@
 /* tcp query packet values templates */
 #define DEVICE_NAME_TEMPLATE(s) "qemu-" #s "-device"
 
+/* state strings */
+#define STATE_STR_UNKNOWN(d) "cpu " #d " in unknown state"
+#define STATE_STR_DEBUG(d) "cpu " #d " in debug state"
+#define STATE_STR_RUNNING(d) "cpu " #d " running"
+#define STATE_STR_HALTED(d) "cpu " #d " currently halted"
+#define STATE_STR_INIT_HALTED "vm halted since boot"
+#define STATE_STR_INIT_RUNNING "vm running since boot"
+#define STATE_STR_BREAK_HW "stopped beacuse of HW breakpoint"
+#define STATE_STEP_PERFORMED "stopped beacuse of single step"
+#define STATE_STR_BREAK_READ(d) "stopped beacuse of read access at " #d
+#define STATE_STR_BREAK_WRITE(d) "stopped beacuse of write access at " #d
+#define STATE_STR_BREAK_RW(d) "stopped beacuse of read or write access at " #d
+#define STATE_STR_BREAK_UNKNOWN "stopped for unknown reason"
+
 typedef struct MCDProcess {
     uint32_t pid;
     bool attached;
@@ -67,6 +81,12 @@ enum RSState {
     RS_DATAEND,
 };
 
+typedef struct breakpoint_st {
+    uint32_t type;
+    uint64_t address;
+    uint32_t id;
+} breakpoint_st;
+
 typedef struct mcd_trigger_into_st {
     char type[ARGUMENT_STRING_LENGTH];
     char option[ARGUMENT_STRING_LENGTH];
@@ -74,6 +94,17 @@ typedef struct mcd_trigger_into_st {
     uint32_t nr_trigger;
 } mcd_trigger_into_st;
 
+typedef struct mcd_cpu_state_st {
+    const char *state;
+    bool memory_changed;
+    bool registers_changed;
+    bool target_was_stopped;
+    uint32_t bp_type;
+    uint64_t bp_address;
+    const char *stop_str;
+    const char *info_str;
+} mcd_cpu_state_st;
+
 typedef struct MCDState {
     bool init;       /* have we been initialised? */
     CPUState *c_cpu; /* current CPU for everything */
@@ -506,6 +537,16 @@ void handle_close_core(GArray *params, void *user_ctx);
  */
 void handle_open_server(GArray *params, void *user_ctx);
 
+/**
+ * handle_query_state() - Handler for the state query.
+ *
+ * This function collects all data stored in the
+ * cpu_state member of the mcdserver_state and formats and sends it to the
+ * library.
+ * @params: GArray with all TCP packet parameters.
+ */
+void handle_query_state(GArray *params, void *user_ctx);
+
 /* helpers */
 
 /**
diff --git a/mcdstub/mcdstub.c b/mcdstub/mcdstub.c
index ca98d01ee7..657f80d2a2 100644
--- a/mcdstub/mcdstub.c
+++ b/mcdstub/mcdstub.c
@@ -95,6 +95,15 @@ void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
     mcd_query_cmds_table[cmd_number] = query_cores;
     cmd_number++;
 
+
+    MCDCmdParseEntry query_state = {
+        .handler = handle_query_state,
+        .cmd = QUERY_ARG_STATE,
+    };
+    strcpy(query_state.schema, (char[2]) { ARG_SCHEMA_CORENUM, '\0' });
+    mcd_query_cmds_table[cmd_number] = query_state;
+}
+
 void reset_mcdserver_state(void)
 {
     g_free(mcdserver_state.processes);
@@ -605,6 +614,100 @@ void mcd_sigterm_handler(int signal)
 }
 #endif
 
+void mcd_vm_state_change(void *opaque, bool running, RunState state)
+{
+    CPUState *cpu = mcdserver_state.c_cpu;
+
+    if (mcdserver_state.state == RS_INACTIVE) {
+        return;
+    }
+
+    if (cpu == NULL) {
+        if (running) {
+            /*
+             * this is the case if qemu starts the vm
+             * before a mcd client is connected
+             */
+            const char *mcd_state;
+            mcd_state = CORE_STATE_RUNNING;
+            const char *info_str;
+            info_str = STATE_STR_INIT_RUNNING;
+            mcdserver_state.cpu_state.state = mcd_state;
+            mcdserver_state.cpu_state.info_str = info_str;
+        }
+        return;
+    }
+
+    const char *mcd_state;
+    const char *stop_str;
+    const char *info_str;
+    uint32_t bp_type = 0;
+    uint64_t bp_address = 0;
+    switch (state) {
+    case RUN_STATE_RUNNING:
+        mcd_state = CORE_STATE_RUNNING;
+        info_str = STATE_STR_RUNNING(cpu->cpu_index);
+        stop_str = "";
+        break;
+    case RUN_STATE_DEBUG:
+        mcd_state = CORE_STATE_DEBUG;
+        info_str = STATE_STR_DEBUG(cpu->cpu_index);
+        if (cpu->watchpoint_hit) {
+            switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) {
+            case BP_MEM_READ:
+                bp_type = MCD_BREAKPOINT_READ;
+                stop_str = STATE_STR_BREAK_READ(cpu->watchpoint_hit->hitaddr);
+                break;
+            case BP_MEM_WRITE:
+                bp_type = MCD_BREAKPOINT_WRITE;
+                stop_str = STATE_STR_BREAK_WRITE(cpu->watchpoint_hit->hitaddr);
+                break;
+            case BP_MEM_ACCESS:
+                bp_type = MCD_BREAKPOINT_RW;
+                stop_str = STATE_STR_BREAK_RW(cpu->watchpoint_hit->hitaddr);
+                break;
+            default:
+                stop_str = STATE_STR_BREAK_UNKNOWN;
+                break;
+            }
+            bp_address = cpu->watchpoint_hit->hitaddr;
+            cpu->watchpoint_hit = NULL;
+        } else if (cpu->singlestep_enabled) {
+            /* we land here when a single step is performed */
+            stop_str = STATE_STEP_PERFORMED;
+        } else {
+            bp_type = MCD_BREAKPOINT_HW;
+            stop_str = STATE_STR_BREAK_HW;
+            tb_flush(cpu);
+        }
+        /* deactivate single step */
+        cpu_single_step(cpu, 0);
+        break;
+    case RUN_STATE_PAUSED:
+        info_str = STATE_STR_HALTED(cpu->cpu_index);
+        mcd_state = CORE_STATE_HALTED;
+        stop_str = "";
+        break;
+    case RUN_STATE_WATCHDOG:
+        info_str = STATE_STR_UNKNOWN(cpu->cpu_index);
+        mcd_state = CORE_STATE_UNKNOWN;
+        stop_str = "";
+        break;
+    default:
+        info_str = STATE_STR_UNKNOWN(cpu->cpu_index);
+        mcd_state = CORE_STATE_UNKNOWN;
+        stop_str = "";
+        break;
+    }
+
+    /* set state for c_cpu */
+    mcdserver_state.cpu_state.state = mcd_state;
+    mcdserver_state.cpu_state.bp_type = bp_type;
+    mcdserver_state.cpu_state.bp_address = bp_address;
+    mcdserver_state.cpu_state.stop_str = stop_str;
+    mcdserver_state.cpu_state.info_str = info_str;
+}
+
 int mcd_put_packet(const char *buf)
 {
     return mcd_put_packet_binary(buf, strlen(buf));
