diff --git a/aprsd/aprsd.py b/aprsd/aprsd.py
index 3530cd4..5d5587c 100644
--- a/aprsd/aprsd.py
+++ b/aprsd/aprsd.py
@@ -67,7 +67,8 @@ def main():
     # First import all the possible commands for the CLI
     # The commands themselves live in the cmds directory
     from .cmds import (  # noqa
-        completion, dev, healthcheck, listen, send_message, server,
+        completion, dev, healthcheck, list_plugins, listen, send_message,
+        server,
     )
     cli()
 
diff --git a/aprsd/cmds/list_plugins.py b/aprsd/cmds/list_plugins.py
new file mode 100644
index 0000000..94d51e9
--- /dev/null
+++ b/aprsd/cmds/list_plugins.py
@@ -0,0 +1,59 @@
+import inspect
+import logging
+from textwrap import indent
+
+import click
+from tabulate import tabulate
+
+from aprsd import cli_helper, plugin
+from aprsd.plugins import (
+    email, fortune, location, notify, ping, query, time, version, weather,
+)
+
+from ..aprsd import cli
+
+
+LOG = logging.getLogger("APRSD")
+
+
+@cli.command()
+@cli_helper.add_options(cli_helper.common_options)
+@click.pass_context
+@cli_helper.process_standard_options_no_config
+def list_plugins(ctx):
+    """List the built in plugins available to APRSD."""
+
+    modules = [email, fortune, location, notify, ping, query, time, version, weather]
+    plugins = []
+
+    for module in modules:
+        entries = inspect.getmembers(module, inspect.isclass)
+        for entry in entries:
+            cls = entry[1]
+            if issubclass(cls, plugin.APRSDPluginBase):
+                info = {
+                    "name": cls.__qualname__,
+                    "path": f"{cls.__module__}.{cls.__qualname__}",
+                    "version": cls.version,
+                    "docstring": cls.__doc__,
+                    "short_desc": cls.short_description,
+                }
+
+                if issubclass(cls, plugin.APRSDRegexCommandPluginBase):
+                    info["command_regex"] = cls.command_regex
+                    info["type"] = "RegexCommand"
+
+                if issubclass(cls, plugin.APRSDWatchListPluginBase):
+                    info["type"] = "WatchList"
+
+                plugins.append(info)
+
+    lines = []
+    headers = ("Plugin Name", "Plugin Path", "Type", "Info")
+
+    for entry in plugins:
+        lines.append(
+            (entry["name"], entry["path"], entry["type"], entry["short_desc"]),
+        )
+
+    click.echo(indent(tabulate(lines, headers, disable_numparse=True), " "))
diff --git a/aprsd/plugin.py b/aprsd/plugin.py
index cc3b85c..53acec1 100644
--- a/aprsd/plugin.py
+++ b/aprsd/plugin.py
@@ -12,6 +12,7 @@ import threading
 import pluggy
 from thesmuggler import smuggle
 
+import aprsd
 from aprsd import client, messaging, packets, threads
 
 
@@ -51,7 +52,7 @@ class APRSDPluginBase(metaclass=abc.ABCMeta):
     config = None
     rx_count = 0
     tx_count = 0
-    version = "1.0"
+    version = aprsd.__version__
 
     # Holds the list of APRSDThreads that the plugin creates
     threads = []
diff --git a/aprsd/plugins/email.py b/aprsd/plugins/email.py
index 029be53..4a0d0f4 100644
--- a/aprsd/plugins/email.py
+++ b/aprsd/plugins/email.py
@@ -59,9 +59,9 @@ class EmailInfo:
 class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
     """Email Plugin."""
 
-    version = "1.0"
     command_regex = "^-.*"
     command_name = "email"
+    short_description = "Send and Receive email"
 
     # message_number:time combos so we don't resend the same email in
     # five mins {int:int}
diff --git a/aprsd/plugins/fortune.py b/aprsd/plugins/fortune.py
index f76ad56..b30fc73 100644
--- a/aprsd/plugins/fortune.py
+++ b/aprsd/plugins/fortune.py
@@ -11,9 +11,9 @@ LOG = logging.getLogger("APRSD")
 class FortunePlugin(plugin.APRSDRegexCommandPluginBase):
     """Fortune."""
 
-    version = "1.0"
     command_regex = "^[fF]"
     command_name = "fortune"
+    short_description = "Give me a fortune"
 
     fortune_path = None
 
diff --git a/aprsd/plugins/location.py b/aprsd/plugins/location.py
index a0381d9..939661b 100644
--- a/aprsd/plugins/location.py
+++ b/aprsd/plugins/location.py
@@ -11,9 +11,9 @@ LOG = logging.getLogger("APRSD")
 class LocationPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
     """Location!"""
 
-    version = "1.0"
     command_regex = "^[lL]"
     command_name = "location"
+    short_description = "Where in the world is a CALLSIGN's last GPS beacon?"
 
     def setup(self):
         self.ensure_aprs_fi_key()
diff --git a/aprsd/plugins/notify.py b/aprsd/plugins/notify.py
index 59adc5a..b7ae4c5 100644
--- a/aprsd/plugins/notify.py
+++ b/aprsd/plugins/notify.py
@@ -15,7 +15,7 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
     seen was older than the configured age limit.
     """
 
-    version = "1.0"
+    short_description = "Notify me when a CALLSIGN is recently seen on APRS-IS"
 
     def process(self, packet):
         LOG.info("NotifySeenPlugin")
diff --git a/aprsd/plugins/ping.py b/aprsd/plugins/ping.py
index 77d78ec..b709574 100644
--- a/aprsd/plugins/ping.py
+++ b/aprsd/plugins/ping.py
@@ -10,9 +10,9 @@ LOG = logging.getLogger("APRSD")
 class PingPlugin(plugin.APRSDRegexCommandPluginBase):
     """Ping."""
 
-    version = "1.0"
     command_regex = "^[pP]"
     command_name = "ping"
+    short_description = "reply with a Pong!"
 
     @trace.trace
     def process(self, packet):
diff --git a/aprsd/plugins/query.py b/aprsd/plugins/query.py
index a658e74..bdc273a 100644
--- a/aprsd/plugins/query.py
+++ b/aprsd/plugins/query.py
@@ -11,9 +11,9 @@ LOG = logging.getLogger("APRSD")
 class QueryPlugin(plugin.APRSDRegexCommandPluginBase):
     """Query command."""
 
-    version = "1.0"
     command_regex = r"^\!.*"
     command_name = "query"
+    short_description = "APRSD Owner command to query messages in the MsgTrack"
 
     @trace.trace
     def process(self, packet):
diff --git a/aprsd/plugins/time.py b/aprsd/plugins/time.py
index faf9a5a..8cfdd83 100644
--- a/aprsd/plugins/time.py
+++ b/aprsd/plugins/time.py
@@ -14,9 +14,9 @@ LOG = logging.getLogger("APRSD")
 class TimePlugin(plugin.APRSDRegexCommandPluginBase):
     """Time command."""
 
-    version = "1.0"
     command_regex = "^[tT]"
     command_name = "time"
+    short_description = "What is the current local time."
 
     def _get_local_tz(self):
         return pytz.timezone(time.strftime("%Z"))
@@ -52,9 +52,9 @@ class TimePlugin(plugin.APRSDRegexCommandPluginBase):
 class TimeOpenCageDataPlugin(TimePlugin, plugin.APRSFIKEYMixin):
     """geocage based timezone fetching."""
 
-    version = "1.0"
     command_regex = "^[tT]"
     command_name = "time"
+    short_description = "Current time of GPS beacon timezone. Uses OpenCage"
 
     def setup(self):
         self.ensure_aprs_fi_key()
@@ -114,9 +114,9 @@ class TimeOpenCageDataPlugin(TimePlugin, plugin.APRSFIKEYMixin):
 class TimeOWMPlugin(TimePlugin, plugin.APRSFIKEYMixin):
     """OpenWeatherMap based timezone fetching."""
 
-    version = "1.0"
     command_regex = "^[tT]"
     command_name = "time"
+    short_description = "Current time of GPS beacon's timezone. Uses OpenWeatherMap"
 
     def setup(self):
         self.ensure_aprs_fi_key()
diff --git a/aprsd/plugins/version.py b/aprsd/plugins/version.py
index 2a5e41a..6a05690 100644
--- a/aprsd/plugins/version.py
+++ b/aprsd/plugins/version.py
@@ -10,9 +10,9 @@ LOG = logging.getLogger("APRSD")
 class VersionPlugin(plugin.APRSDRegexCommandPluginBase):
     """Version of APRSD Plugin."""
 
-    version = "1.0"
     command_regex = "^[vV]"
     command_name = "version"
+    short_description = "What is the APRSD Version"
 
     # message_number:time combos so we don't resend the same email in
     # five mins {int:int}
diff --git a/aprsd/plugins/weather.py b/aprsd/plugins/weather.py
index 0817c8e..c656073 100644
--- a/aprsd/plugins/weather.py
+++ b/aprsd/plugins/weather.py
@@ -23,9 +23,9 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
     "weather" - returns weather near the calling callsign
     """
 
-    version = "1.0"
     command_regex = "^[wW]"
     command_name = "USWeather"
+    short_description = "Provide USA only weather of GPS Beacon location"
 
     @trace.trace
     def process(self, packet):
@@ -86,9 +86,9 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase):
 
     """
 
-    version = "1.0"
     command_regex = "^[metar]"
     command_name = "USMetar"
+    short_description = "USA only METAR of GPS Beacon location"
 
     @trace.trace
     def process(self, packet):
@@ -178,9 +178,9 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
 
     """
 
-    version = "1.0"
     command_regex = "^[wW]"
     command_name = "OpenWeatherMap"
+    short_description = "OpenWeatherMap weather of GPS Beacon location"
 
     def help(self):
         _help = [
@@ -308,9 +308,9 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
     docker build -f Dockerfile -t avwx-api:master .
     """
 
-    version = "1.0"
     command_regex = "^[mM]"
     command_name = "AVWXWeather"
+    short_description = "AVWX weather of GPS Beacon location"
 
     def help(self):
         _help = [
diff --git a/requirements.in b/requirements.in
index 1d23d98..94cc54b 100644
--- a/requirements.in
+++ b/requirements.in
@@ -20,3 +20,4 @@ thesmuggler
 update_checker
 flask-socketio
 eventlet
+tabulate
diff --git a/requirements.txt b/requirements.txt
index 9560913..9bf8a0a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -104,6 +104,8 @@ six==1.16.0
     #   imapclient
     #   pyopenssl
     #   signalslot
+tabulate==0.8.9
+    # via -r requirements.in
 thesmuggler==1.0.1
     # via -r requirements.in
 update-checker==0.18.0