---
title: "TYPO3 Extension Documentation"
description: "Skill: typo3-extension-documentation"
canonical: index.html
navigation-title: "TYPO3 Extension Documentation"
---

<a id="typo3-extension-documentation"></a>

# TYPO3 Extension Documentation

- [Markdown source](#markdown-source)
- [References](#references)
  - [Where every task starts](#where-every-task-starts)
  - [Documentation decision checklist](#documentation-decision-checklist)

**Skill:** `typo3-extension-documentation`

Create, update or review documentation for a TYPO3 project or extension:
Documentation/ manuals, README, configuration and integration guides, examples,
migration notes, screenshots and user-facing XLF labels.

<a id="markdown-source"></a>

## Markdown source

```markdown
---
name: typo3-extension-documentation
description: 'Create, update or review documentation for a TYPO3 project or extension: Documentation/ manuals, README, configuration and integration guides, examples, migration notes, screenshots and user-facing XLF labels.'
compatibility: Needs the typo3-dev-companion MCP server, which owns every lookup this workflow routes to and publishes this skill together with the references/base.md it opens on. Install it from github.com/TYPO3/dev-companion and run typo3-dev-companion install in the project. A copy taken out of that repository's skills directory alone has neither the tools nor that base file.
---

# TYPO3 Extension Documentation

Document the implementation that exists, and bind TYPO3 claims to the active
version. Keep this skill as routing and documentation workflow. Do not copy API
reference material into it.

## Gather authoritative evidence

Work through [references/base.md](references/base.md) first. It fixes the order
every task here starts in and says why that order is not interchangeable. This
workflow leans hardest on its second step. `typo3_extension_describe` reports
whether a manual and a README exist at all. It reports which XLF files there
are, with the source language each declares.

Then, for this workflow:

- `typo3_documentation_lookup` with several short English queries and the target
  version, for every external TYPO3 API or workflow claim. Prefer the links it
  returns over hand-built documentation URLs.
- `typo3_label_lookup` before you add or reword a label, and
  `typo3_translation_domain_lookup` for the extension's own XLF path.
- Read its public configuration, registration files, templates, examples,
  existing manual and tests. Code is the source for what this package
  implements. Documentation that outruns it is the defect this workflow exists
  to avoid.

For backend documentation, verify modules through `typo3_backend_module_lookup`,
icons through `typo3_icon_lookup`, and undeclared Fluid prefixes through
`typo3_fluid_namespace_list` when those facts appear.

## Choose the documentation surface

Read [references/checklist.md](references/checklist.md) for the audience and
surface selection, secret hygiene, evidence conflicts, and the completion gate.

- Extend the existing `Documentation/` structure. Do not create a competing
  manual layout. Where step 2 reported no manual, or one older than
  `guides.xml`, call `typo3_rule_lookup` with
  `documentId="extension/documentation/manual"`. It holds the files that make a
  directory a manual, whole, and the command that renders it before you publish.
- Keep README content concise: the purpose, the essential setup, and the
  canonical documentation link where one exists.
- Separate the administrator and developer setup from the editor workflows.
- Derive configuration keys, defaults, types, and examples from the checkout.
- Mark a version requirement or a migration behavior only with evidence. That is
  Composer constraints, the installed changelog, or the official documentation.
- Use runnable, minimal examples that match the extension's namespaces and file
  layout.

Do not claim that a command, module, option, label, or rendered result exists
without a check with its owner. Do not document an internal implementation
detail as stable public API.

## Write and verify

- Preserve the repository's existing markup format, heading hierarchy, link
  style, terminology, and line-wrap conventions.
- Reuse canonical TYPO3 terminology and existing project labels.
- Make prerequisites, commands, expected outcomes, and failure conditions
  explicit.
- Validate internal links, referenced paths, configuration examples, and code
  identifiers against the checkout.
- Validate every docs.typo3.org link the page carries with
  `typo3_permalink_lookup`. It takes the identifiers and the URLs together and
  answers what each one reaches. A link the host redirects still resolves in a
  browser and points at another branch than the page claims. So a read of the
  page is not the check.
- Run documentation, lint, and test commands only when the project declares them
  or the task establishes them.
- Report the files you updated and the validation you performed. Report any
  behavior you could not verify from the installation or project.
- Draft the message for what you wrote with `typo3_commit_message_guide` and
  `workflow="project"`. A manual ships from the repository it documents, which
  is the workflow that argument names.

## Where the page stops and the code begins

**When a page would claim what the code does not do, invoke the skill that owns
that code first.** That is a step, not a note about ownership. Load the skill by
name and work from it. The finding at that moment is that the behaviour is
missing, not the manual.

If you write the page anyway, the manual outruns the package it ships with. What
crosses over is the surface, the behaviour the page was going to claim, and its
source files. You write the page from what comes back verified.

This skill owns documentation and user-facing wording changes. Test
implementation is `typo3-extension-testing`. Conformance assessment is
`typo3-extension-health`. Backend-module code is
`typo3-backend-module-development`, and a content element is
`typo3-content-element-development`. Stop before you edit that owner's files.
Document only the public behaviour it hands back verified.
```

<a id="references"></a>

## References

<a id="where-every-task-starts"></a>

### Where every task starts

```markdown
# Where every task starts

## Nothing starts until the server answers

A skill is a file the installer left behind. It loads and reads the same whether
the tools behind it are there or not, and neither side notices. So the first
call below is also the check.

- A client may carry this server's name in each tool's name:
  `mcp__<server>__typo3_project_describe`. So a search for the bare name comes
  back empty where the server is there. A search for a tool's schema needs the
  same form. A `select:` on the bare names returns nothing where the tools are
  there. Look for the qualified form before you read an empty result as an
  answer about the server.
- No `typo3_` tool in this session, or a first call that errors: stop. Say that
  this workflow needs the server and it is not there, and name what came back.
- Do not fall back to general TYPO3 knowledge, and do not start to read the
  checkout. That answer carries this workflow's order and confidence and none of
  its evidence. Nothing in it says which of the two it is.
- Continue only when the user asks you to after you said so. Repeat it in the
  answer and in every finding a lookup would have carried.

## The order

This is an order rather than a list. Each step decides what the next one is
worth. Where a step below carries a condition to skip it, that condition is
narrow on purpose. A skipped prescription teaches the next reader to skip the
ones that matter too.

1. **`typo3_project_describe`** — the repository and whether it holds an
   installation yet. It reports the TYPO3 and PHP version, the project's own
   extensions, its sites, and the commands this repository declares. That
   version filters every later answer. A check the repository does not declare
   is a wrong answer however sensible it sounds.

   The answer ends with the whole procedures this server carries, as ids. That
   list is the only place a client that renders no resource list sees their
   names. Each one is a `typo3_rule_lookup` with that `documentId` rather than a
   search.
2. **`typo3_extension_describe`** for each extension in scope. It says what the
   extension registers, and what it ships beside that. That is its manual, its
   README, its test layers, and its XLF files with the source language each one
   declares. It also says what the extension does *not* ship, and that is the
   half no file listing gives you.

   Where step 1 reported no extension, that answer is this step, and there is
   nothing to call. Say so. A core checkout is that case, because step 1 names
   the project's own extensions and not TYPO3's.
3. **`typo3_task_guide`** with a short English task, the paths it touches, the
   target version and the change type. It answers the workflow this task belongs
   to and the checks that come with it.

   Run it in every session, this skill's own tasks included. The guide builds
   the brief from the paths as well as the task text. No skill knows which paths
   the caller holds.

   A skill that covers the task is not that brief. A skipped step costs the
   hints and the core checks those paths match. Where the guide's own answer
   named this skill, this is one call for an answer already in the session. The
   price of a step there is nothing to decide about.
4. **`typo3_hint_lookup`** for each subsystem in scope, with its concrete paths.
   One query per subsystem. A single broad query is not subsystem evidence.

   Where step 3 ran with those paths, its answer says whether you still owe this
   step. A brief that carried everything the lookup matched says so: "these are
   everything typo3_hint_lookup matches for these paths". There the guide made
   the call, and the same query returns the same hints. A brief that stopped
   short says that instead and names the ids it left. You owe those: fetch them
   by id rather than repeat the query.

   Read the sentence rather than the populated `hints` key. That key is present
   either way and does not tell the two apart. `omittedHints` is that sentence
   as data. It is empty where the brief carried everything, and it holds the ids
   the brief left where it stopped short.
5. **`typo3_changelog_lookup` with `type: deprecation`**, at each major the
   package declares. Omit the query and raise `limit` to carry that major whole.
   Those two are the changelog's own axes, and the extension's vocabulary is not
   among them. An entry carries a query only when its title carries every word
   of it at once. The core titled those entries about its own code.

   That is one call per declared major, and what comes back is the major. Every
   entry carries its own index tags. `ext:core`, `ext:frontend`, `ext:form` and
   the rest name the system extension a change is **in**. `TCA`, `TypoScript`,
   `Fluid`, `YAML`, `Backend`, `Frontend` name the surface.

   Step 2 picks the package's entries out of that answer by those tags. The tags
   are the system extensions it requires, renders through or registers into, and
   the kinds of file it ships. That costs no further call. An extension key of
   your own is not among them and matches nothing. `tag` narrows one question
   inside a major rather than composes the sweep out of eleven.

   You check the answers against step 2, which is the other half the words did.
   Verify each identifier that comes back in the checkout. A deprecation nothing
   here calls is not a finding.

   Carry the `FullyScanned` / `PartiallyScanned` tag into the answer. It says
   whether the Extension Scanner can find the remaining call sites or whether
   that reading is yours. Bounded this way, you can write the sweep before you
   open a file. That is why it is a step of the order rather than something the
   reading stumbles into.

   **What its silence is worth.** A changelog records change events. So a
   pattern nothing has touched for ten majors has no entry at all. An empty
   sweep is therefore not an answer about what still works. "Does this still
   work in version N" goes to `typo3_documentation_lookup` at that version. Ask
   it here, and whenever the reading raises it again.

   That is a question for a documented surface: a ViewHelper, a TCA type, a
   TypoScript setting. The manual matches page titles, section paths and what
   each manual declares by name, never the text of a page. Declared is a
   property, a class or method the manual documents, a console command. You
   reach one by its own name where the query writes that name the way code
   does. You also reach it where the query is nothing but the name. A PHP
   identifier the manual does not declare has no page named after it.

   An identifier goes to `typo3_changelog_lookup` under its own name. That
   reaches the entries that write it, however the core titled the change. Then
   it goes to the class below. Where the manual has no page for a surface
   either, that is a result and not an answer. Undocumented is not unsupported.

   **A second declared major.** A package that declares more than one asks a
   second question of every deprecation the sweep returns. Is the replacement on
   the lower one? The entry's `issue` is a query of its own, and it reaches
   every entry filed under that number. The Feature the core announced the
   replacement in is among them.

   The version the core released it in settles that question. Where the number
   reaches no sibling, nobody wrote an entry for the replacement.
   `typo3_rule_lookup` with
   `documentId="extension/compatibility/a-declared-major-that-is-not-installed"`
   is the reading that closes it.

   **Where you do not owe the sweep.** A task that produces no change does not
   reach this step at all. The property is what the task produces. A triage, a
   reproduction and a review illustrate it; they are not the list you read it
   off. The sweep asks what a package will have to stop calling. A task that
   writes nothing is not going to call anything.

   The exemption ends where the workflow produces a change. A review asked to
   make the change is that other workflow. It starts this order again with the
   files it is about to write. To carry somebody else's patch onto current code
   is on the same side. It writes commits. The sweep says whether the code that
   moved under the patch deprecated something the patch calls.

   Skip the sweep only where the change touches no TYPO3 API: a code style
   fixer, a CI file, an `.editorconfig`. A deprecation is a statement about API
   the package calls. So a change that calls none has nothing for the sweep to
   land on. The sweep is empty before it runs.

   That condition is worth a statement, because this step is the largest answer
   the order asks for. It is one call per declared major, with that major's
   deprecations whole. You read which side a change falls on off the files it
   touches, never off the task it started as. One PHP file edited along the way
   puts it back among the ordinary ones.

   A skip there costs the deprecation no finding would have walked into. How
   small the change is decides nothing either. Three statements can call a
   deprecated API as easily as three hundred.

   A test file is one of those wherever it sits. It calls the API it exercises
   and the framework around it, and both deprecate. A fixture is exempt where it
   is data the suite reads, and not where it is a class.

**Before the reading**, write down what the order established. That is the
version that filters every later answer, the packages in scope, and the commands
this repository declares. Write down which steps what discharged. Those are
answers already in the session rather than a second reading. What the files show
belongs to the report at the other end. A caller who cannot see what an answer
rests on cannot tell it from one that rests on nothing.

**Then** read the checkout. Not before. A file list first makes everything after
the list look optional. The conventions then arrive as a footnote to a verdict
that has already formed.

**Before the first edit**, name the files this change will create, change or
delete. A deletion is the caller's to ask for, and this is somebody else's
checkout. It is the one act nothing here can put back.

**Last**, the report names every step of this order it did not reach, and what
stood in for it. That is an answer already in the session, a condition that made
the step empty, or an exemption. A reader cannot tell a step passed over in
silence from one somebody dropped.

## When the lookups run out

A behaviour question that survives the lookups above is one you read out of the
installed source. Do not guess at it. The class that implements the behaviour
and the one it inherits from answer it. That reading is the step after the
lookups. It replaces a change to the code until it works.

A first change that did not work is evidence about the reading. So the second
attempt at one failure reads the source rather than changes the code again.

What it settles is what this installation does and never what TYPO3 supports. So
a finding says that you could not settle the question beyond the version
installed. An answer built on the reading names the version it holds for.

## What each runtime lookup adds after the extension answer

`typo3_extension_describe` in step 2 says what one package registers. The
lookups below say what the installation resolved. That is a different fact even
where the words are the same. So step 2 has made none of these calls:

- `typo3_backend_module_lookup` — the tree position, the labels, the access
  level, the routes and the navigation component the parent module supplies.
  Step 2 lists the modules the package declares. A declaration cannot show that
  inheritance.
- `typo3_icon_lookup` — whether any installed package registers an identifier.
  That validates the ones a template uses. Step 2 lists the identifiers this
  package contributes.
- `typo3_label_lookup` — the labels as the installation resolves them, with its
  overrides applied. Step 2 lists the package's XLF files and the source
  language each declares, never what a unit says here.
- `typo3_fluid_namespace_list` — the prefixes any template may use without a
  declaration, from every package at once. Step 2 lists the package's own
  declarations. So an empty list there is no evidence that no package registers
  a prefix globally.
- `typo3_configuration_lookup` — the resolved configuration value, after every
  extension has had its say. For a form data group it gives the order the
  providers really run in. Step 2 answers nothing about that surface at all.
  What a registration declares is not what the installation resolves.
- `typo3_service_lookup` — the class the container really injects for a service
  id, an interface or a tag. Decorations and overrides count. Step 2 lists what
  the package's own `Services.yaml` declares, never what won.
- `typo3_schema_lookup` — the columns TYPO3 derives for a table from its TCA. It
  gives the type, the nullability and the default each one gets. Step 2 lists
  the tables the package registers and nothing about their shape.
- `typo3_flexform_lookup` — the data structure the installation resolves a
  `type=flex` field to, sheet by sheet, with listeners and migrations applied.
  Step 2 lists the content elements a package registers, never the structure
  each one's form builds.
- `typo3_record_lookup` — the rows of any table the installation has TCA for. It
  says how many there are and where they sit. It says what one column holds
  across them and which rows depart from its default. Step 2 has no row in its
  answer at all.

None of these says whether what it reports is right. `typo3_hint_lookup` and
`typo3_documentation_lookup` do. A subsystem its own runtime lookup confirmed
can still break every rule that governs it. So it is not established until you
asked both.

## A rule reads in both directions

It says what new code should do, and it says what this checkout already does
wrong. A file that settled into the opposite of a rule is a finding, not a local
style to preserve. Consistency with a project's own habit establishes nothing
about whether the habit is right.

## What the code is for is evidence, and the repository states it

A mechanism that costs something is not a defect because it costs. Before you
report one, find what it is there for and say so. That is the manual, the
README, the changelog, the setting that drives it, or the declared versions.

Where the documentation states a purpose, what you have is a trade-off, not a
defect. Name it with its cost and its alternative. Where you cannot find one,
the finding says that you could not establish one, not that none exists. If you
skip this, your review is a list of everything the author did on purpose.

## What a finding rests on is part of the finding

Three things carry one. A file you read, at its path and its line. A command you
ran, with what it printed. A mechanism you traced into an installed package. Say
which of the three it is. If you leave it unsaid, a finding from a CI file
weighs as much as one with a verified line.

Where one of the project's own commands would settle it, run it.
`typo3_project_describe` marks each command it lists **check**, **change** or
**unknown**, read off the declared body. A check reports and hands the code back
as it was. So even a task told not to change files runs it. The linter the
repository already declares is the cheapest evidence in it.

Do not run a change under that instruction. Name an unknown in the answer as
evidence that is available, and do not run it unasked. An unknown is a test
suite, a shell pipeline, a console command.

What a check prints is not the finding. The configuration that makes it fail is
still what the finding is about. The run takes that finding from derived to
established.

## What this server does not know

It does not read your working tree. You establish which files changed, which
branch you are on, and whether a path or an identifier still exists there. Then
pass the concrete paths back, because that turns a general convention into an
answer about this code.

## Query it in English

The knowledge is English and the match is lexical. So a query in another
language reaches the loanwords the two happen to share and nothing else.
Translate the subject before the call and the answer back afterwards, whatever
language you speak with the user.
```

<a id="documentation-decision-checklist"></a>

### Documentation decision checklist

```markdown
# Documentation decision checklist

Read this before you choose the documentation surface, and again before you
finish.

## Audience and surface

- Identify the reader: evaluator, integrator or administrator, editor, extension
  developer, or maintainer.
- Put the purpose and the essential setup in the README. Use the existing
  Documentation/ manual for durable configuration, workflows, API, and migration
  material.
- Separate installation and administration from editor workflows and developer
  extension points.
- Extend the repository's existing structure and markup. Do not create a second
  canonical manual.

## Evidence and conflicts

- The checkout is the authority for what this package implements: registration,
  keys, defaults, paths, examples, and supported behavior.
- The installation lookups are the authority for effective registrations and
  runtime state. A parser miss is not proof of absence. If dynamic PHP code and
  parsed output disagree, describe the parser limitation and verify the code
  path.
- Composer constraints and versioned official documentation own compatibility
  and external TYPO3 API claims.
- Tests may demonstrate behavior. They do not turn internal implementation
  details into promised public API.

## Secret hygiene

- Never copy passwords, tokens, private keys, personal addresses, internal
  hosts, or machine-specific paths into examples.
- Replace required credentials with named placeholders. Explain how the reader
  supplies them securely.
- When existing documentation contains plausible credentials or personal data,
  flag it explicitly and do not reproduce it.

## Completion gate

Stop the evidence search when every requested audience and surface has:

1. an authoritative implementation source;
2. version evidence for external TYPO3 claims;
3. a verified example or an explicit unverified marker;
4. validated paths, identifiers, and links; and
5. no unresolved contradiction that changes user instructions.

Report the unknowns that remain instead of a broad search. Run only declared
validation commands. Finish with the changed files, the checks, and the
unverified behavior.
```
