# Deprecating My Symfony Profiler MCP Bundle: Right About the Problem, Half-Wrong About the Solution

> Source: <https://dev.to/hamdi_laadhari/deprecating-my-symfony-profiler-mcp-bundle-right-about-the-problem-half-wrong-about-the-solution-567l>
> Published: 2026-09-16 21:02:13+00:00

In March 2025 I built [mcp-profiler-bundle](https://github.com/killerwolf/mcp-profiler-bundle), a Symfony bundle that let AI coding agents read the Symfony Profiler: the requests your app just served, the queries they ran, the exceptions they threw. The bet was simple. An agent that can see what the app actually did should debug it better than one that can only read the source.

This week I deprecated it. Symfony now ships the official version of that idea, and rereading my code for the deprecation notice left me with an uncomfortable verdict: I was right about the problem, and half-wrong about the solution.

MCP, the Model Context Protocol, is a JSON-RPC protocol that lets a client like Claude Desktop, Cursor or Cline call tools on a local server. My bundle added a `bin/console mcp:server:run` command that spoke it over stdio and exposed four tools: `profiler:list`, `profiler:get_by_token`, `profiler:get_collectors` and `profiler:get_collector`.

There was no official PHP SDK back then. My first commit leaned on a community library, `james2037/mcp-php-server`, which only existed as `dev-master`. By the 0.1.0 tag on 31 March 2025 it was gone, and the whole protocol fit in one `match`:

``` php
// Command/RunMCPServerCommand.php
$payload = json_decode($line, true, JSON_THROW_ON_ERROR);
$method = $payload['method'] ?? null;

$response = match ($method) {
    'initialize' => $this->sendInitialize(),
    'tools/list' => $this->sendToolsList(),
    'tools/call' => $this->callTool($payload['params'] ?? []),
    'notifications/initialized' => null,
    default => $this->sendProtocolError(\sprintf('Method "%s" not found', $method)),
};
```

Hold on to that first line. It comes back later.

It found some users: 11 stars, 2 forks and roughly 5,900 Packagist installs. Version 0.2.0 added multi-kernel (`APP_ID`) support a week after launch, and in February 2026 an outside contributor, Rhodri Pugh, sent a PHP 8.4 fix.

The idea is official now. In September 2025, Symfony announced the official PHP MCP SDK, built with The PHP Foundation and Anthropic's MCP team. On 23 December 2025, [Symfony AI Mate](https://symfony.com/doc/current/ai/components/mate.html) shipped its first release, about nine months after my 0.1.0: an MCP dev server giving coding agents access to a Symfony app's internals. The container came first, profiler access landed in 0.3 in January 2026, and it handles multi-kernel apps too.

Symfony's [spotlight on Mate](https://symfony.com/blog/symfony-ai-spotlight-mate-an-agent-s-way-into-your-runtime) opens on the same premise: an agent can read all your code and still not know what happened on the last request. Mate is past 100,000 installs a month.

In August 2026, Mate 0.13 deleted its MCP server ([PR #2380](https://github.com/symfony/ai/pull/2380)). It no longer depends on `mcp/sdk`, and the agent runs plain commands instead, like these from Mate's docs and its profiler skill:

```
vendor/bin/mate tools:list
vendor/bin/mate tools:call symfony-profiler-list --statusCode=500 --limit=5
vendor/bin/mate resources:read symfony-profiler://profile/<token>
```

A bare CLI has a problem an MCP server doesn't: nothing tells the agent it exists. Mate's answer is Agent Skills — `SKILL.md` files installed into `.agents/skills/` (mirrored into `.claude/skills/`) that say when to reach for which tool. The profiler skill reads like a triage procedure: filter for the failing request instead of scrolling, check which collectors it has, then read them in symptom order. `exception` first for a 5xx; `time`, `db`, then `memory` for a slow request.

Johannes Wachter, who made that change, then [measured it](https://johanneswachter.dev/blog/mate-lab/): Claude Haiku, two tasks with injected performance bugs, ten runs per setup, counting whether the agent touched Mate at all. The bare CLI: 0 runs out of 20. The MCP server: 5 out of 20. The CLI with Skills and project instructions: 10 out of 10 on one task, 5 out of 10 on the harder one. He calls it a pattern from a small sample, not a proven effect, and concludes that "transport alone wasn't the deciding variable. Visibility was."

That's the half I got wrong. I treated exposing the data as the product. Tools give the agent eyes; skills tell it where to look. My bundle handed over four tools with one-line descriptions ("Lists available profiler tokens.") and left the agent to work out the rest.

|  | `mcp-profiler-bundle` (2025) | Symfony AI Mate 0.13 (2026) | 
|---|---|---|
| How the agent calls it | MCP over stdio, hand-written JSON-RPC | plain CLI, no server process | 
| Needs the app to boot | yes, it's a `bin/console` command | no, reads the profiler and the dumped container from disk | 
| Finding a profile | the last N ( `limit` ) | method, URL, IP, status, date range, kernel context | 
| Collector output | raw collector data or VarDumper dumps | formatters for Doctrine, exceptions, logs, mail, memory, requests, timing and translations | 
| Secrets | no redaction | cookies, session data, auth headers and secret env vars redacted | 
| Prompt injection | nothing | output wrapped and marked as untrusted data | 
| Telling the agent what to do | four one-line tool descriptions | Agent Skills plus `AGENTS.md` /`CLAUDE.md` | 
| Beyond the profiler | nothing | service container, Monolog logs | 

Rereading the code was humbling. PHPStan flags the first two of these problems in CI, on a step I'd marked `continue-on-error: true` the day I set up CI:

```
 ------ -----------------------------------------------------------------------
  Line   Command/RunMCPServerCommand.php
 ------ -----------------------------------------------------------------------
  40     While loop condition is always true.
         🪪  while.alwaysTrue
  61     Unreachable statement - code above always terminates.
         🪪  deadCode.unreachable
  68     Constant JSON_THROW_ON_ERROR is not allowed for parameter #3 $depth
         of function json_decode.
         🪪  argument.invalidConstant
```

`fgets()` returns `false`, and the loop sleeps a millisecond and tries again, forever.`JSON_THROW_ON_ERROR` sits in `json_decode()`'s `$depth` parameter, not `$flags`, so bad input decodes to `null`. Instead of a `-32700` parse error with a `-32601 Method "" not found` with id `0`. And any other notification, like `notifications/cancelled`, gets an error reply, which JSON-RPC 2.0 forbids.` profilerlist` and `profilerget_by_token`, and the README documented different names altogether.` ip`, `url`, `method` and `status_code`. The rewrite kept them in `ProfilerList::execute()` but put only `limit` in the tool schema.`.env` values into `$_SERVER`, that includes `APP_SECRET` and `DATABASE_URL`. `profiler:get_collector` dumped whatever the agent asked for, unfiltered, and the optional `bin/run-mcp.sh` wrapper (adapted from `var/mcp/stdout.log`.` bin/console` didn't start, and neither did the server. Mate reads everything from disk, so it still answers when the app won't boot.
The protocol has moved on, too. MCP's [2026-07-28 revision](https://modelcontextprotocol.io/specification/2026-07-28/changelog) removed the `initialize` handshake my `match` is built around and made a new `server/discover` method mandatory.

None of this is a verdict against MCP, by the way. `symfony/mcp-bundle`, for building your own MCP servers, does over 600,000 installs a month. The shift is narrower: for a dev tool the agent runs on your machine, a CLI plus skills got picked up at least as reliably, without a server process to babysit.

So I retired it properly. [PR #14](https://github.com/killerwolf/mcp-profiler-bundle/pull/14) puts a deprecation notice at the top of the README, with the migration:

```
composer require --dev symfony/ai-mate
vendor/bin/mate init
composer require --dev symfony/ai-symfony-mate-extension symfony/ai-monolog-mate-extension
vendor/bin/mate discover
```

`mate discover` also installs the skills. For frontend work, Simon André's [Symfony UX skills](https://github.com/smnandre/symfony-ux-skills) cover Stimulus, Turbo, Twig Components and Live Components.

Even deprecating it took a fix. CI had been red since June, failing on the PHP CS Fixer step, and the log on the deprecation PR showed the culprit: with no config file, PHP CS Fixer 3.95 opens an interactive `init` wizard, which crashes in CI with `Failed to read template file.` A `.php-cs-fixer.dist.php` turned it green.

[0.2.1](https://github.com/killerwolf/mcp-profiler-bundle/releases/tag/0.2.1) is the final release, Rhodri's fix included, and the repository stays public. The lesson I'm keeping: access was the easy half. The half that makes it useful is deciding what the agent should look at, in what order, and what it must never see.
