Skip to content

Releases: puneetindersingh/bulk-ai-cli-connector

2.4.0: easier setup

Choose a tag to compare

@puneetindersingh puneetindersingh released this 27 Oct 00:33

Easier setup for site owners.

  • The key screen moved from Tools > Agent API Keys to Settings > AI CLI Connector. The old address forwards to it.
  • A Settings link next to the plugin on the Plugins screen.
  • After activation, a dismissible Set up now notice on the Dashboard and Plugins screen. It goes away once a key is made, and an update never shows it.
  • A new key is shown inside a ready-to-paste message for your AI tool (site, username, key, REST address, header, first request), with one Copy button. The key is cleared from the page only after a confirmed copy.
  • Security: three independent review rounds; the message the AI tool reads is fixed English so no translation can change it, and no translated format string can crash the key screen.

Existing keys keep working.

2.3.2: live panel commerce checks only on WooCommerce products

Choose a tag to compare

@puneetindersingh puneetindersingh released this 26 Oct 04:11
  • Live SEO panel: no price, offer or product checks on pages that are not WooCommerce products, so a service page is no longer told to add a price nobody set.
  • Live SEO panel: fewer busywork warnings. A short title or description and a page without structured data are notes now; the Social row warns only without a share image; nested organizers and sponsors are not checked as the business.
  • Live SEO panel: the canonical check ignores tracking parameters (utm_*, gclid, fbclid and similar) and parameter order.
  • GET /status reports merchant_enrichment.enabled false when WooCommerce is not active.

The live SEO panel no longer asks for prices or product markup on pages that are not WooCommerce products, and shows fewer busywork warnings.

2.3.1: /update with a last check stamped in the future

Choose a tag to compare

@puneetindersingh puneetindersingh released this 23 Oct 06:01
  • Fix: POST /update with check answered "up to date" without asking WordPress.org when the stored time of the last update check was in the future. The "checked less than a minute ago" shortcut took any future time as recent, so the check was skipped until the server's clock caught up (seen on a live site, about an hour ahead). Only a last check between 0 and 60 seconds ago counts now; a future one is treated as stale and the check runs. If WordPress.org does not answer, what WordPress knew is put back and the route answers 503, as before; a last check stamped in the future is put back as 0, so WordPress's own scheduled checks are not held back either.
  • GET /update also reports server_time and checked_in_future, so a stored check time ahead of the server's clock is visible.

Fixes POST /update reporting "up to date" when the last update check was stored with a time in the future.

2.3.0: live SEO panel and SEO change history

Choose a tag to compare

@puneetindersingh puneetindersingh released this 23 Oct 05:35
  • New: live SEO panel. Signed-in users who may edit the page in view get an "SEO" item in the admin bar with a count of problems. It opens a panel over the page with four tabs. Overview: the search result and every tag read from the rendered page (title, description, robots including an X-Robots-Tag header, canonical, H1, social tags, images, links), each judged. Edit: SEO title, description and focus keyword with length checks, saved into Yoast SEO or Rank Math. Schema: every JSON-LD block with the plugin that printed it, missing required and recommended properties, duplicate Organization or WebSite nodes, broken JSON, links to an @id that is not on the page; custom schema can be added or set to replace the page's schema. History: the SEO changes on this post.
  • New: change history. Every change to a post's SEO title, description, keyword, canonical, robots, social fields or custom schema is kept on the post (the last 30), with the time, the user and where it came from: the editor, an API key, the REST API, WP-CLI or the live panel.
  • New: "Live on the site" in the block editor's document sidebar and as a side box in the classic editor: what the published page sends, read again after every save, with the last changes.
  • New route POST /live, for the panel only. It needs a signed-in browser session with a REST nonce (keys and application passwords use /bulk), the right to edit that post or term, and each field passes the same checks as in /bulk, including the SEO plugin's own role rules for its settings groups.

Adds a live SEO panel on the site and in the editors, and a history of SEO changes per post. Nothing changes in what your pages print.

2.2.0: Elementor choice settings and inserts by copy

Choose a tag to compare

@puneetindersingh puneetindersingh released this 23 Oct 04:39
  • New: Elementor choice settings in /content. Dropdown, choose and switch controls on a widget's Content tab, including the fields of repeater rows (for example a Post Info row's type, taxonomy and link), are listed with kind: choice and their options. A write is accepted only when the value is one of those options. Heading levels stay kind: tag. Needs Elementor running, because the options come from its registered controls.
  • New: add an Elementor element by copying an existing one. POST /content {"inserts": [{"ref": "<element id>", "position": "after", "copy_from": {"post_id": 123, "el": "<element id>"}}]} clones an element from the same page or another Elementor page you can edit, with new element and row ids and without its CSS ID, before, after or inside ref. Elements only go where Elementor accepts them (a section placed in a column becomes an inner section). Up to 20 per request, applied before updates[], with the usual dry run, base_hash, backup and revert. New atomic=1 writes nothing when any update or insert in the request fails.
  • Security: the copy source passes the same checks as editing it (edit rights, the builder gate and Elementor's own permission). Unless the site allows raw HTML (the bulkseme_allow_unfiltered_html filter) and the request sends unsafe_html=1, a copy, including an administrator's, keeps only the standard element keys (no local styles), its text is cleaned like a direct write (the result says sanitized: true when anything was cleaned or left out), and they cannot copy HTML, shortcode, template, code or WordPress widgets, any widget with a code control, Custom CSS, custom attributes, dynamic tags, or any other setting that holds markup or a script link; nor set a choice to a raw output value. Size limits per copy, per request and per page (8 MB, 24 levels) stop runaway growth.
  • /status reports supports_choice_settings and supports_inserts.

Adds Elementor dropdown and switch settings and inserting an element by copy, both checked against Elementor's own permissions and capped for accounts without unfiltered_html.

2.1.0: ACF repeater rows and ACF on terms

Choose a tag to compare

@puneetindersingh puneetindersingh released this 23 Oct 04:39
  • New: add a row to an Advanced Custom Fields repeater or flexible content field. Send an update {"op": "add_row", "el": "<field>", "position": 2, "layout": "<layout>", "values": {"<sub field>": "text"}} in POST /content. GET /content lists every repeater and flexible content field under containers, with its row count and the sub fields a new row may fill. The rows below the insert move down with every value they hold, images and nested rows included. Text is cleaned like any other write, the field's maximum row count holds, and a stored value that belongs to another field, or that names a PHP class, stops the insert before anything is written. The backup holds the whole field, so /content/revert puts the rows back exactly.
  • New: Advanced Custom Fields on taxonomy terms. With kind=term, GET /content lists the term's ACF text fields and rows under acf, and POST /content with updates[] edits them and adds rows, with the same backup and revert as on posts. The term description is left alone.
  • Fix: a write to text kept in post meta (ACF, Beaver Builder, SiteOrigin, Accordions, and Elementor when its own resync is not available) bumped the modified date by saving the post again. For a user without unfiltered_html that ran the stored title, content and excerpt through kses and could rewrite markup the write never aimed at, such as the trailing semicolon of a style attribute. Only the date changes now.
  • Security hardening: kind=term no longer reaches a taxonomy that has neither an edit screen nor a REST route (such as post formats), the same reach WordPress itself gives. Present since 2.0.0.
  • Security hardening: on a network, the fallback that reads ACF field definitions from stored meta when no field group matches is for super admins only.
  • New filter bulkseme_user_can_edit_term_acf keeps a role out of a term's ACF fields, as bulkseme_user_can_edit_with_builder does for posts.
  • /status reports supports_acf_rows and supports_term_acf.

Adds rows to ACF repeaters and flexible content, and ACF fields on terms. If you filter bulkseme_user_can_edit_with_builder to keep a role out of ACF, add the same rule to the new bulkseme_user_can_edit_term_acf.

2.0.2: security release

Choose a tag to compare

@puneetindersingh puneetindersingh released this 13 Oct 05:34

Security release. Recommended for every site.

The same build is in the WordPress.org plugin directory: https://wordpress.org/plugins/bulk-seo-meta-editor-for-ai-agents/

Install: update from the Plugins screen, or upload the attached zip under Plugins → Add New → Upload Plugin and choose "Replace current with uploaded".

2.0.2

  • Security: /import read an uploaded CSV file line by line, so one quoted cell that held line breaks was read as several rows. A post title can hold line breaks and /export wrote it as it was, so an Author could plant rows in a title, and they were applied with the rights of whoever re-imported that export: SEO fields of posts the Author may not edit, and the site-wide title settings. The file is now read as a whole, the way RFC 4180 defines it, and line breaks and other control characters are taken out of every exported cell. If you read an export in a script, use a CSV reader over the whole text, never line by line. Present in every earlier version. Found in an independent review; not known to have been used.
  • Security: stored JSON-LD schema is no longer printed on a password-protected post until the visitor has entered the password.
  • Security hardening: a key with the default scope signs in on this plugin's own routes by name, and no longer on everything under its REST namespace, so it cannot reach a route another plugin registers there.
  • Fix: /export ended with a PHP error when post_type, status or taxonomy was sent as a list (?status[]=draft). Lists are accepted now. A list sent where one value belongs is refused cleanly, and a list sent as an id is no longer read as post 1.
  • Fix: a URL field (canonical, Open Graph image, Twitter image) holding bytes that are not valid UTF-8, written through WordPress's own REST route, was answered with a 500. Such bytes no longer reach the database.
  • Hardening: WPBakery design rules rebuilt with rebuild_wpb_css go through the filter WordPress uses for inline styles. HTML written to a SiteOrigin widget or Beaver Builder module that the plugin has no field map for has the quotes in its text stored as entities.
  • /import of a file: 10 MB limit, a file that ends inside a quoted cell is refused, and rows whose cell count does not match the header are counted in skipped_rows.
  • New: GET and POST /update. See which version is installed and which one WordPress.org offers, and install the offered version without opening wp-admin. It runs WordPress's own update code, so the build can only come from WordPress.org; installing needs the right to update plugins, a site that forbids file changes is left alone, and a key with the default scope cannot use the route.
  • Security hardening: a key that is revoked here is also taken out of the copy a companion build keeps of the keys it handed over, whether or not that build is active at the time.
  • MCP server 1.7.2: list_posts lists only post types the site exposes, every tool argument that reaches a request path is checked, requests time out after 60 seconds, an answer over 20 MB is refused, and the dependency lock file is refreshed.
  • The GitHub repository moved to github.com/puneetindersingh/bulk-ai-cli-connector.

bulk-seo-meta-editor-for-ai-agents-2.0.2.zip sha256: d520acbb3b5b33fbce7acb6ed098b9dc379e5e53acdf9ed4750f44421b11576c

2.0.1: Bulk AI CLI Connector

Choose a tag to compare

@puneetindersingh puneetindersingh released this 13 Oct 02:52

Bulk AI CLI Connector (formerly Bulk SEO Meta Editor for AI Agents). Connect Claude, ChatGPT and MCP agents to WordPress: bulk edit SEO meta, JSON-LD schema, redirects and Elementor or Beaver Builder page copy over the REST API, with undo.

The same build is in the WordPress.org plugin directory: https://wordpress.org/plugins/bulk-seo-meta-editor-for-ai-agents/

Install: upload the attached zip under Plugins → Add New → Upload Plugin, or install it from the directory. The zip and the plugin folder keep the original slug, bulk-seo-meta-editor-for-ai-agents, so existing sites update in place.

2.0.1

  • Security hardening: /clone now checks a stored meta value in exactly the form WordPress reads it back. A value planted straight into the database with a NUL byte in front of a serialised object could get past the check that keeps /clone from recreating objects of other classes. Using it needed a row written outside WordPress and a caller with the unfiltered_html capability. Found in an independent review; not known to have been used.

2.0.0

  • Renamed from "Bulk SEO Meta Editor for AI Agents" to "Bulk AI CLI Connector". The plugin address, folder, REST routes and stored data are unchanged.
  • New: page content. GET /content and POST /content read and edit the visible text of a page for Elementor (classic and Elementor 4 atomic widgets, heading level, link URLs and per-element custom CSS), Beaver Builder, SiteOrigin Page Builder (core text widgets and the SiteOrigin Widgets Bundle: editor, headline, button, call to action, accordion, tabs, hero, features, testimonials and image), the Accordions plugin and Advanced Custom Fields text fields, and the post content of Gutenberg, classic, Divi and WPBakery pages. POST /content/replace runs a literal find and replace. Add kind=term&taxonomy=<slug> for a term description, which keeps headings, lists and tables.
  • New: a backup before every content write, with GET /content/backups and POST /content/revert. Backups are kept in a hidden post type that nobody can read or edit outside these two routes, the newest ten per item, and a backup can only be restored onto the item it came from.
  • New: dry_run and base_hash on content writes.
  • New: POST /clone copies a page or post into a new draft with its template, design meta, featured image and terms. SEO fields are not copied.
  • New: GET, POST and DELETE /redirect. Rules go through Rank Math's Redirections when that module is on, otherwise the plugin stores and serves them.
  • New: GET, POST and DELETE /merchant-policy. Adds return policy, shipping details, brand and a price start date to WooCommerce Product schema that the SEO plugin left without them. Opt out with add_filter('bulkseme_merchant_enrichment', '__return_false').
  • New: API keys. Made on Tools > Agent API Keys, shown once, stored as SHA-256 hashes, REST only, HTTPS only. Send a key in the X-Bulkseme-Key header or as the password of HTTP Basic auth. A key is accepted on this plugin's own routes only, unless it is made with the wider scope, which covers every REST route. A key is tied to the account that made it and is removed when that account is deleted. POST /register-key stores the hash of a key from an administrator's own browser session.
  • Security: content routes check edit_post or edit_term per item and follow the page builder's own access rules (Elementor's excluded roles, Beaver Builder's Builder Access). Every value is cleaned with wp_kses_post, administrators included. Storing unfiltered HTML needs the bulkseme_allow_unfiltered_html filter, the unfiltered_html capability and unsafe_html: 1 in the request.
  • Security: redirects and merchant policy need manage_options. Redirects written through Rank Math also need its rank_math_redirections capability.
  • Security: posts of a type that has no edit screen and no REST route of its own are now out of reach of every route, including /bulk, /export and /schema. Before this, a user could rename such a record through post_title in /bulk if WordPress counted them as its author, and that included a privacy request they had filed themselves, whose title is the address the request acts on. Recommended update for every site.
  • Security: stored JSON-LD is limited to 128 KB and 5,000 values per post or term (schema_too_large), because it is read on every view of the page. Filters: bulkseme_schema_max_bytes, bulkseme_schema_max_values.
  • Security: on a site with neither Yoast SEO nor Rank Math, storing schema now needs edit_others_posts (Editor and above), in line with Yoast SEO's own default for schema settings. Filter: bulkseme_user_can_edit_field_group.
  • Fix: titles and descriptions that use replacement variables are stored intact. WordPress's general text cleaner removed anything that looked like a percent-encoded byte, so %%category%% was stored as %tegory%% and Rank Math's %date% as te%. The active SEO plugin's own text cleaner is now used. Post titles keep their percent signs too.
  • Fix: a merchant policy number that is not finite (for example 1e400) is refused. It could not be printed as JSON and emptied the product schema block.
  • /status reports the new capabilities: supports_content, supports_redirects, supports_api_key, supports_clone, builders and more. Page builder version numbers, the redirect engine and the merchant policy summary are only shown to administrators. Filter: bulkseme_status.
  • MCP server 1.7.1: post ids and post type names are checked before they are used in a request path.
  • The plugin is now split into files under includes/. No setting, stored value or existing route changed name.
  • Deleting the plugin now removes its API key hashes, on every site of a network.

bulk-seo-meta-editor-for-ai-agents-2.0.1.zip sha256: ad09d138b7994af1689edb94ea663eb350bceadb88cbcb771506c2004fef889c

v1.2.1 — WordPress.org submission build

Choose a tag to compare

@puneetindersingh puneetindersingh released this 27 Apr 01:46

First WordPress.org Plugin Directory submission build (submitted 2026-04-27, awaiting human review).

What's in this release

  • WordPress plugin: `bulk-seo-meta-editor-for-ai-agents.zip` — drop-in WP plugin, activate after Yoast SEO or Rank Math
  • Bundled MCP server source in `mcp-server/` for Claude Code / Claude Desktop integration

Plugin features

  • REST API surface for Yoast SEO and Rank Math meta fields
  • Auto-detects active SEO plugin
  • Bulk update up to 100 posts in one call
  • CSV import/export round-trip
  • Per-post permission checks (Contributors limited to their own posts)
  • Allowlist enforcement (only SEO meta keys accepted)

Endpoints

`/wp-json/seo-meta-bridge/v1/status` — detect active plugin + list field aliases
`/wp-json/seo-meta-bridge/v1/bulk` — bulk update (max 100 items)
`/wp-json/seo-meta-bridge/v1/export` — CSV export
`/wp-json/seo-meta-bridge/v1/import` — CSV/JSON import
`/wp-json/wp/v2/posts/{id}` — standard route also works (meta keys registered)

Compliance

  • WP Plugin Check static analysis: PASSED clean
  • Tested: WordPress 6.6 and 6.9, PHP 7.4 / 8.1 / 8.3
  • License: GPL-2.0-or-later

v1.2.1 changes from initial work

  • WP_Filesystem replaces fopen on CSV imports
  • Manual CSV builder replaces fputcsv on exports
  • Tested-up-to bumped to 6.9
  • Short description trimmed to <=150 chars