diff --git a/apistrap/decorators.py b/apistrap/decorators.py
index 5617f2b..4de1007 100644
--- a/apistrap/decorators.py
+++ b/apistrap/decorators.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 import abc
 import inspect
 from functools import wraps
-from typing import TYPE_CHECKING, Callable, Optional, Sequence, Type
+from typing import TYPE_CHECKING, Callable, Optional, Sequence, Type, Union
 
 from schematics import Model
 from schematics.exceptions import DataError
@@ -11,6 +11,7 @@ from schematics.exceptions import DataError
 from apistrap.errors import ApiClientError, InvalidFieldsError
 from apistrap.examples import ExamplesMixin, model_examples_to_openapi_dict
 from apistrap.schematics_converters import schematics_model_to_schema_object
+from apistrap.tags import TagData
 from apistrap.types import FileResponse
 
 if TYPE_CHECKING:
@@ -263,13 +264,20 @@ class TagsDecorator:
     A decorator that adds tags to the OpenAPI specification of the decorated view function.
     """
 
-    def __init__(self, tags: Sequence[str]):
+    def __init__(self, extension: Apistrap, tags: Sequence[Union[str, TagData]]):
         self._tags = tags
+        self._extension = extension
 
     def __call__(self, wrapped_func: Callable):
         _ensure_specs_dict(wrapped_func)
         wrapped_func.specs_dict.setdefault("tags", [])
-        wrapped_func.specs_dict["tags"].extend(self._tags)
+
+        for tag in self._tags:
+            wrapped_func.specs_dict["tags"].append(tag.name if isinstance(tag, TagData) else tag)
+
+            if isinstance(tag, TagData):
+                self._extension.add_tag_data(tag)
+
         return wrapped_func
 
 
diff --git a/apistrap/extension.py b/apistrap/extension.py
index 9d931d8..285ae00 100644
--- a/apistrap/extension.py
+++ b/apistrap/extension.py
@@ -1,6 +1,6 @@
 import abc
 from abc import ABCMeta
-from typing import Callable, Dict, List, Optional, Type
+from typing import Callable, Dict, List, Optional, Type, Union
 
 from apispec import APISpec
 from apispec.utils import OpenAPIVersion
@@ -8,6 +8,7 @@ from schematics import Model
 
 from apistrap.decorators import IgnoreDecorator, IgnoreParamsDecorator, SecurityDecorator, TagsDecorator
 from apistrap.errors import ApistrapExtensionError
+from apistrap.tags import TagData
 
 
 class SecurityScheme(metaclass=ABCMeta):
@@ -238,11 +239,21 @@ class Apistrap(metaclass=ABCMeta):
         self.security_schemes.append(scheme)
         self.spec.components.security_scheme(scheme.name, scheme.to_openapi_dict())
 
-    def tags(self, *tags: str):
+    def add_tag_data(self, tag: TagData) -> None:
+        """
+        Add information about a tag to the specification.
+        :param tag: data about the tag
+        """
+
+        spec = self.spec.to_dict()
+        if "tags" not in spec or tag.name not in map(lambda t: t["name"], spec["tags"]):
+            self.spec.tag(tag.to_dict())
+
+    def tags(self, *tags: Union[str, TagData]) -> TagsDecorator:
         """
         A decorator that adds tags to the OpenAPI specification of the decorated view function.
         """
-        return TagsDecorator(tags)
+        return TagsDecorator(self, tags)
 
     def ignore(self):
         """
diff --git a/apistrap/tags.py b/apistrap/tags.py
new file mode 100644
index 0000000..95bcd4e
--- /dev/null
+++ b/apistrap/tags.py
@@ -0,0 +1,16 @@
+from dataclasses import dataclass
+from typing import Optional
+
+
+@dataclass
+class TagData:
+    name: str
+    description: Optional[str] = None
+
+    def to_dict(self) -> dict:
+        result = {"name": self.name}
+
+        if self.description is not None:
+            result["description"] = self.description
+
+        return result

Support adding metadata to tags
see https://swagger.io/specification/#tagObject - this could play nicely with Flask blueprints
diff --git a/tests/test_tag_data.py b/tests/test_tag_data.py
new file mode 100644
index 0000000..a233fa7
--- /dev/null
+++ b/tests/test_tag_data.py
@@ -0,0 +1,22 @@
+from apistrap.flask import FlaskApistrap
+from apistrap.tags import TagData
+
+
+def test_tag_data(client, app):
+    oapi = FlaskApistrap()
+    oapi.add_tag_data(TagData("Tag"))
+    oapi.add_tag_data(TagData("Tag with description", "Description"))
+    oapi.init_app(app)
+
+    response = client.get("/spec.json")
+
+    assert len(response.json["tags"]) == 2
+
+    assert response.json["tags"][0] == {
+        "name": "Tag"
+    }
+
+    assert response.json["tags"][1] == {
+        "name": "Tag with description",
+        "description": "Description"
+    }
diff --git a/tests/test_tags.py b/tests/test_tags.py
index c634fb0..ddf92f7 100644
--- a/tests/test_tags.py
+++ b/tests/test_tags.py
@@ -1,6 +1,8 @@
 import pytest
 from flask import jsonify
 
+from apistrap.tags import TagData
+
 
 @pytest.fixture()
 def app_with_tags(app, flask_apistrap):
@@ -33,3 +35,29 @@ def test_repeated_tags(app_with_repeated_tags, client):
 
     assert "tags" in path
     assert sorted(path["tags"]) == ["Tag 1", "Tag 2", "Tag 3", "Tag 4"]
+
+
+@pytest.fixture(params=(True, False))
+def app_with_tag_data(app, flask_apistrap, request):
+    tag = TagData("Tag", "Description")
+
+    if request.param:
+        flask_apistrap.add_tag_data(tag)
+
+    @app.route("/", methods=["GET"])
+    @flask_apistrap.tags(tag)
+    def view():
+        return jsonify()
+
+
+def test_tag_data(app_with_tag_data, client):
+    response = client.get("/spec.json")
+    path = response.json["paths"]["/"]["get"]
+
+    assert "tags" in path
+    assert path["tags"] == ["Tag"]
+
+    assert response.json["tags"] == [{
+        "name": "Tag",
+        "description": "Description"
+    }]

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-mock pytest-flask pytest-aiohttp pytest-cov
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohappyeyeballs==2.6.1
aiohttp==3.11.14
aiosignal==1.3.2
apispec==1.2.0
-e git+https://github.com/iterait/apistrap.git@9b36862e26a6449ff33cb6d913e94413070a3e27#egg=apistrap
async-timeout==5.0.1
attrs==25.3.0
blinker==1.9.0
click==8.1.8
coverage==7.8.0
exceptiongroup==1.2.2
Flask==3.1.0
frozenlist==1.5.0
idna==3.10
importlib_metadata==8.6.1
iniconfig==2.1.0
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.2
more-itertools==10.6.0
multidict==6.2.0
packaging==24.2
pluggy==1.5.0
propcache==0.3.1
pytest==8.3.5
pytest-aiohttp==1.1.0
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-flask==1.3.0
pytest-mock==3.14.0
schematics==2.1.1
tomli==2.2.1
typing_extensions==4.13.0
Werkzeug==3.1.3
yarl==1.18.3
zipp==3.21.0

name: apistrap
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohappyeyeballs==2.6.1
      - aiohttp==3.11.14
      - aiosignal==1.3.2
      - apispec==1.2.0
      - apistrap==0.4.0
      - async-timeout==5.0.1
      - attrs==25.3.0
      - blinker==1.9.0
      - click==8.1.8
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - flask==3.1.0
      - frozenlist==1.5.0
      - idna==3.10
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - itsdangerous==2.2.0
      - jinja2==3.1.6
      - markupsafe==3.0.2
      - more-itertools==10.6.0
      - multidict==6.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - propcache==0.3.1
      - pytest==8.3.5
      - pytest-aiohttp==1.1.0
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-flask==1.3.0
      - pytest-mock==3.14.0
      - schematics==2.1.1
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - werkzeug==3.1.3
      - yarl==1.18.3
      - zipp==3.21.0
prefix: /opt/conda/envs/apistrap

tests/test_tag_data.py::test_tag_data tests/test_tags.py::test_simple_tags tests/test_tags.py::test_repeated_tags tests/test_tags.py::test_tag_data[True] tests/test_tags.py::test_tag_data[False]
apistrap/extension.py apistrap/tags.py apistrap/decorators.py
apistrap/extension.py apistrap/tags.py apistrap/decorators.py