Recent Activities
This page shows what are we working on.
-
1.0.13
-
Version 1.0.13 43bc3d
-
Merge pull request #44 from breezefront/chore/mcp-graphql-review-followups
chore(mcp): document graphql guard params, look the tool up by name 4aade7 -
chore(mcp): document graphql guard params, look the tool up by name
Follow-ups from the review of #43.
`assertReadOnly()`, `getDefinitionKeywords()` and `skipStringValue()` were
missing `@param` annotations, which `phpcs --standard=Magento2` reports on
`Model/McpServer.php` (18 warnings before, 15 after — the rest are pre-existing).
`testGraphqlToolIsDocumentedAsReadOnly` picked the graphql tool out of
`tools/list` by its position in the array. The position shifts as soon as another
skill is registered, so the test would start asserting against the wrong tool
rather than failing for a reason anyone could read. It now looks the tool up by
name and asserts the entry exists.
No behaviour change. ./bin/test unit: OK (339 tests, 680 assertions).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> e3bc09 -
Merge pull request #43 from breezefront/fix/mcp-graphql-read-only
fix(mcp): reject mutations in the read-only graphql tool 84c5b5 -
fix(mcp): reject mutations in the read-only graphql tool
The `graphql` MCP tool is documented as "read-only catalog access" in its own
tool description, in the README and on breezefront.com, but `McpServer::graphql()`
forwarded the `query` argument to the storefront GraphQL endpoint verbatim. No
auth header is sent, so the document executes in guest scope — and guest scope is
not read-only: Magento accepts `createEmptyCart`, `addProductsToCart`,
`setGuestEmail` and the rest of the guest cart surface there. An agent connected
over MCP could therefore write to the store through a tool advertised as read-only.
`assertReadOnly()` now refuses any top-level definition other than `query` and
`fragment` before the request goes out.
The operation type is read from the definition position of the document rather
than by searching for the word "mutation", which would be both bypassable and
wrong. `getDefinitionKeywords()` walks the document tracking brace and paren
depth, skips comments and string literals (including block strings), and records
the keyword that opens each top-level definition — treating the anonymous
shorthand `{ ... }` as `query`. So a leading newline, a `# comment`, an operation
name, variable definitions with object default values or a directive cannot
smuggle a keyword past it, and `mutation` used as a field name, an alias or
inside a string argument is not mistaken for an operation.
What the check covers: every definition in the document, so a mutation appended
after a legitimate query is rejected regardless of `operationName`; mutations and
subscriptions; type-system definitions (an allowlist, so unknown keywords are
refused rather than passed through).
What it does not cover: field-level read restrictions — any data guest scope can
already read stays readable, which is what "read-only" claims and no more. It is
not a GraphQL validator either; a malformed document (an unterminated string, say)
may still be forwarded, and the endpoint rejects it before executing anything.
`/llms.txt` is a separate case. Its "read-only" claim describes the store's own
storefront `/graphql`, which Magento serves and this module cannot restrict, so
the wording is corrected there instead of enforced: the endpoint is unauthenticated
and does accept guest mutations, and the file documents read queries only.
Also corrects the assumption in docs/plan/mcp-server.md that seeded this —
"Catalog GraphQL is public/read-only, so no auth risk" conflated public with
read-only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> a63cf6
-
-
1.0.12
-
Version 1.0.12 e71d2a
-
Merge pull request #42 from breezefront/feat/ai-landing-page-builder
feat: build a landing page from a text brief (build_page skill) ee8d3c -
fix(content-builder): report every repair and honour the config cache
Review of #42 found four gaps between what the skill promises and what it
does.
Silent repairs. The validator clamped out-of-range numbers and padded short
children arrays without saying so, while the skill states that every repair
lands in "warnings" — a merchant saw a page that did not match the brief with
no way to tell why. coerceNumber() now reports both the clamp and the
fallback-to-default, and normalizeChildren() reports a short slot list. An
omitted "children" stays silent: that is a deliberately empty container, not a
mis-count.
Grid rule keyed off a component name. rejectNestedContainers() compared
$definition['name'] against the literal 'grid', so a rename in the Content
Builder, or a second grid-like container, would drop the rule without a sound.
Catalog::allowsOnlyLeafChildren() now derives it from the "slots" metadata of
the layout preset, which is what actually makes those slots CSS grid areas.
Catalog cache outlived the config cache type. Catalog took
Framework\App\CacheInterface, so it kept a 24-hour copy even with the config
cache disabled, while the Content Builder's own registry re-read the XML.
Editing bcb_components.xml left the two disagreeing until a full cache:flush.
Switched to Framework\Config\CacheInterface, matching ComponentRegistry.
JSON extraction cut on the wrong brace. The old slice ran from the earliest
'[' or '{' to the latest ']' or '}', so prose carrying a stray brace ("the
layout (a {row} first): [...]") produced a broken span. jsonCandidates() now
offers the fenced block, the bare reply and the widest run of each bracket
kind separately, and decode() keeps the first that parses.
Also covers Console\Command\BuildPage, which had no test: option handling,
the stdout/stderr split that keeps `> page.json` valid, and the failure exit
code.
Suite: 305 tests, 578 assertions. phpcs --standard=Magento2: 0 errors.
Verified end to end against the real 14-component registry — the repaired
document renders through Swissup\BreezeContentBuilder\Model\ContentRenderer
and now emits 14 warnings where it previously emitted 8.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> c725c4 -
feat(content-builder): build a landing page from a text brief
Adds a build_page skill that composes a Breeze Content Builder document
out of the components a store actually has installed, so a merchant can
describe a landing page instead of assembling it section by section.
The skill returns {"components": [...], "warnings": [...]} for a CMS page
and saves nothing — the caller decides whether to store the draft through
the Content Builder's own save endpoint.
No dependency on Swissup_BreezeContentBuilder: CatalogReader collects
bcb_components.xml through Module\Dir\Reader, which is a file name rather
than a class, so the AI module still installs on its own and the skill
simply reports that no components are available.
Model output cannot be trusted as-is. ComponentValidator checks it against
the same catalog the prompt was built from — dropping unknown component
types and props, snapping select values back into their option lists,
clamping numbers to their declared range, and resizing the children arrays
of containers to the slot count the type requires. Every repair is
reported so the merchant can see why the result differs from the brief.
Slot arithmetic lives in Catalog::resolveSlotCount and mirrors the admin
editor's component-factory.js, keeping one source of truth for tabs, grid
presets, columns and rows.
Also exposed over MCP (brief instead of attribute) and through
bin/magento breezeai:page:build, which prints the document on stdout and
the warnings on stderr. --show-catalog dumps the catalog exactly as the
model receives it.
Vision input and URL screenshots are deliberately out of scope here: this
lands the part that had to work first, which is a model emitting a
schema-valid document.
Refs #23
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 1bf01f
-
-
3.2.0
-
3.1.1
-
1.1.1
-
1.1.0
-
1.0.10
-
1.0.9
-
Version 1.0.9 033a62
-
Added AI-readable descriptions to component definitions
Each component now carries a <description> element instead of an XML
comment above its declaration, so the text is available to code and not
just to whoever opens the file. ComponentXmlReader exposes it as the
"description" key of every definition.
The wording is aimed at a consumer that has to pick a component without
seeing the storefront: what the component is for, which component to
prefer when two of them overlap, how many children slots a container
takes, and which props must be left empty because their values are
entity IDs or widget directives that cannot be guessed.
First consumer is the AI landing page builder, which turns the registry
into the component catalog it sends to the model.
Refs breezefront/module-breeze-ai#23
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> bad797 -
Added AI-readable descriptions to component definitions
Each component now carries a <description> element instead of an XML
comment above its declaration, so the text is available to code and not
just to whoever opens the file. ComponentXmlReader exposes it as the
"description" key of every definition.
The wording is aimed at a consumer that has to pick a component without
seeing the storefront: what the component is for, which component to
prefer when two of them overlap, how many children slots a container
takes, and which props must be left empty because their values are
entity IDs or widget directives that cannot be guessed.
First consumer is the AI landing page builder, which turns the registry
into the component catalog it sends to the model.
Refs breezefront/module-breeze-ai#23
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 9d3287
-
-
3.2.0
-
Version 3.2.0 4c4818
-
Per page limits according to design e54d9e
-
Fixed empty search slideout in Safari browser after closing keyboard cb1f77
-
Show testimonials block under category decsription, see #14 b30944
-
Theme Editor: Allow using gradients as bg color 3cb9ff
-
Improve recent products widget styles in sidebar 3d94fb
-
Revert "Move testimonials below category description"
This reverts commit 046f5bb650b031b250b05a0009b5ec2edc27a619. 66ab0d -
Move testimonials below category description 046f5b
-
Use `apollo-` prefix for custom quote icon 7f3938
-
Prevent zoom om mobile devices when focusing quantity field 0de256
-
Limit upsell and related products to 6 items on the product page 94dd46
-
Added BLB installation: Testimonials widget on category pages 60339b
-
Testimonials and BCB slider styles bb8fa5
-
Fixed qty overlap with actions in minicart on small screens, see #16 96cd74
-
Enabled mobile slider layout for columns and grid 77cb58
-
Created separate mobile slider 8880b4
-
Sync products widget styles with product list. Closes #13 241874
-
-
3.1.2
-
1.0.2
-
1.4.9
-
3.2.0
-
3.1.2