diff --git a/.gitignore b/.gitignore
index fcc3e0be27b75860d4ed9a1054ba5799db774104..341bfea4f1381b6eb59e548f395859bad35520d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ __pycache__/
 *.egg-info/
 dist/
 build/
+src/caosdb/version.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7f4dea462916b8bb6a792e75283da0cabc8614b..c7e92345efdf7e3c3c94928324c8e31bbee6797a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - An `auth_token` parameter for `caosdb.configure_connection(...)`. This parameter accepts a plain text auth token (which can only be issued by the CaosDB Server). Under the hood, auth tokens are stored plain, instead of urlencoded now.
 - New type of exception: `ConfigurationException` for misconfigurations.
 - Some unit tests, mainly for the `caosdb.connection.authentication` module
+* Advanced setup.py for easy versioning and publication as pypi repository.
 
 ### Changed
 - [pytest](https://docs.pytest.org/en/latest/) is the new preferred unit test frame work.
diff --git a/README.md b/README.md
index 637a8ffcdb35b44289499ca85239b5ba85626df6..242081ba042969b4728ec330ac8d2f4d93626bc9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,3 @@
-<!--THIS FILE HAS BEEN GENERATED BY A SCRIPT. PLEASE DON'T CHANGE IT MANUALLY.-->
-Project migrated to https://gitlab.com/caosdb
-
 # Welcome
 
 This is the **CaosDB Python Client Library** repository and a part of the
diff --git a/release.sh b/release.sh
new file mode 100644
index 0000000000000000000000000000000000000000..49ecd59be2ba720791035ffe1a57e7c21fd23b71
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+python setup.py bdist_wheel
+python -m twine upload dist/*
diff --git a/setup.py b/setup.py
index 792ef41a663eaa289253c450389828e5c138cd15..b461efca3b870c3564dcdbb03d518e3b6cadd8da 100755
--- a/setup.py
+++ b/setup.py
@@ -1,43 +1,171 @@
 #!/usr/bin/env python
 # -*- encoding: utf-8 -*-
 #
-# ** header v3.0
-# This file is a part of the CaosDB Project.
-#
-# Copyright (C) 2018 Research Group Biomedical Physics,
-# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-#
-# ** end header
-#
-"""Install and (nose-)test caosdb."""
-from setuptools import setup, find_packages
-
-setup(name='PyCaosDB',
-      version='0.1.0',
-      description='Python Interface for CaosDB',
-      author='Timm Fitschen',
-      author_email='timm.fitschen@ds.mpg.de',
-      packages=find_packages('src'),
-      package_dir={'': 'src'},
-      install_requires=['lxml>=3.6.4',
-                        'PyYaml>=3.12', 'future', 'PySocks>=1.6.7'],
-      extras_require={'keyring': ['keyring>=13.0.0']},
-      setup_requires=["pytest-runner>=2.0,<3dev"],
-      tests_require=["pytest", "pytest-cov", "coverage>=4.4.2"],
-      package_data={
+#
+"""caosdb"""
+import os
+import subprocess
+import sys
+
+from setuptools import find_packages, setup
+
+########################################################################
+# The following code is largely based on code in numpy
+########################################################################
+#
+# Copyright (c) 2005-2019, NumPy Developers.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#    * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#
+#    * Redistributions in binary form must reproduce the above
+#       copyright notice, this list of conditions and the following
+#       disclaimer in the documentation and/or other materials provided
+#       with the distribution.
+#
+#    * Neither the name of the NumPy Developers nor the names of any
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+########################################################################
+
+MAJOR = 0
+MINOR = 2
+MICRO = 4
+ISRELEASED = False
+VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO)
+
+
+# Return the git revision as a string
+def git_version():
+    def _minimal_ext_cmd(cmd):
+        # construct minimal environment
+        env = {}
+
+        for k in ['SYSTEMROOT', 'PATH', 'HOME']:
+            v = os.environ.get(k)
+
+            if v is not None:
+                env[k] = v
+        # LANGUAGE is used on win32
+        env['LANGUAGE'] = 'C'
+        env['LANG'] = 'C'
+        env['LC_ALL'] = 'C'
+        out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, env=env)
+
+        return out
+
+    try:
+        out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD'])
+        GIT_REVISION = out.strip().decode('ascii')
+    except (subprocess.SubprocessError, OSError):
+        GIT_REVISION = "Unknown"
+
+    return GIT_REVISION
+
+
+def get_version_info():
+    # Adding the git rev number needs to be done inside write_version_py(),
+    # otherwise the import of caosdb.version messes up the build under
+    # Python 3.
+    FULLVERSION = VERSION
+
+    if os.path.exists('.git'):
+        GIT_REVISION = git_version()
+    elif os.path.exists('src/caosdb/version.py'):
+        # must be a source distribution, use existing version file
+        try:
+            from caosdb.version import git_revision as GIT_REVISION
+        except ImportError:
+            raise ImportError("Unable to import git_revision. Try removing "
+                              "src/caosdb/version.py and the build directory "
+                              "before building.")
+    else:
+        GIT_REVISION = "Unknown"
+
+    if not ISRELEASED:
+        FULLVERSION += '.dev0+' + GIT_REVISION[:7]
+
+    return FULLVERSION, GIT_REVISION
+
+
+def write_version_py(filename='src/caosdb/version.py'):
+    cnt = """
+# THIS FILE IS GENERATED FROM caosdb SETUP.PY
+#
+short_version = '%(version)s'
+version = '%(version)s'
+full_version = '%(full_version)s'
+git_revision = '%(git_revision)s'
+release = %(isrelease)s
+
+if not release:
+    version = full_version
+"""
+    FULLVERSION, GIT_REVISION = get_version_info()
+
+    a = open(filename, 'w')
+    try:
+        a.write(cnt % {'version': VERSION,
+                       'full_version': FULLVERSION,
+                       'git_revision': GIT_REVISION,
+                       'isrelease': str(ISRELEASED)})
+    finally:
+        a.close()
+
+
+def setup_package():
+    # load README
+    with open("README.md", "r") as fh:
+        long_description = fh.read()
+
+    src_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "src")
+    sys.path.insert(0, src_path)
+
+    # Rewrite the version file everytime
+    write_version_py()
+
+    metadata = dict(
+        name='caosdb',
+        version=get_version_info()[0],
+        description='Python Interface for CaosDB',
+        long_description=long_description,
+        long_description_content_type="text/markdown",
+        author='Timm Fitschen',
+        author_email='t.fitschen@indiscale.com',
+        packages=find_packages('src'),
+        package_dir={'': 'src'},
+        install_requires=['lxml>=3.6.4',
+                          'PyYaml>=3.12', 'future', 'PySocks>=1.6.7'],
+        extras_require={'keyring': ['keyring>=13.0.0']},
+        setup_requires=["pytest-runner>=2.0,<3dev"],
+        tests_require=["pytest", "pytest-cov", "coverage>=4.4.2"],
+        package_data={
             'caosdb': ['cert/indiscale.ca.crt'],
-      }
-      )
+        }
+    )
+    try:
+        setup(**metadata)
+    finally:
+        del sys.path[0]
+    return
+
+
+if __name__ == '__main__':
+    setup_package()
diff --git a/src/caosdb/connection/authentication/keyring.py b/src/caosdb/connection/authentication/keyring.py
index abdbf112e20279d9c7211d01bebd296869edea5d..1dc986174acbe23191305632afda91cda0c718d2 100644
--- a/src/caosdb/connection/authentication/keyring.py
+++ b/src/caosdb/connection/authentication/keyring.py
@@ -73,7 +73,7 @@ def _call_keyring(**config):
                                      "password in question.")
     url = config.get("url")
     username = config.get("username")
-    app = "PyCaosDB — {}".format(url)
+    app = "caosdb — {}".format(url)
     password = _call_keyring(app=app, username=username)
     external_keyring = _get_external_keyring()
     password = external_keyring.get_password(app, username)
diff --git a/src/caosdb/connection/connection.py b/src/caosdb/connection/connection.py
index 1a40f4eb48c71a8fc632e88b310d9f5ce50cf209..3e2ec2cdba5e4cddce6557969c661ba82c1c16d2 100644
--- a/src/caosdb/connection/connection.py
+++ b/src/caosdb/connection/connection.py
@@ -32,6 +32,7 @@ from builtins import str  # pylint: disable=redefined-builtin
 from errno import EPIPE as BrokenPipe
 from socket import error as SocketError
 
+from caosdb.version import version
 from caosdb.configuration import get_config
 from caosdb.exceptions import (AuthorizationException, CaosDBException,
                                ClientErrorException, ConfigurationException,
@@ -87,8 +88,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
     """
 
     def __init__(self):
-        self._useragent = ("PyCaosDB - "
-                           "DefaultCaosDBServerConnection")
+        self._useragent = ("caosdb-pylib/{version} - {implementation}".format(version=version, implementation=type(self).__name__))
         self._http_con = None
         self._base_path = None
 
@@ -120,6 +120,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
 
         if headers is None:
             headers = {}
+        headers["User-Agent"] = self._useragent
         try:
             self._http_con = StreamingHTTPSConnection(
                 # TODO looks as if configure needs to be done first.
diff --git a/tox.ini b/tox.ini
index 0ad658afb4bf82d39a066437d5f0955915845ef6..97f7753720f8b8647476654518e0c65e1ec7906e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,10 @@
 [tox]
-envlist=py34, py35, py36, py37
+envlist=py34, py35, py36, py37, py38
 skip_missing_interpreters = true
+
 [testenv]
-deps=nose
+deps = .
+    nose
     pytest
     pytest-cov
 commands=py.test --cov=caosdb -vv {posargs}