Creating MCP Extensions
MCP extensions are Composer packages that declare themselves using a specific configuration
in composer.json, similar to PHPStan extensions.
Quick Start
1. Configure composer.json
1 2 3 4 5 6 7 8 9 10 11 12
{
"name": "vendor/my-extension",
"type": "library",
"require": {
"symfony/ai-mate": "^0.1"
},
"extra": {
"ai-mate": {
"scan-dirs": ["src", "lib"]
}
}
}
The extra.ai-mate section is required for your package to be discovered as an extension.
2. Create MCP Capabilities
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
use Mcp\Capability\Attribute\McpTool;
use Psr\Log\LoggerInterface;
class MyTool
{
// Dependencies are automatically injected
public function __construct(
private LoggerInterface $logger,
) {
}
#[McpTool(name: 'my-tool', description: 'What this tool does')]
public function execute(string $param): string
{
$this->logger->info('Tool executed', ['param' => $param]);
return 'Result: ' . $param;
}
}
3. Install and Enable
1 2
$ composer require vendor/my-extension
$ vendor/bin/mate discover
The discover command will automatically add your extension to .mate/extensions.php:
1 2 3
return [
'vendor/my-extension' => ['enabled' => true],
];
To disable an extension, set enabled to false:
1 2 3 4
return [
'vendor/my-extension' => ['enabled' => true],
'vendor/unwanted-extension' => ['enabled' => false],
];
Dependency Injection
Tools, Resources, and Prompts support constructor dependency injection via Symfony's DI Container. Dependencies are automatically resolved and injected.
Configuring Services
Register service configuration files in your composer.json:
1 2 3 4 5 6 7 8 9 10
{
"extra": {
"ai-mate": {
"scan-dirs": ["src"],
"includes": [
"config/services.php"
]
}
}
}
Create service configuration files using Symfony DI format:
1 2 3 4 5 6 7 8 9 10 11 12
// config/services.php
use App\MyApiClient;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return function (ContainerConfigurator $configurator) {
$services = $configurator->services();
// Register a service with parameters
$services->set(MyApiClient::class)
->arg('$apiKey', '%env(MY_API_KEY)%')
->arg('$baseUrl', 'https://api.example.com');
};
Configuration Reference
Scan Directories
extra.ai-mate.scan-dirs (optional)
- Default: Package root directory
- Relative to package root
- Multiple directories supported
Service Includes
extra.ai-mate.includes (optional)
- Array of service configuration file paths
- Standard Symfony DI configuration format (PHP files)
- Supports environment variables via
%env()%
Security
Extensions must be explicitly enabled in .mate/extensions.php:
- The
discovercommand automatically adds discovered extensions - All extensions default to
enabled: truewhen discovered - Set
enabled: falseto disable an extension
Troubleshooting
Extensions Not Discovered
If your extensions aren't being found:
Verify composer.json configuration:
Ensure your package has the
extra.ai-matesection:1 2 3 4 5 6 7
{ "extra": { "ai-mate": { "scan-dirs": ["src"] } } }Run discovery:
1
$ vendor/bin/mate discoverCheck the extensions file:
1
$ cat .mate/extensions.phpVerify your package is listed and
enabledistrue.
Extensions Not Loading
If extensions are discovered but not loading:
Check enabled status in
.mate/extensions.php:1 2 3
return [ 'vendor/my-extension' => ['enabled' => true], // Must be true ];- Verify scan directories exist and contain PHP files with MCP attributes.
Check for PHP errors in your extension code:
1
$ php -l src/MyTool.php
Tools Not Appearing
If your MCP tools don't appear in the AI assistant:
Verify MCP attributes are correctly applied:
1 2 3 4 5 6 7 8 9 10
use Mcp\Capability\Attribute\McpTool; class MyTool { #[McpTool(name: 'my-tool', description: 'Description here')] public function execute(): string { return 'result'; } }- Check that classes are in scan directories defined in
composer.json. - Restart your AI assistant after making changes.
- Check server logs for registration errors.
Tool Execution Fails
If tools are visible but fail when called:
Check return types - tools must return scalar values or arrays:
1 2 3 4 5 6
// Good public function execute(): string { return 'result'; } public function execute(): array { return ['key' => 'value']; } // Bad - objects are not directly serializable public function execute(): object { return new stdClass(); }- Check for exceptions in your tool code.
- Verify dependencies are properly injected.
Dependency Injection Issues
If dependencies aren't being injected:
Register services in your
services.phporconfig/services.php:1 2 3
$services->set(MyService::class) ->autowire() ->autoconfigure();Check interface bindings:
1
$services->alias(MyInterface::class, MyImplementation::class);Verify service configuration is listed in
composer.json:1 2 3 4 5 6 7
{ "extra": { "ai-mate": { "includes": ["config/services.php"] } } }
For general server issues and debugging tips, see the Troubleshooting guide.