phpClaw for Magento
The Magento adapter is the PhpClaw_Magento module. It adds admin pages under phpClaw, two
bin/magento commands, two REST routes, and 15 tools.
Install
Section titled “Install”| Requirement | Version |
|---|---|
| PHP | 8.1 or later |
| Magento | 2.4 |
composer require phpclaw/phpclaw-magentobin/magento module:enable PhpClaw_Magentobin/magento setup:upgradebin/magento setup:di:compilebin/magento cache:flushsetup:upgrade creates phpclaw_conversations, phpclaw_messages and phpclaw_memory, and grants
the phpClaw ACL resources to every role that already has full admin access.
Set a provider and key on phpClaw → Settings. bin/magento config:set refuses phpclaw/...
paths (“path doesn’t exist”) because the module declares no system.xml. Then run:
bin/magento phpclaw:run "check order queue status"
To update the module later:
composer update phpclaw/phpclaw-magento --with-dependenciesbin/magento setup:upgradebin/magento setup:di:compilebin/magento cache:flushSettings
Section titled “Settings”Settings are stored in core_config_data:
| Path | Default | Notes |
|---|---|---|
phpclaw/general/provider | empty | the provider to use |
phpclaw/general/model | empty | the PHPCLAW_MODEL environment variable, then the provider’s default model |
phpclaw/general/api_key | empty | stored encrypted when saved from Settings, as are cloud_key, cloud_signing_secret and fallback_api_key |
phpclaw/general/base_url | empty | for the Custom provider; must be https:// and not an internal address |
phpclaw/general/system_prompt | empty | added to every request |
phpclaw/general/store_messages | 1 | |
phpclaw/general/max_iterations | empty, meaning 20 | |
phpclaw/general/remote_skill_urls | empty | HTTPS URLs of skill files |
phpclaw/general/cloud_key, cloud_signing_secret, cloud_disable | empty | configure phpClaw Cloud; the key is used only while store_messages is on |
phpclaw/general/fallback_provider | empty, meaning off | Settings lists only providers with the same tool format as the provider, never Custom |
phpclaw/general/fallback_model | empty | the fallback provider’s default model |
phpclaw/general/fallback_api_key | empty | hidden in Settings when the fallback provider is off or Ollama |
phpclaw/general/rate_limit_rpm | 0, meaning off | 0 to 600 |
phpclaw/general/response_cache | 0 | 1 turns the response cache on |
phpclaw/general/response_cache_ttl | 3600 | 60 to 86400; shown in Settings when the cache is on |
phpclaw/general/max_token_budget | 0, meaning off | 0 to 10,000,000 |
phpclaw/general/max_tools_per_turn | 0, the automatic per-turn limit | -1 sends every tool; below -1 is stored as -1 |
phpclaw/general/always_include_tools | empty | comma-separated tool names offered on every turn, outside the limit; a name that is not a tool is skipped, and a denied tool stays denied |
phpclaw/security/shell_allowlist | empty | comma separated; empty uses core’s default allowlist: ls, pwd, df, wc, date, uptime, hostname, whoami |
phpclaw/security/tool_deny | empty | comma-separated tool names or groups |
With store_messages off, the agent stores no message content. See Memory.
The seven fallback_*, rate_limit_rpm, response_cache* and max_token_budget paths switch on the
building blocks. Values are clamped to their ranges when read. The rate limit and
the response cache are kept in Magento’s application cache, tagged so a clean of phpClaw’s entries
leaves the rest of the cache alone.
max_tools_per_turn and always_include_tools are fields on the Settings page and apply to the CLI
commands, the Chat page and the REST API; the MCP server’s tool list does not use them.
If provider is empty, phpClaw core detects one from the server’s environment variables, such as
PHPCLAW_PROVIDER or OPENAI_API_KEY, and falls back to anthropic, even when api_key is set. Set
the provider explicitly.
ACL resources
Section titled “ACL resources”etc/acl.xml declares a PhpClaw_Magento::phpclaw parent with one resource per page:
| Resource | Allows |
|---|---|
PhpClaw_Magento::phpclaw_settings | the Settings page and Test Connection, and seeing every admin’s conversations |
PhpClaw_Magento::phpclaw_chat | the Chat page, both REST routes, and every Magento tool |
PhpClaw_Magento::phpclaw_analytics | the Analytics page |
PhpClaw_Magento::phpclaw_guide | the Guide page |
PhpClaw_Magento::phpclaw_about | the About page |
Grant them to a role under System → Permissions → User Roles → Role Resources.
A Magento tool call without the resource returns a FORBIDDEN error. The five core tools,
shell_exec, http_request, file_read, code_search and project_info, check no Magento
resource and run for any caller that reaches the agent.
What counts as the console
Section titled “What counts as the console”The adapter treats a run as the console only when PHP runs from the command line and the area is
none of adminhtml, webapi_rest, webapi_soap, graphql, frontend and crontab. That is
bin/magento. In the console the tool check is skipped and conversations store no owner. Cron is not
the console: its Magento tools run the ACL check, and with no admin user they refuse; the core tools
run without a check.
All 15 are registered on every run. Agent runs (admin chat, REST, phpclaw:run) also get core’s
load_skill, because core’s discovered skills, at least php_best_practices, are always registered; phpclaw:mcp-server serves the 15 only:
| Group | Tools |
|---|---|
| Magento | magento_products, magento_inventory, magento_categories, magento_stores, magento_report, magento_cache, read_log, magento_orders, magento_customer, db_query |
| Core | shell_exec, http_request, file_read, code_search, project_info |
| Tool | Does |
|---|---|
magento_products | fetches a product by SKU or searches products |
magento_inventory | shows stock for a SKU, or lists products at or below a stock threshold |
magento_categories | walks the category tree: top level, children of a category, or products in one |
magento_stores | lists websites, store groups and store views as a hierarchy |
magento_report | sales report for a date range: revenue, orders, average order value or top products |
magento_cache | shows each cache type’s state; it never flushes |
read_log | reads the last lines of var/log/system.log, or var/log/exception.log when system.log does not exist |
magento_orders | fetches an order by increment id, or lists orders by status and date |
magento_customer | looks up a customer by email or id, or counts customers |
db_query | runs one read-only SELECT |
See Tools for the core tools. The adapter builds shell_exec (with
phpclaw/security/shell_allowlist) and http_request itself, since core marks both opt-in. The
allowlist matches the first word of the command exactly, case included. file_write and file_edit are not registered: core marks them opt-in, and the
adapter asks core only for its default tools. A module can add them through the
phpclaw_extra_tools event, as a tool object it builds or as a class name, which is built with no
constructor arguments.
db_query runs one read-only SELECT, adds LIMIT 500 when the query has no limit, and refuses the
core_config_data, phpclaw_conversations and phpclaw_messages tables. Modules keep credentials
such as API keys in core_config_data, and no phpClaw tool reads that table.
Denying tools
Section titled “Denying tools”| Group | Tools |
|---|---|
group:commerce | magento_products, magento_orders, magento_customer, magento_inventory |
group:catalog | magento_categories, magento_stores, magento_report |
group:system | magento_cache, db_query, read_log |
phpclaw/security/tool_deny has no Settings field, and bin/magento config:set refuses the path for
the same reason.
Approval
Section titled “Approval”Every agent is built with CliApprovalGate. In a terminal you are asked before a tool changes
something; in the admin and the REST API there is nobody to ask, so the change is refused. See
Security: approval. In the admin and the REST API that refusal covers any
shell_exec command on the allowlist but outside core’s read-only command list, and every call to a tool that changes
data, such as file_write or file_edit, when a module has added one. The MCP server does not use
the approval gate; its tools/list marks tools that can change data with readOnlyHint: false, destructiveHint: true.
Commands
Section titled “Commands”bin/magento phpclaw:run "check order queue status"bin/magento phpclaw:run "summarise today's orders" --streambin/magento phpclaw:run "and yesterday?" --conv-id=01JQABCDE...bin/magento phpclaw:run "and the day before?" --continue --trace
bin/magento phpclaw:chat
bin/magento phpclaw:mcp-serverphpclaw:run takes --stream, --conv-id, --continue and --trace. phpclaw:chat opens an
interactive session and takes --conv-id and --continue. phpclaw:mcp-server serves the tools over
stdio; see MCP.
One-shot options and the chat session
Section titled “One-shot options and the chat session”After each reply phpclaw:run prints the provider, model, tokens, the tools that ran and the
conversation id, then conv-id=<id>. --conv-id=<id> continues that stored conversation; an id with
no stored conversation prints a warning and starts a new one. --continue picks the last
conversation, whose id is kept in var/phpclaw/.last-conversation. That is one file per install, so
every console user on the server continues the same conversation. --trace prints one line per tool
call, the tool name plus a short path or command, never the result.
bin/magento phpclaw:chat keeps one conversation open, streams each reply and prints the tool calls
as they run. It needs a terminal: with piped input it refuses and exits 1. A line starting with /
is a command and is never sent to the agent:
| Command | What it does |
|---|---|
/help | List these commands |
/tools | List the active tools |
/model <name> | Switch the model for this session only |
/provider <slug> | Switch the provider for this session only |
/new | Start a new conversation |
/conv | Print the conversation id |
/quiet | Turn the tool trace off or on |
/clear | Clear the screen |
/exit | Leave; Ctrl+D also leaves |
While the agent works, a Thinking... spinner shows until the first word of the reply arrives. It
hides before each tool call is traced or approved and shows again while the model works after it. It
is not shown when the output is not a terminal, and without the pcntl extension a still
Thinking... line is printed instead of the animation.
On leaving it prints bin/magento phpclaw:chat --conv-id=<id> to resume. /model and /provider
never write the admin settings.
REST API
Section titled “REST API”Both routes need an admin bearer token whose user holds PhpClaw_Magento::phpclaw_chat:
curl -X POST "https://example.com/rest/V1/integration/admin/token" \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"..."}'| Method | Route | Body | Returns |
|---|---|---|---|
| POST | /rest/V1/phpclaw/send | message | text, provider, model, tokens, iterations |
| POST | /rest/V1/phpclaw/chat/stream | message, optional conversationId | Server-Sent Events |
curl -X POST "https://example.com/rest/V1/phpclaw/send" \ -H "Authorization: Bearer <admin-token>" \ -H "Content-Type: application/json" \ -d '{"message":"how many orders came in today?"}'send starts a new conversation on every call. An empty message is a 400, a guard block 422,
and any other failure 500.
A spent token budget answers 422 with “Token budget reached for this run.” and a rate limit 429 with
“Rate limit reached, try again shortly.” chat/stream sends tool_before, tool_after, chunk, then
done or error; for those two limits the error event carries the message and a code of 422 or
429. Naming another
admin’s conversation is refused before the stream starts.
Conversations and ownership
Section titled “Conversations and ownership”Each conversation stores its creator’s admin user id in phpclaw_conversations.admin_user_id: from
the admin session, or from the token for REST calls. Conversations created in a console area store
0. An admin sees and opens only their own conversations; opening another’s is refused with 403.
Admins holding PhpClaw_Magento::phpclaw_settings see every conversation.
Model/IdentityResolver reads the backend session and, for REST and SOAP calls, Magento’s user
context, which it accepts only for admin tokens. Both routes refuse any other caller, such as an
integration token, with 403 before the agent runs.
admin_user_id is written on insert and is not in the update columns, so a later save cannot change
the owner. The index is PHPCLAW_CONV_USER_NS_UPDATED in etc/db_schema.xml. Analytics caches its
counts under a key that includes the acting admin id, or all for the manage-all tier.
The agent always uses the database-backed router memory, RouterMemory: the conversations
namespace goes to ConversationMemory, everything else to ResourceMemory and phpclaw_memory.
Using the agent in code
Section titled “Using the agent in code”Inject PhpClaw\Magento\Factory\PhpClawFactoryInterface and call create(), which returns a fully
configured PhpClaw\Contracts\ClawInterface. The module declares the preference in etc/di.xml.
The module does not register PhpClaw\Claw itself, and building one by hand skips the settings,
memory and Magento tools.
use PhpClaw\Magento\Factory\PhpClawFactoryInterface;
final class OrderSummary{ public function __construct(private readonly PhpClawFactoryInterface $phpClawFactory) {}
public function summarise(): string { return $this->phpClawFactory->create()->send('Summarise today\'s orders.')->text; }}Extending
Section titled “Extending”A module registers additions by observing these events and adding entries to the transport
object:
| Event | Adds |
|---|---|
phpclaw_extra_tools | tools; a tool with the same name as a built-in replaces it |
phpclaw_extra_skills | skills |
phpclaw_extra_guards | guards |
phpclaw_extra_hooks | lifecycle listeners |
phpclaw_extra_memory | memory drivers |
phpclaw_extra_providers | providers |
Lifecycle events
Section titled “Lifecycle events”Every lifecycle event is also dispatched as a Magento event named phpclaw_ followed by the event
name with dots replaced by underscores, such as phpclaw_agent_after, with the context under data.
The event names are listed on the Hooks page.