AI engineering

Connecting Claude Code to MCP servers in a TYPO3 project

5 min read

Give the agent real TYPO3 sources

Hi folks,

Ask any model a TYPO3 question and you get an answer that sounds right. The problem is the version. TYPO3 has shipped a new LTS roughly every one and a half years for a long time, and the model’s memory is a soup of all of them: a hook from v8, a signal slot from v9, an event from v12. In v14, even the backend main module “Web” is now called “Content”. Plausible is easy. Correct for your installed core is the hard part.

The Model Context Protocol (MCP) is one way to fix that. An MCP server is a small program that offers tools to the agent, and the agent calls them instead of guessing. In this post I show how I connect Claude Code to MCP servers in a TYPO3 project, with the TYPO3 Dev Companion as the main example, and how I keep control over what the agent may call.

Two ways to add a server

The quick way is the command line:

claude mcp add --scope project --transport stdio typo3-dev-companion 
  -- php /Users/me/.local/share/typo3-dev-companion/bin/typo3-dev-companion

Everything after -- is the command that starts the server; everything before it belongs to Claude Code. The --scope decides where the configuration lands:

  • local (the default): only you, only this project.
  • project: written to .mcp.json in the project root, so it can go into git for the whole team.
  • user: you, in all projects.

The other way is to write .mcp.json directly. It is a small file and I like to see it in a diff:

{
  "mcpServers": {
    "typo3-dev-companion": {
      "type": "stdio",
      "command": "php",
      "args": ["${TYPO3_DEV_COMPANION_HOME:-/opt/typo3-dev-companion}/bin/typo3-dev-companion"],
      "env": {
        "TYPO3_DEV_COMPANION_CONSOLE": "ddev exec vendor/bin/typo3"
      }
    }
  }
}

Claude Code expands ${VAR} and ${VAR:-default} in command, args and env. That is how one committed file works on several machines, where the checkout lives in different places. A project server from .mcp.json is not started blindly: in an interactive session Claude Code asks you once to approve it. If you said no by mistake, claude mcp reset-project-choices brings the question back.

The env value is specific to the Dev Companion: it names the command that runs the TYPO3 console. In a DDEV project the server runs on the host, so it needs to know that the console lives behind ddev exec. Its manual also has a variable to name the installation directory, for the case where the server picks the wrong one.

Why the TYPO3 Dev Companion

The Dev Companion is an MCP server from the TYPO3 Association, written in plain PHP. It answers for TYPO3 12.4, 13.4, 14.3 and main, and it only reads: it writes nothing into the installation. It is still a 0.x release, and the README warns that names and tools can change, so pin a commit if you depend on it.

Setting it up is a standalone checkout plus one install command run from the project root:

git clone https://github.com/TYPO3/dev-companion.git ~/.local/share/typo3-dev-companion
composer install --working-dir="$HOME/.local/share/typo3-dev-companion"
cd ~/Developer/my-typo3-project
~/.local/share/typo3-dev-companion/bin/typo3-dev-companion install --agent=claude

The install writes the entry into .mcp.json and publishes its task skills where Claude Code reads them. Do not add the package to the project’s composer.json in an old project; it needs PHP 8.2, and an old v10 site in DDEV may still run PHP 7.4.

The tools I use most during upgrades:

  • typo3_changelog_lookup: Breaking, Deprecation, Feature and Important entries, filtered by version and type. “All breaking changes of 14” is one call.
  • typo3_documentation_lookup: official docs for the target version, not for whatever version the search engine likes.
  • typo3_project_describe: installed core, PHP versions, extensions, sites and their sets.
  • typo3_server_scope: what the server covers and which installation it reads.

Scoping what the agent may call

Every MCP tool appears in Claude Code under the name mcp__<server>__<tool>. Permission rules use exactly those names. In .claude/settings.json I allow the read-only lookups without a prompt and deny the one tool that writes:

{
  "permissions": {
    "allow": [
      "mcp__typo3-dev-companion__*"
    ],
    "deny": [
      "mcp__typo3-dev-companion__typo3_feedback_record"
    ]
  }
}

The feedback tool writes into the server’s own checkout, not into your project, so it is harmless. I deny it anyway because I want to decide when feedback is sent. Deny wins over allow, so the pattern above is safe. Note that allow globs must start with the literal server prefix: a bare "mcp__*" in allow is skipped, while the same glob in deny blocks all MCP tools at once.

The same idea works for any other server. A database MCP server, for example, should get read-only credentials in its env, not the root user of DDEV. Permission rules decide which tool may be called. They do not make a tool with write access safe.

Check that it really answers

This is the step people skip, and I skipped it too in the beginning. A configured server is not a running server. Check three things:

  1. claude mcp list in the terminal shows each server with its health, like connected or failed.
  2. /mcp inside a session shows the same and lets you reconnect.
  3. Ask a question and look at the tool calls, not the answer. “Which TYPO3 version does the server see, and which installation?” should produce a call to typo3_server_scope. If the answer comes without any tool call, the model answered from memory, and you are back where you started.

One more trap from the Dev Companion manual that I find important: when the installation is not reachable (DDEV stopped), the server falls back to reading the package files and says so in the answer. An empty result then proves nothing. Start DDEV and ask again before you conclude that something does not exist.

And after a composer update to the next major, reconnect the server in /mcp. A running server keeps the core it started with.

Is it worth the setup?

For my own hoja.in migration I worked without it: the migration plugin has a small script that reads the changelog files directly from vendor/typo3/cms-core/Documentation/Changelog/, which also works and needs no server. But a lookup with type and version filters is faster than grep, and the documentation part is something grep cannot give. For multi-site upgrades with several hops, I would not start without a version-bound source anymore, whichever form it takes.

Feedbacks are the champion’s breakfast, so tell me which MCP servers you use with TYPO3.

Further reading

Leave a comment

Your email address will not be published.