Symfony AI - Mate Component
The Mate component provides an MCP (Model Context Protocol) server that enables AI assistants to interact with PHP applications (including Symfony) through standardized tools. This is a development tool, not intended for production use.
Installation
1
$ composer require --dev symfony/ai-mate
Purpose
Symfony AI Mate is a development tool that creates a local MCP server to enhance your AI assistant (JetBrains AI, Claude, GitHub Copilot, Cursor, etc.) with specific knowledge about your PHP application and development environment.
Important: This is intended for development and debugging only, not for production deployment.
This is the core package that creates and manages your MCP server. It works with any PHP application - while it includes Symfony-specific tools via bridges, the core functionality is framework-agnostic.
Quick Start
Install with composer:
1
$ composer require --dev symfony/ai-mate
Initialize configuration:
1
$ vendor/bin/mate init
This creates:
mate/directory with configuration filesmate/srcdirectory for custom extensionsmate/AGENT_INSTRUCTIONS.mdplaceholder (refreshed bymate discover)mcp.jsonfor MCP clients that support it (e.g. Claude Desktop)bin/codexandbin/codex.batwrappers for Codex runtime MCP injection
While generating mcp.json, mate init asks which PHP binary the coding agent should use to
launch the server. The default is detected from the environment: for a containerized setup where
a .ddev/ directory is present, it defaults to ddev exec php so the host-side agent starts
Mate inside the container; otherwise it defaults to php. Accept the default or provide your own
launch command (for example docker compose exec php php for a plain Docker Compose setup).
It also updates your composer.json with the following configuration:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
{
"autoload-dev": {
"psr-4": {
"Mate\\": "mate/src/"
}
},
"extra": {
"ai-mate": {
"extension": false,
"scan-dirs": ["mate/src"],
"includes": ["mate/config.php"]
}
}
}
The extension: false flag prevents your application from being discovered as a reusable Mate
extension when it is installed as a dependency elsewhere. Remove it, or set it to true, only
if your package should be discoverable by other projects.
After running mate init, update your autoloader:
1
$ composer dump-autoload
Automatic Discovery
The symfony/ai-mate package installs the optional Composer plugin
symfony/ai-mate-composer-plugin. After your project has been initialized and
mate/extensions.php exists, Composer automatically runs:
1
$ vendor/bin/mate discover --composer
after composer install and composer update. This refreshes discovered extensions and
regenerates the managed instruction artifacts.
Before initialization, the Composer plugin does not modify your project. It only prints a hint to run:
1
$ vendor/bin/mate init
Use vendor/bin/mate discover whenever you want to refresh extensions manually after changing
Mate configuration, adding instructions, or working on local extensions.
Discover available extensions:
1
$ vendor/bin/mate discover
This command also refreshes:
mate/AGENT_INSTRUCTIONS.md- Managed AI Mate instruction section in
AGENTS.md
Start the MCP server:
1
$ vendor/bin/mate serve
For Codex, start with the generated wrapper (./bin/codex); Codex does not read this project's mcp.json:
1
$ ./bin/codex
Add Custom Tools
The easiest way to add tools is to create a mate/src folder next to your src and tests directories,
then add a class with a method using the #[McpTool] attribute:
1 2 3 4 5 6 7 8 9 10 11 12 13
// mate/MyTool.php
namespace Mate;
use Mcp\Capability\Attribute\McpTool;
class MyTool
{
#[McpTool(name: 'my_tool', description: 'My custom tool')]
public function execute(string $param): array
{
return ['result' => $param];
}
}
More about attributes and how to configure Prompts, Resources and more can be found at the MCP SDK documentation.
Configuration
The configuration folder is called mate and is located in your project's root directory.
It contains two important files:
mate/extensions.php- Enable/disable extensionsmate/config.php- Configure settings
Tip
The folder and default configuration is automatically generated by running mate init.
Extensions Configuration
mate/extensions.php records which extensions are enabled, plus the state of every Agent Skill
they ship. Mate maintains it for you, so reach for a command before the editor:
1 2 3 4
$ vendor/bin/mate discover # register newly installed extensions and install their skills
$ vendor/bin/mate skills:install # rebuild the generated skill folders from the recorded state
$ vendor/bin/mate skills:list # show which skills are enabled and how they are installed
$ vendor/bin/mate skills:validate # check the generated folders against the recorded state
Editing the file is for the settings that express your intent: whether an extension is enabled, and
the enabled and mode keys of a skill (see Skills). Every other key is rewritten on the
next install:
1 2 3 4 5 6 7 8 9
// mate/extensions.php
// This file is managed by Mate - use `discover` or `skills:*` commands
// over manual editing. Only changes to `mode` or `enabled` are kept,
// every other key is overwritten by Mate.
return [
'vendor/package-name' => ['enabled' => true],
'vendor/another-package' => ['enabled' => false],
];
Services Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// mate/config.php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $container): void {
$container->parameters()
// Override default parameters here
// ->set('mate.cache_dir', sys_get_temp_dir().'/mate')
// ->set('mate.env_file', ['.env'])
;
$container->services()
// Register your custom services here
;
};
Disabling Specific Features
Use the MateHelper class to disable specific features:
1 2 3 4 5 6 7 8
use Symfony\AI\Mate\Container\MateHelper;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $container): void {
MateHelper::disableFeatures($container, [
'symfony/ai-mate' => ['server-info'],
]);
};
Environment Variables
Use %env(VAR_NAME)% syntax in service configuration to reference environment variables.
See the Symfony documentation on environment variables for more information.
Adding Third-Party Extensions
Install the package:
1
$ composer require vendor/symfony-toolsDiscover available tools (auto-generates/updates
mate/extensions.php):1
$ vendor/bin/mate discoverWhen the project has already been initialized, Composer also refreshes discovery automatically after
composer installandcomposer update.Optionally disable specific extensions:
1 2 3 4 5
// mate/extensions.php return [ 'vendor/symfony-tools' => ['enabled' => true], 'vendor/unwanted-tools' => ['enabled' => false], ];
To create a third party extension, see Creating MCP Extensions.
Available Bridges
Symfony Bridge
The Symfony bridge (symfony/ai-symfony-mate-extension) provides container introspection and
profiler data access tools for Symfony applications.
Container Introspection
MCP Tools:
symfony-services- List Symfony services from the compiled container and optionally filter by service ID or class name using thequeryparametersymfony-service-detail- Get full details of a single service by its exact ID
Configuration:
Single cache directory (default):
1 2
$container->parameters()
->set('ai_mate_symfony.cache_dir', '%mate.root_dir%/var/cache');
Multiple directories with contexts (e.g., for multi-kernel applications that split their
cache per APP_ID):
1 2 3 4 5
$container->parameters()
->set('ai_mate_symfony.cache_dir', [
'website' => '%mate.root_dir%/var/cache/website',
'admin' => '%mate.root_dir%/var/cache/admin',
]);
When using multiple directories, symfony-services returns the services grouped by context and
symfony-service-detail includes the context a service was found in. Both tools accept an
optional context parameter to narrow the lookup to a single kernel.
Troubleshooting:
Container not found:
Ensure the cache directory parameter points to the correct location. The bridge looks for compiled container XML files in the cache directory, including kernels with custom class names.
Services not appearing:
- Clear Symfony cache:
bin/console cache:clear - Ensure the container is compiled (warm up cache)
- Verify the container XML file exists in the cache directory
Profiler Data Access
When symfony/http-kernel and symfony/web-profiler-bundle are installed, profiler tools
become available for accessing Symfony profiler data.
MCP Tools:
symfony-profiler-list- List available profiler profiles with summary data, supports filtering by date range (from/toparameters) and limiting results (uselimit: 1for the latest profile)symfony-profiler-get- Get a specific profile by token
All tools return profiles with a resource_uri field that points to the full profile resource.
MCP Resources:
symfony-profiler://profile/{token}- Full profile details including metadata and list of available collectors with URIssymfony-profiler://profile/{token}/{collector}- Detailed collector-specific data (request, response, exception, events, etc.)
When the related dependencies are installed, collector data is normalized for AI consumption for:
- Doctrine DBAL queries
- Symfony Mailer messages
- Symfony Translation usage
If no formatter is registered for a collector, Mate falls back to exposing the collector's raw data.
Configuration:
Single profiler directory (default):
1 2
$container->parameters()
->set('ai_mate_symfony.profiler_dir', '%mate.root_dir%/var/cache/dev/profiler');
Multiple directories with contexts (e.g., for multi-kernel applications):
1 2 3 4 5
$container->parameters()
->set('ai_mate_symfony.profiler_dir', [
'website' => '%mate.root_dir%/var/cache/website/dev/profiler',
'admin' => '%mate.root_dir%/var/cache/admin/dev/profiler',
]);
When using multiple directories, profiles include a context field for filtering.
Example Usage:
Search for errors:
1 2 3 4 5 6 7 8 9 10 11
// Using symfony-profiler-list tool
{
"method": "tools/call",
"params": {
"name": "symfony-profiler-list",
"arguments": {
"statusCode": 500,
"limit": 20
}
}
}
Access full profile via resource:
1 2 3 4 5 6 7
// Using resource template
{
"method": "resources/read",
"params": {
"uri": "symfony-profiler://profile/abc123"
}
}
Access specific collector:
1 2 3 4 5 6
{
"method": "resources/read",
"params": {
"uri": "symfony-profiler://profile/abc123/exception"
}
}
Security:
Cookies, session data, authentication headers, and sensitive environment variables are automatically redacted from profiler data.
Extensibility:
Create custom collector formatters by implementing CollectorFormatterInterface and
registering via DI tag ai_mate_symfony.profiler_collector_formatter.
Troubleshooting:
Profiles not found:
- Ensure the profiler directory parameter points to the correct location
- Verify Symfony profiler is enabled in your environment
- Generate some HTTP requests to create profile data
Collector data not available:
- Check that the specific collector is enabled in Symfony profiler configuration
- Verify the profile was captured with that collector active
Monolog Bridge
The Monolog bridge (symfony/ai-monolog-mate-extension) provides log search and analysis tools:
monolog-search- Search log entries by text term with optional filters (supportsregexparameter for regex patterns andlevelfilter)monolog-context-search- Search logs by context field valuemonolog-tail- Get the last N log entriesmonolog-list-files- List available log filesmonolog-list-channels- List all log channels
Single log directory (default):
1 2
$container->parameters()
->set('ai_mate_monolog.log_dir', '%mate.root_dir%/var/log');
Multiple directories with contexts (e.g., for multi-kernel applications that split their
logs per APP_ID):
1 2 3 4 5
$container->parameters()
->set('ai_mate_monolog.log_dir', [
'website' => '%mate.root_dir%/var/log/website',
'admin' => '%mate.root_dir%/var/log/admin',
]);
When using multiple directories, log entries and files carry a kernel_context field, and all
Monolog tools accept an optional kernelContext parameter to restrict the lookup to a single
kernel. The field is named kernel_context rather than context to keep it apart from the
Monolog context of a log record.
Troubleshooting
Logs not found:
Ensure the log directory parameter points to the correct location where your Monolog log files are stored.
Log parsing errors:
- Verify log format is standard Monolog line format or JSON
- Check file permissions on log files
- Ensure log files are not empty or corrupted
Built-in Tools
The core package provides basic system information tools:
server-info- Get PHP runtime environment details: version, OS, OS family, and loaded extensions
Skills
Agent Skills are SKILL.md files that give your coding agent
structured, multi-step "how-to" knowledge for a task. Extensions can ship skills alongside their MCP tools, and Mate
installs them onto the filesystem where coding agents read them — a polyfill until skills can be
served over MCP directly.
You usually do not run anything: mate discover (which also runs automatically after
composer require) installs the skills of every enabled extension. To sync them manually, use
mate skills:install.
Each skill is installed under a mate- prefixed directory name (e.g. mate-demo-skill) to
avoid clashing with skills you maintain from other sources; the name in the installed
SKILL.md is rewritten to match. Skills land in two locations:
.agents/skills/is the source of truth, read directly by Codex, OpenCode and GitHub Copilot..claude/skills/mirrors.agents/skills/via relative symlinks for Claude Code, which only reads its own directory.
Both folders are generated output: skills:install is an idempotent reconciler that rebuilds them
from source on every run and prunes skills that are gone or disabled. Do not edit them by hand — your
changes are overwritten on the next run and reported as errors by mate skills:validate.
Skills are copied, never symlinked into vendor/. What your agent loads is a real file you can
open and diff, and a package update cannot silently change it underneath you. Mate does not touch
your .gitignore: committing the generated folders is recommended, because it turns an upstream
skill change into a reviewable diff instead of something that lands silently.
All skill state lives in mate/extensions.php. Two keys per skill carry your intent:
enabledcontrols whether the skill is installed at all. Usemate skills:disable <name>andmate skills:enable <name>to flip it.modeis eithermanaged, where Mate builds the skill from the package, oroverride, which hands ownership to you: Mate then builds from your ownmate/skills/<name>/copy and never writes intomate/skills/. Usemate skills:override <name>to switch, andmate skills:reset <name>to hand the skill back.
The skills:* commands set both for you, which is the recommended way to change them — they also
reinstall, so the recorded state below never falls out of step with your intent. Editing the two keys
by hand works as well; the next install picks the change up.
Everything else is written by Mate and rewritten on every install — the resulting state
(managed, override or disabled), the source it was built from, the source_hash
and hash pair used to detect drift, and the generated targets:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// mate/extensions.php
return [
'vendor/package' => [
'enabled' => true,
'skills' => [
'demo-skill' => [
'enabled' => true,
'mode' => 'managed',
'state' => 'managed',
'source' => 'vendor/vendor/package/skills/demo-skill',
'source_hash' => 'sha256:...',
'hash' => 'sha256:...',
'targets' => [
'.agents/skills/mate-demo-skill',
'.claude/skills/mate-demo-skill',
],
],
],
],
];
Use mate skills:list for an overview, and mate skills:validate to check the generated folders
against that record: it reports hand-edited content, missing folders, and sources that moved on since
the last install. It also looks at the installed content itself: it warns when a Markdown link points
at a file that is not part of the skill, and suggests a better description when it is too short or
never says when the skill applies, because that description is all an agent has when it decides
whether to load the skill. Those description findings are suggestions, not warnings: they are printed
but never change the exit code, not even with --strict. mate skills:prune removes leftover
mate-* folders.
To see what an install would do before it does it, run mate skills:install --dry-run: the same
reconciler runs and reports what it would install, rebuild or remove, but nothing is written.
The core package itself ships a system-information skill describing how to inspect the PHP
runtime and installed package versions via the server-info tool.
Commands
mate init-
Initialize AI Mate configuration and create the
mate/directory. mate discover-
Scan for MCP extensions in installed packages. This command will:
- Scan your vendor directory for packages with
extra.ai-mateconfiguration - Generate or update
mate/extensions.phpwith discovered extensions - Preserve existing enabled/disabled states for known extensions
- Default new extensions to enabled
- Install Agent Skills shipped by enabled extensions (see Skills)
- Scan your vendor directory for packages with
mate skills:install-
Install the Agent Skills shipped by your enabled extensions so your coding agent can use
them. This runs automatically as part of
mate discover; use it for an explicit re-sync. Pass--dry-runto see what a run would install, rebuild or remove without writing anything. See Skills. mate skills:list- List declared and installed skills with their enabled, mode, state and status information. Read-only diagnostic. See Skills.
mate skills:validate-
Check the generated skill folders against the state recorded in
mate/extensions.php, and the installed content itself for dead links and descriptions an agent cannot act on. Exits with a non-zero status when a skill is broken; pass--strictto fail on warnings too. Suggestions about a description never affect the exit code. Read-only. See Skills. mate skills:prune-
Remove generated
mate-*folders that no longer belong to any skill. Pass--dry-runto see what would be removed. See Skills. mate skills:override <name>-
Take ownership of a skill: copy the package's version into
mate/skills/<name>/and switch it to'mode' => 'override'. Accepts the installed (mate-…) or the original name. Pass--forceto replace an existing copy. See Skills. mate skills:reset <name>-
Hand an overridden skill back to Mate, so it is built from the package again. Your copy under
mate/skills/<name>/is kept unless you pass--delete-copy. See Skills. mate skills:disable <name>-
Hide a skill from coding agents: remove its generated folders and record it as disabled. The
entry stays in
mate/extensions.php, and a copy of your own undermate/skills/is left untouched. See Skills. mate skills:enable <name>- Make a disabled skill visible again and rebuild its generated folders. See Skills.
mate serve- Start the MCP server with stdio transport.
mate clear-cache- Clear the MCP server cache.
mate debug:capabilities-
Display all discovered MCP capabilities grouped by extension. This command is useful for:
- Verifying extension installation and capability registration
- Debugging missing or misconfigured extensions
- Understanding which package provides each capability
- Inspecting available tools during development
Options:
--format=FORMAT-
Output format:
text(default),json, ortoon. Thetoonformat requireshelgesverre/toon. --extension=EXTENSION-
Filter by extension package name (e.g.,
symfony/ai-monolog-mate-extension) --type=TYPE-
Filter by capability type:
tool,resource,prompt, ortemplate
Examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Show all capabilities $ vendor/bin/mate debug:capabilities # Show only tools $ vendor/bin/mate debug:capabilities --type=tool # Show capabilities from specific extension $ vendor/bin/mate debug:capabilities --extension=symfony/ai-monolog-mate-extension # JSON output for scripting $ vendor/bin/mate debug:capabilities --format=json # TOON output for token-efficient inspection $ vendor/bin/mate debug:capabilities --format=toon # Root project capabilities $ vendor/bin/mate debug:capabilities --extension=_custom mate debug:extensions-
Display detailed information about discovered and loaded MCP extensions. This command is useful for:
- Understanding which extensions are discovered vs enabled vs loaded
- Debugging extension loading issues
- Verifying extension configuration from
mate/extensions.php - Inspecting scan directories and include files
- Troubleshooting why an extension isn't providing capabilities
Status Indicators:
[enabled]-
Extension is configured to load in
mate/extensions.php [loaded]- Extension successfully loaded into the DI container
[not loaded]- Extension failed to load (package removed, error, etc.) - useful for troubleshooting
Options:
--format=FORMAT-
Output format:
text(default),json, ortoon. Thetoonformat requireshelgesverre/toon. --show-all- Show all discovered extensions including disabled ones
Examples:
1 2 3 4 5 6 7 8 9 10 11
# Show enabled extensions $ vendor/bin/mate debug:extensions # Show all extensions (including disabled) $ vendor/bin/mate debug:extensions --show-all # JSON output for scripting $ vendor/bin/mate debug:extensions --format=json # TOON output for token-efficient inspection $ vendor/bin/mate debug:extensions --format=toon mate mcp:tools:list-
List all available MCP tools with their metadata. This command provides a compact overview of tools for quick reference and filtering.
Options:
--filter=PATTERN-
Filter tools by name pattern (supports wildcards like
search*or*logs) --extension=EXTENSION- Filter tools by extension package name
--format=FORMAT-
Output format:
table(default),json, ortoon. Thetoonformat requireshelgesverre/toon.
Examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# List all tools $ vendor/bin/mate mcp:tools:list # Filter by name pattern $ vendor/bin/mate mcp:tools:list --filter="monolog*" $ vendor/bin/mate mcp:tools:list --filter="*search" # Show tools from specific extension $ vendor/bin/mate mcp:tools:list --extension=symfony/ai-monolog-mate-extension # JSON output for scripting $ vendor/bin/mate mcp:tools:list --format=json # TOON output for token-efficient inspection $ vendor/bin/mate mcp:tools:list --format=toon # Combined filters $ vendor/bin/mate mcp:tools:list --extension=symfony/ai-monolog-mate-extension --filter="*search" mate mcp:tools:inspect-
Display detailed information about a specific MCP tool including its full JSON schema. This command is useful for understanding tool parameters and requirements.
Arguments:
tool-name- Name of the tool to inspect (required)
Options:
--format=FORMAT-
Output format:
text(default),json, ortoon. Thetoonformat requireshelgesverre/toon.
Examples:
1 2 3 4 5 6 7 8 9 10 11
# Inspect a specific tool $ vendor/bin/mate mcp:tools:inspect server-info # Inspect extension tool $ vendor/bin/mate mcp:tools:inspect monolog-search # JSON output for scripting $ vendor/bin/mate mcp:tools:inspect server-info --format=json # TOON output for token-efficient inspection $ vendor/bin/mate mcp:tools:inspect server-info --format=toon mate mcp:tools:call-
Execute MCP tools via JSON input parameters. This command allows you to test and debug tools by executing them directly from the command line.
Arguments:
tool-name- Name of the tool to execute (required)
json-input- JSON object with tool parameters (required)
Options:
--format=FORMAT-
Output format:
pretty(default),json, ortoon. Thetoonformat requireshelgesverre/toon.
Examples:
1 2 3 4 5 6 7 8 9 10 11
# Execute tool with empty parameters $ vendor/bin/mate mcp:tools:call server-info '{}' # Execute tool with parameters $ vendor/bin/mate mcp:tools:call monolog-search '{"term": "error", "level": "error"}' # JSON output format $ vendor/bin/mate mcp:tools:call server-info '{}' --format=json # TOON output for token-efficient inspection $ vendor/bin/mate mcp:tools:call server-info '{}' --format=toon
Security
Discovered extensions are written to mate/extensions.php and new entries default to
enabled: true. Disable specific packages by setting their enabled flag to false.
Packages that set extra.ai-mate.extension to false are excluded from discovery entirely.