diff --git a/swank.lisp b/swank.lisp
index 1e37757..16b28e2 100644
--- a/swank.lisp
+++ b/swank.lisp
@@ -46,6 +46,8 @@
            #:*swank-pprint-bindings*
            #:*record-repl-results*
            #:*inspector-verbose*
+           ;; Entry point for extendability
+           #:*event-hook*
            ;; This is SETFable.
            #:debug-on-swank-error
            ;; These are re-exported directly from the backend:
@@ -224,6 +226,13 @@ include some arbitrary initial value like NIL."
   (dolist (function functions)
     (apply function arguments)))
 
+(defun run-hook-with-args-until-success (functions &rest arguments)
+  "Call each of FUNCTIONS with ARGUMENTS until one returns non-nil."
+  (dolist (function functions)
+    (let ((result (apply function arguments)))
+      (when result
+        (return result)))))
+
 (defvar *new-connection-hook* '()
   "This hook is run each time a connection is established.
 The connection structure is given as the argument.
@@ -1188,45 +1197,51 @@ The processing is done in the extent of the toplevel restart."
              (repl-loop connection)))
          :name name))
 
+(defvar *event-hook* '()
+  "Hook run when an event is to be handled.
+If a function return non-nil, it stops further event hook execution,
+as well as the default event dispatching.")
+
 (defun dispatch-event (event)
   "Handle an event triggered either by Emacs or within Lisp."
   (log-event "dispatch-event: ~s~%" event)
-  (destructure-case event
-    ((:emacs-rex form package thread-id id)
-     (let ((thread (thread-for-evaluation thread-id)))
-       (cond (thread 
-              (push thread *active-threads*)
-              (send-event thread `(:emacs-rex ,form ,package ,id)))
-             (t
-              (encode-message 
-               (list :invalid-rpc id
-                     (format nil "Thread not found: ~s" thread-id))
-               (current-socket-io))))))
-    ((:return thread &rest args)
-     (let ((tail (member thread *active-threads*)))
-       (setq *active-threads* (nconc (ldiff *active-threads* tail)
-				     (cdr tail))))
-     (encode-message `(:return ,@args) (current-socket-io)))
-    ((:emacs-interrupt thread-id)
-     (interrupt-worker-thread thread-id))
-    (((:write-string
-       :debug :debug-condition :debug-activate :debug-return :channel-send
-       :presentation-start :presentation-end
-       :new-package :new-features :ed :indentation-update
-       :eval :eval-no-wait :background-message :inspect :ping
-       :y-or-n-p :read-from-minibuffer :read-string :read-aborted)
-      &rest _)
-     (declare (ignore _))
-     (encode-message event (current-socket-io)))
-    (((:emacs-pong :emacs-return :emacs-return-string) thread-id &rest args)
-     (send-event (find-thread thread-id) (cons (car event) args)))
-    ((:emacs-channel-send channel-id msg)
-     (let ((ch (find-channel channel-id)))
-       (send-event (channel-thread ch) `(:emacs-channel-send ,ch ,msg))))
-    ((:reader-error packet condition)
-     (encode-message `(:reader-error ,packet 
-                                     ,(safe-condition-message condition))
-                     (current-socket-io)))))
+  (or (run-hook-with-args-until-success *event-hook* event)
+      (destructure-case event
+        ((:emacs-rex form package thread-id id)
+         (let ((thread (thread-for-evaluation thread-id)))
+           (cond (thread 
+                  (push thread *active-threads*)
+                  (send-event thread `(:emacs-rex ,form ,package ,id)))
+                 (t
+                  (encode-message 
+                   (list :invalid-rpc id
+                         (format nil "Thread not found: ~s" thread-id))
+                   (current-socket-io))))))
+        ((:return thread &rest args)
+         (let ((tail (member thread *active-threads*)))
+           (setq *active-threads* (nconc (ldiff *active-threads* tail)
+                                         (cdr tail))))
+         (encode-message `(:return ,@args) (current-socket-io)))
+        ((:emacs-interrupt thread-id)
+         (interrupt-worker-thread thread-id))
+        (((:write-string
+           :debug :debug-condition :debug-activate :debug-return :channel-send
+           :presentation-start :presentation-end
+           :new-package :new-features :ed :indentation-update
+           :eval :eval-no-wait :background-message :inspect :ping
+           :y-or-n-p :read-from-minibuffer :read-string :read-aborted)
+          &rest _)
+         (declare (ignore _))
+         (encode-message event (current-socket-io)))
+        (((:emacs-pong :emacs-return :emacs-return-string) thread-id &rest args)
+         (send-event (find-thread thread-id) (cons (car event) args)))
+        ((:emacs-channel-send channel-id msg)
+         (let ((ch (find-channel channel-id)))
+           (send-event (channel-thread ch) `(:emacs-channel-send ,ch ,msg))))
+        ((:reader-error packet condition)
+         (encode-message `(:reader-error ,packet 
+                                         ,(safe-condition-message condition))
+                         (current-socket-io))))))
 
 (defvar *event-queue* '())
 (defvar *events-enqueued* 0)
