Important update (November 2026). Since Symfony AI 0.13, Symfony AI Mate is no longer an MCP server. According to the Symfony blog, the MCP server has been removed, and the commands documentation no longer lists a
servecommand. Mate is now a plain CLI that the agent calls directly. This article takes that change into account: the approach is the same (give the AI verified context), but Mate is no longer plugged in as an MCP server.
1. The real problem: an AI working blind
If you ask an AI assistant to “send a confirmation email after an order” without giving it access to the project, it answers with generic Symfony conventions. It assumes the service it needs is autowired. It invents a plausible route, for example app_order_confirm. It infers columns from the entity name: Order would have a status and a customer_email. The code looks right, and that is exactly why the problem is serious.
The cost isn’t visible right away. This code sometimes passes static analysis and compiles, then fails at runtime. It can also bypass the real architecture: a direct call to the mailer when the team goes through a dedicated Messenger bus, an EntityManager used instead of the business repository, a services.yaml configuration ignored. These mistakes are caught late, often in code review or during acceptance testing.
The usual workaround is to paste snippets into the chat: a piece of configuration, the output of debug:router, a stack trace. That stays partial and quickly goes stale. Above all, the result depends on what the developer thought to paste. Someone else, or the same person a week later, won’t get the same result, and the session can’t be replayed.
2. MCP and Symfony AI Mate: what are we talking about exactly?
The Model Context Protocol in brief
The Model Context Protocol (MCP) is an open protocol. It lets an AI client, such as Claude Code, call tools and read resources exposed by a server, often a local process started over stdio. The client discovers what the server offers, and the model chooses to call a given tool with given parameters. Every call shows up in the session.
The role of Symfony AI Mate
Symfony AI Mate is the component of the Symfony AI ecosystem that gives a coding agent access to the running application: profiler, container, logs. It builds on what the console commands already provide (debug:container, debug:router…) and on the profiler, and presents it as tools the agent can query.
Up to version 0.12, Mate was indeed an MCP server started with vendor/bin/mate serve (stdio transport). Since 0.13, the maintainers have chosen a CLI combined with a discovery layer. According to the Symfony blog, mate init creates the mate/ directory and writes the instructions for the agent, then mate discover rewrites a managed block in the AGENTS.md file and adds the @AGENTS.md import to CLAUDE.md (commands documentation). Skills are also installed in .agents/skills/, with a mirror in .claude/skills/. Also according to the maintainers, a benchmark showed that agents used this discovery layer as reliably as the former MCP server.
According to the official documentation, installation goes like this:
composer require --dev symfony/ai-mate
vendor/bin/mate init
# Recommended extensions for a Symfony application
composer require --dev symfony/ai-symfony-mate-extension symfony/ai-monolog-mate-extension
# List the tools actually available
vendor/bin/mate tools:list
# Documented call example: the latest recorded profile
vendor/bin/mate tools:call symfony-profiler-list --limit=1
Don’t rely on a fixed list of tools, including the one in this article. The scope depends on the version and on the installed extensions (community extensions also exist, listed by awesome-mate). The command that settles it is vendor/bin/mate tools:list in your own project.
3. Reliable context, source by source
Service container and routes
Before writing code that depends on a service, the agent checks that the service exists and looks at its aliases and arguments. Mate’s Symfony extension covers the container with symfony-services (search) and symfony-service-detail (details of one service). For routes, we did not find a clearly documented dedicated Mate tool, and bin/console debug:router route_name remains the reference. In both cases, the agent starts from a verified fact rather than an assumed convention:
bin/console debug:container 'AppMessengerOrderBus'
bin/console debug:router app_order_show
Logs and profiler
This is probably the clearest gain. Faced with a 500 error, the agent no longer has to guess the cause: it opens the profile of the request in question (executed SQL queries, render time, dispatched events) and reads the associated Monolog logs. The proposed fix then rests on the actual trace rather than a generic hypothesis.
Database
To write Doctrine queries or migrations that fit the existing schema, the agent must know the actual schema, not the one it infers from entity names. We did not find an official Mate tool dedicated to the database: only the executed SQL queries are visible, through the profiler’s db collector. Possible sources are bin/console doctrine:mapping:info, bin/console doctrine:schema:validate, a community extension, or a generic database MCP server connected read-only to the dev database. A few representative rows are enough: there is no point dumping whole tables into the context.
4. Limiting hallucinations and drift
The goal is to move from “I think that” to “I checked that”. With an instrumented project, every technical claim by the agent can rest on a call visible in the session, and the reviewer can ask: “which tool gave you this information?”. You still have to write the rules down. Claude Code reads a CLAUDE.md file at the project root, and Mate already uses it to import AGENTS.md. Here is a deliberately short example:
# Working guidelines — Symfony project
@AGENTS.md
## Rules
- Before injecting a service: check that it exists and look at its aliases
(`vendor/bin/mate tools:list`, then the container tool, or `bin/console debug:container <id>`).
- Before using a route: `bin/console debug:router <name>`.
- Before proposing a fix: read the logs and the profile of the request concerned.
- Never change the schema without generating a Doctrine migration (`bin/console make:migration`).
- Never read `.env.local`, `.env.*.local` or any API key.
- Dev environment only.
- Always finish with `vendor/bin/phpunit` and `vendor/bin/phpstan analyse`.
A guideline is not a permission. You therefore also need technical limits:
- Read-only database: a dedicated SQL user with only the
SELECTprivilege, on the dev database. - Dev environment only: Mate and its extensions are installed with
--devand must never reach production. - Human validation of sensitive actions (migrations, file deletion, network commands): let Claude Code ask for confirmation instead of allowing everything.
- Secrets exclusion: explicitly block reading them in
.claude/settings.json.
{
"permissions": {
"deny": [
"Read(./.env.local)",
"Read(./.env.*.local)",
"Read(./config/secrets/**)"
]
}
}
5. Making the work reproducible and auditable
An instrumented session leaves a trace: tools called, data consulted, hypotheses checked or discarded. In code review, this trace is worth more than “the AI suggested it”. Within the team, it makes it possible to understand why a solution was chosen.
For every workstation to behave the same way, the configuration must be versioned. For an MCP server, Claude Code reads a .mcp.json file at the repository root (“project” scope). This file contains a mcpServers object whose entries each declare command and args, and Claude Code asks you to approve each server the first time it encounters it. For Mate before 0.13, the configuration looked like this (indicative example, reconstructed from the former serve command: check it against the documentation for your version):
{
"mcpServers": {
"symfony-ai-mate": {
"command": "./vendor/bin/mate",
"args": ["serve"]
}
}
}
This file no longer works with Mate 0.13 and later, since serve is gone. Some Mate-based projects also remove this declaration when they migrate. With current versions, what you version to guarantee reproducibility is the AGENTS.md and CLAUDE.md files, the generated skills, Mate’s configuration, and the composer.lock that pins the version. The .mcp.json file remains useful for the project’s other MCP servers, for example a read-only database server. Don’t put any token in it: Claude Code expands environment variables (${VAR}) in this file, which keeps secrets out of the repository.
Context reduces mistakes but does not eliminate them all. Only automated verification catches them: PHPUnit for behavior, PHPStan for types and non-existent calls. Run both at the end of every assisted task, with a green result as the completion condition.
6. Equivalents in other ecosystems
Laravel
Laravel Boost (official package laravel/boost) is an MCP server, unlike Mate today. Among other things, it exposes the PHP and Laravel versions, the database engine, the Eloquent models, browser logs, Tinker, and a search over the documentation matching the installed versions. Installation as confirmed by the documentation:
composer require laravel/boost --dev
php artisan boost:install
# Manual registration in Claude Code if needed
claude mcp add -s local -t stdio laravel-boost php artisan boost:mcp
Django, Node/JS and Rails
- Django: django-mcp-server, a community package. It publishes tools written by subclassing
MCPToolset, through an HTTP endpoint at/mcp. - Next.js: next-devtools-mcp, maintained by Vercel and cited in the official documentation. From Next.js 16 on, the dev server also exposes an MCP endpoint at
/_next/mcp. - NestJS: we did not find an official tool during our research, so we cite none.
- Ruby on Rails: rails-mcp-server, a community gem. It exposes files, routes, models, the schema and Rails/Turbo/Stimulus/Kamal documentation.
Python: FastAPI and a home-made server
For FastAPI, fastapi-mcp (Tadata, community) exposes existing endpoints as MCP tools. The API has changed between versions: the current documentation uses mount_http(), older versions used mount(). Check your installed version with pip show fastapi-mcp.
from fastapi import FastAPI
from fastapi_mcp import FastApiMCP
app = FastAPI()
mcp = FastApiMCP(app)
mcp.mount_http() # /mcp endpoint by default
If no dedicated tool exists for your stack, the official Python MCP SDK (the mcp package) lets you write a read-only server in a few lines:
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("read-only-schema")
@mcp.tool()
def read_schema() -> str:
"""Return the versioned SQL schema, without modifying anything."""
return Path("db/schema.sql").read_text()
if __name__ == "__main__":
mcp.run()
Summary
- Symfony – AI Mate (official, 0.x): profiler, container, logs, extensible tools via
#[MateTool]. Particularity: CLI and skills since 0.13, no more MCP server. Its strength is access to the profiler, that is, to the actual execution of a request. - Laravel – Boost (official): versions, models, database, logs, version-matched documentation. It remains a true MCP server, and its documentation search is more polished than what Mate offers.
- Django – django-mcp-server (community): a framework for writing your own tools, rather than ready-made introspection.
- Next.js – next-devtools-mcp (official, Vercel): access to the running dev server.
- Rails – rails-mcp-server (community): routes, models, schema, documentation.
- FastAPI – fastapi-mcp (community): exposes existing endpoints, which is not project introspection.
What these tools have in common is routes, schema, logs and documentation for the exact version. To choose, favor maintained tools that are read-only by default, then test them on a real task before rolling them out more widely.
7. Limits, costs and setup best practices
Neither an MCP server nor an introspection CLI replaces business understanding or architectural decisions. They also depend on the quality of dev data: inconsistent fixtures or a dev schema that has drifted from production mislead the AI with as much confidence as a hallucination.
Watch the size of the context. A whole log file or a complete profile dilutes useful information and increases the cost of every exchange. Prefer targeted calls: one specific profile, the last few log lines, one named service.
Finally, start small. Enable the read tools first (container, routes, logs, profiler), measure the gain on a few real tasks (fewer back-and-forths, fewer corrections in review), then extend gradually. And if you migrate to Mate 0.13, remove the old mate serve entry from your .mcp.json, re-run vendor/bin/mate discover and check that the generated files are properly versioned.
Sources
- Symfony AI – Mate Component (Symfony Docs)
- Symfony AI Spotlight: Mate, an Agent’s Way into Your Runtime (Symfony Blog)
- symfony/ai – UPGRADE.md
- symfony/ai-mate – README
- Symfony AI
- MatesOfMate – awesome-mate
- Symfony AI Mate and DDEV (bitExpert)
- Model Context Protocol specification
- Claude Code: .mcp.json configuration and scopes
- Laravel Boost (official documentation)
- django-mcp-server (PyPI)
- Next.js – MCP guide
- rails-mcp-server (RubyGems)
- tadata-org/fastapi_mcp
- Official Python MCP SDK