symfony-php-mcp
Provides tools for reading and analyzing Symfony projects, including project metadata, routes, Twig templates, services, and PHP source code, enabling AI models to understand and work with Symfony applications.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@symfony-php-mcp List all Symfony routes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
symfony-php-mcp
A production-ready Model Context Protocol (MCP) server that bridges your Symfony/PHP project with LLMs such as Claude. It exposes tools that let the AI read your project's routes, services, Twig templates, and PHP source code — without ever needing direct filesystem access from the LLM itself.
Claude Desktop / Claude Code
│
│ MCP (stdio)
▼
symfony-php-mcp ──► reads ──► composer.json, symfony.lock, services.yaml, *.twig
──► runs ──► php bin/console debug:router / debug:containerTable of Contents
Related MCP server: phpustik MCP Server
Features
Tool | What it does | PHP needed? |
| Reads | No |
| Runs | Yes |
| Finds a | No |
| Reads | Optional |
| Reads a PHP file, strips doc-block comments to save tokens | No |
Quick Start
# Requires uv — https://docs.astral.sh/uv/
SYMFONY_PROJECT_ROOT=/path/to/your/symfony/app uvx symfony-php-mcpThe server speaks MCP over stdio and is designed to be launched by your MCP client (Claude Desktop, Claude Code, etc.), not run manually.
Installation
Via uvx (recommended)
uvx runs the package from the PyPI / GitHub registry with no permanent install:
// claude_desktop_config.json
{
"mcpServers": {
"symfony": {
"command": "uvx",
"args": ["symfony-php-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/path/to/your/symfony-app"
}
}
}
}Install from GitHub (before it's on PyPI):
{
"mcpServers": {
"symfony": {
"command": "uvx",
"args": ["--from", "git+https://github.com/maschmann/symfony-php-mcp", "symfony-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/path/to/your/symfony-app"
}
}
}
}Via local clone
git clone https://github.com/maschmann/symfony-php-mcp
cd symfony-php-mcp
uv sync{
"mcpServers": {
"symfony": {
"command": "uv",
"args": ["run", "--project", "/path/to/symfony-php-mcp", "symfony-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/path/to/your/symfony-app"
}
}
}
}Configuration
Configuration is resolved in priority order: environment variables → .symfony-mcp.json → built-in defaults.
Environment Variables
Set these in your MCP client's env block.
Variable | Default | Description |
| current working directory | Required. Absolute path to your Symfony project (the directory containing |
|
| PHP binary or wrapper command. Use |
| (none) | Docker container name. When set, commands run as |
| (none) | Optional |
|
| Path to |
|
| Seconds before a PHP subprocess is killed. Increase for large projects or slow containers. |
Project-local config file (.symfony-mcp.json)
Place this file in your Symfony project root to commit PHP runtime preferences alongside the project code. Great for teams using DDEV or Docker Compose.
{
"php_executable": "php",
"docker_container": null,
"docker_exec_user": null,
"console_path": "bin/console",
"command_timeout": 30
}Example for a DDEV project:
{
"php_executable": "ddev php",
"command_timeout": 60
}Example for a Docker Compose project:
{
"docker_container": "my-project-php-1",
"docker_exec_user": "www-data",
"command_timeout": 45
}Configuration priority
Environment variables (MCP client env block)
↓ (override)
.symfony-mcp.json (in SYMFONY_PROJECT_ROOT)
↓ (override)
Built-in defaultsEnvironment variables always win. This means you can commit a .symfony-mcp.json with sensible defaults for your team while still being able to override them per-machine via env vars.
Claude Desktop Setup
The claude_desktop_config.json file lives at:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
Local PHP
{
"mcpServers": {
"symfony": {
"command": "uvx",
"args": ["symfony-php-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/home/alice/projects/my-symfony-app"
}
}
}
}Docker
Works with any Docker Compose project. The container must be running when Claude Desktop starts (or before you use the tools).
{
"mcpServers": {
"symfony": {
"command": "uvx",
"args": ["symfony-php-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/home/alice/projects/my-symfony-app",
"DOCKER_CONTAINER": "my-symfony-app-php-1",
"DOCKER_EXEC_USER": "www-data"
}
}
}
}Finding your container name: run
docker psand look at theNAMEScolumn.
DDEV
{
"mcpServers": {
"symfony": {
"command": "uvx",
"args": ["symfony-php-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/home/alice/projects/my-symfony-app",
"PHP_EXECUTABLE": "ddev php"
}
}
}
}Alternatively, commit a .symfony-mcp.json to your project:
{
"php_executable": "ddev php"
}Then the MCP config only needs SYMFONY_PROJECT_ROOT.
Lando
{
"mcpServers": {
"symfony": {
"command": "uvx",
"args": ["symfony-php-mcp"],
"env": {
"SYMFONY_PROJECT_ROOT": "/home/alice/projects/my-symfony-app",
"PHP_EXECUTABLE": "lando php"
}
}
}
}Sail
Laravel Sail is a thin Docker wrapper but the pattern works for Symfony projects using a similar setup:
{
"env": {
"SYMFONY_PROJECT_ROOT": "/home/alice/projects/my-project",
"DOCKER_CONTAINER": "my-project-laravel.test-1"
}
}Tools Reference
get_project_overview
Returns a Markdown summary of the project. Call this first to give the LLM context before using other tools.
Parameters: none
Returns:
# Symfony Project: `acme/store`
> An e-commerce platform built with Symfony
## Runtime
| Key | Value |
|-----|-------|
| PHP requirement | `>=8.2` |
| Symfony version | `7.1.3` |
| APP_ENV | `dev` |
## Installed Packages
### Symfony Components
| Package | Version | Dev? |
...find_route
Finds routes matching a URL pattern or route name.
Parameters:
Parameter | Type | Required | Description |
| string | Yes | Substring or regex to match against route paths and names. E.g. |
| string | No | HTTP method filter: |
Example:
Tool: find_route
url_pattern: /api/user
method: GETFound **3** route(s) matching `/api/user`
| Route Name | Path | Methods | Controller |
|------------|------|---------|------------|
| `api_user_list` | `/api/users` | `GET` | `UserController::list` |
| `api_user_show` | `/api/users/{id}` | `GET` | `UserController::show` |
| `api_user_me` | `/api/user/me` | `GET` | `UserController::me` |Requires: PHP + bin/console
analyze_twig
Analyses a Twig template without running PHP.
Parameters:
Parameter | Type | Required | Description |
| string | Yes | Template name as used in Twig, or a partial name. E.g. |
Example:
Tool: analyze_twig
template_name: user/show.html.twig## Template: `templates/user/show.html.twig`
### Inheritance
Extends: `base.html.twig`
### Included / Embedded Templates
| Type | Template |
|------|----------|
| include | `_partials/user_card.html.twig` |
| include | `_partials/breadcrumb.html.twig` |
### Defined Blocks
`title`, `content`, `scripts`
### Template Variables
`user`, `posts`, `pagination`list_services
Lists service definitions from config/services.yaml or the compiled container.
Parameters:
Parameter | Type | Default | Description |
| string |
| Regex/substring to filter by service ID or class. E.g. |
| bool |
| Use |
Example – find all mailer services:
Tool: list_services
filter_pattern: mailer
use_container_debug: trueread_code_context
Reads a PHP file with optional comment stripping to reduce token usage.
Parameters:
Parameter | Type | Default | Description |
| string | — | Path relative to project root. E.g. |
| bool |
| Strip |
| bool |
| Also strip |
Example:
Tool: read_code_context
file_path: src/Controller/UserController.php## `src/Controller/UserController.php`
| Property | Value |
|----------|-------|
| Original | 187 lines / 6,842 chars |
| After processing | 134 lines / 4,201 chars |
| Token savings | ~39% (block comments stripped) |
```php
1 | <?php
2 |
3 | namespace App\Controller;
...Docker / Container Environments (in depth)
How command routing works
When DOCKER_CONTAINER is set, every PHP/console invocation becomes:
docker exec [-u <DOCKER_EXEC_USER>] <DOCKER_CONTAINER> php bin/console <args>When PHP_EXECUTABLE is set to ddev php:
ddev php bin/console <args>The server never modifies the project files inside the container.
Docker Compose tips
Container name – use
docker psto find the exact name. Fordocker compose, it's usually<project>-<service>-1.File paths –
SYMFONY_PROJECT_ROOTshould point to the host path since the server reads files directly via the Python filesystem layer. Onlydebug:router/debug:containercommands run inside the container.Working directory – commands are run in
SYMFONY_PROJECT_ROOTon the host. If your container mounts the project at a different path, setCONSOLE_PATHaccordingly — or ensurebin/consoleis accessible from the host path.Container not running – the server will return a helpful error. Start your containers first:
docker compose up -d.
DDEV tips
# List DDEV projects
ddev list
# Ensure the project is running
ddev start
# Test PHP is accessible
ddev php --version.symfony-mcp.json for DDEV:
{
"php_executable": "ddev php",
"command_timeout": 60
}Lando tips
# Ensure the project is running
lando start
# Test PHP is accessible
lando php --version.symfony-mcp.json for Lando:
{
"php_executable": "lando php"
}Development
# Clone
git clone https://github.com/maschmann/symfony-php-mcp
cd symfony-php-mcp
# Install dependencies (requires uv — https://docs.astral.sh/uv/)
uv sync
# Run the server (will block waiting for MCP stdio input)
uv run symfony-php-mcp
# Run tests
uv run pytest
# Lint
uv run ruff check src/
uv run ruff format --check src/Project structure
src/symfony_mcp/
├── server.py # FastMCP server, lifespan, tool decorators, main()
├── config.py # ServerConfig: env vars + .symfony-mcp.json merge logic
├── executor.py # PhpExecutor: subprocess wrapper with Docker/wrapper support
├── indexer.py # SymbolIndex: PHP regex scanner + JSON persistence
└── tools/
├── project.py # get_project_overview
├── router.py # find_route
├── twig.py # analyze_twig
├── services.py # list_services
├── code.py # read_code_context
└── index.py # build_index, find_symbol, search_codeSymbol index
The index is stored at <symfony-project>/.symfony-mcp-index.json. Add it to the project's .gitignore:
# symfony-php-mcp symbol index
.symfony-mcp-index.jsonTypical workflow:
1. build_index — first time, or after major refactors
2. find_symbol "UserController" — get file + line number instantly
3. read_code_context src/...php — read the implementationThe index updates incrementally (only changed files are re-scanned), so calling build_index after saving a few files is fast.
Adding a new tool
Create
src/symfony_mcp/tools/my_tool.pywith a plain function.Register it in
server.pywith@mcp.tool().Add a docstring – FastMCP uses it as the tool description.
Troubleshooting
"Binary not found: 'php'"
PHP is not in the PATH used by the MCP server process.
Fix options:
Install PHP:
apt install php-cli/brew install phpUse a full path:
PHP_EXECUTABLE=/usr/bin/php8.3Use Docker:
DOCKER_CONTAINER=my-php-containerUse DDEV:
PHP_EXECUTABLE=ddev php
"Symfony console not found"
SYMFONY_PROJECT_ROOT is pointing to the wrong directory, or bin/console is missing.
Fix: Make sure SYMFONY_PROJECT_ROOT is the directory that contains both composer.json and bin/console.
ls /your/project/bin/console # should exist"Command timed out"
The PHP command took longer than COMMAND_TIMEOUT seconds.
Fix: Increase the timeout:
// .symfony-mcp.json
{ "command_timeout": 120 }Or set COMMAND_TIMEOUT=120 in the MCP env block.
"Cannot inspect container"
The Docker container is not running.
Fix:
docker compose up -d # Docker Compose
ddev start # DDEV
lando start # LandoClaude Desktop doesn't see the server
Restart Claude Desktop after editing
claude_desktop_config.json.Check the MCP server logs in Claude Desktop → Settings → Developer → MCP Servers.
Run the server manually to check for errors:
SYMFONY_PROJECT_ROOT=/path/to/project uvx symfony-php-mcpIt should start silently (waiting for stdio input). Any error on startup will print to stderr.
"Error parsing config/services.yaml"
Your services.yaml has a syntax error or uses YAML features not supported by PyYAML.
Fix: Use use_container_debug=true in list_services as a fallback:
Tool: list_services
filter_pattern: App\
use_container_debug: trueLicense
MIT — see LICENSE.
Available Tools
8 toolsanalyze_twigA
Analyse a Twig template and return its structural metadata.
No PHP execution required – pure filesystem analysis.
Extracts:
extends parent (inheritance chain)
{% include %} and {% embed %} directives
{% import %} and {% from ... import %} macro imports
{% block %} definitions
Template variables referenced in {{ }} expressions
Args: template_name: Template path as used in Twig (e.g. "user/show.html.twig") or a partial name (e.g. "show" or "user/show"). The templates/ directory is searched recursively.
Returns a structured Markdown report.
Inputs: template_name (required). Returns: result
build_indexA
Scan PHP files and build (or update) the symbol index.
The index stores all classes, interfaces, traits, enums, and their methods so that find_symbol can locate any symbol instantly without re-scanning.
The index is persisted to <project_root>/.symfony-mcp-index.json and loaded automatically on next server start. Only changed files are re-scanned (incremental), so subsequent calls are fast.
Args: directories: Directories to scan, relative to the project root. Default: auto-detected (src/, app/, lib/). Example: ["src", "lib"] force: Re-scan every file even if it hasn't changed. Use this after a major refactor or rename.
Run this once after pointing the server at a new project, then again after significant code changes.
Inputs: directories, force. Returns: result
find_routeA
Find Symfony routes matching a URL pattern or route name.
Runs php bin/console debug:router --format=json and filters the results.
Args: url_pattern: Substring or regex to match against route paths and route names. Examples: "/api/users", "user_show", "^/admin" method: Optional HTTP method filter (GET, POST, PUT, PATCH, DELETE). Empty string = no filter (matches all methods).
Returns a Markdown table with: route name, path, allowed methods, controller. For single matches, the full route definition is included.
Requires PHP and bin/console to be accessible (configure via DOCKER_CONTAINER or PHP_EXECUTABLE if using Docker/DDEV/Lando).
Inputs: url_pattern (required), method. Returns: result
find_symbolA
Search the symbol index for a PHP class, interface, trait, enum, or method.
Returns file paths and line numbers — use the result directly with read_code_context to inspect the implementation.
Requires build_index to have been run at least once.
Args: name: Name to search for (case-insensitive substring or full name). Examples: "UserController", "UserRepo", "findByEmail", "App\Entity" kind: Optional filter. One of: class, interface, trait, enum, method. Empty = search all symbol types.
Typical workflow:
build_index — index the project (once, or after big changes)
find_symbol "Foo" — locate the file and line
read_code_context — read the file for full implementation
Inputs: name (required), kind. Returns: result
get_project_overviewA
Return a Markdown overview of the Symfony project.
Reads composer.json, symfony.lock (or composer.lock), and .env to report:
PHP version requirement
Exact Symfony version installed
All installed packages, grouped by category
PSR-4 autoload namespaces
Composer scripts
APP_ENV setting
Call this tool first before using other tools to understand the project.
Inputs: no named parameters. Returns: result
list_servicesA
List Symfony service definitions.
Two modes:
YAML mode (default, fast, no PHP required): Reads config/services.yaml directly. Shows explicitly defined services.
Container debug mode (use_container_debug=true): Runs
php bin/console debug:container --format=json. Shows the full compiled container including auto-wired services – useful for finding framework/bundle services.
Args: filter_pattern: Regex or substring to filter service IDs or class names. Examples: "App\Service", "mailer", "doctrine" Empty = show all defined services. use_container_debug: Set to true to query the full compiled DI container. Requires PHP and bin/console to be accessible.
Returns a Markdown table with: service ID, class, public flag, tags.
Inputs: filter_pattern, use_container_debug. Returns: result
read_code_contextA
Read a file from the Symfony project, with optional comment stripping.
Stripping PHPDoc / block comments (/* ... */) significantly reduces token usage without losing functional information – use strip_doc_comments=true (the default) for faster, cheaper analysis.
Args: file_path: Path relative to the project root. Examples: "src/Controller/UserController.php" "src/Entity/User.php" Absolute paths inside the project are also accepted. strip_doc_comments: Remove /** ... / and / ... */ blocks. Default: true. strip_line_comments: Also remove // single-line comments. Default: false (inline comments are useful context).
Returns the file content with line numbers and a token-savings summary.
Security: only files inside the project root can be read (path traversal is blocked).
Inputs: file_path (required), strip_doc_comments, strip_line_comments. Returns: result
search_codeA
Live regex/substring search across project files.
No index required — scans files directly each time. Useful for finding usages, checking for patterns, or searching non-PHP files.
Args: pattern: Python regex or plain substring to search for. Examples: "UserRepository", "findByEmail(", "#[Route" path_glob: Glob relative to project root. Default: /*.php Other examples: src//.php, templates/**/.twig, config/**/*.yaml, **/*.php context_lines: Lines of surrounding context to show (0–5). Default: 2. max_results: Max matching snippets to return (1–200). Default: 30.
Returns highlighted snippets with file path and line number.
Inputs: pattern (required), path_glob, context_lines, max_results. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool has a clearly distinct purpose: Twig analysis, index building, route finding, symbol lookup, project overview, service listing, file reading, and regex search. No overlaps or ambiguity.
All tool names follow a consistent verb_noun pattern (e.g., analyze_twig, build_index, find_route) and use snake_case uniformly, making the set predictable.
With 8 tools, the server covers the essential operations for Symfony project inspection without being excessive or sparse. The count is well-scoped for its purpose.
The tool surface covers core read/inspection tasks (routes, services, symbols, code, Twig), but lacks a tool to list all Twig templates or other structural elements, a minor gap.
Maintenance
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Security-first WordPress MCP server. 129 tools for Claude, ChatGPT, Gemini. Free on wp.org.
Related MCP Servers
- AlicenseAqualityDmaintenanceA Model Context Protocol (MCP) server designed to easily dump your codebase context into Large Language Models (LLMs).12 npm3Apache 2.0
- AlicenseBqualityDmaintenanceEnables AI assistants to deeply interact with the PHP ecosystem, including runtime, static analysis, security scanning, testing, Composer, and frameworks like Laravel and Symfony. It exposes over 30 tools, 8 resources, and 7 prompts via MCP, allowing natural language commands to run PHP linting, static analysis, audits, tests, and project initialization.41MIT

octopilot-mcpofficial
FlicenseAqualityDmaintenanceModel Context Protocol (MCP) server for Octopilot — enables AI agents to detect, generate, build, and wire up new repositories end-to-end using the Octopilot CI/CD toolchain.7-- AlicenseNot gradedqualityDmaintenanceMCP server that exposes Symfony profiler runtime data (requests, queries, logs) to AI agents like Claude Code for debugging and optimization.MIT