Abilities for AI agents
Let an AI agent list, add and remove icons through WordPress abilities: what each ability takes and returns, how it is gated, and how to call it.
WordPress 6.9 added the Abilities API: a plugin describes what it can do, with an input and output schema, and AI tools can discover and run those abilities through REST or an MCP adapter. Easy SVG Pro registers four of them. An agent can read every icon on the site, add a new one to the icon library, and add or remove icons in an icon set. Every icon it writes goes through the same sanitizer an upload meets.
The four abilities
| Ability | What it does | Writes? | REST method |
|---|---|---|---|
easy-svg-pro/list-icons | Lists icons from the library, collections and sets, paged | No | GET |
easy-svg-pro/add-icon | Adds one icon to the free icon library | Yes, never overwrites | POST |
easy-svg-pro/add-icons-to-set | Adds icons to an existing icon set | Yes, never overwrites | POST |
easy-svg-pro/remove-icon-from-set | Removes icons from a set by name | Yes, no undo | DELETE |
All four sit in the category easy-svg-pro, labelled "Easy SVG".
When they exist
An agent sees the abilities only when all of this is true:
WordPress has the Abilities API
WordPress 6.9 or newer. On older versions nothing is registered and nothing breaks.
The licence is active and the feature is on
Turn on AI agent abilities under Icons → Features, in the group For developers. It has its own switch on purpose: an agent that can write icons should be a decision, not a side effect.
The set abilities also need the set editor
add-icons-to-set and remove-icon-from-set refuse with easy_svg_pro_no_editor (HTTP 409) unless Icon set editor is on as well.
The user may manage options
Every ability's permission check requires manage_options. An agent with an application password acts as that WordPress user, with that user's capabilities.
Which icons list-icons reports also depends on features: the free library only while its icons feature is on in the free plugin, collections from themes, folders and code only while Multiple collections is on, and sets only while Icon set editor is on.
Calling them over REST
WordPress serves abilities under /wp-json/wp-abilities/v1/. Authenticate with an application password of an administrator. WordPress only offers application passwords over HTTPS, or on a site whose environment type is local.
export SITE="https://example.com"
export AUTH="agent-bot:abcd efgh ijkl mnop qrst uvwx"
# What can this site do?
curl -s -u "$AUTH" "$SITE/wp-json/wp-abilities/v1/abilities?category=easy-svg-pro"
The run endpoint is /wp-json/wp-abilities/v1/abilities/<name>/run. WordPress picks the HTTP method from each ability's annotations: read only abilities run with GET, destructive and idempotent ones with DELETE, everything else with POST. For GET and DELETE the input goes in the input query parameter; for POST it goes in the JSON body under input.
List icons
curl -s -u "$AUTH" -G "$SITE/wp-json/wp-abilities/v1/abilities/easy-svg-pro/list-icons/run" \
--data-urlencode "input[search]=arrow" \
--data-urlencode "input[per_page]=50"
{
"items": [
{ "slug": "arrow-right", "label": "Arrow Right", "collection": "acme", "source": "set" }
],
"total": 1,
"page": 1,
"per_page": 50,
"has_more": false,
"truncated": false
}
Input fields:
| Field | Type | Default | Meaning |
|---|---|---|---|
source | all, library, collection, set | all | Which kind of source to read |
collection | string | none | Only this collection or set slug |
search | string | none | Case insensitive match on slug and label |
include_markup | boolean | false | Add each icon's sanitized SVG markup |
page | integer, 1 or more | 1 | Page number |
per_page | integer, 1 to 500 | 100 | Icons per page |
Add an icon to the library
curl -s -u "$AUTH" -X POST "$SITE/wp-json/wp-abilities/v1/abilities/easy-svg-pro/add-icon/run" \
-H "Content-Type: application/json" \
-d '{"input":{"label":"Arrow","markup":"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 12h14"/></svg>"}}'
{ "added": true, "slug": "arrow-2", "label": "Arrow", "collection": "easy-svg", "source": "library" }
A name that is taken gets a number. The slug in the answer is the one WordPress really stored, read back after the write. Use it, never one you derived from the label yourself.
Add icons to a set
icons is an object keyed by the name each icon should get. Each value carries label and markup.
curl -s -u "$AUTH" -X POST "$SITE/wp-json/wp-abilities/v1/abilities/easy-svg-pro/add-icons-to-set/run" \
-H "Content-Type: application/json" \
-d '{"input":{"set_id":42,"icons":{"cart":{"label":"Cart","markup":"<svg ...>...</svg>"}}}}'
{ "set_id": 42, "slug": "acme", "report": [ { "slug": "cart", "state": "added" } ] }
A name the set already holds is reported as exists and left alone; the ability has no way to overwrite. slug is the collection slug templates use, for example acme/cart.
Remove icons from a set
curl -s -u "$AUTH" -X DELETE -G "$SITE/wp-json/wp-abilities/v1/abilities/easy-svg-pro/remove-icon-from-set/run" \
--data-urlencode "input[set_id]=42" \
--data-urlencode "input[slugs][]=cart"
{ "set_id": 42, "slug": "acme", "report": [ { "slug": "cart", "state": "removed" } ] }
A name the set does not hold comes back as not_found; the others are still removed.
Calling them from PHP
Abilities are plain WordPress objects, so your own code can run them too, for example from a queue worker that processes requests an agent left in a table.
/**
* Add one icon to the library through the ability, with every check it applies.
*/
function acme_agent_add_icon( $label, $markup ) {
if ( ! function_exists( 'wp_get_ability' ) ) {
return new WP_Error( 'acme_no_abilities', 'This WordPress has no Abilities API.' );
}
$ability = wp_get_ability( 'easy-svg-pro/add-icon' );
if ( null === $ability ) {
return new WP_Error( 'acme_no_ability', 'Turn on AI agent abilities under Icons, Features.' );
}
$result = $ability->execute(
array(
'label' => (string) $label,
'markup' => (string) $markup,
)
);
if ( is_wp_error( $result ) ) {
error_log( sprintf( 'Icon refused: %s (%s)', $result->get_error_message(), $result->get_error_code() ) );
}
return $result;
}
execute() validates the input against the schema and runs the permission check, so it needs a current user with manage_options. If this code runs outside a REST request and writes sets, also add easy_svg_pro_set_write_context so it takes the set lock.
Errors an agent should understand
Every refusal is a WP_Error with a stable code, so a client can branch on it.
| Code | HTTP | Meaning |
|---|---|---|
easy_svg_pro_missing_label, easy_svg_pro_missing_markup | 400 | Required input missing |
easy_svg_pro_missing_set_id, easy_svg_pro_missing_icons, easy_svg_pro_missing_slugs | 400 | Required set input missing |
easy_svg_pro_bad_name | 422 | The label cannot become an icon name |
easy_svg_pro_not_svg | 422 | The sanitizer did not accept it as SVG |
easy_svg_pro_empty | 422 | Nothing drawable was left |
easy_svg_pro_too_large | 422 | Larger than the site's size limit; the message and max_mb name it |
easy_svg_pro_too_complex | 422 | Too many parts to read safely |
easy_svg_pro_no_store | 409 | Icon library off, or the free plugin too old |
easy_svg_pro_no_editor | 409 | Icon set editor off |
easy_svg_pro_no_sanitizer, easy_svg_pro_not_saved | 500 | Server side problem, nothing stored |
Pitfalls
list-iconsdoes not return set IDs. The set abilities needset_id, butlist-iconsreports sets by slug only. Look the ID up yourself, for example withGET /wp-json/easy-svg-pro/v1/sets/list(internal route) or in the Icon Sets tab, and give it to the agent.- Set slugs in
list-iconsare post slugs. On sites that ran older versions a set's registered collection may beacme-xwhilelist-iconssaysacme. The set abilities answer the registered slug. - The same icon twice. With Multiple collections and Icon set editor both on, a set's icons appear once with
source: collectionand once withsource: set. Filter bysourcewhen you count. - An unknown
sourcereturns nothing. A typo such assetsgives an empty list, not everything. That is deliberate. - Refused icons are not in the report. In
add-icons-to-set, an icon the sanitizer refuses is dropped before the merge and simply does not appear inreport. Compare the names you sent with the names you got back. - An unknown set does not fail.
add-icons-to-setandremove-icon-from-setwith aset_idthat is not an icon set answer with an emptyslugand an emptyreportinstead of an error. Treat an emptyslugas failure. - No size cap per call. The REST route behind the set abilities takes at most 25 icons; the abilities do not apply that cap. Send reasonable batches anyway.
- MCP needs an adapter. The abilities carry
mcpmetadata, but Easy SVG Pro does not ship an MCP server. Check the key names against the adapter your site runs.
Give agents their own WordPress user and their own application password, and revoke it when the project ends. manage_options is a strong capability: the same password can change every setting on the site, not only icons.