diff --git a/kwalitee/cli.py b/kwalitee/cli.py
index c0efdacb8b052a312904461250ae3c98beec737c..d827b1d7566e2c2ce6583bd988b5c477d6eebf42 100644
--- a/kwalitee/cli.py
+++ b/kwalitee/cli.py
@@ -8,8 +8,6 @@ from pkg_resources import resource_filename
 
 from .repo import Repo
 
-LOG = getLogger('kwalitee.cli')
-
 def _check_cloned(ctx):
     uncloned = []
     for repo in ctx.repos:
@@ -20,6 +18,7 @@ def _check_cloned(ctx):
 
 class CliCtx():
     def __init__(self, config_file):
+        self.log = getLogger('ocrd.kwalitee')
         with open(config_file, 'r') as f_config_file:
             self.config = safe_load(f_config_file.read())
             self.repos = []
@@ -45,9 +44,9 @@ def cli(ctx, config_file, **kwargs): # pylint: disable=unused-argument
 def clone_all(ctx):
     for repo in ctx.repos:
         if repo.is_cloned():
-            LOG.info("Already cloned %s" % repo)
+            ctx.log.info("Already cloned %s" % repo)
         else:
-            LOG.info("Cloning %s" % repo)
+            ctx.log.info("Cloning %s" % repo)
             repo.clone()
 
 @cli.command('pull', help='''
@@ -58,7 +57,7 @@ def clone_all(ctx):
 def pull_all(ctx):
     _check_cloned(ctx)
     for repo in ctx.repos:
-        LOG.info("Pulling %s" % repo)
+        ctx.log.info("Pulling %s" % repo)
         repo.pull()
 
 
@@ -73,7 +72,7 @@ def generate_json(ctx, output=None):
     ret = []
     _check_cloned(ctx)
     for repo in ctx.repos:
-        LOG.info("# Assessing %s" % repo.name)
+        ctx.log.info("# Assessing %s" % repo.name)
         repo.clone()
         ret.append(repo.to_json())
         #  print('%s %s -> %s' % (repo.path.is_dir(), repo.url, repo.path))
@@ -82,3 +81,20 @@ def generate_json(ctx, output=None):
         Path(output).write_text(json_str)
     else:
         print(json_str)
+
+@cli.command('ocrd-tool')
+@click.option('-o', '--output', help="Output file. Omit to print to STDOUT")
+@pass_ctx
+def generate_tool_json(ctx, output=None):
+    '''
+    Return one big list of ocrd tools
+    '''
+    ret = {}
+    _check_cloned(ctx)
+    for repo in ctx.repos:
+        ret = {**ret, **repo.get_ocrd_tools()}
+    json_str = json.dumps(ret, indent=4, sort_keys=True)
+    if output:
+        Path(output).write_text(json_str)
+    else:
+        print(json_str)
diff --git a/kwalitee/repo.py b/kwalitee/repo.py
index 1624e2c38b913908a022f046d93ce911b7e5adbd..d3735b75a85eb3fb046c171ca808b225a332176d 100644
--- a/kwalitee/repo.py
+++ b/kwalitee/repo.py
@@ -5,12 +5,10 @@ from shlex import split as X
 from ocrd_utils import pushd_popd, getLogger
 import requests as R
 
-LOG = getLogger('kwalitee.repo')
-
-
 class Repo():
 
     def __init__(self, config, url, official=False, compliant_cli=False):
+        self.log = getLogger('kwalitee.repo')
         self.url = url
         self.config = config
         self.name = Path(url).name
@@ -30,17 +28,17 @@ class Repo():
 
     def clone(self):
         if self.is_cloned():
-            LOG.debug("Already cloned: %s" % self.path)
+            self.log.debug("Already cloned: %s" % self.path)
             return
         with pushd_popd(self.config['repodir']):
-            LOG.debug("Cloning %s" % self.url)
+            self.log.debug("Cloning %s" % self.url)
             result = self._run('git clone --depth 1 "%s"' % self.url)
-            LOG.debug("Result: %s" % result)
+            self.log.debug("Result: %s" % result)
 
 
     def get_git_stats(self):
         ret = {}
-        LOG.info("  Fetching git info")
+        self.log.info("  Fetching git info")
         with pushd_popd(self.path):
             ret['number_of_commits'] = self._run('git rev-list HEAD --count').stdout
             ret['last_commit'] = self._run(r'git log -1 --format=%cd ').stdout
@@ -50,7 +48,7 @@ class Repo():
 
     def get_file_contents(self):
         ret = {}
-        LOG.info("  Getting file contents")
+        self.log.info("%s  Getting file contents" % self.url)
         with pushd_popd(self.path):
             for path in [Path(x) for x in ['ocrd-tool.json', 'Dockerfile', 'README.md', 'setup.py']]:
                 if path.is_file():
@@ -67,11 +65,15 @@ class Repo():
             ret['name'] = self._run('python3 setup.py --name').stdout
             ret['author'] = self._run('python3 setup.py --author').stdout
             ret['author-email'] = self._run('python3 setup.py --author-email').stdout
-        LOG.info("  Fetching pypi info")
+        self.log.info("  Fetching pypi info")
         response = R.get('https://pypi.python.org/pypi/%s/json' % ret['name'])
         ret['pypi'] = json.loads(response.text) if response.status_code == 200 else None
         return ret
 
+    def get_ocrd_tools(self):
+        ot = json.loads(self.get_file_contents()['ocrd-tool.json'])
+        return ot['tools']
+
     def to_json(self):
         desc = {}
         desc['url'] = self.url