The LlmAnalyzer CLI command (genaker:agento:llm) uses an extensible tool-calling architecture that allows AI to interact with your Magento database, search files, read code, and perform various operations. Tools are registered via Magento's Dependency Injection system, making it easy to add custom operations.
User Query
↓
AI analyzes query and selects tool(s)
↓
Tool executes (SQL, DESCRIBE, custom operations)
↓
Results returned to AI
↓
AI provides final answer
The module includes four default tools:
Executes SQL queries against the Magento database.
Parameters:
query(string, required) - SQL query to executereason(string, required) - Why this query is needed
Example:
{
"tool": "execute_sql_query",
"arguments": {
"query": "SELECT * FROM customer_entity WHERE email LIKE '%test%' LIMIT 10",
"reason": "Find customers with test emails"
}
}Returns:
[
'success' => true,
'row_count' => 10,
'columns' => ['entity_id', 'email', 'firstname'],
'data' => [...],
'preview' => 'Formatted preview for AI'
]Gets the structure of a database table.
Parameters:
table_name(string, required) - Name of the table
Example:
{
"tool": "describe_table",
"arguments": {
"table_name": "sales_order"
}
}Returns:
[
'success' => true,
'table' => 'sales_order',
'columns' => [
['Field' => 'entity_id', 'Type' => 'int', 'Key' => 'PRI'],
['Field' => 'customer_id', 'Type' => 'int', 'Key' => 'MUL']
],
'preview' => 'Field: entity_id | Type: int | Key: PRI'
]Searches for text patterns in files. Useful for finding code, configurations, or specific content across the codebase.
Parameters:
pattern(string, required) - The search pattern (supports regex)file_path(string, required) - File path or directory to search in (relative to Magento root). Use * for wildcard (e.g., "app/code/**/*.php")max_results(integer, optional) - Maximum number of results to return (default: 50)
Example:
{
"tool": "grep_files",
"arguments": {
"pattern": "class Customer",
"file_path": "app/code/**/*.php",
"max_results": 20
}
}Returns:
[
'success' => true,
'pattern' => 'class Customer',
'file_path' => 'app/code/**/*.php',
'match_count' => 5,
'matches' => [
['file' => 'app/code/Vendor/Module/Model/Customer.php', 'line' => 10, 'content' => 'class Customer extends AbstractModel'],
...
],
'preview' => 'Found 5 matches:\napp/code/Vendor/Module/Model/Customer.php:10 - class Customer extends AbstractModel'
]Reads the contents of a file. Useful for examining code, configurations, or documentation.
Parameters:
file_path(string, required) - Path to the file (relative to Magento root)start_line(integer, optional) - Start reading from this line number (1-indexed)end_line(integer, optional) - End reading at this line number (1-indexed)max_lines(integer, optional) - Maximum number of lines to read (default: 500)
Example:
{
"tool": "read_file",
"arguments": {
"file_path": "app/code/Vendor/Module/Model/Customer.php",
"start_line": 1,
"end_line": 50
}
}Returns:
[
'success' => true,
'file_path' => 'app/code/Vendor/Module/Model/Customer.php',
'total_lines' => 200,
'lines_read' => 50,
'start_line' => 1,
'end_line' => 50,
'content' => [
['line' => 1, 'content' => '<?php'],
['line' => 2, 'content' => 'namespace Vendor\\Module\\Model;'],
...
],
'preview' => 'File: app/code/Vendor/Module/Model/Customer.php (showing lines 1-50 of 200)\n...'
]Create a class implementing Genaker\MagentoMcpAi\Api\DatabaseToolInterface:
<?php
namespace YourVendor\YourModule\Model\DatabaseTool;
use Genaker\MagentoMcpAi\Api\DatabaseToolInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\ResourceConnection;
class ShowTablesTool implements DatabaseToolInterface
{
private $resourceConnection;
public function __construct(ResourceConnection $resourceConnection)
{
$this->resourceConnection = $resourceConnection;
}
public function getName(): string
{
return 'show_tables';
}
public function getDescription(): string
{
return 'List all tables in the database, optionally filtered by pattern. Useful for discovering available tables.';
}
public function getParametersSchema(): array
{
return [
'type' => 'object',
'properties' => [
'pattern' => [
'type' => 'string',
'description' => 'Optional table name pattern (e.g., "customer%" to find customer-related tables)'
]
],
'required' => []
];
}
public function execute(array $arguments, bool $allowDangerous = false): array
{
$pattern = $arguments['pattern'] ?? '';
$connection = $this->resourceConnection->getConnection();
$sql = "SHOW TABLES";
if ($pattern) {
$sql .= " LIKE " . $connection->quote($pattern);
}
$results = $connection->fetchAll($sql);
$tables = array_column($results, 'Tables_in_' . $connection->getSchemaName());
return [
'success' => true,
'tables' => $tables,
'count' => count($tables),
'preview' => implode("\n", array_slice($tables, 0, 20))
];
}
}Add your tool to the registry in your module's etc/di.xml:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<!-- Register your custom tool -->
<type name="Genaker\MagentoMcpAi\Model\DatabaseTool\Registry">
<arguments>
<argument name="tools" xsi:type="array">
<item name="show_tables" xsi:type="object">YourVendor\YourModule\Model\DatabaseTool\ShowTablesTool</item>
</argument>
</arguments>
</type>
<!-- Configure your tool's dependencies -->
<type name="YourVendor\YourModule\Model\DatabaseTool\ShowTablesTool">
<arguments>
<argument name="resourceConnection" xsi:type="object">Magento\Framework\App\ResourceConnection</argument>
</arguments>
</type>
</config>After registering your tool:
bin/magento cache:clean config
rm -rf generated/code/*interface DatabaseToolInterface
{
/**
* Get unique tool name (lowercase with underscores)
* @return string
*/
public function getName(): string;
/**
* Get tool description for AI
* @return string
*/
public function getDescription(): string;
/**
* Get OpenAPI-style JSON schema for parameters
* @return array
*/
public function getParametersSchema(): array;
/**
* Execute the tool
*
* @param array $arguments Tool arguments from AI
* @param bool $allowDangerous Whether dangerous operations are allowed
* @return array Result with 'success', 'data', 'preview', etc.
* @throws \Exception
*/
public function execute(array $arguments, bool $allowDangerous = false): array;
}Your tool's execute() method should return:
[
'success' => true, // boolean - required
'data' => [...], // array - your actual results
'preview' => '...', // string - formatted for AI consumption
'row_count' => 10, // int - optional: number of rows
'columns' => [...], // array - optional: column names
// ... any other metadata
]On Error:
Throw a LocalizedException or return:
['error' => 'Error message']Tools that perform write operations should include dangerous keywords in their name:
insert_*update_*delete_*drop_*truncate_*alter_*
These tools are automatically filtered out in safe mode (default). They only appear when --allow-dangerous flag is used.
Example Dangerous Tool:
public function getName(): string
{
return 'update_customer_email'; // Contains "update" - will be filtered in safe mode
}<?php
namespace YourVendor\YourModule\Model\DatabaseTool;
use Genaker\MagentoMcpAi\Api\DatabaseToolInterface;
use Magento\Framework\App\ResourceConnection;
class ShowTablesTool implements DatabaseToolInterface
{
private $resourceConnection;
public function __construct(ResourceConnection $resourceConnection)
{
$this->resourceConnection = $resourceConnection;
}
public function getName(): string
{
return 'show_tables';
}
public function getDescription(): string
{
return 'List all database tables, optionally filtered by pattern';
}
public function getParametersSchema(): array
{
return [
'type' => 'object',
'properties' => [
'pattern' => [
'type' => 'string',
'description' => 'Table name pattern (e.g., "customer%")'
]
],
'required' => []
];
}
public function execute(array $arguments, bool $allowDangerous = false): array
{
$pattern = $arguments['pattern'] ?? '';
$connection = $this->resourceConnection->getConnection();
$sql = "SHOW TABLES";
if ($pattern) {
$sql .= " LIKE " . $connection->quote($pattern);
}
$results = $connection->fetchAll($sql);
$schemaName = $connection->getSchemaName();
$tables = array_column($results, 'Tables_in_' . $schemaName);
return [
'success' => true,
'tables' => $tables,
'count' => count($tables),
'preview' => implode("\n", array_slice($tables, 0, 20))
];
}
}<?php
namespace YourVendor\YourModule\Model\DatabaseTool;
use Genaker\MagentoMcpAi\Api\DatabaseToolInterface;
use Magento\Framework\App\ResourceConnection;
class ExplainQueryTool implements DatabaseToolInterface
{
private $resourceConnection;
public function __construct(ResourceConnection $resourceConnection)
{
$this->resourceConnection = $resourceConnection;
}
public function getName(): string
{
return 'explain_query';
}
public function getDescription(): string
{
return 'Analyze SQL query execution plan to understand performance';
}
public function getParametersSchema(): array
{
return [
'type' => 'object',
'properties' => [
'query' => [
'type' => 'string',
'description' => 'SQL query to analyze'
]
],
'required' => ['query']
];
}
public function execute(array $arguments, bool $allowDangerous = false): array
{
$query = $arguments['query'] ?? '';
if (empty($query)) {
throw new \Exception('Query is required');
}
$connection = $this->resourceConnection->getConnection();
$explainQuery = "EXPLAIN " . $query;
$results = $connection->fetchAll($explainQuery);
return [
'success' => true,
'execution_plan' => $results,
'preview' => $this->formatExplainResults($results)
];
}
private function formatExplainResults(array $results): string
{
// Format EXPLAIN results for AI
$formatted = [];
foreach ($results as $row) {
$formatted[] = sprintf(
"Table: %s | Type: %s | Rows: %s | Key: %s",
$row['table'] ?? '',
$row['type'] ?? '',
$row['rows'] ?? '',
$row['key'] ?? ''
);
}
return implode("\n", $formatted);
}
}<?php
namespace YourVendor\YourModule\Model\DatabaseTool;
use Genaker\MagentoMcpAi\Api\DatabaseToolInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
class UpdateCustomerTool implements DatabaseToolInterface
{
private $resourceConnection;
public function __construct(ResourceConnection $resourceConnection)
{
$this->resourceConnection = $resourceConnection;
}
public function getName(): string
{
return 'update_customer'; // Contains "update" - dangerous tool
}
public function getDescription(): string
{
return 'Update customer information (REQUIRES --allow-dangerous flag)';
}
public function getParametersSchema(): array
{
return [
'type' => 'object',
'properties' => [
'customer_id' => [
'type' => 'integer',
'description' => 'Customer ID'
],
'field' => [
'type' => 'string',
'description' => 'Field to update'
],
'value' => [
'type' => 'string',
'description' => 'New value'
]
],
'required' => ['customer_id', 'field', 'value']
];
}
public function execute(array $arguments, bool $allowDangerous = false): array
{
if (!$allowDangerous) {
throw new LocalizedException(
__('This tool requires --allow-dangerous flag')
);
}
$customerId = $arguments['customer_id'] ?? null;
$field = $arguments['field'] ?? '';
$value = $arguments['value'] ?? '';
if (!$customerId || !$field) {
throw new LocalizedException(__('customer_id and field are required'));
}
$connection = $this->resourceConnection->getConnection();
$tableName = $connection->getTableName('customer_entity');
$connection->update(
$tableName,
[$field => $value],
['entity_id = ?' => $customerId]
);
return [
'success' => true,
'message' => "Updated customer $customerId field $field",
'affected_rows' => 1
];
}
}<type name="Genaker\MagentoMcpAi\Model\DatabaseTool\Registry">
<arguments>
<argument name="tools" xsi:type="array">
<item name="your_tool_name" xsi:type="object">YourVendor\YourModule\Model\DatabaseTool\YourTool</item>
</argument>
</arguments>
</type><type name="Genaker\MagentoMcpAi\Model\DatabaseTool\Registry">
<arguments>
<argument name="tools" xsi:type="array">
<item name="show_tables" xsi:type="object">YourVendor\YourModule\Model\DatabaseTool\ShowTablesTool</item>
<item name="explain_query" xsi:type="object">YourVendor\YourModule\Model\DatabaseTool\ExplainQueryTool</item>
<item name="analyze_indexes" xsi:type="object">YourVendor\YourModule\Model\DatabaseTool\AnalyzeIndexesTool</item>
</argument>
</arguments>
</type><!-- Register tool -->
<type name="Genaker\MagentoMcpAi\Model\DatabaseTool\Registry">
<arguments>
<argument name="tools" xsi:type="array">
<item name="custom_tool" xsi:type="object">YourVendor\YourModule\Model\DatabaseTool\CustomTool</item>
</argument>
</arguments>
</type>
<!-- Configure tool dependencies -->
<type name="YourVendor\YourModule\Model\DatabaseTool\CustomTool">
<arguments>
<argument name="resourceConnection" xsi:type="object">Magento\Framework\App\ResourceConnection</argument>
<argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument>
<argument name="customService" xsi:type="object">YourVendor\YourModule\Service\CustomService</argument>
</arguments>
</type>- Use lowercase with underscores:
show_tables,explain_query - Be descriptive:
analyze_query_performance,export_table_data - Dangerous tools: Include keywords (
insert,update,delete, etc.) in name
- Validate Input: Always validate and sanitize tool arguments
- Error Handling: Throw
LocalizedExceptionwith clear error messages - Security: Check
$allowDangerousflag for write operations - Preview Format: Provide a concise preview string for AI context
- Limit Results: For large datasets, limit results in preview (full data still returned)
- Documentation: Write clear descriptions - AI uses these to decide which tool to call
Test your custom tool:
<?php
namespace YourVendor\YourModule\Test\Integration\Model\DatabaseTool;
use PHPUnit\Framework\TestCase;
use YourVendor\YourModule\Model\DatabaseTool\YourTool;
class YourToolTest extends TestCase
{
public function testToolExecution(): void
{
$tool = $this->createMock(YourTool::class);
// Test your tool logic
}
}# Basic query (uses tools automatically)
bin/magento genaker:agento:llm "show me all customers"
# With dangerous operations
bin/magento genaker:agento:llm "update customer email" --allow-dangerous- User asks: "What tables contain customer data?"
- AI calls:
describe_table("customer_entity") - Tool returns: Table structure
- AI calls:
show_tables({"pattern": "customer%"}) - Tool returns: List of customer-related tables
- AI provides: Final answer with table names
- Check
di.xmlregistration - Clear cache:
bin/magento cache:clean config - Clear generated code:
rm -rf generated/code/* - Verify tool implements
DatabaseToolInterface
- Check tool name matches exactly
- Verify parameters match schema
- Check tool's
execute()method for exceptions - Review command output for error messages
- Ensure
--allow-dangerousflag is used - Check tool name contains dangerous keyword
- Verify
isDangerousTool()logic in Registry
// Get all tools formatted for AI
$tools = $registry->getToolsForAI($allowDangerous);
// Get specific tool
$tool = $registry->getTool('tool_name');
// Get all registered tools
$allTools = $registry->getTools();- AI receives tool definitions via
getToolsForAI() - AI decides to call a tool
- Command parses tool call from AI response
- Command gets tool from registry:
getTool($name) - Command executes:
$tool->execute($arguments, $allowDangerous) - Results displayed and sent back to AI
- AI uses results to provide final answer
EXTENDING_DATABASE_TOOLS.md- Quick start guideMAGENTO_AI_QUERY_ANALYZER.md- CLI command documentation- Default tool implementations in
Model/DatabaseTool/