freento / module-mcp-audit
MCP Audit Tools for Magento 2
Requires
- php: ^8.1
- freento/module-mcp: ^1.2
- magento/magento-coding-standard: *
README
Add-on for Freento_Mcp that registers a set of audit and diagnostic MCP tools. The tools are designed for AI-driven store audits — collecting structured data about the filesystem, database, configuration, logs, indexers, cron, third-party modules, code quality and email delivery, all exposed over the standard MCP protocol provided by Freento_Mcp.
Features
- 16 ready-to-use audit tools registered into the parent module's
ToolRegistry - Single admin toggle to hide every audit tool everywhere (tools/list, tools/call, ACL UI)
- Log error grouping with smart message normalization (UUIDs, IDs, quoted values, emails)
- Two-step on-demand SQL query counter for any frontend URL (bypasses FPC)
- Sensitive
env.phpkeys (passwords, secrets, hosts, ports, etc.) automatically masked - Curated critical-settings check (deployment mode, 2FA, MySQL optimizer, Xdebug/APCu, key rotation, …)
Requirements
- Magento 2.4.x
- PHP 8.1+
freento/module-mcp^1.2(loaded as a module sequence dependency)magento/magento-coding-standard(declared as a composer dependency; required byrun_code_sniffer)
Installation
Via Composer
composer require freento/module-mcp-audit php bin/magento module:enable Freento_McpAudit php bin/magento setup:upgrade php bin/magento cache:flush
Manual
Copy the module to app/code/Freento/McpAudit/ and run:
php bin/magento module:enable Freento_McpAudit php bin/magento setup:upgrade php bin/magento cache:flush
Configuration
Stores > Configuration > Freento > MCP > MCP Audit
| Field | Path | Description |
|---|---|---|
| Enable Audit Tools | freento_mcp/audit/enabled |
When disabled, all McpAudit tools are hidden everywhere — they are not exposed via tools/list, cannot be called, and do not appear in the ACL Rule tools list. |
The toggle is enforced at the ToolRegistry level via Plugin\ToolRegistryPlugin, which filters out every tool whose class lives under the Freento\McpAudit\ namespace when the module is disabled.
Available Tools
Database & Filesystem
| Tool | Description |
|---|---|
get_table_sizes |
Database table sizes (rows + MB) sorted by size, with optional LIKE pattern, min-size and limit filters. |
get_log_file_sizes |
Files in var/log and var/report sorted by size, with each file's inode change time (ctime). |
Logs & Errors
| Tool | Description |
|---|---|
get_log_errors |
Greps var/log/*.log for ERROR/CRITICAL entries, groups identical errors after normalizing UUIDs, IDs, quoted strings and emails. Filterable by date range (silently truncated to the first 31 days starting at from_date), sortable by count/first_seen/last_seen. Defaults: from_date/to_date = today, limit = 50 (max 200). |
Indexers & Cron
| Tool | Description |
|---|---|
get_indexer_statuses |
All indexers with status (valid/invalid/working), mode (schedule/save) and changelog backlog count. |
get_cron_config |
Cron groups configuration: schedule/lifetime/cleanup settings (XML defaults vs DB overrides), use_separate_process flag, and per-group job list with cron expressions. |
get_cron_schedule |
Cron schedule history (cron_schedule table) with filtering, sorting and aggregation, plus a derived execution_time field (finished_at - executed_at, rendered as a human-readable duration like 5m 30s). |
Configuration
| Tool | Description |
|---|---|
get_critical_settings |
Curated checks that are awkward to express via the generic config tools: deployment mode, admin URL, 2FA status, active cart rules without a coupon (always-on rules), MySQL optimizer_switch / optimizer_use_condition_selectivity, Xdebug + APCu state, search terms count, presence of an active admin user, and whether the encryption key has been rotated. |
get_config_data |
Fully resolved ScopeConfigInterface values (config.xml + core_config_data + env.php overrides). Accepts exact paths or path prefixes; supports default/websites/stores scopes and all_scopes: true for a full dump. |
get_php_ini_settings |
ini_get() lookup for an explicit list of php.ini directives. |
get_deployment_config |
Values from app/etc/env.php for an explicit paths list. Sensitive entries are silently omitted — match either a substring (password, passwd, secret, token, crypt, salt, private, credential, api_key, access_key, bearer, oauth, signature, hmac) or a full path segment (host, port, server, dbname, username, user, pwd, pass, key, auth, pem, cert). |
Modules & Code Quality
| Tool | Description |
|---|---|
get_third_party_modules |
Installed non-Magento and non-PayPal modules grouped by vendor, with composer name and install location (app/code vs vendor/). Optional include_latest_version queries the configured composer repositories for available stable upgrades. |
run_code_sniffer |
Runs vendor/bin/phpcs --standard=Magento2 against a module name (Vendor_Module), relative path, or absolute path. Output is parsed from JSON, filtered by severity, capped at 200 KB, and grouped per file. |
Catalog & Customer
| Tool | Description |
|---|---|
get_customer_groups |
customer_group rows with filtering and aggregation (entity-tool framework). |
get_eav_attributes |
eav_attribute rows with filtering by entity type, backend type, frontend input, native vs user-defined. |
Performance & Email
| Tool | Description |
|---|---|
count_sql_queries |
Counts SQL queries executed for an arbitrary storefront URL (the SqlCounterPlugin is registered in the frontend area only — admin/API requests are not counted). Two-step flow: action=start returns a counter ID and a visit URL containing ?_sql_counter=<id>; visiting that URL triggers the plugin (FPC bypassed by the unique query string); action=get returns the count. |
send_test_email |
Sends a test email through the configured customer-create-account template, using the store's transactional sender. Useful for verifying SMTP and trans_email/ident_general/*. |
Architecture
How tools are registered
etc/di.xml extends the parent module's Freento\Mcp\Model\ToolRegistry constructor's tools array argument, plus attaches ToolRegistryPlugin:
<type name="Freento\Mcp\Model\ToolRegistry"> <arguments> <argument name="tools" xsi:type="array"> <item name="get_table_sizes" xsi:type="object">Freento\McpAudit\Model\Tool\GetTableSizes</item> <!-- … 15 more entries … --> </argument> </arguments> <plugin name="freento_mcp_audit_tool_registry" type="Freento\McpAudit\Plugin\ToolRegistryPlugin" sortOrder="100"/> </type>
How the SQL counter works
Plugin\SqlCounterPlugin is wired (in etc/frontend/di.xml, frontend area only) as a before plugin on Magento\Framework\DB\LoggerInterface::logStats. When a storefront request includes the _sql_counter=<id> parameter:
- The plugin reads
var/tmp/sql_counter/<id>.pending(created bycount_sql_querieswithaction=start). - Every call to
LoggerInterface::logStatsincrements an in-memory counter. Any SQL whose text contains the literal substringsql_counteris skipped as a defensive guard against self-counting. - On plugin instance teardown (
__destruct, end of request lifecycle), the result is written tovar/tmp/sql_counter/<id>.resultand the pending file is deleted. - A subsequent
count_sql_queriescall withaction=getreturns the result and cleans up.
The _sql_counter parameter doubles as a cache-busting query string, so the counted response is guaranteed not to be served from FPC.
API Reference
All tools are invoked through the parent Freento_Mcp JSON-RPC endpoint (/freento_mcp/index/index). The audit module only adds tools — the request format, authentication, and transport are documented in the parent module README.
List audit tools
tools/list returns every registered tool; audit tools are the ones whose names appear in the table above.
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Call an audit tool
get_table_sizes — top 10 largest tables
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":2,"method":"tools/call", "params":{"name":"get_table_sizes","arguments":{"limit":10}} }'
get_log_errors — grouped errors for a date range
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":3,"method":"tools/call", "params":{"name":"get_log_errors","arguments":{ "from_date":"2026-04-01","to_date":"2026-04-30", "sort_by":"count","sort_dir":"desc","limit":20 }} }'
get_config_data — read a subtree across all scopes
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":4,"method":"tools/call", "params":{"name":"get_config_data","arguments":{ "paths":["dev/css","dev/js"],"all_scopes":true }} }'
get_deployment_config — values from app/etc/env.php
Sensitive segments (password, host, dbname, …) are silently omitted.
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":5,"method":"tools/call", "params":{"name":"get_deployment_config","arguments":{ "paths":["session/save","cache"] }} }'
get_php_ini_settings
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":6,"method":"tools/call", "params":{"name":"get_php_ini_settings","arguments":{ "keys":["memory_limit","max_execution_time", "opcache.memory_consumption","opcache.validate_timestamps"] }} }'
count_sql_queries — two-step flow
# 1. Start: get a counter ID and a visit URL curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":7,"method":"tools/call", "params":{"name":"count_sql_queries","arguments":{ "action":"start","url":"https://your-store.com/" }} }' # 2. Visit the returned URL (browser or curl). The _sql_counter param bypasses FPC. curl -s "https://your-store.com/?_sql_counter=<counter_id>" >/dev/null # 3. Get the result curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":8,"method":"tools/call", "params":{"name":"count_sql_queries","arguments":{ "action":"get","counter_id":"<counter_id>" }} }'
run_code_sniffer
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":9,"method":"tools/call", "params":{"name":"run_code_sniffer","arguments":{ "path":"Freento_McpAudit","severity":"errors","limit":50 }} }'
send_test_email
curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":10,"method":"tools/call", "params":{"name":"send_test_email","arguments":{ "email":"qa@example.com","store_id":1 }} }'
Entity-list tools (get_cron_schedule, get_customer_groups, get_eav_attributes)
These extend the parent module's entity-tool framework, so they accept the same filters / function / field / group_by / limit / sort_by / sort_dir parameters as the rest of Freento_Mcp (see parent README — Filters / Aggregation).
# Failed cron jobs in the last 7 days, grouped by job_code curl -X POST https://your-store.com/freento_mcp/index/index \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token>" \ -d '{ "jsonrpc":"2.0","id":11,"method":"tools/call", "params":{"name":"get_cron_schedule","arguments":{ "filters":{ "status":{"in":["error","missed"]}, "scheduled_at":{"gte":"2026-04-29"} }, "function":"count","group_by":"job_code","limit":20 }} }'
License
MIT