How to add custom tools
If your module already implements a REST API (webapi.xml) or extends the GraphQL schema, you do not need to create custom MCP tools for it. The built-in REST API and GraphQL Query tools automatically discover and expose all endpoints from installed modules.
Custom tools are useful when you need specialized behavior that goes beyond what REST API or GraphQL provides — for example, combining data from multiple sources, performing complex computations, or providing a simplified interface for a specific task.
The AI Integration extension supports adding custom tools via Magento's dependency injection. A custom tool can be added from any Magento module — you do not need to modify the MCP module itself.
Step 1: Create the tool class
Create a class that extends Mirasvit\Mcp\Tool\AbstractTool and implement the three required methods.
<?php
declare(strict_types=1);
namespace Vendor\Module\Tool;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Mirasvit\Mcp\Tool\AbstractTool;
use Mirasvit\Mcp\Tool\PermissionDetailsFactory;
use Mirasvit\Mcp\Tool\PermissionItemFactory;
class ProductCountTool extends AbstractTool
{
private ProductRepositoryInterface $productRepository;
private SearchCriteriaBuilder $searchCriteriaBuilder;
public function __construct(
PermissionDetailsFactory $permissionDetailsFactory,
PermissionItemFactory $permissionItemFactory,
ProductRepositoryInterface $productRepository,
SearchCriteriaBuilder $searchCriteriaBuilder
) {
parent::__construct($permissionDetailsFactory, $permissionItemFactory);
$this->productRepository = $productRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
}
public function getDescription(): string
{
return 'Returns the total number of products in the catalog, optionally filtered by type.';
}
public function getInputSchema(): array
{
return [
'type' => 'object',
'properties' => [
'product_type' => [
'type' => 'string',
'description' => 'Filter by product type (e.g., simple, configurable). Returns all types if not specified.',
],
],
];
}
public function execute(array $arguments): array
{
$type = $arguments['product_type'] ?? null;
if ($type) {
$this->searchCriteriaBuilder->addFilter('type_id', $type);
}
$searchCriteria = $this->searchCriteriaBuilder->create();
$result = $this->productRepository->getList($searchCriteria);
return [
'total_count' => $result->getTotalCount(),
'product_type' => $type ?? 'all',
];
}
}
What you need to implement
| Method | Description | Required |
|---|---|---|
getDescription() | Description shown to the AI client. | Yes |
getInputSchema() | JSON Schema object describing input parameters. | Yes |
execute() | Main execution method. Receives input, returns result array. | Yes |
getTitle() | Defaults to the class name with spaces (e.g., ProductCount → Product Count). | No |
getUsageHint() | Defaults to the description. | No |
getPermissionDetails() | Access summary for the consent screen and admin UI. Defaults to the tool's description. | No |
Tool naming convention
The tool name is automatically resolved from the class namespace and name:
{vendor}_{module}_{classname}
where:
- vendor — first namespace segment, lowercased
- module — second namespace segment, lowercased
- classname — class short name with
Toolsuffix stripped, lowercased
For the example above: Vendor\Module\Tool\ProductCountTool → vendor_module_productcount.
You do not need to specify the tool name anywhere — it is derived automatically.
Step 2: Register the tool in di.xml
Add the tool to the ToolPool via dependency injection in your module's etc/di.xml:
<type name="Mirasvit\Mcp\Model\ToolPool">
<arguments>
<argument name="tools" xsi:type="array">
<item name="product_count" xsi:type="object">Vendor\Module\Tool\ProductCountTool</item>
</argument>
</arguments>
</type>
The name attribute is an arbitrary key used for DI merging — it does not need to match the tool name.
Step 3: Run setup and test
php -f bin/magento setup:di:compile
php -f bin/magento cache:clean
After compilation, verify the tool by:
- Checking System -> Permissions -> User Roles -> [Role] -> Role Resources — the tool should appear under MCP Tools -> Vendor -> Module -> Product Count.
- Connecting an AI client and asking it to list available tools.
Automatic ACL registration
ACL resources for custom tools are registered automatically — you do not need to create an acl.xml file. The MCP module dynamically builds the ACL tree from all registered tools:
MCP Tools
├── Built in
│ └── System
│ ├── Store Info
│ ├── Database Reader
│ └── ...
└── Vendor
└── Module
└── Product Count
The ACL resource ID is derived from the tool name with a Mirasvit_Mcp::tools_ prefix. For vendor_module_productcount, the resource ID is Mirasvit_Mcp::tools_vendor_module_productcount.
Vendor and module names in the tree are derived from the class namespace. To customize the display labels, override getGroupLabel() and getCategoryLabel() in your tool class.
Reference
ToolInterface methods
Every tool must implement Mirasvit\Mcp\Api\ToolInterface. The AbstractTool base class provides default implementations for most methods — you only need to implement the three required methods listed above.
| Method | Description |
|---|---|
getName | Unique tool identifier. Auto-resolved by AbstractTool from the class namespace. |
getTitle | Human-readable title for admin UI. Auto-generated from class name. |
getDescription | Description shown to the AI client explaining what the tool does. |
getUsageHint | Usage guidance shown in tooltips and MCP tool hints. Defaults to description. |
getInputSchema | JSON Schema object describing the tool's input parameters. |
execute | Main execution method — receives input, returns result array. |
getPermissionDetails | Access summary displayed on the consent screen and in the admin MCP Info tab. |
Input schema format
The getInputSchema() method must return a JSON Schema object. The AI client uses this schema to understand what parameters the tool accepts.
Common patterns:
// Required string parameter
'query' => [
'type' => 'string',
'description' => 'SQL query to execute',
],
// Optional integer with default
'limit' => [
'type' => 'integer',
'description' => 'Maximum rows to return',
'default' => 100,
],
// Enum parameter
'format' => [
'type' => 'string',
'description' => 'Output format',
'enum' => ['json', 'csv', 'table'],
],
To mark parameters as required, add a required array at the top level of the schema:
return [
'type' => 'object',
'properties' => [ /* ... */ ],
'required' => ['query'],
];
Permission details
The getPermissionDetails() method returns a ToolPermissionDetailsInterface object with:
- Access summary — a short text describing what the tool can access. Shown on the OAuth consent screen and in the admin UI.
- Items — optional nested list of permission details (e.g., list of accessible tables for Database Reader).
For simple tools, use the factory inherited from AbstractTool:
public function getPermissionDetails(
int $roleId,
bool $isConsentContext = false
): ToolPermissionDetailsInterface {
return $this->permissionDetailsFactory->create([
'accessSummary' => 'Read-only access to product catalog data.',
]);
}
If not overridden, the default implementation uses the tool's getDescription() as the access summary.